Commit 2a1b7452e437bd10c2cd1f2eacbac6b143adf374

Authored by fengwotao
1 parent ea305212

feat: 新增plopjs配置,为此项目快速生成组件模板,比如需要新加一个组件,直接快速生成配置代码

... ... @@ -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>
... ...
  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 +}
... ...
  1 +const componentsGenerator = require('./component-template/prompt')
  2 +
  3 +module.exports = plop => {
  4 + plop.setGenerator('component', componentsGenerator)
  5 +}
... ...