Переглянути джерело

Merge branch 'master-fetch-dev' into master-fetch

奔跑的面条 3 роки тому
батько
коміт
2ec4f56943
47 змінених файлів з 1019 додано та 356 видалено
  1. 6 2
      src/api/axios.ts
  2. 15 3
      src/components/GoUserInfo/index.vue
  3. 1 0
      src/hooks/index.ts
  4. 25 11
      src/hooks/useChartDataFetch.hook.ts
  5. 93 0
      src/hooks/useChartDataPondFetch.hook.ts
  6. 38 9
      src/hooks/useLifeHandler.hook.ts
  7. 3 5
      src/packages/components/Charts/Bars/CapsuleChart/index.vue
  8. 2 1
      src/packages/components/Charts/Lines/LineCommon/config.ts
  9. 2 1
      src/packages/components/Charts/Lines/LineGradientSingle/config.ts
  10. 2 1
      src/packages/components/Charts/Lines/LineGradients/config.ts
  11. 2 1
      src/packages/components/Charts/Lines/LineLinearSingle/config.ts
  12. 2 1
      src/packages/components/Charts/Maps/MapBase/config.ts
  13. 2 1
      src/packages/components/Charts/Pies/PieCircle/config.ts
  14. 2 1
      src/packages/components/Charts/Pies/PieCommon/config.ts
  15. 10 4
      src/packages/components/Tables/Tables/TableList/index.vue
  16. 22 5
      src/packages/index.d.ts
  17. 20 2
      src/packages/public/publicConfig.ts
  18. 10 4
      src/plugins/icon.ts
  19. 11 0
      src/store/modules/chartEditStore/chartEditStore.d.ts
  20. 2 1
      src/store/modules/chartEditStore/chartEditStore.ts
  21. 8 0
      src/store/modules/chartLayoutStore/chartLayoutStore.d.ts
  22. 21 15
      src/store/modules/chartLayoutStore/chartLayoutStore.ts
  23. 62 7
      src/views/chart/ContentCharts/components/ChartsItemBox/index.vue
  24. 0 1
      src/views/chart/ContentCharts/components/ChartsOptionContent/index.vue
  25. 158 103
      src/views/chart/ContentCharts/components/ChartsSearch/index.vue
  26. 2 2
      src/views/chart/ContentConfigurations/components/CanvasPage/index.vue
  27. 1 1
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue
  28. 3 4
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue
  29. 3 2
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataRequest/components/RequestTargetConfig/index.vue
  30. 0 0
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/importTemplate.ts
  31. 3 0
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/index.ts
  32. 24 59
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/index.vue
  33. 3 0
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.ts
  34. 215 0
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue
  35. 0 3
      src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventMonacoEditor/index.ts
  36. 51 0
      src/views/chart/ContentConfigurations/components/ChartEvent/components/index.scss
  37. 4 9
      src/views/chart/ContentConfigurations/components/ChartEvent/index.vue
  38. 2 0
      src/views/chart/ContentEdit/components/EditTools/hooks/useFile.hooks.ts
  39. 65 0
      src/views/chart/ContentEdit/components/EditTools/hooks/useSyncUpdate.hook.ts
  40. 10 56
      src/views/chart/ContentEdit/components/EditTools/index.vue
  41. 2 1
      src/views/chart/ContentLayers/index.vue
  42. 77 27
      src/views/chart/hooks/useSync.hook.ts
  43. 16 8
      src/views/edit/index.vue
  44. 1 0
      src/views/login/index.vue
  45. 2 0
      src/views/preview/components/PreviewRenderGroup/index.vue
  46. 13 2
      src/views/preview/components/PreviewRenderList/index.vue
  47. 3 3
      src/views/preview/wrapper.vue

+ 6 - 2
src/api/axios.ts

@@ -1,10 +1,10 @@
 import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
-import { ResultEnum } from "@/enums/httpEnum"
+import { ResultEnum, ModuleTypeEnum } from "@/enums/httpEnum"
 import { PageEnum, ErrorPageNameMap } from "@/enums/pageEnum"
 import { StorageEnum } from '@/enums/storageEnum'
 import { axiosPre } from '@/settings/httpSetting'
 import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
-import { redirectErrorPage, getLocalStorage, routerTurnByName, httpErrorHandle } from '@/utils'
+import { redirectErrorPage, getLocalStorage, routerTurnByName, isPreview } from '@/utils'
 import { fetchAllowList } from './axios.config'
 import includes from 'lodash/includes'
 
@@ -39,6 +39,10 @@ axiosInstance.interceptors.request.use(
 // 响应拦截器
 axiosInstance.interceptors.response.use(
   (res: AxiosResponse) => {
+    // 预览页面错误不进行处理
+    if (isPreview()) {
+      return Promise.resolve(res.data)
+    }
     const { code } = res.data as { code: number }
 
     // 成功

+ 15 - 3
src/components/GoUserInfo/index.vue

@@ -27,7 +27,9 @@
 <script lang="ts" setup>
 import { h, ref } from 'vue'
 import { NAvatar, NText } from 'naive-ui'
-import { renderIcon } from '@/utils'
+import { renderIcon, getLocalStorage } from '@/utils'
+import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
+import { StorageEnum } from '@/enums/storageEnum'
 import { logout, renderLang } from '@/utils'
 import { GoSystemSet } from '@/components/GoSystemSet/index'
 import { GoSystemInfo } from '@/components/GoSystemInfo/index'
@@ -64,7 +66,17 @@ const renderUserInfo = () => {
       }),
       h('div', null, [
         h('div', null, [
-          h(NText, { depth: 2 }, { default: () => '奔跑的面条' })
+            h(NText, { depth: 2 }, {
+                default: () => {
+                    const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
+                    if (info) {
+                        return info[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_NAME];
+                    }
+                    else {
+                        return 'admin';
+                    }
+                }
+            })
         ])
       ])
     ]
@@ -137,4 +149,4 @@ const handleSelect = (key: string) => {
   cursor: pointer;
   transform: scale(0.7);
 }
-</style>
+</style>

+ 1 - 0
src/hooks/index.ts

@@ -3,5 +3,6 @@ export * from '@/hooks/usePreviewScale.hook'
 export * from '@/hooks/useCode.hook'
 export * from '@/hooks/useChartDataFetch.hook'
 export * from '@/hooks/useSystemInit.hook'
+export * from '@/hooks/useChartDataPondFetch.hook'
 export * from '@/hooks/useLifeHandler.hook'
 export * from '@/hooks/useLang.hook'

+ 25 - 11
src/hooks/useChartDataFetch.hook.ts

@@ -1,6 +1,7 @@
 import { ref, toRefs, toRaw } from 'vue'
 import type VChart from 'vue-echarts'
 import { customizeHttp } from '@/api/http'
+import { useChartDataPondFetch } from '@/hooks/'
 import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
 import { RequestDataTypeEnum } from '@/enums/httpEnum'
@@ -23,6 +24,22 @@ export const useChartDataFetch = (
   const vChartRef = ref<typeof VChart | null>(null)
   let fetchInterval: any = 0
 
+  // 数据池
+  const { addGlobalDataInterface } = useChartDataPondFetch()
+  const { requestDataPondId } = toRefs(targetComponent.request)
+
+  // 组件类型
+  const { chartFrame } = targetComponent.chartConfig
+  
+  // eCharts 组件配合 vChart 库更新方式
+  const echartsUpdateHandle = (dataset: any) => {
+    if (chartFrame === ChartFrameEnum.ECHARTS) {
+      if (vChartRef.value) {
+        vChartRef.value.setOption({ dataset: dataset })
+      }
+    }
+  }
+
   const requestIntervalFn = () => {
     const chartEditStore = useChartEditStore()
 
@@ -41,9 +58,6 @@ export const useChartDataFetch = (
       requestInterval: targetInterval
     } = toRefs(targetComponent.request)
 
-    // 组件类型
-    const { chartFrame } = targetComponent.chartConfig
-
     // 非请求类型
     if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
 
@@ -58,16 +72,11 @@ export const useChartDataFetch = (
         clearInterval(fetchInterval)
 
         const fetchFn = async () => {
-          const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.requestGlobalConfig))
+          const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
           if (res) {
             try {
               const filter = targetComponent.filter
-              // eCharts 组件配合 vChart 库更新方式
-              if (chartFrame === ChartFrameEnum.ECHARTS) {
-                if (vChartRef.value) {
-                  vChartRef.value.setOption({ dataset: newFunctionHandle(res?.data, res, filter) })
-                }
-              }
+              echartsUpdateHandle(newFunctionHandle(res?.data, res, filter))
               // 更新回调函数
               if (updateCallback) {
                 updateCallback(newFunctionHandle(res?.data, res, filter))
@@ -94,6 +103,11 @@ export const useChartDataFetch = (
     }
   }
 
-  isPreview() && requestIntervalFn()
+  if (isPreview()) {
+    // 判断是否有数据池对应 id
+    requestDataPondId
+      ? addGlobalDataInterface(targetComponent, useChartEditStore, updateCallback || echartsUpdateHandle)
+      : requestIntervalFn()
+  }
   return { vChartRef }
 }

+ 93 - 0
src/hooks/useChartDataPondFetch.hook.ts

@@ -0,0 +1,93 @@
+import { toRaw } from 'vue'
+import { customizeHttp } from '@/api/http'
+import { CreateComponentType } from '@/packages/index.d'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { RequestGlobalConfigType, RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d'
+import { newFunctionHandle } from '@/utils'
+
+// 获取类型
+type ChartEditStoreType = typeof useChartEditStore
+
+// 数据池存储的数据类型
+type DataPondMapType = {
+  updateCallback: (...args: any) => any
+  filter?: string | undefined
+}
+
+// 数据池 Map 中请求对应 callback
+const mittDataPondMap = new Map<string, DataPondMapType[]>()
+
+// 创建单个数据项轮询接口
+const newPondItemInterval = (
+  requestGlobalConfig: RequestGlobalConfigType,
+  requestDataPondItem: RequestDataPondItemType,
+  dataPondMapItem?: DataPondMapType[]
+) => {
+  if (!dataPondMapItem) return
+
+  // 请求
+  const fetchFn = async () => {
+    try {
+      const res = await customizeHttp(toRaw(requestDataPondItem.dataPondRequestConfig), toRaw(requestGlobalConfig))
+
+      if (res) {
+        try {
+          // 遍历更新回调函数
+          dataPondMapItem.forEach(item => {
+            item.updateCallback(newFunctionHandle(res?.data, res, item.filter))
+          })
+        } catch (error) {
+          console.error(error)
+          return error
+        }
+      }
+    } catch (error) {
+      return error
+    }
+  }
+
+  // 立即调用
+  fetchFn()
+}
+
+/**
+ * 数据池接口处理
+ */
+export const useChartDataPondFetch = () => {
+  // 新增全局接口
+  const addGlobalDataInterface = (
+    targetComponent: CreateComponentType,
+    useChartEditStore: ChartEditStoreType,
+    updateCallback: (...args: any) => any
+  ) => {
+    const chartEditStore = useChartEditStore()
+    const { requestDataPond } = chartEditStore.getRequestGlobalConfig
+
+    // 组件对应的数据池 Id
+    const requestDataPondId = '111' || (targetComponent.request.requestDataPondId as string)
+    // 新增数据项
+    const mittPondIdArr = mittDataPondMap.get(requestDataPondId) || []
+    mittPondIdArr.push({
+      updateCallback: updateCallback,
+      filter: targetComponent.filter
+    })
+    mittDataPondMap.set(requestDataPondId, mittPondIdArr)
+  }
+
+  // 初始化数据池
+  const initDataPond = (requestGlobalConfig: RequestGlobalConfigType) => {
+    const { requestDataPond } = requestGlobalConfig
+    // 根据 mapId 查找对应的数据池配置
+    for (let pondKey of mittDataPondMap.keys()) {
+      const requestDataPondItem = requestDataPond.find(item => item.dataPondId === pondKey)
+      if (requestDataPondItem) {
+        newPondItemInterval(requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey))
+      }
+    }
+  }
+
+  return {
+    addGlobalDataInterface,
+    initDataPond
+  }
+}

+ 38 - 9
src/hooks/useLifeHandler.hook.ts

@@ -1,4 +1,4 @@
-import { CreateComponentType, EventLife } from '@/packages/index.d'
+import { CreateComponentType, CreateComponentGroupType, EventLife, BaseEvent } from '@/packages/index.d'
 import * as echarts from 'echarts'
 
 // 所有图表组件集合对象
@@ -7,26 +7,55 @@ const components: { [K in string]?: any } = {}
 // 项目提供的npm 包变量
 export const npmPkgs = { echarts }
 
-export const useLifeHandler = (chartConfig: CreateComponentType) => {
-  const events = chartConfig.events || {}
+// 组件事件处理 hook
+export const useLifeHandler = (chartConfig: CreateComponentType | CreateComponentGroupType) => {
+  // 处理基础事件
+  const baseEvent: { [key: string]: any } = {}
+  for (const key in chartConfig.events.baseEvent) {
+    const fnStr: string | undefined = (chartConfig.events.baseEvent as any)[key]
+    // 动态绑定基础事件
+    if (fnStr) {
+      baseEvent[key] = generateBaseFunc(fnStr)
+    }
+  }
+
   // 生成生命周期事件
+  const events = chartConfig.events.advancedEvents || {}
   const lifeEvents = {
-    [EventLife.BEFORE_MOUNT](e: any) {
+    [EventLife.VNODE_BEFORE_MOUNT](e: any) {
       // 存储组件
       components[chartConfig.id] = e.component
-      const fnStr = (events[EventLife.BEFORE_MOUNT] || '').trim()
+      const fnStr = (events[EventLife.VNODE_BEFORE_MOUNT] || '').trim()
       generateFunc(fnStr, e)
     },
-    [EventLife.MOUNTED](e: any) {
-      const fnStr = (events[EventLife.MOUNTED] || '').trim()
+    [EventLife.VNODE_MOUNTED](e: any) {
+      const fnStr = (events[EventLife.VNODE_MOUNTED] || '').trim()
       generateFunc(fnStr, e)
     }
   }
-  return lifeEvents
+  return { ...baseEvent, ...lifeEvents }
+}
+
+/**
+ * 生成基础函数
+ * @param fnStr 用户方法体代码
+ * @param event 鼠标事件
+ */
+ export function generateBaseFunc(fnStr: string) {
+  try {
+    return new Function(`
+      return (
+        async function(mouseEvent){
+          ${fnStr}
+        }
+      )`)()
+  } catch (error) {
+    console.error(error)
+  }
 }
 
 /**
- *
+ * 生成高级函数
  * @param fnStr 用户方法体代码
  * @param e 执行生命周期的动态组件实例
  */

+ 3 - 5
src/packages/components/Charts/Bars/CapsuleChart/index.vue

@@ -2,7 +2,7 @@
   <div
     v-if="state.mergedConfig"
     class="go-dv-capsule-chart"
-    :style="{ 
+    :style="{
       fontSize: numberSizeHandle(state.mergedConfig.valueFontSize),
       paddingLeft: numberSizeHandle(state.mergedConfig.paddingLeft),
       paddingRight: numberSizeHandle(state.mergedConfig.paddingRight)
@@ -53,11 +53,10 @@
 
 <script setup lang="ts">
 import { onMounted, watch, reactive, PropType } from 'vue'
-import merge from 'lodash/merge'
-import cloneDeep from 'lodash/cloneDeep'
 import { useChartDataFetch } from '@/hooks'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
 import config, { option } from './config'
+import cloneDeep from 'lodash/cloneDeep'
 
 type DataProps = {
   name: string | number
@@ -114,12 +113,11 @@ watch(
 
 const calcData = (data: any) => {
   mergeConfig(props.chartConfig.option)
-
   calcCapsuleLengthAndLabelData()
 }
 
 const mergeConfig = (data: any) => {
-  state.mergedConfig = merge(cloneDeep(state.defaultConfig), data || {})
+  state.mergedConfig = cloneDeep(data || {})
 }
 
 // 数据解析

+ 2 - 1
src/packages/components/Charts/Lines/LineCommon/config.ts

@@ -2,6 +2,7 @@ import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
 import { LineCommonConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
@@ -47,7 +48,7 @@ export const option = {
 
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = LineCommonConfig.key
-  public chartConfig = LineCommonConfig
+  public chartConfig = cloneDeep(LineCommonConfig)
   // 图表配置项
   public option = echartOptionProfixHandle(option, includes)
 }

+ 2 - 1
src/packages/components/Charts/Lines/LineGradientSingle/config.ts

@@ -3,6 +3,7 @@ import { LineGradientSingleConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
 import { graphic } from 'echarts/core'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
@@ -58,7 +59,7 @@ const options = {
 
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = LineGradientSingleConfig.key
-  public chartConfig = LineGradientSingleConfig
+  public chartConfig = cloneDeep(LineGradientSingleConfig)
   // 图表配置项
   public option = echartOptionProfixHandle(options, includes)
 }

+ 2 - 1
src/packages/components/Charts/Lines/LineGradients/config.ts

@@ -3,6 +3,7 @@ import { LineGradientsConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
 import { graphic } from 'echarts/core'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
@@ -85,7 +86,7 @@ const option = {
 
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = LineGradientsConfig.key
-  public chartConfig = LineGradientsConfig
+  public chartConfig = cloneDeep(LineGradientsConfig)
   // 图表配置项
   public option = echartOptionProfixHandle(option, includes)
 }

+ 2 - 1
src/packages/components/Charts/Lines/LineLinearSingle/config.ts

@@ -2,6 +2,7 @@ import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
 import { LineLinearSingleConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
@@ -54,7 +55,7 @@ export const option = {
 
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = LineLinearSingleConfig.key
-  public chartConfig = LineLinearSingleConfig
+  public chartConfig = cloneDeep(LineLinearSingleConfig)
   // 图表配置项
   public option = echartOptionProfixHandle(option, includes)
 }

+ 2 - 1
src/packages/components/Charts/Maps/MapBase/config.ts

@@ -2,6 +2,7 @@ import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
 import { MapBaseConfig } from './index'
 import { chartInitConfig } from '@/settings/designSetting'
 import { CreateComponentType } from '@/packages/index.d'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = []
@@ -151,6 +152,6 @@ export const MapDefaultConfig = { ...option }
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = MapBaseConfig.key
   public attr = { ...chartInitConfig, w: 750, h: 800, zIndex: -1 }
-  public chartConfig = MapBaseConfig
+  public chartConfig = cloneDeep(MapBaseConfig)
   public option = echartOptionProfixHandle(option, includes)
 }

+ 2 - 1
src/packages/components/Charts/Pies/PieCircle/config.ts

@@ -1,6 +1,7 @@
 import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
 import { PieCircleConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
+import cloneDeep from 'lodash/cloneDeep'
 
 export const includes = []
 
@@ -57,7 +58,7 @@ const option = {
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = PieCircleConfig.key
 
-  public chartConfig = PieCircleConfig
+  public chartConfig = cloneDeep(PieCircleConfig)
 
   // 图表配置项
   public option = echartOptionProfixHandle(option, includes)

+ 2 - 1
src/packages/components/Charts/Pies/PieCommon/config.ts

@@ -1,6 +1,7 @@
 import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
 import { PieCommonConfig } from './index'
 import { CreateComponentType } from '@/packages/index.d'
+import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
 export const includes = ['legend']
@@ -61,7 +62,7 @@ const option = {
 export default class Config extends PublicConfigClass implements CreateComponentType {
   public key: string = PieCommonConfig.key
 
-  public chartConfig = PieCommonConfig
+  public chartConfig = cloneDeep(PieCommonConfig)
 
   // 图表配置项
   public option = echartOptionProfixHandle(option, includes)

+ 10 - 4
src/packages/components/Tables/Tables/TableList/index.vue

@@ -62,7 +62,7 @@ const status = reactive({
 const calcRowsData = () => {
   let { dataset, rowNum, sort } = status.mergedConfig
   // @ts-ignore
-  sort && dataset.sort(({ value: a }, { value: b }) => {
+  sort &&dataset.sort(({ value: a }, { value: b  } )  => {
       if (a > b) return -1
       if (a < b) return 1
       if (a === b) return 0
@@ -94,6 +94,7 @@ const calcHeights = (onresize = false) => {
   const { rowNum, dataset } = status.mergedConfig
   const avgHeight = h.value / rowNum
   status.avgHeight = avgHeight
+
   if (!onresize) status.heights = new Array(dataset.length).fill(avgHeight)
 }
 
@@ -131,12 +132,17 @@ const stopAnimation = () => {
 const onRestart = async () => {
   try {
     if (!status.mergedConfig) return
+    let { dataset, rowNum, sort } = status.mergedConfig
     stopAnimation()
     calcRowsData()
-    calcHeights(true)
-    animation(true)
+    let flag = true
+    if (dataset.length <= rowNum) {
+      flag=false
+    }
+    calcHeights(flag)
+    animation(flag)
   } catch (error) {
-    console.log(error)
+    console.error(error)
   }
 }
 

+ 22 - 5
src/packages/index.d.ts

@@ -90,12 +90,24 @@ export const BlendModeEnumList = [
   { label: '亮度', value: 'luminosity' }
 ]
 
+// 基础事件类型(vue不加 on)
+export enum BaseEvent {
+  // 点击
+  ON_CLICK = 'click',
+  // 双击
+  ON_DBL_CLICK = 'dblclick',
+  // 移入
+  ON_MOUSE_ENTER = 'mouseenter',
+  // 移出
+  ON_MOUSE_LEAVE = 'mouseleave',
+}
+
 // vue3 生命周期事件
-export enum EventLife {
+export enum EventLife { 
   // 渲染之后
-  MOUNTED = 'vnodeMounted',
+  VNODE_MOUNTED = 'vnodeMounted',
   // 渲染之前
-  BEFORE_MOUNT = 'vnodeBeforeMount',
+  VNODE_BEFORE_MOUNT = 'vnodeBeforeMount',
 }
 
 // 组件实例类
@@ -123,8 +135,13 @@ export interface PublicConfigType {
   }
   filter?: string
   status: StatusType
-  events?: {
-    [K in EventLife]?: string
+  events: {
+    baseEvent: {
+      [K in BaseEvent]?: string
+    },
+    advancedEvents: {
+      [K in EventLife]?: string
+    }
   }
 }
 

+ 20 - 2
src/packages/public/publicConfig.ts

@@ -1,5 +1,4 @@
 import { getUUID } from '@/utils'
-import { ChartFrameEnum, PublicConfigType, CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
 import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
 import { groupTitle } from '@/settings/designSetting'
 import {
@@ -9,6 +8,14 @@ import {
   RequestContentTypeEnum,
   RequestBodyEnum
 } from '@/enums/httpEnum'
+import {
+  BaseEvent,
+  EventLife,
+  ChartFrameEnum,
+  PublicConfigType,
+  CreateComponentType,
+  CreateComponentGroupType
+} from '@/packages/index.d'
 import { chartInitConfig } from '@/settings/designSetting'
 import cloneDeep from 'lodash/cloneDeep'
 
@@ -82,7 +89,18 @@ export class PublicConfigClass implements PublicConfigType {
   // 数据过滤
   public filter = undefined
   // 事件
-  public events = undefined
+  public events = {
+    baseEvent: {
+      [BaseEvent.ON_CLICK]: undefined,
+      [BaseEvent.ON_DBL_CLICK]: undefined,
+      [BaseEvent.ON_MOUSE_ENTER]: undefined,
+      [BaseEvent.ON_MOUSE_LEAVE]: undefined
+    },
+    advancedEvents: {
+      [EventLife.VNODE_MOUNTED]: undefined,
+      [EventLife.VNODE_BEFORE_MOUNT]: undefined
+    }
+  }
 }
 
 // 多选成组类

+ 10 - 4
src/plugins/icon.ts

@@ -64,7 +64,8 @@ import {
   Images as ImagesIcon,
   List as ListIcon,
   EyeOutline as EyeOutlineIcon,
-  EyeOffOutline as EyeOffOutlineIcon
+  EyeOffOutline as EyeOffOutlineIcon,
+  Albums as AlbumsIcon
 } from '@vicons/ionicons5'
 
 import {
@@ -97,7 +98,8 @@ import {
   Carbon3DCursor as Carbon3DCursorIcon,
   Carbon3DSoftware as Carbon3DSoftwareIcon,
   Filter as FilterIcon,
-  FilterEdit as FilterEditIcon
+  FilterEdit as FilterEditIcon,
+  Laptop as LaptopIcon
 } from '@vicons/carbon'
 
 const ionicons5 = {
@@ -232,7 +234,9 @@ const ionicons5 = {
   ListIcon,
   // 眼睛
   EyeOutlineIcon,
-  EyeOffOutlineIcon
+  EyeOffOutlineIcon,
+  // 图表列表 
+  AlbumsIcon
 }
 
 const carbon = {
@@ -285,7 +289,9 @@ const carbon = {
   Carbon3DSoftwareIcon,
   // 过滤器
   FilterIcon,
-  FilterEditIcon
+  FilterEditIcon,
+  // 图层
+  LaptopIcon
 }
 
 // https://www.xicons.org/#/ 还有很多

+ 11 - 0
src/store/modules/chartEditStore/chartEditStore.d.ts

@@ -185,16 +185,27 @@ type RequestPublicConfigType = {
   requestParams: RequestParams
 }
 
+// 数据池项类型
+export type RequestDataPondItemType = {
+  dataPondId: string,
+  dataPondName: string,
+  dataPondRequestConfig: RequestConfigType
+}
+
 // 全局的图表请求配置
 export interface RequestGlobalConfigType extends RequestPublicConfigType {
   // 组件定制轮询时间
   requestInterval: number
   // 请求源地址
   requestOriginUrl?: string
+  // 公共数据池
+  requestDataPond: RequestDataPondItemType[]
 }
 
 // 单个图表请求配置
 export interface RequestConfigType extends RequestPublicConfigType {
+  // 所选全局数据池的对应 id
+  requestDataPondId?: string
   // 组件定制轮询时间
   requestInterval?: number
   // 获取数据的方式

+ 2 - 1
src/store/modules/chartEditStore/chartEditStore.ts

@@ -126,11 +126,12 @@ export const useChartEditStore = defineStore({
       chartThemeColor: defaultTheme || 'dark',
       // 全局配置
       chartThemeSetting: globalThemeJson,
-      // 预览方式
+      // 适配方式
       previewScaleType: previewScaleType
     },
     // 数据请求处理(需存储给后端)
     requestGlobalConfig: {
+      requestDataPond: [],
       requestOriginUrl: '',
       requestInterval: requestInterval,
       requestIntervalUnit: requestIntervalUnit,

+ 8 - 0
src/store/modules/chartLayoutStore/chartLayoutStore.d.ts

@@ -1,3 +1,8 @@
+export enum ChartModeEnum {
+  SINGLE= 'single',
+  DOUBLE = 'double'
+}
+
 export enum LayerModeEnum {
   THUMBNAIL = 'thumbnail',
   TEXT = 'text'
@@ -7,6 +12,7 @@ export enum ChartLayoutStoreEnum {
   LAYERS = 'layers',
   CHARTS = 'charts',
   DETAILS = 'details',
+  Chart_TYPE = 'chartType',
   LAYER_TYPE = 'layerType'
 }
 
@@ -17,6 +23,8 @@ export interface ChartLayoutType {
   [ChartLayoutStoreEnum.CHARTS]: boolean
   // 详情设置
   [ChartLayoutStoreEnum.DETAILS]: boolean
+  // 组件展示方式
+  [ChartLayoutStoreEnum.Chart_TYPE]: ChartModeEnum
   // 层级展示方式
   [ChartLayoutStoreEnum.LAYER_TYPE]: LayerModeEnum
 }

+ 21 - 15
src/store/modules/chartLayoutStore/chartLayoutStore.ts

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia'
-import { ChartLayoutType, LayerModeEnum } from './chartLayoutStore.d'
+import { ChartLayoutType, LayerModeEnum, ChartModeEnum } from './chartLayoutStore.d'
 import { setLocalStorage, getLocalStorage } from '@/utils'
 import { StorageEnum } from '@/enums/storageEnum'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -8,22 +8,25 @@ const chartEditStore = useChartEditStore()
 
 const { GO_CHART_LAYOUT_STORE } = StorageEnum
 
-const storageChartLayout: ChartLayoutType = getLocalStorage(GO_CHART_LAYOUT_STORE)
+const storageChartLayout: Partial<ChartLayoutType> = getLocalStorage(GO_CHART_LAYOUT_STORE)
 
 // 编辑区域布局和静态设置
 export const useChartLayoutStore = defineStore({
   id: 'useChartLayoutStore',
-  state: (): ChartLayoutType =>
-    storageChartLayout || {
-      // 图层控制
-      layers: true,
-      // 图表组件
-      charts: true,
-      // 详情设置(收缩为true)
-      details: false,
-      // 图层类型(默认图片)
-      layerType: LayerModeEnum.THUMBNAIL
-    },
+  state: (): ChartLayoutType => ({
+    // 图层控制
+    layers: true,
+    // 图表组件
+    charts: true,
+    // 详情设置(收缩为true)
+    details: false,
+    // 组件列表展示类型(默认单列)
+    chartType: ChartModeEnum.SINGLE,
+    // 图层类型(默认图片)
+    layerType: LayerModeEnum.THUMBNAIL,
+    // 防止值不存在
+    ...storageChartLayout
+  }),
   getters: {
     getLayers(): boolean {
       return this.layers
@@ -34,6 +37,9 @@ export const useChartLayoutStore = defineStore({
     getDetails(): boolean {
       return this.details
     },
+    getChartType(): ChartModeEnum {
+      return this.chartType
+    },
     getLayerType(): LayerModeEnum {
       return this.layerType
     }
@@ -41,8 +47,8 @@ export const useChartLayoutStore = defineStore({
   actions: {
     setItem<T extends keyof ChartLayoutType, K extends ChartLayoutType[T]>(key: T, value: K): void {
       this.$patch(state => {
-        state[key]= value
-      });
+        state[key] = value
+      })
       setLocalStorage(GO_CHART_LAYOUT_STORE, this.$state)
       // 重新计算拖拽区域缩放比例
       setTimeout(() => {

+ 62 - 7
src/views/chart/ContentCharts/components/ChartsItemBox/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="go-content-charts-item-box">
+  <div class="go-content-charts-item-box" :class="{ double: chartMode === ChartModeEnum.DOUBLE }">
     <!-- 每一项组件的渲染 -->
     <div
       class="item-box"
@@ -11,21 +11,30 @@
       @dblclick="dblclickHandle(item)"
     >
       <div class="list-header">
-        <mac-os-control-btn :mini="true" :disabled="true"></mac-os-control-btn>
-        <n-text class="list-header-text" depth="3">{{ item.title }}</n-text>
+        <mac-os-control-btn class="list-header-control-btn" :mini="true" :disabled="true"></mac-os-control-btn>
+        <n-text class="list-header-text" depth="3">
+          <n-ellipsis>{{ item.title }}</n-ellipsis>
+        </n-text>
       </div>
       <div class="list-center go-flex-center">
         <img class="list-img" v-lazy="item.image" alt="图表图片" />
       </div>
+      <div class="list-bottom">
+        <n-text class="list-bottom-text" depth="3">
+          <n-ellipsis style="max-width: 90%">{{ item.title }}</n-ellipsis>
+        </n-text>
+      </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { PropType } from 'vue'
+import { PropType, ref, Ref, computed } from 'vue'
 import { MacOsControlBtn } from '@/components/Tips/MacOsControlBtn/index'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
 import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
+import { ChartModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
+import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
 import { componentInstall, loadingStart, loadingFinish, loadingError } from '@/utils'
 import { DragKeyEnum } from '@/enums/editPageEnum'
 import { createComponent } from '@/packages'
@@ -41,6 +50,13 @@ defineProps({
   }
 })
 
+const chartLayoutStore = useChartLayoutStore()
+
+// 组件展示状态
+const chartMode: Ref<ChartModeEnum> = computed(() => {
+  return chartLayoutStore.getChartType
+})
+
 // 拖拽处理
 const dragStartHandle = (e: DragEvent, item: ConfigType) => {
   // 动态注册图表组件
@@ -80,13 +96,19 @@ const dblclickHandle = async (item: ConfigType) => {
 
 <style lang="scss" scoped>
 /* 列表项宽度 */
-$itemWidth: 86%;
+$itemWidth: 100%;
+$halfItemWidth: 46%;
 /* 内容高度 */
 $centerHeight: 100px;
+$halfCenterHeight: 50px;
 @include go('content-charts-item-box') {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  gap: 9px;
+  padding: 10px;
   .item-box {
-    margin: 0 7%;
-    margin-bottom: 15px;
+    margin: 0;
     width: $itemWidth;
     overflow: hidden;
     border-radius: 6px;
@@ -122,6 +144,39 @@ $centerHeight: 100px;
         @extend .go-transition;
       }
     }
+    .list-bottom {
+      display: none;
+      .list-bottom-text {
+        font-size: 12px;
+        padding-left: 5px;
+      }
+    }
+  }
+  /* 缩小展示 */
+  &.double {
+    .list-header {
+      padding: 2px 5px;
+      .list-header-text {
+        display: none;
+      }
+      .list-header-control-btn {
+        transform: scale(0.7);
+      }
+    }
+    .item-box {
+      width: $halfItemWidth;
+    }
+    .list-center {
+      height: $halfCenterHeight;
+      padding-bottom: 0px;
+      .list-img {
+        height: $halfCenterHeight;
+        width: auto;
+      }
+    }
+    .list-bottom {
+      display: block;
+    }
   }
 }
 </style>

+ 0 - 1
src/views/chart/ContentCharts/components/ChartsOptionContent/index.vue

@@ -123,7 +123,6 @@ $menuWidth: 65px;
     display: flex;
     flex-direction: column;
     align-items: center;
-    padding: 10px 0;
   }
   @include deep() {
     .n-menu-item {

+ 158 - 103
src/views/chart/ContentCharts/components/ChartsSearch/index.vue

@@ -1,82 +1,104 @@
 <template>
-  <div class="go-chart-search">
-    <n-popover
-      class="chart-search-popover"
-      :show-arrow="false"
-      :show="showPopover"
-      :to="false"
-      trigger="hover"
-      placement="bottom-start"
-    >
-      <template #trigger>
-        <n-input-group>
-          <n-input
-            v-model:value.trim="search"
-            size="small"
-            :loading="loading"
-            placeholder="请输入组件名称"
-            @update:value="searchHandle"
-          >
-            <template #suffix>
-              <n-icon v-show="!loading" :component="SearchIcon" />
-            </template>
-          </n-input>
-        </n-input-group>
-      </template>
-
-      <div class="search-list-box">
-        <n-scrollbar style="max-height: 500px">
-          <n-empty
-            v-show="!searchRes.length"
-            size="small"
-            description="没有找到组件~"
-          ></n-empty>
-          <div
-            class="list-item go-flex-items-center go-ellipsis-1"
-            v-for="item in searchRes"
-            :key="item.key"
-            :title="item.title"
-            @click="selectChartHandle(item)"
-          >
-            <img class="list-item-img" v-lazy="item.image" alt="展示图" />
-            <n-text class="list-item-fs" depth="2">{{ item.title }}</n-text>
-          </div>
-        </n-scrollbar>
-        <div class="popover-modal"></div>
-      </div>
-    </n-popover>
+  <div class="go-chart-search-box">
+    <div class="chart-search go-transition" :class="{ 'chart-search-focus': isFocus }">
+      <n-popover
+        class="chart-search-popover"
+        :show-arrow="false"
+        :show="showPopover"
+        :to="false"
+        trigger="hover"
+        placement="bottom-start"
+      >
+        <template #trigger>
+          <n-input-group>
+            <n-input
+              size="small"
+              placeholder="搜索组件"
+              v-model:value.trim="search"
+              :loading="loading"
+              @focus="focusHandle(true)"
+              @blur="focusHandle(false)"
+              @update:value="searchHandle"
+            >
+              <template #suffix>
+                <n-icon v-show="!loading" :component="SearchIcon" />
+              </template>
+            </n-input>
+          </n-input-group>
+        </template>
+
+        <div class="search-list-box">
+          <n-scrollbar style="max-height: 500px">
+            <n-empty v-show="!searchRes.length" size="small" description="没有找到组件~"></n-empty>
+            <div
+              class="list-item go-flex-items-center go-ellipsis-1"
+              v-for="item in searchRes"
+              :key="item.key"
+              :title="item.title"
+              @click="selectChartHandle(item)"
+            >
+              <img class="list-item-img" v-lazy="item.image" alt="展示图" />
+              <n-text class="list-item-fs" depth="2">{{ item.title }}</n-text>
+            </div>
+          </n-scrollbar>
+          <div class="popover-modal"></div>
+        </div>
+      </n-popover>
+    </div>
+    <n-button-group class="btn-group go-transition" :class="{ 'btn-group-focus': isFocus }" style="display: flex">
+      <n-button
+        ghost
+        size="small"
+        :key="index"
+        :type="chartMode === item.value ? 'primary' : 'tertiary'"
+        v-for="(item, index) in chartModeList"
+        @click="changeChartModeType(item.value)"
+      >
+        <n-tooltip :show-arrow="false" trigger="hover">
+          <template #trigger>
+            <n-icon size="14" :component="item.icon" />
+          </template>
+          {{ item.label }}
+        </n-tooltip>
+      </n-button>
+    </n-button-group>
   </div>
 </template>
 
 <script setup lang="ts">
 import { ref, onUnmounted } from 'vue'
 import { icon } from '@/plugins'
+import { createComponent } from '@/packages'
+import { ConfigType, CreateComponentType } from '@/packages/index.d'
 import { themeColor, MenuOptionsType } from '../../hooks/useAside.hook'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
-import { ConfigType, CreateComponentType } from '@/packages/index.d'
-import { createComponent } from '@/packages'
+import { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
+import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
 import { isString, addEventListener, removeEventListener } from '@/utils'
 import { fetchConfigComponent, fetchChartComponent } from '@/packages/index'
-import {
-  componentInstall,
-  loadingStart,
-  loadingFinish,
-  loadingError,
-} from '@/utils'
+import { componentInstall, loadingStart, loadingFinish, loadingError } from '@/utils'
 
 const props = defineProps({
   menuOptions: {
     type: Array,
-    default: () => [],
-  },
+    default: () => []
+  }
 })
 
-const { SearchIcon } = icon.ionicons5
 const chartEditStore = useChartEditStore()
+const chartLayoutStore = useChartLayoutStore()
+const { SearchIcon, AlbumsIcon, GridIcon } = icon.ionicons5
+const isFocus = ref<boolean>(false)
 const showPopover = ref<boolean>(false)
 const loading = ref<boolean | undefined>(undefined)
 const search = ref<string | null>(null)
 const searchRes = ref<ConfigType[]>([])
+const chartMode = ref<ChartModeEnum>(chartLayoutStore.getChartType)
+
+const chartModeList = [
+  { label: '单列', icon: AlbumsIcon, value: ChartModeEnum.SINGLE },
+  { label: '双列', icon: GridIcon, value: ChartModeEnum.DOUBLE }
+]
 
 // 组件数组提取
 const listFormatHandle = (options: any[]) => {
@@ -106,9 +128,7 @@ const searchHandle = (key: string | null) => {
   }
   loading.value = true
   showPopover.value = true
-  searchRes.value = List.filter(
-    (e: ConfigType) => !key || e.title.toLowerCase().includes(key.toLowerCase())
-  )
+  searchRes.value = List.filter((e: ConfigType) => !key || e.title.toLowerCase().includes(key.toLowerCase()))
   setTimeout(() => {
     loading.value = undefined
   }, 500)
@@ -145,6 +165,17 @@ const selectChartHandle = async (item: ConfigType) => {
   }
 }
 
+// 聚焦设置
+const focusHandle = (value: boolean) => {
+  isFocus.value = value
+}
+
+// 修改图表展示方式
+const changeChartModeType = (value: ChartModeEnum) => {
+  chartMode.value = value
+  chartLayoutStore.setItem(ChartLayoutStoreEnum.Chart_TYPE, value)
+}
+
 addEventListener(document, 'click', (e: Event) => {
   listenerCloseHandle(e)
 })
@@ -155,54 +186,78 @@ onUnmounted(() => {
 </script>
 
 <style lang="scss" scoped>
-$width: 178px;
-@include go('chart-search') {
-  width: $width;
-  margin-right: -12px;
-  .chart-search-popover {
-    .search-list-box {
-      width: calc(#{$width} - 30px);
-      .list-item {
-        z-index: 2;
-        position: relative;
-        cursor: pointer;
-        padding: 2px;
-        padding-left: 6px;
-        margin-bottom: 5px;
-        &-fs {
-          font-size: 12px;
-        }
-        &-img {
-          height: 28px;
-          margin-right: 5px;
-          border-radius: 5px;
-        }
-        &:hover {
-          &::before {
-            content: '';
-            position: absolute;
-            width: 3px;
-            height: 100%;
-            left: 0;
-            top: 0;
-            border-radius: 2px;
-            background-color: v-bind('themeColor');
+$width: 98px;
+$searchWidth: 176px;
+
+@include go('chart-search-box') {
+  display: flex;
+  .chart-search {
+    width: $width;
+    margin-right: 10px;
+    &.chart-search-focus {
+      width: $searchWidth;
+      &.chart-search {
+        margin-right: 0;
+      }
+    }
+    @include deep() {
+      .chart-search-popover {
+        padding-left: 5px;
+        padding-right: 8px;
+      }
+    }
+    .chart-search-popover {
+      .search-list-box {
+        width: 163px;
+        .list-item {
+          z-index: 2;
+          position: relative;
+          cursor: pointer;
+          padding: 2px;
+          padding-left: 6px;
+          margin-bottom: 5px;
+          &-fs {
+            font-size: 12px;
           }
-          &::after {
-            z-index: -1;
-            content: '';
-            position: absolute;
-            width: 100%;
-            height: 100%;
-            opacity: 0.1;
-            left: 0;
-            top: 0;
+          &-img {
+            height: 28px;
+            margin-right: 5px;
             border-radius: 5px;
-            background-color: v-bind('themeColor');
+          }
+          &:hover {
+            &::before {
+              content: '';
+              position: absolute;
+              width: 3px;
+              height: 100%;
+              left: 0;
+              top: 0;
+              border-radius: 2px;
+              background-color: v-bind('themeColor');
+            }
+            &::after {
+              z-index: -1;
+              content: '';
+              position: absolute;
+              width: 100%;
+              height: 100%;
+              opacity: 0.1;
+              left: 0;
+              top: 0;
+              border-radius: 5px;
+              background-color: v-bind('themeColor');
+            }
           }
         }
       }
     }
   }
+  .btn-group {
+    width: 68px;
+    overflow: hidden;
+    &.btn-group-focus {
+      width: 0px;
+    }
+  }
 }
-</style>
+</style>

+ 2 - 2
src/views/chart/ContentConfigurations/components/CanvasPage/index.vue

@@ -75,7 +75,7 @@
         </n-button>
       </n-space>
       <n-space>
-        <n-text>预览方式</n-text>
+        <n-text>适配方式</n-text>
         <n-button-group>
           <n-button
             v-for="item in previewTypeList"
@@ -302,7 +302,7 @@ const customRequest = (options: UploadCustomRequestOptions) => {
   })
 }
 
-// 选择预览方式
+// 选择适配方式
 const selectPreviewType = (key: PreviewScaleEnum) => {
   chartEditStore.setEditCanvasConfig(EditCanvasConfigEnum.PREVIEW_SCALE_TYPE, key)
 }

+ 1 - 1
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue

@@ -125,7 +125,7 @@ const sendHandle = async () => {
   if (!targetData.value?.request) return
   loading.value = true
   try {
-    const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig))
+    const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.getRequestGlobalConfig))
     loading.value = false
     if (res) {
       if(!res?.data && !targetData.value.filter) window['$message'].warning('您的数据不符合默认格式,请配置过滤器!')

+ 3 - 4
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue

@@ -102,12 +102,11 @@
 
 <script lang="ts" setup>
 import { ref, computed, watch, toRefs, toRaw } from 'vue'
-import { MonacoEditor } from '@/components/Pages/MonacoEditor'
 import { useTargetData } from '../../../hooks/useTargetData.hook'
-import { RequestHttpEnum, RequestDataTypeEnum, ResultEnum } from '@/enums/httpEnum'
+import { MonacoEditor } from '@/components/Pages/MonacoEditor'
 import { icon } from '@/plugins'
 import { goDialog, toString } from '@/utils'
-import { http, customizeHttp } from '@/api/http'
+import { customizeHttp } from '@/api/http'
 import cloneDeep from 'lodash/cloneDeep'
 
 const { DocumentTextIcon } = icon.ionicons5
@@ -128,7 +127,7 @@ const sourceData = ref<any>('')
 // 动态获取数据
 const fetchTargetData = async () => {
   try {
-    const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig))
+    const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.getRequestGlobalConfig))
     if (res) {
       sourceData.value = res
       return

+ 3 - 2
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataRequest/components/RequestTargetConfig/index.vue

@@ -3,8 +3,9 @@
   <n-divider class="go-my-3" title-placement="left"></n-divider>
   <setting-item-box
     :itemRightStyle="{
-      gridTemplateColumns: '5fr 2fr 1fr'
+      gridTemplateColumns: '6fr 2fr'
     }"
+    style="padding-right: 25px;"
   >
     <template #name>
       地址
@@ -134,7 +135,7 @@ const apiList = [
   },
   {
     value: `【三维地球】${threeEarth01Url}`
-  },
+  }
 ]
 </script>
 

+ 0 - 0
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventMonacoEditor/importTemplate.ts → src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/importTemplate.ts


+ 3 - 0
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/index.ts

@@ -0,0 +1,3 @@
+import  ChartEventAdvancedHandle from './index.vue'
+
+export { ChartEventAdvancedHandle }

+ 24 - 59
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventMonacoEditor/index.vue → src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventAdvancedHandle/index.vue

@@ -10,13 +10,15 @@
         编辑
       </n-button>
     </template>
-    <n-card>
+    <n-card class="collapse-show-box">
       <!-- 函数体 -->
       <div v-for="eventName in EventLife" :key="eventName">
         <p>
+          <span class="func-annotate">// {{ EventLifeName[eventName] }}</span>
+          <br />
           <span class="func-keyword">async {{ eventName }}</span> (e, components, echarts, node_modules) {
         </p>
-        <p class="go-ml-4"><n-code :code="(targetData.events || {})[eventName]" language="typescript"></n-code></p>
+        <p class="go-ml-4"><n-code :code="(targetData.events.advancedEvents || {})[eventName]" language="typescript"></n-code></p>
         <p>}<span>,</span></p>
       </div>
     </n-card>
@@ -30,13 +32,15 @@
           <n-text>高级事件编辑器(配合源码使用)</n-text>
         </n-space>
       </template>
+
       <template #header-extra> </template>
+
       <n-layout has-sider sider-placement="right">
         <n-layout style="height: 580px; padding-right: 20px">
           <n-tabs v-model:value="editTab" type="card" tab-style="min-width: 100px;">
             <!-- 提示 -->
             <template #suffix>
-              <n-text class="tab-tip" type="warning">tips: {{ EventLifeTip[editTab] }}</n-text>
+              <n-text class="tab-tip" type="warning">提示: {{ EventLifeTip[editTab] }}</n-text>
             </template>
             <n-tab-pane
               v-for="(eventName, index) in EventLife"
@@ -50,7 +54,7 @@
                 <span class="func-keyNameWord">{{ eventName }}(e, components, echarts, node_modules)&nbsp;&nbsp;{</span>
               </p>
               <!-- 编辑主体 -->
-              <monaco-editor v-model:modelValue="events[eventName]" height="480px" language="javascript" />
+              <monaco-editor v-model:modelValue="advancedEvents[eventName]" height="480px" language="javascript" />
               <!-- 函数结束 -->
               <p class="go-pl-3 func-keyNameWord">}</p>
             </n-tab-pane>
@@ -136,7 +140,7 @@
               <template #icon>
                 <n-icon :component="DocumentTextIcon" />
               </template>
-              提示
+              说明
             </n-tag>
             <n-text class="go-ml-2" depth="2">通过提供的参数可为图表增加定制化的tooltip、交互事件等等</n-text>
           </div>
@@ -158,29 +162,27 @@ import { useTargetData } from '../../../hooks/useTargetData.hook'
 import { templateList } from './importTemplate'
 import { npmPkgs } from '@/hooks'
 import { icon } from '@/plugins'
-import { goDialog, toString } from '@/utils'
 import { CreateComponentType, EventLife } from '@/packages/index.d'
-import { Script } from 'vm'
 
 const { targetData, chartEditStore } = useTargetData()
 const { DocumentTextIcon, ChevronDownIcon, PencilIcon } = icon.ionicons5
 
 const EventLifeName = {
-  [EventLife.BEFORE_MOUNT]: '渲染之前',
-  [EventLife.MOUNTED]: '渲染之后'
+  [EventLife.VNODE_BEFORE_MOUNT]: '渲染之前',
+  [EventLife.VNODE_MOUNTED]: '渲染之后'
 }
 
 const EventLifeTip = {
-  [EventLife.BEFORE_MOUNT]: '此时组件 DOM 还未存在',
-  [EventLife.MOUNTED]: '此时组件 DOM 已经存在'
+  [EventLife.VNODE_BEFORE_MOUNT]: '此时组件 DOM 还未存在',
+  [EventLife.VNODE_MOUNTED]: '此时组件 DOM 已经存在'
 }
 
 // 受控弹窗
 const showModal = ref(false)
 // 编辑区域控制
-const editTab = ref(EventLife.MOUNTED)
+const editTab = ref(EventLife.VNODE_MOUNTED)
 // events 函数模板
-let events = ref({ ...targetData.value.events })
+let advancedEvents = ref({ ...targetData.value.events.advancedEvents })
 // 事件错误标识
 const errorFlag = ref(false)
 
@@ -190,7 +192,7 @@ const validEvents = () => {
   let message = ''
   let name = ''
 
-  errorFlag.value = Object.entries(events.value).every(([eventName, str]) => {
+  errorFlag.value = Object.entries(advancedEvents.value).every(([eventName, str]) => {
     try {
       // 支持await,验证语法
       const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
@@ -221,11 +223,14 @@ const saveEvents = () => {
     window['$message'].error('事件函数错误,无法进行保存')
     return
   }
-  if (Object.values(events.value).join('').trim() === '') {
+  if (Object.values(advancedEvents.value).join('').trim() === '') {
     // 清空事件
-    targetData.value.events = undefined
+    targetData.value.events.advancedEvents = {
+      vnodeBeforeMount: undefined,
+      vnodeMounted: undefined,
+    }
   } else {
-    targetData.value.events = { ...events.value }
+    targetData.value.events.advancedEvents = { ...advancedEvents.value }
   }
   closeEvents()
 }
@@ -234,52 +239,12 @@ watch(
   () => showModal.value,
   (newData: boolean) => {
     if (newData) {
-      events.value = { ...targetData.value.events }
+      advancedEvents.value = { ...targetData.value.events.advancedEvents }
     }
   }
 )
 </script>
 
 <style lang="scss" scoped>
-/* 外层也要使用 */
-.func-keyword {
-  color: #b478cf;
-}
-
-@include go('chart-data-monaco-editor') {
-  .func-keyNameWord {
-    color: #70c0e8;
-  }
-  .tab-tip {
-    font-size: 12px;
-  }
-  &.n-card.n-modal,
-  .n-card {
-    @extend .go-background-filter;
-  }
-}
-@include deep() {
-  .n-layout,
-  .n-layout-sider {
-    background-color: transparent;
-  }
-  .go-editor-area {
-    max-height: 530px;
-  }
-  .checkbox--hidden:checked {
-    & + label {
-      .n-icon {
-        transition: all 0.3s;
-        transform: rotate(180deg);
-      }
-    }
-    & ~ .go-editor-area {
-      display: none;
-    }
-  }
-  // 优化代码换行
-  .n-code > pre {
-    white-space: break-spaces;
-  }
-}
+@import '../index.scss';
 </style>

+ 3 - 0
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.ts

@@ -0,0 +1,3 @@
+import  ChartEventBaseHandle from './index.vue'
+
+export { ChartEventBaseHandle }

+ 215 - 0
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue

@@ -0,0 +1,215 @@
+<template>
+  <n-collapse-item title="基础事件配置" name="1">
+    <template #header-extra>
+      <n-button type="primary" tertiary size="small" @click.stop="showModal = true">
+        <template #icon>
+          <n-icon>
+            <pencil-icon />
+          </n-icon>
+        </template>
+        编辑
+      </n-button>
+    </template>
+    <n-card class="collapse-show-box">
+      <!-- 函数体 -->
+      <div v-for="eventName in BaseEvent" :key="eventName">
+        <p>
+          <span class="func-annotate">// {{ EventTypeName[eventName] }}</span>
+          <br />
+          <span class="func-keyword">async {{ eventName }}</span> (mouseEvent, components) {
+        </p>
+        <p class="go-ml-4">
+          <n-code :code="(targetData.events.baseEvent || {})[eventName]" language="typescript"></n-code>
+        </p>
+        <p>}<span>,</span></p>
+      </div>
+    </n-card>
+  </n-collapse-item>
+
+  <!-- 弹窗 -->
+  <n-modal class="go-chart-data-monaco-editor" v-model:show="showModal" :mask-closable="false">
+    <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1200px; height: 700px">
+      <template #header>
+        <n-space>
+          <n-text>基础事件编辑器</n-text>
+        </n-space>
+      </template>
+
+      <template #header-extra> </template>
+      <n-layout has-sider sider-placement="right">
+        <n-layout style="height: 580px; padding-right: 20px">
+          <n-tabs v-model:value="editTab" type="card" tab-style="min-width: 100px;">
+            <!-- 提示 -->
+            <template #suffix>
+              <n-text class="tab-tip" type="warning">提示: ECharts 组件会拦截鼠标事件</n-text>
+            </template>
+            <n-tab-pane
+              v-for="(eventName, index) in BaseEvent"
+              :key="index"
+              :tab="`${EventTypeName[eventName]}-${eventName}`"
+              :name="eventName"
+            >
+              <!-- 函数名称 -->
+              <p class="go-pl-3">
+                <span class="func-keyword">async function &nbsp;&nbsp;</span>
+                <span class="func-keyNameWord">{{ eventName }}(mouseEvent)&nbsp;&nbsp;{</span>
+              </p>
+              <!-- 编辑主体 -->
+              <monaco-editor v-model:modelValue="baseEvent[eventName]" height="480px" language="javascript" />
+              <!-- 函数结束 -->
+              <p class="go-pl-3 func-keyNameWord">}</p>
+            </n-tab-pane>
+          </n-tabs>
+        </n-layout>
+        <n-layout-sider
+          :collapsed-width="14"
+          :width="340"
+          show-trigger="bar"
+          collapse-mode="transform"
+          content-style="padding: 12px 12px 0px 12px;margin-left: 3px;"
+        >
+          <n-tabs default-value="1" justify-content="space-evenly" type="segment">
+            <!-- 验证结果 -->
+            <n-tab-pane tab="验证结果" name="1" size="small">
+              <n-scrollbar trigger="none" style="max-height: 505px">
+                <n-collapse class="go-px-3" arrow-placement="right" :default-expanded-names="[1, 2, 3]">
+                  <template v-for="error in [validEvents()]" :key="error">
+                    <n-collapse-item title="错误函数" :name="1">
+                      <n-text depth="3">{{ error.errorFn || '暂无' }}</n-text>
+                    </n-collapse-item>
+                    <n-collapse-item title="错误信息" :name="2">
+                      <n-text depth="3">{{ error.name || '暂无' }}</n-text>
+                    </n-collapse-item>
+                    <n-collapse-item title="堆栈信息" :name="3">
+                      <n-text depth="3">{{ error.message || '暂无' }}</n-text>
+                    </n-collapse-item>
+                  </template>
+                </n-collapse>
+              </n-scrollbar>
+            </n-tab-pane>
+            <!-- 辅助说明 -->
+            <n-tab-pane tab="变量说明" name="2">
+              <n-scrollbar trigger="none" style="max-height: 505px">
+                <n-collapse class="go-px-3" arrow-placement="right" :default-expanded-names="[1, 2]">
+                  <n-collapse-item title="mouseEvent" :name="1">
+                    <n-text depth="3">鼠标事件对象</n-text>
+                  </n-collapse-item>
+                </n-collapse>
+              </n-scrollbar>
+            </n-tab-pane>
+          </n-tabs>
+        </n-layout-sider>
+      </n-layout>
+
+      <template #action>
+        <n-space justify="space-between">
+          <div class="go-flex-items-center">
+            <n-tag :bordered="false" type="primary">
+              <template #icon>
+                <n-icon :component="DocumentTextIcon" />
+              </template>
+              说明
+            </n-tag>
+            <n-text class="go-ml-2" depth="2">编写方式同正常 JavaScript 写法</n-text>
+          </div>
+
+          <n-space>
+            <n-button size="medium" @click="closeEvents">取消</n-button>
+            <n-button size="medium" type="primary" @click="saveEvents">保存</n-button>
+          </n-space>
+        </n-space>
+      </template>
+    </n-card>
+  </n-modal>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch, toRefs, toRaw } from 'vue'
+import { MonacoEditor } from '@/components/Pages/MonacoEditor'
+import { useTargetData } from '../../../hooks/useTargetData.hook'
+import { CreateComponentType, BaseEvent } from '@/packages/index.d'
+import { icon } from '@/plugins'
+
+const { targetData, chartEditStore } = useTargetData()
+const { DocumentTextIcon, ChevronDownIcon, PencilIcon } = icon.ionicons5
+
+const EventTypeName = {
+  [BaseEvent.ON_CLICK]: '单击',
+  [BaseEvent.ON_DBL_CLICK]: '双击',
+  [BaseEvent.ON_MOUSE_ENTER]: '鼠标进入',
+  [BaseEvent.ON_MOUSE_LEAVE]: '鼠标移出'
+}
+
+// 受控弹窗
+const showModal = ref(false)
+// 编辑区域控制
+const editTab = ref(BaseEvent.ON_CLICK)
+// events 函数模板
+let baseEvent = ref({ ...targetData.value.events.baseEvent })
+// 事件错误标识
+const errorFlag = ref(false)
+
+// 验证语法
+const validEvents = () => {
+  let errorFn = ''
+  let message = ''
+  let name = ''
+
+  errorFlag.value = Object.entries(baseEvent.value).every(([eventName, str]) => {
+    try {
+      // 支持await,验证语法
+      const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
+      new AsyncFunction(str)
+      return true
+    } catch (error: any) {
+      message = error.message
+      name = error.name
+      errorFn = eventName
+      return false
+    }
+  })
+  return {
+    errorFn,
+    message,
+    name
+  }
+}
+
+// 关闭事件
+const closeEvents = () => {
+  showModal.value = false
+}
+
+// 新增事件
+const saveEvents = () => {
+  if (validEvents().errorFn) {
+    window['$message'].error('事件函数错误,无法进行保存')
+    return
+  }
+  if (Object.values(baseEvent.value).join('').trim() === '') {
+    // 清空事件
+    targetData.value.events.baseEvent = {
+      [BaseEvent.ON_CLICK]: undefined,
+      [BaseEvent.ON_DBL_CLICK]: undefined,
+      [BaseEvent.ON_MOUSE_ENTER]: undefined,
+      [BaseEvent.ON_MOUSE_LEAVE]: undefined
+    }
+  } else {
+    targetData.value.events.baseEvent = { ...baseEvent.value }
+  }
+  closeEvents()
+}
+
+watch(
+  () => showModal.value,
+  (newData: boolean) => {
+    if (newData) {
+      baseEvent.value = { ...targetData.value.events.baseEvent }
+    }
+  }
+)
+</script>
+
+<style lang="scss" scoped>
+@import '../index.scss';
+</style>

+ 0 - 3
src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventMonacoEditor/index.ts

@@ -1,3 +0,0 @@
-import  ChartEventMonacoEditor from './index.vue'
-
-export { ChartEventMonacoEditor }

+ 51 - 0
src/views/chart/ContentConfigurations/components/ChartEvent/components/index.scss

@@ -0,0 +1,51 @@
+/* 外层也要使用 */
+.func-keyword {
+  color: #b478cf;
+}
+
+.func-annotate {
+  color: #70c0e8;
+}
+
+@include go('chart-data-monaco-editor') {
+  .func-keyNameWord {
+    color: #70c0e8;
+  }
+  .tab-tip {
+    font-size: 12px;
+  }
+  &.n-card.n-modal,
+  .n-card {
+    @extend .go-background-filter;
+  }
+}
+@include deep() {
+  .n-layout,
+  .n-layout-sider {
+    background-color: transparent;
+  }
+  .collapse-show-box {
+    .n-card__content {
+      padding-left: 20px;
+      padding-right: 10px;
+    }
+  }
+  .go-editor-area {
+    max-height: 530px;
+  }
+  .checkbox--hidden:checked {
+    & + label {
+      .n-icon {
+        transition: all 0.3s;
+        transform: rotate(180deg);
+      }
+    }
+    & ~ .go-editor-area {
+      display: none;
+    }
+  }
+  // 优化代码换行
+  .n-code > pre {
+    white-space: break-spaces;
+  }
+}

+ 4 - 9
src/views/chart/ContentConfigurations/components/ChartEvent/index.vue

@@ -5,20 +5,15 @@
       组件 id:
       <n-text>{{ targetData.id }}</n-text>
     </n-text>
-    <n-collapse-item title="基础事件配置" name="1">
-      <div class="go-event">
-        <n-text depth="3">【单击、双击、移入、移出】在开发中,即将上线!</n-text>
-        <br/>
-        <n-text depth="3">(备注:高级事件模块可自行实现上述功能)</n-text>
-      </div>
-    </n-collapse-item>
-    <chart-event-monaco-editor></chart-event-monaco-editor>
+    <chart-event-base-handle></chart-event-base-handle>
+    <chart-event-advanced-handle></chart-event-advanced-handle>
   </n-collapse>
 </template>
 
 <script setup lang="ts">
 import { ref } from 'vue'
-import { ChartEventMonacoEditor } from './components/ChartEventMonacoEditor'
+import { ChartEventAdvancedHandle } from './components/ChartEventAdvancedHandle'
+import { ChartEventBaseHandle } from './components/ChartEventBaseHandle'
 import { useTargetData } from '../hooks/useTargetData.hook'
 
 const { targetData } = useTargetData()

+ 2 - 0
src/views/chart/ContentEdit/components/EditTools/hooks/useFile.hooks.ts

@@ -37,6 +37,7 @@ export const useFile = () => {
                 await updateComponent(fileData, false, true)
                 window['$message'].success('导入成功!')
               } catch (error) {
+                console.log(error)
                 window['$message'].error('组件导入失败,请检查文件完整性!')
               }
             },
@@ -47,6 +48,7 @@ export const useFile = () => {
                 await updateComponent(fileData, true, true)
                 window['$message'].success('导入成功!')
               } catch (error) {
+                console.log(error)
                 window['$message'].error('组件导入失败,请检查文件完整性!')
               }
             }

+ 65 - 0
src/views/chart/ContentEdit/components/EditTools/hooks/useSyncUpdate.hook.ts

@@ -0,0 +1,65 @@
+import { watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { useSync } from '@/views/chart/hooks/useSync.hook'
+import { ChartEnum } from '@/enums/pageEnum'
+import { SavePageEnum } from '@/enums/editPageEnum'
+import { editToJsonInterval } from '@/settings/designSetting'
+
+const { updateComponent, dataSyncUpdate } = useSync()
+const chartEditStore = useChartEditStore()
+
+// 侦听器更新
+const useSyncUpdateHandle = () => {
+  // 定义侦听器变量
+  let timer: any
+  const updateFn = (e: any) => updateComponent(e!.detail, true, false)
+  const syncData = async () => {
+    dataSyncUpdate && (await dataSyncUpdate())
+    dispatchEvent(new CustomEvent(SavePageEnum.CHART, { detail: chartEditStore.getStorageInfo }))
+  }
+
+  // 开启侦听
+  const use = () => {
+    // // 1、定时同步数据
+    // timer = setInterval(() => {
+    //   // 窗口激活并且处于工作台
+    //   document.hasFocus() && syncData()
+    // }, editToJsonInterval)
+    // // 1、定时同步数据
+    // timer = setInterval(() => {
+    //   // 窗口激活并且处于工作台
+    //   document.hasFocus() && syncData()
+    // }, editToJsonInterval)
+    // 2、失焦同步数据
+    addEventListener('blur', syncData)
+
+    // 【监听JSON代码 刷新工作台图表】
+    addEventListener(SavePageEnum.JSON, updateFn)
+  }
+
+  // 关闭侦听
+  const unUse = () => {
+    // clearInterval(timer)
+    // clearInterval(timer)
+    removeEventListener(SavePageEnum.JSON, updateFn)
+    removeEventListener('blur', syncData)
+  }
+
+  // 路由变更时处理
+  const watchHandler = (toName: any, fromName: any) => {
+    if (fromName == ChartEnum.CHART_HOME_NAME) {
+      unUse()
+    }
+    if (toName == ChartEnum.CHART_HOME_NAME) {
+      use()
+    }
+  }
+  
+  return watchHandler
+}
+
+export const useSyncUpdate = () => {
+  const routerParamsInfo = useRoute()
+  watch(() => routerParamsInfo.name, useSyncUpdateHandle(), { immediate: true })
+}

+ 10 - 56
src/views/chart/ContentEdit/components/EditTools/index.vue

@@ -62,20 +62,18 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, h, watch } from 'vue'
+import { ref, computed } from 'vue'
 import { useSettingStore } from '@/store/modules/settingStore/settingStore'
 import { ToolsStatusEnum } from '@/store/modules/settingStore/settingStore.d'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
-import { fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils'
-import { editToJsonInterval } from '@/settings/designSetting'
-import { EditEnum, ChartEnum } from '@/enums/pageEnum'
+import { fetchRouteParamsLocation, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils'
+import { EditEnum } from '@/enums/pageEnum'
 import { StorageEnum } from '@/enums/storageEnum'
 import { useRoute } from 'vue-router'
-import { useSync } from '@/views/chart/hooks/useSync.hook'
-import { SavePageEnum } from '@/enums/editPageEnum'
 import { GoSystemSet } from '@/components/GoSystemSet/index'
 import { exportHandle } from './utils'
 import { useFile } from './hooks/useFile.hooks'
+import { useSyncUpdate } from './hooks/useSyncUpdate.hook'
 import { BtnListType, TypeEnum } from './index.d'
 import { icon } from '@/plugins'
 
@@ -83,7 +81,8 @@ const { DownloadIcon, ShareIcon, PawIcon, SettingsSharpIcon, CreateIcon } = icon
 const settingStore = useSettingStore()
 const chartEditStore = useChartEditStore()
 const routerParamsInfo = useRoute()
-const { updateComponent } = useSync()
+// 初始化编辑 JSON 模块
+useSyncUpdate()
 
 // 鼠标悬停定时器
 let mouseTime: any = null
@@ -138,13 +137,13 @@ const toolsMouseoutHandle = () => {
 
 // 编辑处理
 const editHandle = () => {
-  window['$message'].warning('将开启失焦更新与 5 秒同步更新!')
+  window['$message'].warning('将开启失焦更新!')
+//   window['$message'].warning('将开启失焦更新与 5 秒同步更新!')
   setTimeout(() => {
     // 获取id路径
     const path = fetchPathByName(EditEnum.CHART_EDIT_NAME, 'href')
     if (!path) return
-    let { id } = routerParamsInfo.params as any
-    id = typeof id === 'string' ? id : id[0]
+    const id = fetchRouteParamsLocation()
     updateToSession(id)
     routerTurnByPath(path, [id], undefined, true)
   }, 1000)
@@ -170,51 +169,6 @@ const updateToSession = (id: string) => {
   }
 }
 
-// 侦听器更新
-const useSyncUpdate = () => {
-  // 定义侦听器变量
-  let timer: any
-  const updateFn = (e: any) => updateComponent(e!.detail, true, false)
-  const syncData = () => {
-    if (routerParamsInfo.name == ChartEnum.CHART_HOME_NAME) {
-      dispatchEvent(new CustomEvent(SavePageEnum.CHART, { detail: chartEditStore.getStorageInfo }))
-    }
-  }
-
-  // 开启侦听
-  const use = () => {
-    // 1、定时同步数据
-    timer = setInterval(() => {
-      // 窗口激活并且处于工作台
-      document.hasFocus() && syncData()
-    }, editToJsonInterval)
-    // 2、失焦同步数据
-    addEventListener('blur', syncData)
-
-    // 【监听JSON代码 刷新工作台图表】
-    addEventListener(SavePageEnum.JSON, updateFn)
-  }
-
-  // 关闭侦听
-  const unUse = () => {
-    clearInterval(timer)
-    removeEventListener(SavePageEnum.JSON, updateFn)
-    removeEventListener('blur', syncData)
-  }
-
-  // 路由变更时处理
-  const watchHandler = (toName: any, fromName: any) => {
-    if (fromName == ChartEnum.CHART_HOME_NAME) {
-      unUse()
-    }
-    if (toName == ChartEnum.CHART_HOME_NAME) {
-      use()
-    }
-  }
-  return watchHandler
-}
-
-watch(() => routerParamsInfo.name, useSyncUpdate(), { immediate: true })
 
 // 配置列表
 const btnList: BtnListType[] = [
@@ -234,7 +188,7 @@ const btnList: BtnListType[] = [
   {
     key: 'edit',
     type: TypeEnum.BUTTON,
-    name: '编辑JSON',
+    name: '编辑',
     icon: CreateIcon,
     handle: editHandle
   },

+ 2 - 1
src/views/chart/ContentLayers/index.vue

@@ -81,12 +81,13 @@ import { LayersGroupListItem } from './components/LayersGroupListItem/index'
 import { icon } from '@/plugins'
 
 const { LayersIcon, GridIcon, ListIcon } = icon.ionicons5
+const { LaptopIcon } = icon.carbon
 const chartLayoutStore = useChartLayoutStore()
 const chartEditStore = useChartEditStore()
 const { handleContextMenu, onClickOutSide } = useContextMenu()
 
 const layerModeList = [
-  { label: '缩略图', icon: GridIcon, value: LayerModeEnum.THUMBNAIL },
+  { label: '缩略图', icon: LaptopIcon, value: LayerModeEnum.THUMBNAIL },
   { label: '文本列表', icon: ListIcon, value: LayerModeEnum.TEXT }
 ]
 

+ 77 - 27
src/views/chart/hooks/useSync.hook.ts

@@ -20,23 +20,63 @@ import { PublicGroupConfigClass } from '@/packages/public/publicConfig'
 import merge from 'lodash/merge'
 
 /**
- * 合并处理
- * @param object 模板数据
+ * * 画布-版本升级对旧数据无法兼容的补丁
+ * @param object
+ */
+const canvasVersionUpdatePolyfill = (object: any) => {
+  return object
+}
+
+/**
+ * * 组件-版本升级对旧数据无法兼容的补丁
+ * @param newObject
+ * @param sources
+ */
+const componentVersionUpdatePolyfill = (newObject: any, sources: any) => {
+  try {
+    // 判断是否是组件
+    if (sources.id) {
+      // 处理事件补丁
+      const hasVnodeBeforeMount = 'vnodeBeforeMount' in sources.events
+      const hasVnodeMounted = 'vnodeMounted' in sources.events
+
+      if (hasVnodeBeforeMount) {
+        newObject.events.advancedEvents.vnodeBeforeMount = sources?.events.vnodeBeforeMount
+      }
+      if (hasVnodeMounted) {
+        newObject.events.advancedEvents.vnodeMounted = sources?.events.vnodeMounted
+      }
+      if (hasVnodeBeforeMount || hasVnodeMounted) {
+        sources.events = undefined
+      }
+      return newObject
+    }
+  } catch (error) {
+    return newObject
+  }
+}
+
+/**
+ * * 合并处理
+ * @param newObject 新的模板数据
  * @param sources 新拿到的数据
  * @returns object
  */
-const componentMerge = (object: any, sources: any, notComponent = false) => {
+const componentMerge = (newObject: any, sources: any, notComponent = false) => {
+  // 处理组件补丁
+  componentVersionUpdatePolyfill(newObject, sources)
+
   // 非组件不处理
-  if (notComponent) return merge(object, sources)
-  // 组件排除 options
+  if (notComponent) return merge(newObject, sources)
+  // 组件排除 newObject
   const option = sources.option
-  if (!option) return merge(object, sources)
+  if (!option) return merge(newObject, sources)
 
   // 为 undefined 的 sources 来源对象属性将被跳过详见 https://www.lodashjs.com/docs/lodash.merge
   sources.option = undefined
   if (option) {
     return {
-      ...merge(object, sources),
+      ...merge(newObject, sources),
       option: option
     }
   }
@@ -62,6 +102,9 @@ export const useSync = () => {
       chartHistoryStore.clearBackStack()
       chartHistoryStore.clearForwardStack()
     }
+    // 画布补丁处理
+    projectData.editCanvasConfig = canvasVersionUpdatePolyfill(projectData.editCanvasConfig)
+
     // 列表组件注册
     projectData.componentList.forEach(async (e: CreateComponentType | CreateComponentGroupType) => {
       const intComponent = (target: CreateComponentType) => {
@@ -132,7 +175,7 @@ export const useSync = () => {
             // 分组插入到列表
             chartEditStore.addComponentList(groupClass, false, true)
           } else {
-            await  create(comItem as CreateComponentType)
+            await create(comItem as CreateComponentType)
           }
         }
       } else {
@@ -195,7 +238,7 @@ export const useSync = () => {
   }
 
   // * 数据保存
-  const dataSyncUpdate = throttle(async () => {
+  const dataSyncUpdate = throttle(async (updateImg = true) => {
     if(!fetchRouteParamsLocation()) return
 
     let projectId = chartEditStore.getProjectInfo[ProjectInfoEnum.PROJECT_ID];
@@ -206,25 +249,32 @@ export const useSync = () => {
 
     chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
 
-    // 获取缩略图片
-    const range = document.querySelector('.go-edit-range') as HTMLElement
-    // 生成图片
-    const canvasImage: HTMLCanvasElement = await html2canvas(range, {
-      backgroundColor: null,
-      allowTaint: true,
-      useCORS: true
-    })
+    // 异常处理:缩略图上传失败不影响JSON的保存
+    try {
+      if (updateImg) {
+        // 获取缩略图片
+        const range = document.querySelector('.go-edit-range') as HTMLElement
+        // 生成图片
+        const canvasImage: HTMLCanvasElement = await html2canvas(range, {
+          backgroundColor: null,
+          allowTaint: true,
+          useCORS: true
+        })
 
-    // 上传预览图
-    let uploadParams = new FormData()
-    uploadParams.append('object', base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`))
-    const uploadRes = await uploadFile(uploadParams) as unknown as MyResponseType
-    // 保存预览图
-    if(uploadRes.code === ResultEnum.SUCCESS) {
-      await updateProjectApi({
-        id: fetchRouteParamsLocation(),
-        indexImage: `${systemStore.getFetchInfo.OSSUrl}${uploadRes.data.fileName}`
-      })
+        // 上传预览图
+        let uploadParams = new FormData()
+        uploadParams.append('object', base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`))
+        const uploadRes = await uploadFile(uploadParams) as unknown as MyResponseType
+        // 保存预览图
+        if(uploadRes.code === ResultEnum.SUCCESS) {
+          await updateProjectApi({
+            id: fetchRouteParamsLocation(),
+            indexImage: `${systemStore.getFetchInfo.OSSUrl}${uploadRes.data.fileName}`
+          })
+        }
+      }
+    } catch (e) {
+      console.log(e)
     }
 
     // 保存数据

+ 16 - 8
src/views/edit/index.vue

@@ -38,20 +38,23 @@ import { MonacoEditor } from '@/components/Pages/MonacoEditor'
 import { SavePageEnum } from '@/enums/editPageEnum'
 import { getSessionStorageInfo } from '../preview/utils'
 import type { ChartEditStorageType } from '../preview/index.d'
-import { setSessionStorage } from '@/utils'
+import { setSessionStorage, fetchRouteParamsLocation } from '@/utils'
 import { StorageEnum } from '@/enums/storageEnum'
 import { icon } from '@/plugins'
-
+import { useSync } from '@/views/chart/hooks/useSync.hook'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { ProjectInfoEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
+const chartEditStore = useChartEditStore()
+const { dataSyncUpdate } = useSync()
 const { ChevronBackOutlineIcon, DownloadIcon } = icon.ionicons5
 const showOpenFilePicker: Function = (window as any).showOpenFilePicker
-let content = ref('')
-
+const content = ref('')
 // 从sessionStorage 获取数据
-function getDataBySession() {
-  const localStorageInfo: ChartEditStorageType = getSessionStorageInfo() as unknown as ChartEditStorageType
+async function getDataBySession() {
+  const localStorageInfo: ChartEditStorageType = await getSessionStorageInfo() as unknown as ChartEditStorageType
   content.value = JSON.stringify(localStorageInfo, undefined, 2)
 }
-getDataBySession()
+setTimeout(getDataBySession)
 
 // 返回父窗口
 function back() {
@@ -86,7 +89,7 @@ document.addEventListener('keydown', function (e) {
 addEventListener('blur', updateSync)
 
 // 同步更新
-function updateSync() {
+async function updateSync() {
   if (!window.opener) {
     return window['$message'].error('源窗口已关闭,视图同步失败')
   }
@@ -94,6 +97,11 @@ function updateSync() {
     const detail = JSON.parse(content.value)
     delete detail.id
     // 保持id不变
+    // 带后端版本额外处理请求
+    if (dataSyncUpdate) {
+      chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, fetchRouteParamsLocation())
+      await dataSyncUpdate(false) // JSON界面保存不上传缩略图
+    }
     window.opener.dispatchEvent(new CustomEvent(SavePageEnum.JSON, { detail }))
   } catch (e) {
     window['$message'].error('内容格式有误')

+ 1 - 0
src/views/login/index.vue

@@ -225,6 +225,7 @@ const handleSubmit = async (e: Event) => {
         window['$message'].success(t('login.login_success'))
         routerTurnByName(PageEnum.BASE_HOME_NAME, true)
       }
+      loading.value = false
     } else {
       window['$message'].error(t('login.login_message'))
     }

+ 2 - 0
src/views/preview/components/PreviewRenderGroup/index.vue

@@ -14,6 +14,7 @@
   >
     <component
       :is="item.chartConfig.chartKey"
+      :id="item.id"
       :chartConfig="item"
       :themeSetting="themeSetting"
       :themeColor="themeColor"
@@ -29,6 +30,7 @@ import { CreateComponentGroupType } from '@/packages/index.d'
 import { animationsClass, getFilterStyle, getTransformStyle, getBlendModeStyle } from '@/utils'
 import { getSizeStyle, getComponentAttrStyle, getStatusStyle } from '../../utils'
 import { useLifeHandler } from '@/hooks'
+
 const props = defineProps({
   groupData: {
     type: Object as PropType<CreateComponentGroupType>,

+ 13 - 2
src/views/preview/components/PreviewRenderList/index.vue

@@ -10,7 +10,7 @@
       ...getTransformStyle(item.styles),
       ...getStatusStyle(item.status),
       ...getBlendModeStyle(item.styles) as any
-    } as any"
+    }"
   >
     <!-- 分组 -->
     <preview-render-group
@@ -25,6 +25,7 @@
     <component
       v-else
       :is="item.chartConfig.chartKey"
+      :id="item.id"
       :chartConfig="item"
       :themeSetting="themeSetting"
       :themeColor="themeColor"
@@ -35,7 +36,8 @@
 </template>
 
 <script setup lang="ts">
-import { PropType, computed } from 'vue'
+import { PropType, computed, onMounted } from 'vue'
+import { useChartDataPondFetch } from '@/hooks'
 import { ChartEditStorageType } from '../../index.d'
 import { PreviewRenderGroup } from '../PreviewRenderGroup/index'
 import { CreateComponentGroupType } from '@/packages/index.d'
@@ -43,6 +45,10 @@ import { chartColors } from '@/settings/chartThemes/index'
 import { animationsClass, getFilterStyle, getTransformStyle, getBlendModeStyle } from '@/utils'
 import { getSizeStyle, getComponentAttrStyle, getStatusStyle } from '../../utils'
 import { useLifeHandler } from '@/hooks'
+
+// 初始化数据池
+const { initDataPond } = useChartDataPondFetch()
+
 const props = defineProps({
   localStorageInfo: {
     type: Object as PropType<ChartEditStorageType>,
@@ -61,6 +67,11 @@ const themeColor = computed(() => {
   const chartThemeColor = props.localStorageInfo.editCanvasConfig.chartThemeColor
   return chartColors[chartThemeColor]
 })
+
+// 组件渲染结束初始化数据池
+onMounted(() => {
+  initDataPond(props.localStorageInfo.requestGlobalConfig)
+})
 </script>
 
 <style lang="scss" scoped>

+ 3 - 3
src/views/preview/wrapper.vue

@@ -12,12 +12,12 @@ import { ref } from 'vue'
 import Preview from './index.vue'
 
 let key = ref(Date.now())
-let localStorageInfo: ChartEditStorageType = getSessionStorageInfo() as unknown as ChartEditStorageType
 
-// 数据变更 -> 同步sessionStorage -> reload页面 (重新执行Mounted)
+// 数据变更 -> 组件销毁重建
 ;[SavePageEnum.JSON, SavePageEnum.CHART].forEach((saveEvent: string) => {
   if (!window.opener) return
-  window.opener.addEventListener(saveEvent, (e: any) => {
+  window.opener.addEventListener(saveEvent, async (e: any) => {
+    const localStorageInfo: ChartEditStorageType = await getSessionStorageInfo() as unknown as ChartEditStorageType
     setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [{ ...e.detail, id: localStorageInfo.id }])
     key.value = Date.now()
   })