Jelajahi Sumber

feat: 新增获取项目数据功能,新增同步数据功能

奔跑的面条 3 tahun lalu
induk
melakukan
9a5d71fb5c

+ 20 - 0
src/api/path/project.ts

@@ -32,6 +32,26 @@ export const fetchProjectApi = async (data: object) => {
   }
 }
 
+// * 保存项目
+export const saveProjectApi = async (data: object) => {
+  try { 
+    const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/save/data`, data);
+    return res;
+  } catch {
+    httpErrorHandle();
+  }
+}
+
+// * 修改项目
+export const updateProjectApi = async (data: object) => {
+  try {
+    const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/edit`, data);
+    return res;
+  } catch {
+    httpErrorHandle();
+  }
+}
+
 
 // * 删除项目
 export const deleteProjectApi = async (data: object) => {

+ 12 - 0
src/enums/editPageEnum.ts

@@ -35,4 +35,16 @@ export enum MacKeyboard {
   CTRL = '⌘',
   SHIFT = '⇧',
   ALT = '⌥',
+}
+
+// 同步状态枚举
+export enum SyncEnum {
+  // 等待
+  PENDING,
+  // 开始
+  START,
+  // 成功
+  SUCCESS,
+  // 失败
+  FAILURE
 }

+ 1 - 1
src/i18n/zh/index.ts

@@ -34,7 +34,7 @@ const global = {
 }
 
 const http = {
-  error_message: '接口异常,请检查!',
+  error_message: '获取数据失败,请稍后重试!',
   token_overdue_message: '登录过期,请重新登录!'
 }
 

+ 5 - 2
src/plugins/icon.ts

@@ -52,7 +52,8 @@ import {
   ArrowBack as ArrowBackIcon,
   ArrowForward as ArrowForwardIcon,
   Planet as PawIcon,
-  Search as SearchIcon
+  Search as SearchIcon,
+  Reload as ReloadIcon
 } from '@vicons/ionicons5'
 
 import {
@@ -190,7 +191,9 @@ const ionicons5 = {
   // 狗爪
   PawIcon,
   // 搜索(放大镜)
-  SearchIcon
+  SearchIcon,
+  // 加载
+  ReloadIcon
 }
 
 const carbon = {

+ 8 - 1
src/store/modules/chartEditStore/chartEditStore.d.ts

@@ -1,6 +1,7 @@
 import { CreateComponentType, FilterEnum} from '@/packages/index.d'
 import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
 import { RequestHttpEnum, RequestDataTypeEnum } from '@/enums/httpEnum'
+import { SyncEnum } from '@/enums/editPageEnum'
 import { PreviewScaleEnum } from '@/enums/styleEnum'
 import type {
   ChartColorsNameType,
@@ -17,9 +18,10 @@ export enum EditCanvasTypeEnum {
   LOCK_SCALE = 'lockScale',
   IS_CREATE = 'isCreate',
   IS_DRAG = 'isDrag',
+  SAVE_STATUS = 'saveStatus'
 }
 
-// 编辑区域
+// 编辑区域(临时)
 export type EditCanvasType = {
   // 编辑区域 DOM
   [EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null
@@ -36,6 +38,8 @@ export type EditCanvasType = {
   [EditCanvasTypeEnum.IS_CREATE]: boolean
   // 拖拽中
   [EditCanvasTypeEnum.IS_DRAG]: boolean
+  // 保存状态
+  [EditCanvasTypeEnum.SAVE_STATUS]: SyncEnum
 }
 
 // 滤镜/背景色/宽高主题等
@@ -50,6 +54,7 @@ export enum EditCanvasConfigEnum {
   PREVIEW_SCALE_TYPE = 'previewScaleType',
 }
 
+// 画布属性(需保存)
 export interface EditCanvasConfigType {
   // 滤镜-色相
   [FilterEnum.HUE_ROTATE]: number
@@ -137,6 +142,7 @@ export type RequestGlobalConfigType = {
   // 轮询时间
   requestInterval: number
 }
+
 // 单个图表请求配置
 export type RequestConfigType = {
   // 获取数据的方式
@@ -159,6 +165,7 @@ export interface ChartEditStoreType {
   [ChartEditStoreEnum.COMPONENT_LIST]: CreateComponentType[]
 }
 
+// 需要存储的数据内容
 export interface ChartEditStorage {
   [ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType
   [ChartEditStoreEnum.REQUEST_GLOBAL_CONFIG]: RequestGlobalConfigType

+ 9 - 5
src/store/modules/chartEditStore/chartEditStore.ts

@@ -9,9 +9,11 @@ import { requestInterval, previewScaleType } from '@/settings/designSetting'
 import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
 // 全局设置
 import { useSettingStore } from '@/store/modules/settingStore/settingStore'
+// 历史类型
 import { HistoryActionTypeEnum, HistoryItemType, HistoryTargetTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
-import { MenuEnum } from '@/enums/editPageEnum'
-import { PreviewScaleEnum } from '@/enums/styleEnum'
+// 画布枚举
+import { MenuEnum, SyncEnum } from '@/enums/editPageEnum'
+
 import {
   ChartEditStoreEnum,
   ChartEditStorage,
@@ -47,7 +49,9 @@ export const useChartEditStore = defineStore({
       // 初始化
       isCreate: false,
       // 拖拽中
-      isDrag: false
+      isDrag: false,
+      // 同步中
+      saveStatus: SyncEnum.PENDING
     },
     // 右键菜单
     rightMenuShow: false,
@@ -517,8 +521,8 @@ export const useChartEditStore = defineStore({
           attr.x -= distance
           break;
       }
-    }, 
-    // ----------------
+    },
+    // * 页面缩放设置-----------------
     // * 设置页面大小
     setPageSize(scale: number): void {
       this.setPageStyle('height', `${this.editCanvasConfig.height * scale}px`)

+ 11 - 5
src/views/chart/ContentEdit/components/EditBottom/index.vue

@@ -1,6 +1,10 @@
 <template>
   <div class="go-edit-bottom">
-    <edit-history></edit-history>
+    <div class="go-flex-items-center">
+      <edit-history></edit-history>
+      <n-divider vertical />
+      <edit-data-sync></edit-data-sync>
+    </div>
 
     <n-space class="bottom-ri">
       <!-- 快捷键提示 -->
@@ -55,7 +59,8 @@
 import { reactive, ref, toRefs, watchEffect } from 'vue'
 import { icon } from '@/plugins'
 import { EditHistory } from '../EditHistory/index'
-import EditShortcutKey from '../EditShortcutKey/index.vue'
+import { EditShortcutKey } from '../EditShortcutKey/index'
+import { EditDataSync } from '../EditDataSync/index'
 import { useDesignStore } from '@/store/modules/designStore/designStore'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
 import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
@@ -136,12 +141,13 @@ watchEffect(() => {
 <style lang="scss" scoped>
 $min-width: 500px;
 @include go('edit-bottom') {
-  width: 100%;
-  min-width: $min-width;
-  padding: 0 10px;
   display: flex;
   align-items: center;
   justify-content: space-between;
+  padding: 0 10px;
+  width: 100%;
+  min-width: $min-width;
+  height: 40px;
   .bottom-ri {
     position: relative;
     top: 15px;

+ 3 - 0
src/views/chart/ContentEdit/components/EditDataSync/index.ts

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

+ 75 - 0
src/views/chart/ContentEdit/components/EditDataSync/index.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="go-edit-data-sync go-flex-items-center">
+    <n-text class="status-desc go-ml-2" :type="descType" depth="3">
+      {{ statusDesc }}
+    </n-text>
+    <n-spin
+      v-show="saveStatus === SyncEnum.START"
+      class="status-spin go-ml-2"
+      size="small"
+    >
+      <template #icon>
+        <n-icon size="13">
+          <reload-icon />
+        </n-icon>
+      </template>
+    </n-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, toRefs, watchEffect } from 'vue'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { SyncEnum } from '@/enums/editPageEnum'
+import { icon } from '@/plugins'
+
+const { ReloadIcon } = icon.ionicons5
+const chartEditStore = useChartEditStore()
+const { saveStatus } = toRefs(chartEditStore.getEditCanvas)
+const statusDesc = ref('')
+const descType = ref('')
+
+watchEffect(() => {
+  const statusDescObj = {
+    [SyncEnum.PENDING]: {
+      text: '待同步',
+      type: '',
+    },
+    [SyncEnum.START]: {
+      text: '同步中',
+      type: 'success',
+    },
+    [SyncEnum.SUCCESS]: {
+      text: '同步成功!',
+      type: 'success',
+    },
+    [SyncEnum.FAILURE]: {
+      text: '同步失败!',
+      type: 'error',
+    },
+  }
+  statusDesc.value = statusDescObj[saveStatus.value]['text']
+  descType.value = statusDescObj[saveStatus.value]['type']
+  // 3秒重置展示
+  setTimeout(() => {
+    statusDesc.value = statusDescObj[SyncEnum.PENDING]['text']
+    descType.value = statusDescObj[SyncEnum.PENDING]['type']
+  }, 3000)
+})
+</script>
+
+<style lang="scss" scoped>
+@include go('edit-data-sync') {
+  @include deep() {
+    .n-spin {
+      width: 13px;
+      height: 13px;
+    }
+  }
+  .status-desc {
+    font-size: 12px;
+    line-height: 40px;
+    opacity: .8;
+  }
+}
+</style>

+ 1 - 4
src/views/chart/ContentEdit/components/EditHistory/index.vue

@@ -10,7 +10,7 @@
     >
       <template #trigger>
         <n-button
-          class="mr-10"
+          class="go-mr-1"
           secondary
           size="small"
           :disabled="options.length === 0"
@@ -140,9 +140,6 @@ const handleClick = () => {
 </script>
 
 <style lang="scss" scoped>
-.mr-10 {
-  margin-right: 10px;
-}
 .edit-history-popover {
   .btn-text {
     font-size: 12px;

+ 1 - 0
src/views/chart/ContentEdit/components/EditShortcutKey/index.ts

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

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

@@ -1,61 +1,12 @@
 import { ref, nextTick } from 'vue'
 import { UploadCustomRequestOptions } from 'naive-ui'
 import { FileTypeEnum } from '@/enums/fileTypeEnum'
-import { fetchChartComponent } from '@/packages/index'
-import { CreateComponentType } from '@/packages/index.d'
-import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
-import { ChartEditStoreEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
-import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
 import { readFile, goDialog } from '@/utils'
-import { createComponent } from '@/packages'
-
-// 更新函数
-const updateComponent = async (fileData: any, isSplace = false) => {
-  const chartEditStore = useChartEditStore()
-  const chartHistoryStore = useChartHistoryStore()
-  if (isSplace) {
-    // 清除列表
-    chartEditStore.componentList = []
-    // 清除历史记录
-    chartHistoryStore.clearBackStack()
-    chartHistoryStore.clearForwardStack()
-  }
-  // 列表组件注册
-  fileData.componentList.forEach(async (e: CreateComponentType) => {
-    if (!window['$vue'].component(e.chartConfig.chartKey)) {
-      window['$vue'].component(
-        e.chartConfig.chartKey,
-        fetchChartComponent(e.chartConfig)
-      )
-    }
-  })
-  // 数据赋值
-  for (const key in fileData) {
-    // 组件
-    if (key === ChartEditStoreEnum.COMPONENT_LIST) {
-      for (const comItem of fileData[key]) {
-        // 补充 class 上的方法
-        let newComponent: CreateComponentType = await createComponent(
-          comItem.chartConfig
-        )
-        // 不保存到记录
-        chartEditStore.addComponentList(
-          Object.assign(newComponent, comItem),
-          false,
-          true
-        )
-      }
-    } else {
-      // 非组件(顺便排除脏数据)
-      if(key !== 'editCanvasConfig' && key !== 'requestGlobalConfig') return
-      Object.assign((chartEditStore as any)[key], fileData[key])
-    }
-  }
-}
+import { useSync } from '@/views/chart/hooks/useSync.hook'
 
 export const useFile = () => {
   const importUploadFileListRef = ref()
-
+  const { updateComponent } = useSync()
   // 上传-前置
   //@ts-ignore
   const importBeforeUpload = ({ file }) => {

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

@@ -68,7 +68,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, h } from 'vue';
+import { ref, computed } from 'vue';
 import { useSettingStore } from '@/store/modules/settingStore/settingStore'
 import { ToolsStatusEnum } from '@/store/modules/settingStore/settingStore.d'
 import { exportHandle } from './utils'

+ 1 - 1
src/views/chart/ContentEdit/components/EditTools/utils/index.ts

@@ -9,7 +9,7 @@ export const exportHandle = () => {
 
   // 导出数据
   downloadTextFile(
-    JSON.stringify(chartEditStore.getStorageInfo || [], (k, v) => {
+    JSON.stringify(chartEditStore.getStorageInfo || {}, (k, v) => {
       return v === undefined ? null : v
     }),
     undefined,

+ 5 - 1
src/views/chart/ContentEdit/index.vue

@@ -71,6 +71,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
 
 import { useLayout } from './hooks/useLayout.hook'
 import { useAddKeyboard } from '../hooks/useKeyboard.hook'
+import { useSync } from '../hooks/useSync.hook'
 import { dragHandle, dragoverHandle, useMouseHandle } from './hooks/useDrag.hook'
 import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook'
 
@@ -82,6 +83,7 @@ import { EditTools } from './components/EditTools'
 
 const chartEditStore = useChartEditStore()
 const { handleContextMenu } = useContextMenu()
+const { dataSyncFetch } = useSync()
 
 // 布局处理
 useLayout()
@@ -121,9 +123,11 @@ const rangeStyle = computed(() => {
   }
 })
 
-// 键盘事件
 onMounted(() => {
+  // 键盘事件
   useAddKeyboard()
+  // 获取数据
+  dataSyncFetch()
 })
 </script>
 

+ 127 - 0
src/views/chart/hooks/useSync.hook.ts

@@ -0,0 +1,127 @@
+import { onUnmounted } from 'vue';
+import { useRoute } from 'vue-router'
+import { httpErrorHandle } from '@/utils'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { EditCanvasTypeEnum, ChartEditStoreEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
+import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
+import { fetchChartComponent, createComponent } from '@/packages/index'
+import { CreateComponentType } from '@/packages/index.d'
+// 接口状态
+import { ResultEnum } from '@/enums/httpEnum'
+// 接口
+import { saveProjectApi, fetchProjectApi } from '@/api/path/project'
+// 画布枚举
+import { SyncEnum } from '@/enums/editPageEnum'
+
+// 请求处理
+export const useSync = () => {
+  const chartEditStore = useChartEditStore()
+  const chartHistoryStore = useChartHistoryStore()
+  const routerParamsInfo = useRoute()
+
+  /**
+   * * 组件动态注册
+   * @param projectData 项目数据
+   * @param isSplace 是否替换数据
+   * @returns 
+   */
+  const updateComponent = async (projectData: any, isSplace = false) => {
+    if (isSplace) {
+      // 清除列表
+      chartEditStore.componentList = []
+      // 清除历史记录
+      chartHistoryStore.clearBackStack()
+      chartHistoryStore.clearForwardStack()
+    }
+    // 列表组件注册
+    projectData.componentList.forEach(async (e: CreateComponentType) => {
+      if (!window['$vue'].component(e.chartConfig.chartKey)) {
+        window['$vue'].component(
+          e.chartConfig.chartKey,
+          fetchChartComponent(e.chartConfig)
+        )
+      }
+    })
+    // 数据赋值
+    for (const key in projectData) {
+      // 组件
+      if (key === ChartEditStoreEnum.COMPONENT_LIST) {
+        for (const comItem of projectData[key]) {
+          // 补充 class 上的方法
+          let newComponent: CreateComponentType = await createComponent(
+            comItem.chartConfig
+          )
+          // 不保存到记录
+          chartEditStore.addComponentList(
+            Object.assign(newComponent, comItem),
+            false,
+            true
+          )
+        }
+      } else {
+        // 非组件(顺便排除脏数据)
+        if (key !== 'editCanvasConfig' && key !== 'requestGlobalConfig') return
+        Object.assign((chartEditStore as any)[key], projectData[key])
+      }
+    }
+  }
+
+  // 数据获取
+  const dataSyncFetch = async () => {
+    chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
+    try {
+      const { id } = routerParamsInfo.params
+      const res: any = await fetchProjectApi({ id: id[0] })
+      if (res.code === ResultEnum.SUCCESS) {
+        if (res.data) {
+          const data = JSON.parse(res.data)
+          await updateComponent(data)
+          return
+        }
+        setTimeout(() => {
+          chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
+        }, 1000)
+        return
+      }
+      chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
+      httpErrorHandle()
+    } catch (error) {
+      chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
+      httpErrorHandle()
+    }
+  }
+
+  // 数据保存
+  const dataSyncUpdate = async () => {
+    chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
+    // 获取id
+    const { id } = routerParamsInfo.params
+    const res: any = await saveProjectApi({
+      projectId: id[0],
+      content: JSON.stringify(chartEditStore.getStorageInfo || {})
+    })
+    if (res.code === ResultEnum.SUCCESS) {
+      // 成功状态
+      setTimeout(() => {
+        chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
+      }, 1000)
+      return
+    }
+    // 失败状态
+    chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
+  }
+
+  const syncTiming = setInterval(() => {
+    dataSyncUpdate()
+  }, 15000)
+
+  onUnmounted(() => {
+    clearInterval(syncTiming)
+  })
+
+  return {
+    updateComponent,
+    dataSyncFetch,
+    dataSyncUpdate
+  }
+}