Commit 8729a3386eb614df2c64417bc99ee1bfcd141f84
1 parent
40713fe6
perf(src/): 优化给分组这个整体绑定动态数据,其下所有子组件共享分组数据,子组件支持动态设置自身数据
Showing
9 changed files
with
371 additions
and
7 deletions
| 1 | 1 | import { ref, toRefs, toRaw, watch } from 'vue' |
| 2 | 2 | import type VChart from 'vue-echarts' |
| 3 | 3 | import { useChartDataPondFetch } from '@/hooks/' |
| 4 | -import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d' | |
| 4 | +import { CreateComponentType, ChartFrameEnum, CreateComponentGroupType } from '@/packages/index.d' | |
| 5 | 5 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
| 6 | 6 | import { isPreview, intervalUnitHandle } from '@/utils' |
| 7 | 7 | import { setOption } from '@/packages/public/chart' |
| ... | ... | @@ -110,7 +110,18 @@ export const useChartDataFetch = ( |
| 110 | 110 | |
| 111 | 111 | if (isPreview()) { |
| 112 | 112 | requestIntervalFn() |
| 113 | + const chartEditStore = useChartEditStore() | |
| 113 | 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 | 125 | initial(targetComponent, useChartEditStore, updateCallback) |
| 115 | 126 | } |
| 116 | 127 | return { vChartRef } | ... | ... |
| ... | ... | @@ -160,6 +160,7 @@ export interface CreateComponentType extends PublicConfigType, requestConfig { |
| 160 | 160 | chartConfig: ConfigType |
| 161 | 161 | option: GlobalThemeJsonType |
| 162 | 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 | 262 | */ |
| 263 | 263 | chartEditStore.getComponentList.forEach(targetItem => { |
| 264 | 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 | 291 | targetItem.groupList?.forEach(groupItem => { |
| 267 | 292 | if (groupItem.id === id) { |
| 268 | 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 | +<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 | 51 | // 记录初始化 |
| 52 | 52 | chartHistoryStoreStore.canvasInit(chartEditStore.getEditCanvas) |
| 53 | 53 | /** |
| 54 | - * THINGS_KIT 升级版本这里有冲突 | |
| 54 | + * THINGS_KIT 修改了部分重写引入,升级版本这里有冲突 | |
| 55 | 55 | * 源文件 './ContentHeader/headerRightBtn/index.vue' ./ContentHeader/headerLeftBtn/index.vue' |
| 56 | 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 | 62 | const HeaderTitle = loadAsyncComponent(() => import('./ContentHeader/headerTitle/index.vue')) |
| 61 | 63 | const ContentLayers = loadAsyncComponent(() => import('./ContentLayers/index.vue')) |
| 62 | 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 | 66 | const ContentLoad = loadAsyncComponent(() => import('./ContentLoad/index.vue')) |
| 65 | 67 | |
| 66 | 68 | // 右键 | ... | ... |