Преглед изворни кода

feat: 新增自定义背景图

MTrun пре 3 година
родитељ
комит
0ce954588e

BIN
src/assets/images/canvas/noImage.png


BIN
src/assets/images/canvas/pageBg.png


+ 5 - 0
src/components/LoadingComponent/Skeleton.vue

@@ -0,0 +1,5 @@
+<template>
+  <div>
+     <Skeleton repeat="3" :show="true"/>
+  </div>
+</template>

+ 6 - 1
src/components/LoadingComponent/index.ts

@@ -1,6 +1,7 @@
 import type { App } from 'vue'
 import LoadingComponent from './index.vue'
 import AsyncLoading from './index.vue'
+import AsyncSkeletonLoading from './Skeleton.vue'
 
 // 正常组件
 export { LoadingComponent }
@@ -9,4 +10,8 @@ export { LoadingComponent }
 AsyncLoading.install = (app: App): void => {
   app.component('AsyncLoading', AsyncLoading)
 }
-export { AsyncLoading }
+
+AsyncSkeletonLoading.install = (app: App): void => {
+  app.component('AsyncSkeletonLoading', AsyncSkeletonLoading)
+}
+export { AsyncLoading, AsyncSkeletonLoading }

+ 2 - 2
src/plugins/icon.ts

@@ -101,7 +101,7 @@ const ionicons5 = {
   PencilIcon,
   // 编辑2(锤子)
   HammerIcon,
-  // 预览
+  // 电脑
   DesktopOutlineIcon,
   // 下载
   DownloadIcon,
@@ -115,7 +115,7 @@ const ionicons5 = {
   GridIcon,
   // 电脑1
   TvOutlineIcon,
-  // 浏览器
+  // 预览,浏览器
   BrowsersOutlineIcon,
   // 文档
   DocumentTextIcon,

+ 2 - 0
src/plugins/naive.ts

@@ -84,6 +84,7 @@ import {
   NPopselect,
   NCollapse,
   NCollapseItem,
+  NColorPicker,
   NCollapseTransition
 } from 'naive-ui';
 
@@ -172,6 +173,7 @@ const naive = create({
     NPopselect,
     NCollapse,
     NCollapseItem,
+    NColorPicker,
     NCollapseTransition
   ],
 });

+ 19 - 13
src/store/modules/chartEditStore/chartEditStore.d.ts

@@ -5,13 +5,10 @@ import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHi
 export enum EditCanvasTypeEnum {
   EDIT_LAYOUT_DOM = 'editLayoutDom',
   EDIT_CONTENT_DOM = 'editContentDom',
-  WIDTH = 'width',
-  HEIGHT = 'height',
   OFFSET = 'offset',
   SCALE = 'scale',
   USER_SCALE = 'userScale',
   LOCK_SCALE = 'lockScale',
-  BACKGROUND = 'background'
 }
 
 // 编辑区域
@@ -30,33 +27,42 @@ export type EditCanvasType = {
 }
 
 // 滤镜
-export enum EditCanvasFilterEnum {
+export enum EditCanvasConfigEnum {
+  WIDTH = 'width',
+  HEIGHT = 'height',
   HUE_ROTATE = 'hueRotate',
   SATURATE = 'saturate',
   BRIGHTNESS = 'brightness',
   CONTRAST = 'contrast',
   UN_OPACITY = 'unOpacity',
   CHART_THEME = 'chartTheme',
+  BACKGROUND = 'background',
+  BACKGROUND_IAMGE = 'backgroundImage',
+  SELECT_COLOR = 'selectColor'
 }
+
 export interface EditCanvasConfigType {
   // 大屏宽度
-  [EditCanvasTypeEnum.WIDTH]: number
+  [EditCanvasConfigEnum.WIDTH]: number
   // 大屏高度
-  [EditCanvasTypeEnum.HEIGHT]: number
+  [EditCanvasConfigEnum.HEIGHT]: number
   // 色相
-  [EditCanvasFilterEnum.HUE_ROTATE]: number
+  [EditCanvasConfigEnum.HUE_ROTATE]: number
   // 饱和度
-  [EditCanvasFilterEnum.SATURATE]: number
+  [EditCanvasConfigEnum.SATURATE]: number
   // 亮度
-  [EditCanvasFilterEnum.BRIGHTNESS]: number
+  [EditCanvasConfigEnum.BRIGHTNESS]: number
   // 对比度
-  [EditCanvasFilterEnum.CONTRAST]: number
+  [EditCanvasConfigEnum.CONTRAST]: number
   // 不透明度
-  [EditCanvasFilterEnum.UN_OPACITY]: number
+  [EditCanvasConfigEnum.UN_OPACITY]: number
   // 背景色
-  [EditCanvasTypeEnum.BACKGROUND]?: string
+  [EditCanvasConfigEnum.BACKGROUND]?: string
+  [EditCanvasConfigEnum.BACKGROUND_IAMGE]?: string | ArrayBuffer | null
+  // 图表主题颜色
+  [EditCanvasConfigEnum.CHART_THEME]: string
   // 图表主题颜色
-  [EditCanvasFilterEnum.CHART_THEME]: string
+  [EditCanvasConfigEnum.SELECT_COLOR]: boolean
 }
 
 // 坐标轴信息

+ 3 - 0
src/store/modules/chartEditStore/chartEditStore.ts

@@ -54,6 +54,9 @@ export const useChartEditStoreStore = defineStore({
       unOpacity: 100,
       // 默认背景色
       background: undefined,
+      backgroundImage: undefined,
+      // 是否使用纯颜色
+      selectColor: true,
       // chart 主题色
       chartTheme: defaultTheme || 'dark'
     },

+ 8 - 1
src/utils/componets.ts

@@ -1,5 +1,5 @@
 import { defineAsyncComponent, AsyncComponentLoader } from 'vue'
-import { AsyncLoading } from '@/components/LoadingComponent'
+import { AsyncLoading, AsyncSkeletonLoading } from '@/components/LoadingComponent'
 
 /**
  * * 异步加载组件
@@ -12,3 +12,10 @@ export const loadAsyncComponent = (loader: AsyncComponentLoader<any>) =>
     loadingComponent: AsyncLoading,
     delay: 20,
   })
+  
+export const loadSkeletonAsyncComponent = (loader: AsyncComponentLoader<any>) =>
+  defineAsyncComponent({
+    loader,
+    loadingComponent: AsyncSkeletonLoading,
+    delay: 20,
+  })

+ 16 - 0
src/utils/utils.ts

@@ -91,6 +91,7 @@ export const setDomAttribute = <K extends keyof CSSStyleDeclaration, V extends C
     HTMLElement.style[key] = value
   }
 }
+
 /**
  * * 判断是否是 mac
  * @returns boolean
@@ -98,6 +99,21 @@ export const setDomAttribute = <K extends keyof CSSStyleDeclaration, V extends C
 export const isMac = () => {
   return /macintosh|mac os x/i.test(navigator.userAgent)
 }
+
+/** 
+ * * file转base64
+*/
+export const fileTobase64 = (file:File, callback: Function) => {
+  let reader = new FileReader();
+  reader.readAsDataURL(file);
+  reader.onload = function (e: ProgressEvent<FileReader>) {
+    if(e.target) {
+      let base64 = e.target.result;
+      callback(base64)
+    }
+  };
+};
+
 /**
  * * 挂载监听
  */

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

@@ -34,7 +34,7 @@
 <script setup lang="ts">
 import { ref, computed } from 'vue'
 import { useChartEditStoreStore } from '@/store/modules/chartEditStore/chartEditStore'
-import { EditCanvasFilterEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
+import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
 import {
   chartColors,
   chartColorsName,
@@ -61,7 +61,7 @@ const fetchShowColors = (colors: Array<string>) => {
 }
 
 const selectTheme = (theme: string) => {
-  chartEditStoreStore.setCanvasConfig(EditCanvasFilterEnum.CHART_THEME, theme)
+  chartEditStoreStore.setCanvasConfig(EditCanvasConfigEnum.CHART_THEME, theme)
 }
 </script>
 

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

@@ -1,16 +1,218 @@
 <template>
   <div class="go-canvas-setting">
+    <n-form inline :label-width="45" size="small" label-placement="left">
+      <n-form-item label="宽度">
+        <!-- 尺寸选择 -->
+        <n-input-number
+          size="small"
+          v-model:value="canvasConfig.width"
+          :validator="validator"
+          @update:value="chartEditStoreStore.computedScale"
+        />
+      </n-form-item>
+      <n-form-item label="高度">
+        <n-input-number
+          size="small"
+          v-model:value="canvasConfig.height"
+          :validator="validator"
+          @update:value="chartEditStoreStore.computedScale"
+        />
+      </n-form-item>
+    </n-form>
+
+    <n-card class="upload-box">
+      <n-upload
+        v-model:file-list="uploadFileListRef"
+        :show-file-list="false"
+        :customRequest="customRequest"
+        :onBeforeUpload="beforeUploadHandle"
+      >
+        <n-upload-dragger>
+          <img
+            class="upload-show"
+            v-if="canvasConfig.backgroundImage"
+            :src="canvasConfig.backgroundImage"
+            alt="背景"
+          />
+          <div class="upload-img" v-show="!canvasConfig.backgroundImage">
+            <img src="@/assets/images/canvas/noImage.png" />
+            <n-text class="upload-desc" depth="3">
+              背景图需小于 2M ,格式为 png/jpg/gif 的文件
+            </n-text>
+          </div>
+        </n-upload-dragger>
+      </n-upload>
+    </n-card>
+    <n-space vertical :size="12">
+      <n-space>
+        <n-text>背景色</n-text>
+        <n-color-picker
+          style="width: 326px;"
+          :showPreview="true"
+          :swatches="[
+            '#232324',
+            '#2a2a2b',
+            '#313132',
+            '#373739',
+            '#757575',
+            '#e0e0e0',
+            '#eeeeee',
+            '#fafafa'
+          ]"
+          v-model:value="canvasConfig.background"
+        />
+      </n-space>
+      <n-space>
+        <n-text>使用颜色</n-text>
+        <n-switch
+          size="small"
+          v-model:value="canvasConfig.selectColor"
+          :loading="switchSelectColorLoading"
+          :round="false"
+          :disabled="!canvasConfig.backgroundImage"
+          :onUpdate="switchSelectColorHandle"
+        />
+      </n-space>
+      <n-space>
+        <n-text>背景</n-text>
+        <n-button
+          size="small"
+          :disabled="!canvasConfig.backgroundImage"
+          @click="clearImage"
+        >
+          清除背景图
+        </n-button>
+        <n-button
+          size="small"
+          :disabled="!canvasConfig.background"
+          @click="clearColor"
+        >
+          清除颜色
+        </n-button>
+      </n-space>
+    </n-space>
+
+    <n-divider />
+
+    <!-- 主题选择 -->
     <ChartTheme />
   </div>
 </template>
 
-<script setup>
+<script setup lang="ts">
+import { ref, nextTick, onMounted } from 'vue'
 import { useChartEditStoreStore } from '@/store/modules/chartEditStore/chartEditStore'
-const chartEditStoreStore = useChartEditStoreStore()
+import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
+import { UploadCustomRequestOptions, UploadFileInfo } from 'naive-ui'
 import { ChartTheme } from './components/ChartTheme/index'
+import { fileTobase64 } from '@/utils'
+
+const chartEditStoreStore = useChartEditStoreStore()
+const canvasConfig = chartEditStoreStore.getEditCanvasConfig
+
+const uploadFileListRef = ref()
+const switchSelectColorLoading = ref(false)
+
+// 规则
+const validator = (x: number) => x > 50
+
+// 前置处理
+//@ts-ignore
+const beforeUploadHandle = async ({ file }) => {
+  uploadFileListRef.value = []
+  const type = file.file.type
+  const size = file.file.size
+
+  if (size > 1024 * 1024 * 2) {
+    window['$message'].warning('图片超出 2M 限制,请重新上传!')
+    return false
+  }
+  if (type !== 'image/png' && type !== 'image/jpeg' && type !== 'image/gif') {
+    window['$message'].warning('文件格式不符合,请重新上传!')
+    return false
+  }
+  return true
+}
+
+// 清除背景
+const clearImage = () => {
+  chartEditStoreStore.setCanvasConfig(
+    EditCanvasConfigEnum.BACKGROUND_IAMGE,
+    undefined
+  )
+}
+
+// 清除颜色
+const clearColor = () => {
+  chartEditStoreStore.setCanvasConfig(
+    EditCanvasConfigEnum.BACKGROUND,
+    undefined
+  )
+}
+
+// 启用背景
+const switchSelectColorHandle = () => {
+  switchSelectColorLoading.value = true
+  setTimeout(() => {
+    switchSelectColorLoading.value = false
+  }, 1000)
+}
+
+// 自定义上传操作
+const customRequest = (options: UploadCustomRequestOptions) => {
+  const {
+    file,
+    data,
+    headers,
+    withCredentials,
+    action,
+    onFinish,
+    onError,
+    onProgress
+  } = options
+
+  nextTick(() => {
+    fileTobase64(file.file as File, (e: string | ArrayBuffer | null) => {
+      chartEditStoreStore.setCanvasConfig(
+        EditCanvasConfigEnum.BACKGROUND_IAMGE,
+        e
+      )
+    })
+  })
+}
 </script>
 
 <style lang="scss" scoped>
 @include go(canvas-setting) {
+  padding-top: 20px;
+  .upload-box {
+    cursor: pointer;
+    margin-bottom: 20px;
+    @include deep() {
+      .n-card__content {
+        padding: 0px;
+        overflow: hidden;
+      }
+    }
+    .upload-show {
+      display: block;
+      width: 326px;
+      height: 193px;
+      margin-bottom: -7px;
+      border-radius: 5px;
+    }
+    .upload-img {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      width: 326px;
+      img {
+        height: 150px;
+      }
+      .upload-desc {
+        padding: 10px 0;
+      }
+    }
+  }
 }
 </style>

+ 6 - 11
src/views/chart/ContentDetails/index.vue

@@ -31,7 +31,7 @@
               <n-space>
                 <span> 页面设置 </span>
                 <n-icon size="16" class="icon-position">
-                  <BrowsersOutlineIcon />
+                  <DesktopOutlineIcon />
                 </n-icon>
               </n-space>
             </template>
@@ -72,7 +72,7 @@
 <script setup lang="ts">
 import { shallowRef, ref, toRefs, watch, computed, reactive } from 'vue'
 import { icon } from '@/plugins'
-import { loadAsyncComponent } from '@/utils'
+import { loadAsyncComponent, loadSkeletonAsyncComponent } from '@/utils'
 import { ContentBox } from '../ContentBox/index'
 import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
 import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
@@ -82,17 +82,12 @@ const { getDetails } = toRefs(useChartLayoutStore())
 const { setItem } = useChartLayoutStore()
 const chartEditStoreStore = useChartEditStoreStore()
 
-const { ConstructIcon, FlashIcon, BrowsersOutlineIcon } = icon.ionicons5
+const { ConstructIcon, FlashIcon, DesktopOutlineIcon } = icon.ionicons5
 
 const ContentEdit = loadAsyncComponent(() => import('../ContentEdit/index.vue'))
-const CanvasPage = loadAsyncComponent(() =>
-  import('./components/CanvasPage/index.vue')
-)
-
-const Setting = loadAsyncComponent(() =>
-  import('./components/Setting/index.vue')
-)
-const Behind = loadAsyncComponent(() => import('./components/Behind/index.vue'))
+const CanvasPage = loadSkeletonAsyncComponent(() =>import('./components/CanvasPage/index.vue'))
+const Setting = loadSkeletonAsyncComponent(() =>import('./components/Setting/index.vue'))
+const Behind = loadSkeletonAsyncComponent(() => import('./components/Behind/index.vue'))
 
 const collapsed = ref<boolean>(getDetails.value)
 

+ 13 - 11
src/views/chart/ContentEdit/components/EditRange/index.vue

@@ -9,30 +9,32 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed } from 'vue'
+import { toRefs, computed } from 'vue'
 import { useChartEditStoreStore } from '@/store/modules/chartEditStore/chartEditStore'
 import { useSizeStyle } from '../../hooks/useStyle.hook'
 import { mousedownHandleUnStop } from '../../hooks/useDrop.hook'
 
 const chartEditStoreStore = useChartEditStoreStore()
 
-const canvasConfig = ref(chartEditStoreStore.getEditCanvasConfig)
+const { getEditCanvasConfig } = toRefs(chartEditStoreStore)
 
 const size = computed(() => {
   return {
-    w: canvasConfig.value.width,
-    h: canvasConfig.value.height
+    w: getEditCanvasConfig.value.width,
+    h: getEditCanvasConfig.value.height
   }
 })
 
-const background = computed(() => {
-  const background = canvasConfig.value.background
-  return background ? background : '#232324'
-})
-
 const style = computed(() => {
+  const background = getEditCanvasConfig.value.background
+  const backgroundImage = getEditCanvasConfig.value.backgroundImage
+  const selectColor = getEditCanvasConfig.value.selectColor
+  const backgroundColor = background ? background : null
+  const computed = selectColor
+    ? { background: backgroundColor }
+    : { background: `url(${backgroundImage}) no-repeat center/100% !important` }
   // @ts-ignore
-  return { ...useSizeStyle(size.value), background: background.value }
+  return { ...useSizeStyle(size.value), ...computed }
 })
 </script>
 
@@ -41,7 +43,7 @@ const style = computed(() => {
   position: relative;
   border: 1px solid;
   border-radius: 15px;
-  @include filter-bg-color('background-color2');
+  background: url('@/assets/images/canvas/pageBg.png');
   @include fetch-theme('box-shadow');
   @include filter-border-color('hover-border-color');
   @include fetch-theme-custom('border-color', 'background-color4');

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

@@ -4,7 +4,6 @@
       @select="handleSelect"
       :show="showDropdownRef"
       :options="options"
-      scrollable
       size="small"
       placement="top-start"
       style="max-height: 100vh; overflow-y: auto;"

+ 7 - 3
src/views/chart/ContentEdit/hooks/useDrop.hook.ts

@@ -19,9 +19,13 @@ export const handleDrop = async (e: DragEvent) => {
 
   try {
     loadingStart()
-
     // 获取拖拽数据
     const drayDataString = e!.dataTransfer!.getData(DragKeyEnum.DROG_KEY)
+    if (!drayDataString) {
+      loadingFinish()
+      return
+    } 
+
     const dropData: Exclude<ConfigType, ['node', 'image']> = JSON.parse(
       drayDataString
     )
@@ -67,8 +71,8 @@ export const useMouseHandle = () => {
     onClickoutside()
     chartEditStore.setTargetSelectChart(item.id)
     const scale = chartEditStore.getEditCanvas.scale
-    const width = chartEditStore.getEditCanvas.width
-    const height = chartEditStore.getEditCanvas.height
+    const width = chartEditStore.getEditCanvasConfig.width
+    const height = chartEditStore.getEditCanvasConfig.height
 
     // 获取编辑区域 Dom
     const editcontentDom = chartEditStore.getEditCanvas.editContentDom

+ 2 - 2
src/views/chart/HeaderRightBtn/index.vue

@@ -15,13 +15,13 @@
 import { reactive } from 'vue'
 import { renderIcon } from '@/utils'
 import { icon } from '@/plugins'
-const { DesktopOutlineIcon, SendIcon } = icon.ionicons5
+const { BrowsersOutlineIcon, SendIcon } = icon.ionicons5
 
 const btnList = reactive([
   {
     select: true,
     title: '预览',
-    icon: renderIcon(DesktopOutlineIcon)
+    icon: renderIcon(BrowsersOutlineIcon)
   },
   {
     select: true,

+ 2 - 2
src/views/project/items/components/Card/index.vue

@@ -92,7 +92,7 @@ const {
   CopyIcon,
   TrashIcon,
   PencilIcon,
-  DesktopOutlineIcon,
+  BrowsersOutlineIcon,
   DownloadIcon,
   HammerIcon,
   SendIcon
@@ -122,7 +122,7 @@ const selectOptions = ref([
   {
     label: renderLang('global.r_preview'),
     key: 'preview',
-    icon: renderIcon(DesktopOutlineIcon)
+    icon: renderIcon(BrowsersOutlineIcon)
   },
   {
     label: renderLang('global.r_copy'),