Commit 8729a3386eb614df2c64417bc99ee1bfcd141f84

Authored by fengwotao
1 parent 40713fe6

perf(src/): 优化给分组这个整体绑定动态数据,其下所有子组件共享分组数据,子组件支持动态设置自身数据

1 import { ref, toRefs, toRaw, watch } from 'vue' 1 import { ref, toRefs, toRaw, watch } from 'vue'
2 import type VChart from 'vue-echarts' 2 import type VChart from 'vue-echarts'
3 import { useChartDataPondFetch } from '@/hooks/' 3 import { useChartDataPondFetch } from '@/hooks/'
4 -import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d' 4 +import { CreateComponentType, ChartFrameEnum, CreateComponentGroupType } from '@/packages/index.d'
5 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' 5 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
6 import { isPreview, intervalUnitHandle } from '@/utils' 6 import { isPreview, intervalUnitHandle } from '@/utils'
7 import { setOption } from '@/packages/public/chart' 7 import { setOption } from '@/packages/public/chart'
@@ -110,7 +110,18 @@ export const useChartDataFetch = ( @@ -110,7 +110,18 @@ export const useChartDataFetch = (
110 110
111 if (isPreview()) { 111 if (isPreview()) {
112 requestIntervalFn() 112 requestIntervalFn()
  113 + const chartEditStore = useChartEditStore()
113 const { initial } = useChartDataSocket() 114 const { initial } = useChartDataSocket()
  115 + /**
  116 + * 支持分组也可以接受ws
  117 + * 如果是分组并且绑定了ws
  118 + */
  119 + chartEditStore.componentList.forEach((item:CreateComponentType | CreateComponentGroupType)=>{
  120 + if(item.request.requestUrl?.includes('ws')){
  121 + initial(item, useChartEditStore, updateCallback)
  122 + }
  123 + })
  124 + //
114 initial(targetComponent, useChartEditStore, updateCallback) 125 initial(targetComponent, useChartEditStore, updateCallback)
115 } 126 }
116 return { vChartRef } 127 return { vChartRef }
@@ -38,7 +38,7 @@ watch( @@ -38,7 +38,7 @@ watch(
38 ) 38 )
39 39
40 useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { 40 useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
41 - option.dataset = newData 41 + // option.dataset = newData
42 }) 42 })
43 </script> 43 </script>
44 44
@@ -160,6 +160,7 @@ export interface CreateComponentType extends PublicConfigType, requestConfig { @@ -160,6 +160,7 @@ export interface CreateComponentType extends PublicConfigType, requestConfig {
160 chartConfig: ConfigType 160 chartConfig: ConfigType
161 option: GlobalThemeJsonType 161 option: GlobalThemeJsonType
162 groupList?: Array<CreateComponentType> 162 groupList?: Array<CreateComponentType>
  163 + saveHistoryInput?:string // THINGS_KIT 新增一个接口字段 saveHistoryInput,用于记录当前组件id和输入框的内容,这里不能重写,有很多地方引用到的,这里升级版本有冲突
163 } 164 }
164 165
165 // 组件成组实例类 166 // 组件成组实例类
@@ -262,7 +262,32 @@ export const useSocketStore = defineStore({ @@ -262,7 +262,32 @@ export const useSocketStore = defineStore({
262 */ 262 */
263 chartEditStore.getComponentList.forEach(targetItem => { 263 chartEditStore.getComponentList.forEach(targetItem => {
264 if (targetItem.isGroup) { 264 if (targetItem.isGroup) {
265 - //分组 265 + /**
  266 + * 如果是分组,并且request里是ws,则分组组件这个整体是接受ws绑定的,之前分组是不能绑定ws的
  267 + */
  268 + if(targetItem.request.requestUrl?.includes('ws')){
  269 + const _value = this.getComponentValueByKeys(targetItem, value)
  270 + const { filter } = targetItem
  271 + const { value: filterValue, reason, flag } = useFilterFn(filter, _value)
  272 + targetItem.option.dataset = flag ? filterValue : reason
  273 + try{
  274 + const dumpSaveHistoryInput = JSON.parse((!targetItem.saveHistoryInput ? null: targetItem.saveHistoryInput) as string) ||JSON.parse(window.localStorage.getItem('CACHE_HISTORY_INPUT_VALUE') as string)
  275 + if(!dumpSaveHistoryInput) return
  276 + if(!Array.isArray(dumpSaveHistoryInput)) return
  277 + targetItem.groupList?.forEach((item:CreateComponentType)=>{
  278 + dumpSaveHistoryInput?.forEach((inputItem:{id:string,inputValue:string})=>{
  279 + if(item.id === inputItem.id){
  280 + item.option.dataset= Function('res', `return ${inputItem?.inputValue}`)(targetItem.option?.dataset)
  281 + }
  282 + })
  283 + })
  284 + }catch(e){
  285 + console.log(e)
  286 + }
  287 + }
  288 + /**
  289 + * 这也是分组,但这个是分组这个整体不绑定ws,而是下面的子组件都绑定了ws
  290 + */
266 targetItem.groupList?.forEach(groupItem => { 291 targetItem.groupList?.forEach(groupItem => {
267 if (groupItem.id === id) { 292 if (groupItem.id === id) {
268 const _value = this.getComponentValueByKeys(groupItem, value) 293 const _value = this.getComponentValueByKeys(groupItem, value)
  1 +type Params = {
  2 + [key: string]: string
  3 +}
  4 +
  5 +type ArrayParams = Params[]
  6 +
  7 +export type historyInputValue = { id: string; inputValue: string }
  8 +
  9 +//dataset数据源数据类型不确定,String Array Object Number
  10 +export type datasetType = string | number | Params | ArrayParams
  11 +
  12 +export type saveHistoryInputValueListType = historyInputValue[]
  1 +<template>
  2 + <div class="go-chart-configurations-data" v-if="targetData">
  3 + <n-space vertical>
  4 + <n-tag type="success"> 分组返回的数据源 </n-tag>
  5 + <n-card size="small">
  6 + <n-code word-wrap :code="toString(targetData.option.dataset)" language="json"></n-code>
  7 + </n-card>
  8 + <n-tag type="info"> 使用res点语法或者res中括号语法 </n-tag>
  9 + <n-tag type="warning">示例:res.xxxx</n-tag>
  10 + <span>测试一下</span>
  11 + <n-input type="textarea" @input="handleTestInput($event, option)" size="small" placeholder="请输入"></n-input>
  12 + <n-code word-wrap :code="toString(testInputValue)" language="json"></n-code>
  13 + <n-divider />
  14 + <div v-for="(item, index) in groupList" :key="item.key + index">
  15 + <n-space justify="space-between">
  16 + <n-ellipsis> 组件id </n-ellipsis>
  17 + <n-input size="small" v-model:value="item.id" :disabled="true"></n-input>
  18 + </n-space>
  19 + <n-space justify="space-between">
  20 + <n-ellipsis> 组件名称 </n-ellipsis>
  21 + <n-input size="small" v-model:value="item.chartConfig.title" :disabled="true"></n-input>
  22 + </n-space>
  23 + <n-space vertical justify="space-between">
  24 + <n-ellipsis> 设置数据 </n-ellipsis>
  25 + <n-input
  26 + type="textarea"
  27 + @update:value="handleInput(groupList!, item.id, $event)"
  28 + size="small"
  29 + :placeholder="String(item.option.dataset)"
  30 + ></n-input>
  31 + </n-space>
  32 + <n-divider />
  33 + </div>
  34 + </n-space>
  35 + </div>
  36 +</template>
  37 +
  38 +<script setup lang="ts">
  39 +import { toRefs, ref, watch } from 'vue'
  40 +import { useTargetData } from '../../hooks/useTargetData.hook'
  41 +import { toString } from '@/utils'
  42 +import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
  43 +import { datasetType, saveHistoryInputValueListType, historyInputValue } from './index.d'
  44 +import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
  45 +import { uniqBy } from 'lodash'
  46 +
  47 +const { targetData } = useTargetData()
  48 +
  49 +const { groupList, option } = toRefs(targetData.value)
  50 +
  51 +const testInputValue = ref('')
  52 +
  53 +const saveHistoryInputValueList = ref<saveHistoryInputValueListType>([])
  54 +
  55 +const handleInput = (groupList: CreateComponentType[], id: string, inputValue: string) => {
  56 + saveHistoryInputValueList.value.unshift({
  57 + id,
  58 + inputValue
  59 + })
  60 + handleGroupListById(groupList, id, inputValue, option.value.dataset)
  61 +}
  62 +
  63 +const executeFn = (inputValue: string, dataset: datasetType) => {
  64 + try {
  65 + return Function('res', `return ${inputValue}`)(dataset)
  66 + } catch (e) {
  67 + return inputValue
  68 + }
  69 +}
  70 +
  71 +const setTargetDataset = (inputValue: string, dataset: datasetType) => {
  72 + //不能用!,会排除0,0也是需要显示的
  73 + return executeFn(inputValue, dataset) === undefined ? '' : executeFn(inputValue, dataset)
  74 +}
  75 +
  76 +const handleGroupListById = (
  77 + groupList: CreateComponentType[],
  78 + id: string,
  79 + inputValue: string,
  80 + dataset: datasetType
  81 +) => {
  82 + //原生for循环优化性能
  83 + for (let index = 0, total = groupList.length; index < total; index++) {
  84 + const targetItem = groupList[index]
  85 + if (id === targetItem.id) targetItem.option.dataset = setTargetDataset(inputValue, dataset)
  86 + }
  87 +}
  88 +
  89 +//测试返回
  90 +const handleTestInput = (inputValue: string, { dataset }: GlobalThemeJsonType) => {
  91 + testInputValue.value = setTargetDataset(inputValue, dataset)
  92 +}
  93 +
  94 +watch(
  95 + () => targetData.value,
  96 + (newValue: CreateComponentType | CreateComponentGroupType) => {
  97 + try {
  98 + const uniqHistoryInputValueList = uniqBy(saveHistoryInputValueList.value, 'id')
  99 + if (uniqHistoryInputValueList) {
  100 + newValue.saveHistoryInput = JSON.stringify(uniqHistoryInputValueList)
  101 + window.localStorage.setItem('CACHE_HISTORY_INPUT_VALUE', JSON.stringify(uniqHistoryInputValueList))
  102 + }
  103 + newValue?.groupList?.forEach((item: CreateComponentType) => {
  104 + uniqHistoryInputValueList.forEach((uniqueItem: historyInputValue) => {
  105 + if (uniqueItem.id === item.id) {
  106 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  107 + handleGroupListById(newValue.groupList!, uniqueItem.id, uniqueItem.inputValue, newValue.option.dataset)
  108 + }
  109 + })
  110 + })
  111 + } catch (e) {
  112 + console.log(e)
  113 + }
  114 + },
  115 + {
  116 + deep: true
  117 + }
  118 +)
  119 +</script>
  1 +export enum TabsEnum {
  2 + PAGE_SETTING = 'pageSetting',
  3 + CHART_SETTING = 'chartSetting',
  4 + CHART_ANIMATION = 'chartAnimation',
  5 + CHART_DATA = 'chartData',
  6 + CHART_EVENT = 'chartEvent',
  7 + THINGS_KIT_CHART_DATA = 'thingsKitChartData',//新增ThingsKit自定义分组数据
  8 +}
  1 +<template>
  2 + <n-layout has-sider sider-placement="right">
  3 + <n-layout-content>
  4 + <!-- 图表拖拽区域 -->
  5 + <content-edit></content-edit>
  6 + </n-layout-content>
  7 + <n-layout-sider
  8 + collapse-mode="transform"
  9 + :collapsed-width="0"
  10 + :width="350"
  11 + :collapsed="collapsed"
  12 + :native-scrollbar="false"
  13 + show-trigger="bar"
  14 + @collapse="collapsedHandle"
  15 + @expand="expandHandle"
  16 + >
  17 + <content-box class="go-content-configurations go-boderbox" :show-top="false" :depth="2">
  18 + <!-- 页面配置 -->
  19 + <n-tabs v-if="!selectTarget" class="tabs-box" size="small" type="segment">
  20 + <n-tab-pane
  21 + v-for="item in globalTabList"
  22 + :key="item.key"
  23 + :name="item.key"
  24 + size="small"
  25 + display-directive="show:lazy"
  26 + >
  27 + <template #tab>
  28 + <n-space>
  29 + <span>{{ item.title }}</span>
  30 + <n-icon size="16" class="icon-position">
  31 + <component :is="item.icon"></component>
  32 + </n-icon>
  33 + </n-space>
  34 + </template>
  35 + <component :is="item.render"></component>
  36 + </n-tab-pane>
  37 + </n-tabs>
  38 +
  39 + <!-- 编辑 -->
  40 + <n-tabs v-if="selectTarget" v-model:value="tabsSelect" class="tabs-box" size="small" type="segment">
  41 + <n-tab-pane
  42 + v-for="item in selectTarget.isGroup ? chartsDefaultTabList : chartsTabList"
  43 + :key="item.key"
  44 + :name="item.key"
  45 + size="small"
  46 + display-directive="show:lazy"
  47 + >
  48 + <template #tab>
  49 + <n-space>
  50 + <span>{{ item.title }}</span>
  51 + <n-icon size="16" class="icon-position">
  52 + <component :is="item.icon"></component>
  53 + </n-icon>
  54 + </n-space>
  55 + </template>
  56 + <component :is="item.render"></component>
  57 + </n-tab-pane>
  58 + </n-tabs>
  59 + </content-box>
  60 + </n-layout-sider>
  61 + </n-layout>
  62 +</template>
  63 +
  64 +<script setup lang="ts">
  65 +import { ref, toRefs, watch, computed } from 'vue'
  66 +import { icon } from '@/plugins'
  67 +import { loadAsyncComponent } from '@/utils'
  68 +import { ContentBox } from '../../ContentBox/index'
  69 +import { TabsEnum } from './index.d'
  70 +import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
  71 +import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
  72 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  73 +
  74 +const { getDetails } = toRefs(useChartLayoutStore())
  75 +const { setItem } = useChartLayoutStore()
  76 +const chartEditStore = useChartEditStore()
  77 +
  78 +const { ConstructIcon, FlashIcon, DesktopOutlineIcon, LeafIcon, RocketIcon, AlbumsIcon } = icon.ionicons5
  79 +
  80 +const ContentEdit = loadAsyncComponent(() => import('../../ContentEdit/index.vue'))
  81 +const CanvasPage = loadAsyncComponent(() => import('../components/CanvasPage/index.vue'))
  82 +const ChartSetting = loadAsyncComponent(() => import('../components/ChartSetting/index.vue'))
  83 +const ChartData = loadAsyncComponent(() => import('../components/ChartData/index.vue'))
  84 +const ThingsKitChartData = loadAsyncComponent(() => import('../components/external/ThingsKitChartData/index.vue')) //新增ThingsKit自定义分组数据页面
  85 +const ChartEvent = loadAsyncComponent(() => import('../components/ChartEvent/index.vue'))
  86 +const ChartAnimation = loadAsyncComponent(() => import('../components/ChartAnimation/index.vue'))
  87 +
  88 +const collapsed = ref<boolean>(getDetails.value)
  89 +const tabsSelect = ref<TabsEnum>(TabsEnum.CHART_SETTING)
  90 +
  91 +const collapsedHandle = () => {
  92 + collapsed.value = true
  93 + setItem(ChartLayoutStoreEnum.DETAILS, true)
  94 +}
  95 +
  96 +const expandHandle = () => {
  97 + collapsed.value = false
  98 + setItem(ChartLayoutStoreEnum.DETAILS, false)
  99 +}
  100 +
  101 +const selectTarget = computed(() => {
  102 + const selectId = chartEditStore.getTargetChart.selectId
  103 + // 排除多个
  104 + if (selectId.length !== 1) return undefined
  105 + const target = chartEditStore.componentList[chartEditStore.fetchTargetIndex()]
  106 + if (target?.isGroup) {
  107 + // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  108 + tabsSelect.value = TabsEnum.CHART_SETTING
  109 + }
  110 + return target
  111 +})
  112 +
  113 +watch(getDetails, newData => {
  114 + if (newData) {
  115 + collapsedHandle()
  116 + } else {
  117 + expandHandle()
  118 + }
  119 +})
  120 +
  121 +// 页面设置
  122 +const globalTabList = [
  123 + {
  124 + key: TabsEnum.PAGE_SETTING,
  125 + title: '页面配置',
  126 + icon: DesktopOutlineIcon,
  127 + render: CanvasPage
  128 + }
  129 +]
  130 +
  131 +//THINGS_KIT 新增分组支持数据绑定
  132 +const chartsDefaultTabList = [
  133 + {
  134 + key: TabsEnum.CHART_SETTING,
  135 + title: '定制',
  136 + icon: ConstructIcon,
  137 + render: ChartSetting
  138 + },
  139 + {
  140 + key: TabsEnum.CHART_ANIMATION,
  141 + title: '动画',
  142 + icon: LeafIcon,
  143 + render: ChartAnimation
  144 + },
  145 + {
  146 + key: TabsEnum.CHART_DATA,
  147 + title: '数据',
  148 + icon: FlashIcon,
  149 + render: ChartData
  150 + },
  151 + {
  152 + key: TabsEnum.THINGS_KIT_CHART_DATA,
  153 + title: '分组',
  154 + icon: AlbumsIcon,
  155 + render: ThingsKitChartData
  156 + }
  157 +]
  158 +
  159 +const chartsTabList = [
  160 + ...chartsDefaultTabList.slice(0,2),
  161 + {
  162 + key: TabsEnum.CHART_DATA,
  163 + title: '数据',
  164 + icon: FlashIcon,
  165 + render: ChartData
  166 + },
  167 + {
  168 + key: TabsEnum.CHART_EVENT,
  169 + title: '事件',
  170 + icon: RocketIcon,
  171 + render: ChartEvent
  172 + }
  173 +]
  174 +</script>
  175 +
  176 +<style lang="scss" scoped>
  177 +@include go(content-configurations) {
  178 + overflow: hidden;
  179 + .tabs-box {
  180 + padding: 10px;
  181 + .icon-position {
  182 + padding-top: 2px;
  183 + }
  184 + }
  185 +}
  186 +</style>
@@ -51,16 +51,18 @@ const chartEditStore = useChartEditStore() @@ -51,16 +51,18 @@ const chartEditStore = useChartEditStore()
51 // 记录初始化 51 // 记录初始化
52 chartHistoryStoreStore.canvasInit(chartEditStore.getEditCanvas) 52 chartHistoryStoreStore.canvasInit(chartEditStore.getEditCanvas)
53 /** 53 /**
54 - * THINGS_KIT 升级版本这里有冲突 54 + * THINGS_KIT 修改了部分重写引入,升级版本这里有冲突
55 * 源文件 './ContentHeader/headerRightBtn/index.vue' ./ContentHeader/headerLeftBtn/index.vue' 55 * 源文件 './ContentHeader/headerRightBtn/index.vue' ./ContentHeader/headerLeftBtn/index.vue'
56 * 修改后 './ContentHeader/headerRightBtn/external/index.vue' ./ContentHeader/headerLeftBtn/external/index.vue' 56 * 修改后 './ContentHeader/headerRightBtn/external/index.vue' ./ContentHeader/headerLeftBtn/external/index.vue'
  57 + * 源文件 './ContentConfigurations/index.vue'
  58 + * 修改后 './ContentConfigurations/external/index.vue'
57 */ 59 */
58 -const HeaderLeftBtn = loadAsyncComponent(() => import('./ContentHeader/headerLeftBtn/external/index.vue'))  
59 -const HeaderRightBtn = loadAsyncComponent(() => import('./ContentHeader/headerRightBtn/external/index.vue')) 60 +const HeaderLeftBtn = loadAsyncComponent(() => import('./ContentHeader/headerLeftBtn/external/index.vue'))//修改引入路径!!!
  61 +const HeaderRightBtn = loadAsyncComponent(() => import('./ContentHeader/headerRightBtn/external/index.vue'))//修改引入路径!!!
60 const HeaderTitle = loadAsyncComponent(() => import('./ContentHeader/headerTitle/index.vue')) 62 const HeaderTitle = loadAsyncComponent(() => import('./ContentHeader/headerTitle/index.vue'))
61 const ContentLayers = loadAsyncComponent(() => import('./ContentLayers/index.vue')) 63 const ContentLayers = loadAsyncComponent(() => import('./ContentLayers/index.vue'))
62 const ContentCharts = loadAsyncComponent(() => import('./ContentCharts/index.vue')) 64 const ContentCharts = loadAsyncComponent(() => import('./ContentCharts/index.vue'))
63 -const ContentConfigurations = loadAsyncComponent(() => import('./ContentConfigurations/index.vue')) 65 +const ContentConfigurations = loadAsyncComponent(() => import('./ContentConfigurations/external/index.vue'))//修改引入路径!!!
64 const ContentLoad = loadAsyncComponent(() => import('./ContentLoad/index.vue')) 66 const ContentLoad = loadAsyncComponent(() => import('./ContentLoad/index.vue'))
65 67
66 // 右键 68 // 右键