Ver Fonte

feat: 新增导入导出功能😍

MTrun há 3 anos atrás
pai
commit
d1bb15b883

+ 5 - 5
src/packages/index.ts

@@ -22,12 +22,12 @@ export let packagesList: PackagesType = {
 }
 
 /**
- * * 获取目标拖拽组件配置信息
- * @param dropData
+ * * 获取目标组件配置信息
+ * @param targetData
  */
- export const createComponent = async (dropData: ConfigType) => {
-  const { category, key } = dropData
-  const chart = await import(`./components/${dropData.package}/${category}/${key}/config.ts`)
+ export const createComponent = async (targetData: ConfigType) => {
+  const { category, key } = targetData
+  const chart = await import(`./components/${targetData.package}/${category}/${key}/config.ts`)
   return new chart.default()
 }
 

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

@@ -5,7 +5,6 @@ import debounce from 'lodash/debounce'
 import cloneDeep from 'lodash/cloneDeep'
 import { defaultTheme, globalThemeJson } from '@/settings/chartThemes/index'
 import { requestInterval } from '@/settings/designSetting'
-import { chartMoveDistance } from '@/settings/systemSetting'
 // 记录记录
 import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
 // 全局设置
@@ -63,6 +62,7 @@ export const useChartEditStore = defineStore({
     },
     // 记录临时数据(复制等)
     recordChart: undefined,
+    // -----------------------
     // 画布属性(需存储给后端)
     editCanvasConfig: {
       // 默认宽度

+ 5 - 0
src/store/modules/chartHistoryStore/chartHistoryStore.ts

@@ -100,6 +100,11 @@ export const useChartHistoryStore = defineStore({
     clearForwardStack() {
       this.forwardStack = []
     },
+    // * 清空后退栈(保留初始化)
+    clearBackStack() {
+      const canvasHistory = this.getBackStack[0]
+      this.backStack =  [canvasHistory]
+    },
     // * 撤回
     backAction() {
       try {

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

@@ -121,7 +121,7 @@ const ChartDataSetting = loadAsyncComponent(() =>
   import('./components/ChartDataSetting/index.vue')
 )
 
-// 页面设置
+// 展示颜色列表
 const swatchesColors = [
   '#232324',
   '#2a2a2b',

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

@@ -0,0 +1,107 @@
+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 {
+      // 非组件
+      Object.assign((chartEditStore as any)[key], fileData[key])
+    }
+  }
+}
+
+export const useFile = () => {
+  const importUploadFileListRef = ref()
+  const chartEditStore = useChartEditStore()
+
+  // 上传-前置
+  //@ts-ignore
+  const importBeforeUpload = ({ file }) => {
+    importUploadFileListRef.value = []
+    const type = file.file.type
+    if (type !== FileTypeEnum.JSON && type !== FileTypeEnum.TXT) {
+      window['$message'].warning('仅支持上传 【JSON】 格式文件,请重新上传!')
+      return false
+    }
+    return true
+  }
+
+  // 上传-导入
+  const importCustomRequest = (options: UploadCustomRequestOptions) => {
+    const { file } = options
+    nextTick(() => {
+      if (file.file) {
+        readFile(file.file).then((fileData: any) => {
+          goDialog({
+            message: '请选择导入方式:',
+            positiveText: '新增(可撤回)',
+            negativeText: '覆盖(不可撤回)',
+            negativeButtonProps: { type: 'info', ghost: false },
+            // 新增
+            onNegativeCallback: async () => {
+              fileData = JSON.parse(fileData)
+              await updateComponent(fileData, true)
+              window['$message'].success('导入成功!')
+            },
+            // 覆盖
+            onPositiveCallback: async () => {
+              fileData = JSON.parse(fileData)
+              await updateComponent(fileData)
+              window['$message'].success('导入成功!')
+            }
+          })
+        })
+      } else {
+        window['$message'].error('导入失败,请检查数据或联系管理员!')
+      }
+    })
+  }
+
+  return {
+    importUploadFileListRef,
+    importBeforeUpload,
+    importCustomRequest
+  }
+}

+ 12 - 0
src/views/chart/ContentEdit/components/EditTools/index.d.ts

@@ -0,0 +1,12 @@
+export enum TypeEnum {
+  BOTTON = 'bottom',
+  IMPORTUPLOAD = 'importUpload'
+}
+
+export type BtnListType = {
+  key: string
+  type: TypeEnum
+  name: string
+  icon: any
+  handle?: () => void
+}

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

@@ -17,31 +17,44 @@
       placement="left"
     >
       <template #trigger>
-        <n-button
-          v-show="!isMini"
-          class="btn-item"
-          :class="[btnList.length - 1 === index && 'go-mb-0']"
-          :circle="isAside"
-          secondary
-          @click="item.handle"
-        >
-          <template #icon>
-            <n-icon size="22" v-if="isAside">
-              <component :is="item.icon"></component>
-            </n-icon>
-            <component v-else :is="item.icon"></component>
-          </template>
-          <n-text depth="3" v-show="!isAside">{{ item.name }}</n-text>
-        </n-button>
+        <div v-show="!isMini" class="btn-item" :class="[btnList.length - 1 === index && 'go-mb-0']">
+          <n-button v-if="item.type === TypeEnum.BOTTON" :circle="isAside" secondary @click="item.handle">
+            <template #icon>
+              <n-icon size="22" v-if="isAside">
+                <component :is="item.icon"></component>
+              </n-icon>
+              <component v-else :is="item.icon"></component>
+            </template>
+            <n-text depth="3" v-show="!isAside">{{ item.name }}</n-text>
+          </n-button>
+          <!-- 下载 -->
+          <n-upload
+            v-else-if="item.type === TypeEnum.IMPORTUPLOAD"
+            v-model:file-list="importUploadFileListRef"
+            :show-file-list="false"
+            :customRequest="importCustomRequest"
+            @before-upload="importBeforeUpload"
+          >
+            <n-button :circle="isAside" secondary>
+              <template #icon>
+                <n-icon size="22" v-if="isAside">
+                  <component :is="item.icon"></component>
+                </n-icon>
+                <component v-else :is="item.icon"></component>
+              </template>
+              <n-text depth="3" v-show="!isAside">{{ item.name }}</n-text>
+            </n-button>
+          </n-upload>
+        </div>
       </template>
       <!-- 提示 -->
       <span>{{ item.name }}</span>
     </n-tooltip>
     <!-- PawIcon -->
     <n-icon
+      v-if="isAside"
       v-show="settingStore.getChartToolsStatus === ToolsStatusEnum.ASIDE && isMini"
       size="22"
-      v-if="isAside"
     >
       <PawIcon></PawIcon>
     </n-icon>
@@ -49,10 +62,12 @@
 </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 { importHandle, exportHandle } from './utils'
+import { exportHandle } from './utils'
+import { useFile } from './hooks/useFile.hooks'
+import { BtnListType, TypeEnum } from './index.d'
 import { icon } from '@/plugins'
 
 const { DownloadIcon, ShareIcon, PawIcon } = icon.ionicons5
@@ -63,14 +78,17 @@ let mouseTime: any = null
 const isMini = ref<boolean>(true)
 // 是否是侧边栏
 const isAside = computed(() => settingStore.getChartToolsStatus === ToolsStatusEnum.ASIDE)
+// 文件上传
+const { importUploadFileListRef, importCustomRequest, importBeforeUpload } = useFile()
 
-const btnList = [{
+const btnList: BtnListType[] = [{
   key: 'import',
+  type: TypeEnum.IMPORTUPLOAD,
   name: '导入',
   icon: DownloadIcon,
-  handle: importHandle
 }, {
   key: 'export',
+  type: TypeEnum.BOTTON,
   name: '导出',
   icon: ShareIcon,
   handle: exportHandle
@@ -108,7 +126,7 @@ $asideBottom: 70px;
   border-radius: 25px;
   border: 1px solid;
   mix-blend-mode: luminosity;
-  transition: height ease-in 1s, padding 0.4s, bottom .4s;
+  transition: height ease-in 1s, padding 0.4s, bottom 0.4s;
   @include filter-border-color("hover-border-color-shallow");
   &.aside {
     flex-direction: column;
@@ -118,7 +136,6 @@ $asideBottom: 70px;
     bottom: $asideBottom;
     .btn-item {
       margin-bottom: 10px;
-      padding-bottom: 6px;
       /* 没生效,用上面的 go-mb-0 代替 */
       &:last-child {
         margin-bottom: 0;
@@ -126,6 +143,7 @@ $asideBottom: 70px;
       @include deep() {
         .n-button__icon {
           margin-right: 4px;
+          margin-bottom: 14px;
         }
       }
     }

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

@@ -2,13 +2,19 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
 import { canvasCut, downloadTextFile } from '@/utils'
 const chartEditStore = useChartEditStore()
 
-// 导入
-export const importHandle = () => { }
-
 // 导出
 export const exportHandle = () => {
+  // 取消选中
+  chartEditStore.setTargetSelectChart(undefined)
+
   // 导出数据
-  downloadTextFile(JSON.stringify(chartEditStore.getStorageInfo || []), undefined, 'json')
+  downloadTextFile(
+    JSON.stringify(chartEditStore.getStorageInfo || [], (k, v) => {
+      return v === undefined ? null : v
+    }),
+    undefined,
+    'json'
+  )
 
   // 导出图片
   const ruler = document.getElementById('mb-ruler')
@@ -19,6 +25,7 @@ export const exportHandle = () => {
     window['$message'].error('导出失败!')
     return
   }
+
   // 记录缩放比例
   const scaleTemp = chartEditStore.getEditCanvas.scale
   // 去除标尺Dom
@@ -27,8 +34,7 @@ export const exportHandle = () => {
   chartEditStore.setScale(1, true)
   // 展示水印
   watermark.style.display = 'block'
-  
-  window['$message'].warning('生成截图和数据中, 请耐心等待...')
+
   setTimeout(() => {
     canvasCut(range, () => {
       // 隐藏水印
@@ -40,8 +46,3 @@ export const exportHandle = () => {
     })
   }, 600)
 }
-
-// 改变工具栏展示样式
-export const changeTypeHandle = () => {
-
-}

+ 1 - 1
src/views/preview/hooks/useComInstall.hook.ts

@@ -10,7 +10,6 @@ export const useComInstall = (localStorageInfo: ChartEditStorageType) => {
   const intervalTiming = setInterval(() => {
     if (window['$vue'].component) {
       clearInterval(intervalTiming)
-      show.value = true
       localStorageInfo.componentList.forEach(async (e: CreateComponentType) => {
         if (!window['$vue'].component(e.chartConfig.chartKey)) {
           window['$vue'].component(
@@ -19,6 +18,7 @@ export const useComInstall = (localStorageInfo: ChartEditStorageType) => {
           )
         }
       })
+      show.value = true
     }
   }, 200)