feat;全局组件
This commit is contained in:
parent
930ce1f1f3
commit
38c19cbf06
@ -17,8 +17,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^12.4.0",
|
||||
"axios": "^1.6.5",
|
||||
"dayjs": "^1.11.11",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.7.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"js-base64": "^3.7.7",
|
||||
|
86
main/src/components/custom-echart-bar/index.vue
Normal file
86
main/src/components/custom-echart-bar/index.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ height, width }"></div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, reactive, watchEffect } from 'vue';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useEcharts } from '../../hooks/useEcharts';
|
||||
|
||||
export default {
|
||||
name: 'CustomEchartBar',
|
||||
props: {
|
||||
chartData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
option: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: 'calc(100vh - 78px)',
|
||||
},
|
||||
seriesColor: {
|
||||
type: String,
|
||||
default: '#1890ff',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const chartRef = ref(null);
|
||||
const { setOptions } = useEcharts(chartRef);
|
||||
const option = reactive({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
label: {
|
||||
show: true,
|
||||
backgroundColor: '#333',
|
||||
},
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'bar',
|
||||
type: 'bar',
|
||||
data: [],
|
||||
color: props.seriesColor,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
props.chartData && initCharts();
|
||||
});
|
||||
|
||||
function initCharts() {
|
||||
if (props.option) {
|
||||
Object.assign(option, cloneDeep(props.option));
|
||||
}
|
||||
let seriesData = props.chartData.map((item) => {
|
||||
return item.value;
|
||||
});
|
||||
let xAxisData = props.chartData.map((item) => {
|
||||
return item.name;
|
||||
});
|
||||
option.series[0].data = seriesData;
|
||||
option.series[0].color = props.seriesColor;
|
||||
option.xAxis.data = xAxisData;
|
||||
setOptions(option);
|
||||
}
|
||||
return { chartRef };
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
import SvgIcon from './svg-icon';
|
||||
|
||||
import CustomRichEditor from './custom-rich-editor';
|
||||
import CustomEchartBar from './custom-echart-bar';
|
||||
|
||||
export { SvgIcon, CustomRichEditor };
|
||||
export { SvgIcon, CustomEchartBar, CustomRichEditor };
|
||||
|
84
main/src/hooks/useBreakpoint.js
Normal file
84
main/src/hooks/useBreakpoint.js
Normal file
@ -0,0 +1,84 @@
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { useEventListener } from './useEventListener';
|
||||
|
||||
let globalScreenRef;
|
||||
let globalWidthRef;
|
||||
let globalRealWidthRef;
|
||||
|
||||
const screenMap = new Map();
|
||||
screenMap.set('XS', 480);
|
||||
screenMap.set('SM', 576);
|
||||
screenMap.set('MD', 768);
|
||||
screenMap.set('LG', 992);
|
||||
screenMap.set('XL', 1200);
|
||||
screenMap.set('XXL', 1600);
|
||||
|
||||
export function useBreakpoint() {
|
||||
return {
|
||||
screenRef: computed(() => unref(globalScreenRef)),
|
||||
widthRef: globalWidthRef,
|
||||
realWidthRef: globalRealWidthRef,
|
||||
};
|
||||
}
|
||||
|
||||
// Just call it once
|
||||
export function createBreakpointListen(fn) {
|
||||
const screenRef = ref('XL');
|
||||
const realWidthRef = ref(window.innerWidth);
|
||||
|
||||
function getWindowWidth() {
|
||||
const width = document.body.clientWidth;
|
||||
const xs = screenMap.get('XS');
|
||||
const sm = screenMap.get('SM');
|
||||
const md = screenMap.get('MD');
|
||||
const lg = screenMap.get('LG');
|
||||
const xl = screenMap.get('XL');
|
||||
const xxl = screenMap.set('XXL', 1600);
|
||||
if (width < xs) {
|
||||
screenRef.value = xs;
|
||||
} else if (width < sm) {
|
||||
screenRef.value = sm;
|
||||
} else if (width < md) {
|
||||
screenRef.value = md;
|
||||
} else if (width < lg) {
|
||||
screenRef.value = lg;
|
||||
} else if (width < xl) {
|
||||
screenRef.value = xl;
|
||||
} else {
|
||||
screenRef.value = xxl;
|
||||
}
|
||||
realWidthRef.value = width;
|
||||
}
|
||||
|
||||
useEventListener({
|
||||
el: window,
|
||||
name: 'resize',
|
||||
|
||||
listener: () => {
|
||||
getWindowWidth();
|
||||
resizeFn();
|
||||
},
|
||||
// wait: 100,
|
||||
});
|
||||
|
||||
getWindowWidth();
|
||||
globalScreenRef = computed(() => unref(screenRef));
|
||||
globalWidthRef = computed(() => screenMap.get(unref(screenRef)));
|
||||
globalRealWidthRef = computed(() => unref(realWidthRef));
|
||||
|
||||
function resizeFn() {
|
||||
fn?.({
|
||||
screen: globalScreenRef,
|
||||
width: globalWidthRef,
|
||||
realWidth: globalRealWidthRef,
|
||||
screenMap,
|
||||
});
|
||||
}
|
||||
|
||||
resizeFn();
|
||||
return {
|
||||
screenRef: globalScreenRef,
|
||||
widthRef: globalWidthRef,
|
||||
realWidthRef: globalRealWidthRef,
|
||||
};
|
||||
}
|
107
main/src/hooks/useEcharts.js
Normal file
107
main/src/hooks/useEcharts.js
Normal file
@ -0,0 +1,107 @@
|
||||
import { unref, nextTick, watch, computed, ref } from 'vue';
|
||||
import { useDebounceFn, tryOnUnmounted } from '@vueuse/core';
|
||||
import { useTimeoutFn } from './useTimeout';
|
||||
import { useEventListener } from './useEventListener';
|
||||
import { useBreakpoint } from './useBreakpoint';
|
||||
import echarts from '../utils/echarts';
|
||||
|
||||
export const useEcharts = (elRef, theme = 'default') => {
|
||||
const getDarkMode = computed(() => {
|
||||
return theme === 'default' ? 'dark' : theme;
|
||||
});
|
||||
let chartInstance = null;
|
||||
let resizeFn = resize;
|
||||
const cacheOptions = ref({});
|
||||
let removeResizeFn = () => {};
|
||||
|
||||
resizeFn = useDebounceFn(resize, 200);
|
||||
|
||||
const getOptions = computed(() => {
|
||||
if (getDarkMode.value !== 'dark') {
|
||||
return cacheOptions.value;
|
||||
}
|
||||
return {
|
||||
backgroundColor: 'transparent',
|
||||
...cacheOptions.value,
|
||||
};
|
||||
});
|
||||
|
||||
function initCharts(t = theme) {
|
||||
const el = unref(elRef);
|
||||
if (!el || !unref(el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(el, t);
|
||||
const { removeEvent } = useEventListener({
|
||||
el: window,
|
||||
name: 'resize',
|
||||
listener: resizeFn,
|
||||
});
|
||||
removeResizeFn = removeEvent;
|
||||
const { widthRef } = useBreakpoint();
|
||||
if (unref(widthRef) <= 768 || el.offsetHeight === 0) {
|
||||
useTimeoutFn(() => {
|
||||
resizeFn();
|
||||
}, 30);
|
||||
}
|
||||
}
|
||||
|
||||
function setOptions(options = {}, clear = true) {
|
||||
cacheOptions.value = options;
|
||||
if (unref(elRef)?.offsetHeight === 0) {
|
||||
useTimeoutFn(() => {
|
||||
setOptions(unref(getOptions));
|
||||
}, 30);
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
useTimeoutFn(() => {
|
||||
if (!chartInstance) {
|
||||
initCharts(getDarkMode.value ?? 'default');
|
||||
|
||||
if (!chartInstance) return;
|
||||
}
|
||||
clear && chartInstance?.clear();
|
||||
|
||||
chartInstance?.setOption(unref(getOptions));
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
|
||||
function resize() {
|
||||
chartInstance?.resize();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => getDarkMode.value,
|
||||
(theme) => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
initCharts(theme);
|
||||
setOptions(cacheOptions.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
if (!chartInstance) return;
|
||||
removeResizeFn();
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
});
|
||||
|
||||
function getInstance() {
|
||||
if (!chartInstance) {
|
||||
initCharts(getDarkMode.value ?? 'default');
|
||||
}
|
||||
return chartInstance;
|
||||
}
|
||||
|
||||
return {
|
||||
setOptions,
|
||||
resize,
|
||||
echarts,
|
||||
getInstance,
|
||||
};
|
||||
};
|
38
main/src/hooks/useEventListener.js
Normal file
38
main/src/hooks/useEventListener.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { ref, watch, unref } from 'vue';
|
||||
import { useThrottleFn, useDebounceFn } from '@vueuse/core';
|
||||
|
||||
export function useEventListener({ el = window, name, listener, options, autoRemove = true, isDebounce = true, wait = 80 }) {
|
||||
let remove;
|
||||
const isAddRef = ref(false);
|
||||
|
||||
if (el) {
|
||||
const element = ref(el);
|
||||
|
||||
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
|
||||
const realHandler = wait ? handler : listener;
|
||||
const removeEventListener = (e) => {
|
||||
isAddRef.value = true;
|
||||
e.removeEventListener(name, realHandler, options);
|
||||
};
|
||||
const addEventListener = (e) => e.addEventListener(name, realHandler, options);
|
||||
|
||||
const removeWatch = watch(
|
||||
element,
|
||||
(v, _ov, cleanUp) => {
|
||||
if (v) {
|
||||
!unref(isAddRef) && addEventListener(v);
|
||||
cleanUp(() => {
|
||||
autoRemove && removeEventListener(v);
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
remove = () => {
|
||||
removeEventListener(element.value);
|
||||
removeWatch();
|
||||
};
|
||||
}
|
||||
return { removeEvent: remove };
|
||||
}
|
44
main/src/hooks/useTimeout.js
Normal file
44
main/src/hooks/useTimeout.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { ref, watch } from 'vue';
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
|
||||
export function useTimeoutFn(handle, wait, native = false) {
|
||||
if (typeof handle !== 'function') {
|
||||
throw new Error('handle is not Function!');
|
||||
}
|
||||
|
||||
const { readyRef, stop, start } = useTimeoutRef(wait);
|
||||
if (native) {
|
||||
handle();
|
||||
} else {
|
||||
watch(
|
||||
readyRef,
|
||||
(maturity) => {
|
||||
maturity && handle();
|
||||
},
|
||||
{ immediate: false }
|
||||
);
|
||||
}
|
||||
return { readyRef, stop, start };
|
||||
}
|
||||
|
||||
export function useTimeoutRef(wait) {
|
||||
const readyRef = ref(false);
|
||||
|
||||
let timer;
|
||||
function stop() {
|
||||
readyRef.value = false;
|
||||
timer && window.clearTimeout(timer);
|
||||
}
|
||||
function start() {
|
||||
stop();
|
||||
timer = setTimeout(() => {
|
||||
readyRef.value = true;
|
||||
}, wait);
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
tryOnUnmounted(stop);
|
||||
|
||||
return { readyRef, stop, start };
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// import * as components from '../../../global/components';
|
||||
|
||||
// // 全局注册组件
|
||||
// export const registerGlobalComponents = (app) => {
|
||||
// Object.keys(components).forEach((key) => {
|
||||
// app.component(key, components[key]);
|
||||
// });
|
||||
// };
|
51
main/src/utils/echarts.js
Normal file
51
main/src/utils/echarts.js
Normal file
@ -0,0 +1,51 @@
|
||||
import * as echarts from 'echarts/core';
|
||||
|
||||
import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart } from 'echarts/charts';
|
||||
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
PolarComponent,
|
||||
AriaComponent,
|
||||
ParallelComponent,
|
||||
LegendComponent,
|
||||
RadarComponent,
|
||||
ToolboxComponent,
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
CalendarComponent,
|
||||
GraphicComponent,
|
||||
} from 'echarts/components';
|
||||
|
||||
// TODO 如果想换成SVG渲染,就导出SVGRenderer,
|
||||
// 并且放到 echarts.use 里,注释掉 CanvasRenderer
|
||||
import { /*SVGRenderer*/ CanvasRenderer } from 'echarts/renderers';
|
||||
|
||||
echarts.use([
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
PolarComponent,
|
||||
AriaComponent,
|
||||
ParallelComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
MapChart,
|
||||
RadarChart,
|
||||
// TODO 因为要兼容Online图表自适应打印,所以改成 CanvasRenderer,可能会模糊
|
||||
CanvasRenderer,
|
||||
PictorialBarChart,
|
||||
RadarComponent,
|
||||
ToolboxComponent,
|
||||
DataZoomComponent,
|
||||
VisualMapComponent,
|
||||
TimelineComponent,
|
||||
CalendarComponent,
|
||||
GraphicComponent,
|
||||
]);
|
||||
|
||||
export default echarts;
|
@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<div class="platform">
|
||||
<h2>平台首页</h2>
|
||||
|
||||
<custom-echart-bar height="50vh" :option="{ title: { text: '销售额排行', left: 'center' } }" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
@ -791,6 +791,11 @@
|
||||
resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8"
|
||||
integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==
|
||||
|
||||
"@types/web-bluetooth@^0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
|
||||
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
|
||||
|
||||
"@ungap/structured-clone@^1.2.0":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd"
|
||||
@ -929,6 +934,16 @@
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f"
|
||||
integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==
|
||||
|
||||
"@vueuse/core@^12.4.0":
|
||||
version "12.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/core/-/core-12.4.0.tgz#7a17f12fe941a16fb2ca9dbd60106975290bb34d"
|
||||
integrity sha512-XnjQYcJwCsyXyIafyA6SvyN/OBtfPnjvJmbxNxQjCcyWD198urwm5TYvIUUyAxEAN0K7HJggOgT15cOlWFyLeA==
|
||||
dependencies:
|
||||
"@types/web-bluetooth" "^0.0.20"
|
||||
"@vueuse/metadata" "12.4.0"
|
||||
"@vueuse/shared" "12.4.0"
|
||||
vue "^3.5.13"
|
||||
|
||||
"@vueuse/core@^9.1.0":
|
||||
version "9.13.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz#2f69e66d1905c1e4eebc249a01759cf88ea00cf4"
|
||||
@ -939,11 +954,23 @@
|
||||
"@vueuse/shared" "9.13.0"
|
||||
vue-demi "*"
|
||||
|
||||
"@vueuse/metadata@12.4.0":
|
||||
version "12.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.4.0.tgz#8a21ceec6fb3866b4ed445f0a3dc45dd5a4f2b64"
|
||||
integrity sha512-AhPuHs/qtYrKHUlEoNO6zCXufu8OgbR8S/n2oMw1OQuBQJ3+HOLQ+EpvXs+feOlZMa0p8QVvDWNlmcJJY8rW2g==
|
||||
|
||||
"@vueuse/metadata@9.13.0":
|
||||
version "9.13.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz#bc25a6cdad1b1a93c36ce30191124da6520539ff"
|
||||
integrity sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==
|
||||
|
||||
"@vueuse/shared@12.4.0":
|
||||
version "12.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.4.0.tgz#2d4b2d51068db0cc623f6e0342c54a652a9b466d"
|
||||
integrity sha512-9yLgbHVIF12OSCojnjTIoZL1+UA10+O4E1aD6Hpfo/DKVm5o3SZIwz6CupqGy3+IcKI8d6Jnl26EQj/YucnW0Q==
|
||||
dependencies:
|
||||
vue "^3.5.13"
|
||||
|
||||
"@vueuse/shared@9.13.0":
|
||||
version "9.13.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz#089ff4cc4e2e7a4015e57a8f32e4b39d096353b9"
|
||||
@ -2135,6 +2162,14 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
echarts@^5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
|
||||
integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==
|
||||
dependencies:
|
||||
tslib "2.3.0"
|
||||
zrender "5.6.1"
|
||||
|
||||
ejs@^3.1.6:
|
||||
version "3.1.10"
|
||||
resolved "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
|
||||
@ -6423,6 +6458,11 @@ trough@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
||||
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
||||
|
||||
tslib@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||
|
||||
tslib@^2.0.3, tslib@^2.6.2:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
@ -6865,7 +6905,7 @@ vue-router@^4.2.5:
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.6.4"
|
||||
|
||||
vue@^3.5.11:
|
||||
vue@^3.5.11, vue@^3.5.13:
|
||||
version "3.5.13"
|
||||
resolved "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a"
|
||||
integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==
|
||||
@ -7034,3 +7074,10 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zrender@5.6.1:
|
||||
version "5.6.1"
|
||||
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b"
|
||||
integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==
|
||||
dependencies:
|
||||
tslib "2.3.0"
|
||||
|
@ -32,8 +32,7 @@
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue3-custom-component": "1.1.17"
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
|
@ -1,219 +1,8 @@
|
||||
export const GenKey = (key, prefix = 'EGGY_TEAM_STORE_') => {
|
||||
export const GenKey = (key, prefix = 'SUB_ADMIN_STORE_') => {
|
||||
return prefix ? prefix + key : key;
|
||||
};
|
||||
|
||||
export const CONSTANTS = {
|
||||
PREFIX: 'EGGY_TEAM_STORE_',
|
||||
PREFIX: 'SUB_ADMIN_STORE_',
|
||||
PRIMARY: '#409eff',
|
||||
// 总部商品可编辑属性
|
||||
EDITLIST: ['amount', 'originalPrice', 'activityPrice', 'usePoints', 'points', 'status', 'sortOrder', 'perUseValue', 'displayChannelList'],
|
||||
// 活动管理
|
||||
LATITUDE: [
|
||||
{
|
||||
label: '副本【x】通关计分榜',
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
label: '副本【x】速通榜',
|
||||
value: 11,
|
||||
},
|
||||
{
|
||||
label: '副本【x】大师榜(失误最少)',
|
||||
value: 12,
|
||||
},
|
||||
{
|
||||
label: '副本【x】巅峰榜',
|
||||
value: 13,
|
||||
},
|
||||
{
|
||||
label: '最强闯关计分榜',
|
||||
value: 14,
|
||||
},
|
||||
{
|
||||
label: '挑战模式通关计分榜',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
label: '挑战模式第【x】关速通榜',
|
||||
value: 21,
|
||||
},
|
||||
{
|
||||
label: '挑战模式大师榜(失误最少)',
|
||||
value: 22,
|
||||
},
|
||||
{
|
||||
label: '挑战模式巅峰榜',
|
||||
value: 23,
|
||||
},
|
||||
],
|
||||
// 维度说明
|
||||
LATITUDETIPS: {
|
||||
default: '榜单常驻,用户所有的游戏通关数据得分累计排列显示,不管用户参与什么模式,是否通关,只要游戏结束时候的分数累计进行排行',
|
||||
10: '在榜单发起时间内,指定某个副本的通关分数总和(不通关则不计入总分)',
|
||||
11: '在榜单发起时间内,指定某个副本的通关时间最快(只刷新计入最快的那次)',
|
||||
12: '在榜单发起时间内,指定某个副本的扣分最少(只刷新计入扣分最少的那次)',
|
||||
13: '在榜单发起时间内,指定某个副本的得分最高(只刷新计入得分最高的那次)',
|
||||
14: '在榜单发起时间内,任意副本的得分总和排名(不通关则不计入总分)',
|
||||
20: '在榜单发起时间内,挑战模式的通关分数总和(不通关则不计入总分)',
|
||||
21: '在榜单发起时间内,指定挑战模式的某些关卡的通关时间最快(只刷新计入最快的那次)',
|
||||
22: '在榜单发起时间内,挑战模式的扣分最少(只刷新计入扣分最少的那次)',
|
||||
23: '在榜单发起时间内,挑战模式的得分最高(只刷新计入得分最高的那次)',
|
||||
},
|
||||
// 支付方式
|
||||
PAYTYPE: {
|
||||
prop: 'payType',
|
||||
label: '支付方式',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '余额',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '微信',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '支付宝',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: '商家券',
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
label: '平台券',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
label: '商场券',
|
||||
value: 6,
|
||||
},
|
||||
{
|
||||
label: '积分',
|
||||
value: 7,
|
||||
},
|
||||
{
|
||||
label: '美团',
|
||||
value: 8,
|
||||
},
|
||||
{
|
||||
label: '现金',
|
||||
value: 9,
|
||||
},
|
||||
{
|
||||
label: '抖音',
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
label: '小红书',
|
||||
value: 11,
|
||||
},
|
||||
{
|
||||
label: '线下优惠券',
|
||||
value: 12,
|
||||
},
|
||||
{
|
||||
label: '首单立减',
|
||||
value: 13,
|
||||
},
|
||||
{
|
||||
label: '其它一',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
label: '其它二',
|
||||
value: 21,
|
||||
},
|
||||
{
|
||||
label: '其它三',
|
||||
value: 22,
|
||||
},
|
||||
],
|
||||
formatter: (row) => {
|
||||
let value = '';
|
||||
switch (row.payType) {
|
||||
case 1: {
|
||||
value = '余额';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
value = '微信';
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
value = '支付宝';
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
value = '商家券';
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
value = '平台券';
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
value = '商场券';
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
value = '积分';
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
value = '美团';
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
value = '现金';
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
value = '抖音';
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
value = '小红书';
|
||||
break;
|
||||
}
|
||||
case 12: {
|
||||
value = '线下优惠券';
|
||||
break;
|
||||
}
|
||||
case 13: {
|
||||
value = '首单立减';
|
||||
break;
|
||||
}
|
||||
case 20: {
|
||||
value = '其它一';
|
||||
break;
|
||||
}
|
||||
case 21: {
|
||||
value = '其它二';
|
||||
break;
|
||||
}
|
||||
case 22: {
|
||||
value = '其它三';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
},
|
||||
// 支付分类
|
||||
PAY_CATEGORIES: [
|
||||
{ prop: 'weixin', name: '微信' },
|
||||
{ prop: 'alipay', name: '支付宝' },
|
||||
{ prop: 'cash', name: '现金' },
|
||||
{ prop: 'balance', name: '余额' },
|
||||
{ prop: 'market', name: '商场券' },
|
||||
{ prop: 'platform', name: '平台券' },
|
||||
{ prop: 'store', name: '门店券' },
|
||||
{ prop: 'meituan', name: '美团' },
|
||||
{ prop: 'tiktok', name: '抖音' },
|
||||
{ prop: 'xiaohongshu', name: '小红书' },
|
||||
{ prop: 'offline', name: '线下优惠券' },
|
||||
{ prop: 'firstOrder', name: '首单立减' },
|
||||
],
|
||||
};
|
||||
|
@ -1,5 +1,16 @@
|
||||
import * as components from '../../../main/src/components';
|
||||
|
||||
// const modules = import.meta.glob('../../../main/src/components/**/**.vue');
|
||||
// const components = Object.keys(modules).reduce((acc, path) => {
|
||||
// const component = modules[path].default || modules[path];
|
||||
// const componentName = path
|
||||
// .split('/')
|
||||
// .pop()
|
||||
// .replace(/\.\w+$/, '');
|
||||
// acc[componentName] = component;
|
||||
// return acc;
|
||||
// }, {});
|
||||
|
||||
// 全局注册组件
|
||||
export const registerGlobalComponents = (app) => {
|
||||
Object.keys(components).forEach((key) => {
|
||||
|
52
sub-admin/src/views/demo/chartdemo.data.js
Normal file
52
sub-admin/src/views/demo/chartdemo.data.js
Normal file
@ -0,0 +1,52 @@
|
||||
const colors = ['#4db6ac', '#ffb74d', '#64b5f6', '#e57373', '#9575cd', '#a1887f', '#90a4ae', '#4dd0e1', '#81c784', '#ff8a65'];
|
||||
export const getData = (() => {
|
||||
let dottedBase = +new Date();
|
||||
const barDataSource = [];
|
||||
const barMultiData = [];
|
||||
const barLineData = [];
|
||||
const barLineColors = [];
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
let obj = { name: '', value: 0 };
|
||||
const date = new Date((dottedBase += 1000 * 3600 * 24));
|
||||
obj.name = [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-');
|
||||
obj.value = Math.random() * 200;
|
||||
barDataSource.push(obj);
|
||||
}
|
||||
|
||||
for (let j = 0; j < 2; j++) {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
let obj = { name: '', value: 0, type: 2010 + j + '' };
|
||||
const date = new Date(dottedBase + 1000 * 3600 * 24 * i);
|
||||
obj.name = [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-');
|
||||
obj.value = Math.random() * 200;
|
||||
barMultiData.push(obj);
|
||||
}
|
||||
}
|
||||
const pieData = [
|
||||
{ value: 335, name: '客服电话' },
|
||||
{ value: 310, name: '奥迪官网' },
|
||||
{ value: 234, name: '媒体曝光' },
|
||||
{ value: 135, name: '质检总局' },
|
||||
{ value: 105, name: '其他' },
|
||||
];
|
||||
const radarData = [
|
||||
{ value: 75, name: '政治', type: '文综', max: 100 },
|
||||
{ value: 65, name: '历史', type: '文综', max: 100 },
|
||||
{ value: 55, name: '地理', type: '文综', max: 100 },
|
||||
{ value: 74, name: '化学', type: '文综', max: 100 },
|
||||
{ value: 38, name: '物理', type: '文综', max: 100 },
|
||||
{ value: 88, name: '生物', type: '文综', max: 100 },
|
||||
];
|
||||
for (let j = 0; j < 2; j++) {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
let obj = { name: '', value: 0, type: 2010 + j + '', seriesType: j >= 1 ? 'line' : 'bar' };
|
||||
const date = new Date(dottedBase + 1000 * 3600 * 24 * i);
|
||||
obj.name = [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-');
|
||||
obj.value = Math.random() * 200;
|
||||
barLineData.push(obj);
|
||||
}
|
||||
barLineColors.push(colors[j]);
|
||||
}
|
||||
return { barDataSource, barMultiData, pieData, barLineData, barLineColors, radarData };
|
||||
})();
|
@ -1,4 +1,23 @@
|
||||
<template>
|
||||
<div>echart</div>
|
||||
<div class="p-4">
|
||||
<el-card :bordered="false" style="height: 100%">
|
||||
<el-tabs v-model="activeKey" class="demo-tabs" @tab-click="tabChange">
|
||||
<el-tab-pane label="柱状图" name="1">
|
||||
<custom-echart-bar :chart-data="barDataSource" height="50vh" :option="{ title: { text: '销售额排行', left: 'center' } }" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="echart"></script>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { getData } from './chartdemo.data';
|
||||
|
||||
const activeKey = ref('1');
|
||||
const { barDataSource } = getData;
|
||||
//tab切换
|
||||
function tabChange(key) {
|
||||
console.log('切换的key:', key);
|
||||
}
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user