Commit 2a1b7452e437bd10c2cd1f2eacbac6b143adf374
1 parent
ea305212
feat: 新增plopjs配置,为此项目快速生成组件模板,比如需要新加一个组件,直接快速生成配置代码
Showing
8 changed files
with
330 additions
and
0 deletions
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | "preview": "vite preview", |
13 | 13 | "preview:alone": "vite preview --mode alone.prod", |
14 | 14 | "new": "plop --plopfile ./plop/plopfile.js", |
15 | + "newCom": "plop --plopfile ./plop/external/plopfile.js", | |
15 | 16 | "postinstall": "husky install", |
16 | 17 | "lint": "eslint --ext .js,.jsx,.ts,.tsx,.vue src", |
17 | 18 | "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx,.vue src --fix" | ... | ... |
1 | +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public' | |
2 | +import { {{name}}Config } from './index' | |
3 | +import { CreateComponentType } from '@/packages/index.d' | |
4 | +import cloneDeep from 'lodash/cloneDeep' | |
5 | +import dataJson from './data.json' | |
6 | + | |
7 | +export const includes = ['legend', 'xAxis', 'yAxis', 'grid'] | |
8 | + | |
9 | +export const seriesItem = { | |
10 | +} | |
11 | + | |
12 | +export const option = { | |
13 | + dataset: { ...dataJson }, | |
14 | + series: [seriesItem, seriesItem] | |
15 | +} | |
16 | + | |
17 | +export default class Config extends PublicConfigClass implements CreateComponentType { | |
18 | + public key = {{name}}Config.key | |
19 | + public chartConfig = cloneDeep({{name}}Config) | |
20 | + // 图表配置项 | |
21 | + public option = echartOptionProfixHandle(option, includes) | |
22 | +} | ... | ... |
1 | +<template> | |
2 | + <!-- Echarts 全局设置 --> | |
3 | + <global-setting :optionData="optionData"></global-setting> | |
4 | + <CollapseItem name="xxx" :expanded="true"> | |
5 | + <SettingItemBox name="xx"> | |
6 | + <SettingItem name="xx"> | |
7 | + </SettingItem> | |
8 | + </SettingItemBox> | |
9 | + </CollapseItem> | |
10 | +</template> | |
11 | + | |
12 | +<script setup lang="ts"> | |
13 | +import { PropType, computed } from 'vue' | |
14 | +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
15 | +import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | |
16 | + | |
17 | +const props = defineProps({ | |
18 | + optionData: { | |
19 | + type: Object as PropType<GlobalThemeJsonType>, | |
20 | + required: true | |
21 | + } | |
22 | +}) | |
23 | + | |
24 | +const seriesList = computed(() => { | |
25 | + return props.optionData.series | |
26 | +}) | |
27 | +</script> | ... | ... |
1 | +{ | |
2 | + "dimensions": ["product", "data1", "data2"], | |
3 | + "source": [ | |
4 | + { | |
5 | + "product": "Mon", | |
6 | + "data1": 120, | |
7 | + "data2": 130 | |
8 | + }, | |
9 | + { | |
10 | + "product": "Tue", | |
11 | + "data1": 200, | |
12 | + "data2": 130 | |
13 | + }, | |
14 | + { | |
15 | + "product": "Wed", | |
16 | + "data1": 150, | |
17 | + "data2": 312 | |
18 | + }, | |
19 | + { | |
20 | + "product": "Thu", | |
21 | + "data1": 80, | |
22 | + "data2": 268 | |
23 | + }, | |
24 | + { | |
25 | + "product": "Fri", | |
26 | + "data1": 70, | |
27 | + "data2": 155 | |
28 | + }, | |
29 | + { | |
30 | + "product": "Sat", | |
31 | + "data1": 110, | |
32 | + "data2": 117 | |
33 | + }, | |
34 | + { | |
35 | + "product": "Sun", | |
36 | + "data1": 130, | |
37 | + "data2": 160 | |
38 | + } | |
39 | + ] | |
40 | +} | ... | ... |
1 | +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' | |
2 | +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d' | |
3 | +import { useWidgetKey } from '@/packages/external/useWidgetKey' | |
4 | + | |
5 | +const { key, conKey, chartKey } = useWidgetKey('{{name}}', true) | |
6 | + | |
7 | +export const {{name}}Config: ConfigType = { | |
8 | + key, | |
9 | + chartKey, | |
10 | + conKey, | |
11 | + title: 'xxx', | |
12 | + category: ChatCategoryEnum.BAR, | |
13 | + categoryName: ChatCategoryEnumName.BAR, | |
14 | + package: PackagesCategoryEnum.CHARTS, | |
15 | + chartFrame: ChartFrameEnum.ECHARTS, | |
16 | + image: 'bar_x.png' | |
17 | +} | ... | ... |
1 | +<template> | |
2 | + <v-chart | |
3 | + ref="vChartRef" | |
4 | + :init-options="initOptions" | |
5 | + :theme="themeColor" | |
6 | + :option="option" | |
7 | + :manual-update="isPreview()" | |
8 | + :update-options="{ | |
9 | + replaceMerge: replaceMergeArr | |
10 | + }" | |
11 | + autoresize | |
12 | + @mouseover="handleHighlight" | |
13 | + @mouseout="handleDownplay" | |
14 | + > | |
15 | + </v-chart> | |
16 | +</template> | |
17 | + | |
18 | +<script setup lang="ts"> | |
19 | +import { ref, nextTick, computed, watch, PropType, onMounted } from 'vue' | |
20 | +import VChart from 'vue-echarts' | |
21 | +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' | |
22 | +import { use } from 'echarts/core' | |
23 | +import { CanvasRenderer } from 'echarts/renderers' | |
24 | +import { BarChart } from 'echarts/charts' | |
25 | +import config, { includes, seriesItem } from './config' | |
26 | +import { mergeTheme } from '@/packages/public/chart' | |
27 | +import { useChartDataFetch } from '@/hooks' | |
28 | +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | |
29 | +import { isPreview } from '@/utils' | |
30 | +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' | |
31 | +import isObject from 'lodash/isObject' | |
32 | +import dataJson from './data.json' | |
33 | +import cloneDeep from 'lodash/cloneDeep' | |
34 | +import { useAssembleDataHooks } from '@/hooks/external/useAssembleData.hook' | |
35 | +import { SocketReceiveMessageType } from '@/store/external/modules/socketStore.d' | |
36 | + | |
37 | +const props = defineProps({ | |
38 | + themeSetting: { | |
39 | + type: Object, | |
40 | + required: true | |
41 | + }, | |
42 | + themeColor: { | |
43 | + type: Object, | |
44 | + required: true | |
45 | + }, | |
46 | + chartConfig: { | |
47 | + type: Object as PropType<config>, | |
48 | + required: true | |
49 | + } | |
50 | +}) | |
51 | + | |
52 | +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting) | |
53 | + | |
54 | +use([DatasetComponent, CanvasRenderer, BarChart, GridComponent, TooltipComponent, LegendComponent]) | |
55 | + | |
56 | +const chartEditStore = useChartEditStore() | |
57 | + | |
58 | +const replaceMergeArr = ref<string[]>() | |
59 | + | |
60 | +const option = computed(() => { | |
61 | + return mergeTheme(props.chartConfig.option, props.themeSetting, includes) | |
62 | +}) | |
63 | + | |
64 | + | |
65 | +// dataset 无法变更条数的补丁 | |
66 | +watch( | |
67 | + () => props.chartConfig.option.dataset, | |
68 | + (newData: { dimensions: any }, oldData) => { | |
69 | + try { | |
70 | + if (!isObject(newData) || !('dimensions' in newData)) return | |
71 | + if (Array.isArray(newData?.dimensions)) { | |
72 | + const seriesArr = [] | |
73 | + // 对oldData进行判断,防止传入错误数据之后对旧维度判断产生干扰 | |
74 | + // 此处计算的是dimensions的Y轴维度,若是dimensions.length为0或1,则默认为1,排除X轴维度干扰 | |
75 | + const oldDimensions = | |
76 | + Array.isArray(oldData?.dimensions) && oldData.dimensions.length >= 1 ? oldData.dimensions.length : 1 | |
77 | + const newDimensions = newData.dimensions.length >= 1 ? newData.dimensions.length : 1 | |
78 | + const dimensionsGap = newDimensions - oldDimensions | |
79 | + if (dimensionsGap < 0) { | |
80 | + props.chartConfig.option.series.splice(newDimensions - 1) | |
81 | + } else if (dimensionsGap > 0) { | |
82 | + if (!oldData || !oldData?.dimensions || !Array.isArray(oldData?.dimensions) || !oldData?.dimensions.length) { | |
83 | + props.chartConfig.option.series = [] | |
84 | + } | |
85 | + for (let i = 0; i < dimensionsGap; i++) { | |
86 | + seriesArr.push(cloneDeep(seriesItem)) | |
87 | + } | |
88 | + props.chartConfig.option.series.push(...seriesArr) | |
89 | + } | |
90 | + replaceMergeArr.value = ['series'] | |
91 | + nextTick(() => { | |
92 | + replaceMergeArr.value = [] | |
93 | + }) | |
94 | + } | |
95 | + } catch (error) { | |
96 | + console.log(error) | |
97 | + } | |
98 | + }, | |
99 | + { | |
100 | + deep: false | |
101 | + } | |
102 | +) | |
103 | + | |
104 | +//fix 修复v-chart图表绑定联动组件视图不更新问题 | |
105 | +const updateVChart = async (newData: SocketReceiveMessageType) => { | |
106 | + //区分是普通请求还是ws请求 | |
107 | + if (!isObject(newData) || !('dimensions' in newData)) { | |
108 | + const { data } = newData | |
109 | + const { keys, record } = useAssembleDataHooks(data) | |
110 | + vChartRef.value?.setOption({ | |
111 | + dataset: { | |
112 | + dimensions: ['ts', ...keys], | |
113 | + source: [record] | |
114 | + } | |
115 | + }) | |
116 | + } else { | |
117 | + //异步更新,同步更新会造成联动组件控制,图表不及时更新 | |
118 | + await nextTick().then(() => { | |
119 | + vChartRef.value?.setOption( | |
120 | + { | |
121 | + ...option.value, | |
122 | + dataset: newData | |
123 | + }, | |
124 | + { | |
125 | + notMerge: true | |
126 | + } | |
127 | + ) | |
128 | + }) | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, newData => { | |
133 | + //联动支持分组 | |
134 | + /** | |
135 | + * 修复多个分组,然后下拉框联动,会影响另一个组件 | |
136 | + */ | |
137 | + chartEditStore.getComponentList.forEach(targetItem => { | |
138 | + if (targetItem.isGroup) { | |
139 | + targetItem.groupList?.forEach(groupItem => { | |
140 | + if (groupItem.id === props.chartConfig.id) { | |
141 | + groupItem.option.dataset = newData | |
142 | + } | |
143 | + }) | |
144 | + } else { | |
145 | + if (targetItem.id === props.chartConfig.id) { | |
146 | + targetItem.option.dataset = newData | |
147 | + } | |
148 | + } | |
149 | + }) | |
150 | + // | |
151 | + updateVChart(newData) | |
152 | +}) | |
153 | +</script> | ... | ... |
plop/external/component-template/prompt.js
0 → 100644
1 | +module.exports = { | |
2 | + description: | |
3 | + 'create a component(在packages/components/external,一般是需要重写的组件,这里写的示例生成在src/packages/components/external/Charts/Bars目录下)', | |
4 | + prompts: [ | |
5 | + { | |
6 | + type: 'input', | |
7 | + name: 'name', | |
8 | + message: 'Please enter component name,such as "OverrideComponentName":', | |
9 | + validate(value) { | |
10 | + if (!value || value.trim === '') { | |
11 | + return 'component name is required' | |
12 | + } | |
13 | + return true | |
14 | + } | |
15 | + } | |
16 | + ], | |
17 | + actions: data => { | |
18 | + const dataName = data.name | |
19 | + | |
20 | + const actions = [ | |
21 | + { | |
22 | + type: 'add', | |
23 | + path: `${process.cwd()}/src/packages/components/external/Charts/Bars${dataName}/config.ts`, | |
24 | + templateFile: './component-template/config.ts.hbs', | |
25 | + data: { | |
26 | + name: data.name | |
27 | + } | |
28 | + }, | |
29 | + { | |
30 | + type: 'add', | |
31 | + path: `${process.cwd()}/src/packages/components/external/Charts/Bars${dataName}/config.vue`, | |
32 | + templateFile: './component-template/config.vue.hbs', | |
33 | + data: { | |
34 | + name: data.name | |
35 | + } | |
36 | + }, | |
37 | + { | |
38 | + type: 'add', | |
39 | + path: `${process.cwd()}/src/packages/components/external/Charts/Bars${dataName}/data.json`, | |
40 | + templateFile: './component-template/data.json.hbs', | |
41 | + data: { | |
42 | + name: data.name | |
43 | + } | |
44 | + }, | |
45 | + { | |
46 | + type: 'add', | |
47 | + path: `${process.cwd()}/src/packages/components/external/Charts/Bars${dataName}/index.ts`, | |
48 | + templateFile: './component-template/index.ts.hbs', | |
49 | + data: { | |
50 | + name: data.name | |
51 | + } | |
52 | + }, | |
53 | + { | |
54 | + type: 'add', | |
55 | + path: `${process.cwd()}/src/packages/components/external/Charts/Bars${dataName}/index.vue`, | |
56 | + templateFile: './component-template/index.vue.hbs', | |
57 | + data: { | |
58 | + name: data.name | |
59 | + } | |
60 | + } | |
61 | + ] | |
62 | + | |
63 | + return actions | |
64 | + } | |
65 | +} | ... | ... |