Commit 89ffc88f066e8257fe06f50488794c08838bf556

Authored by xp.Huang
2 parents 931d73b7 e990e729

Merge branch 'ft' into 'main_dev'

perf(components/GoUserInfo): 覆盖external下的GoUserInfo等

See merge request yunteng/thingskit-view!32
Showing 35 changed files with 1134 additions and 10 deletions

11.3 KB | W: | H:

9.54 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
  1 +import GoUserInfo from './index.vue';
  2 +
  3 +export { GoUserInfo };
... ...
  1 +<template>
  2 + <n-dropdown trigger="hover" @select="handleSelect" :show-arrow="true" :options="options">
  3 + <div class="user-info-box">
  4 + <person-icon v-if="fallback"></person-icon>
  5 + <n-avatar v-if="!fallback" round object-fit="cover" size="medium" :src="Person" @error="errorHandle"></n-avatar>
  6 + </div>
  7 + </n-dropdown>
  8 +
  9 + <!-- 系统设置 model -->
  10 + <go-system-set v-model:modelShow="modelShow"></go-system-set>
  11 + <!-- 关于软件 model -->
  12 + <go-system-info v-model:modelShow="modelShowInfo"></go-system-info>
  13 +</template>
  14 +
  15 +<script lang="ts" setup>
  16 +import { h, ref, computed } from 'vue'
  17 +import { NAvatar, NText } from 'naive-ui'
  18 +import { renderIcon } from '@/utils'
  19 +import { logout, renderLang } from '@/utils'
  20 +import { GoSystemSet } from '@/components/GoSystemSet/index'
  21 +import { GoSystemInfo } from '@/components/GoSystemInfo/index'
  22 +import Person from './person.png'
  23 +import { icon } from '@/plugins'
  24 +import { useUserStore } from '@/store/external/modules/user'
  25 +import { useSyncRemote } from '@/views/chart/hooks/external/useRemote.hook'
  26 +const { ChatboxEllipsesIcon, PersonIcon, LogOutOutlineIcon, SettingsSharpIcon } = icon.ionicons5
  27 +
  28 +const t = window['$t']
  29 +
  30 +const { dataSyncUpdate } = useSyncRemote()
  31 +
  32 +const modelShowInfo = ref(false)
  33 +const modelShow = ref(false)
  34 +
  35 +// 是否失败
  36 +const fallback = ref(false)
  37 +
  38 +// 用户图标渲染
  39 +const renderUserInfo = () => {
  40 + const userStoreOverride = useUserStore()
  41 + const { username, avatar } = userStoreOverride.getUserInfo
  42 + console.log(userStoreOverride.getUserInfo)
  43 + return h(
  44 + 'div',
  45 + {
  46 + style: 'display: flex; align-items: center; padding: 8px 12px;'
  47 + },
  48 + [
  49 + h(NAvatar, {
  50 + round: true,
  51 + style: 'margin-right: 12px;',
  52 + src: Person
  53 + }),
  54 + h('div', null, [
  55 + h('div', null, [
  56 + h('div', null, [
  57 + h('div', null, [h(NText, { depth: 2 }, { default: () => (!username ? 'ThingsKit' : username) })])
  58 + ])
  59 + ])
  60 + ])
  61 + ]
  62 + )
  63 +}
  64 +const options = ref([
  65 + {
  66 + label: '我的信息',
  67 + key: 'info',
  68 + type: 'render',
  69 + render: renderUserInfo
  70 + },
  71 + {
  72 + type: 'divider',
  73 + key: 'd1'
  74 + },
  75 + {
  76 + label: renderLang('global.sys_set'),
  77 + key: 'sysSet',
  78 + icon: renderIcon(SettingsSharpIcon)
  79 + },
  80 + // THINGS_KIT 隐藏关于软件
  81 + // {
  82 + // label: renderLang('global.contact'),
  83 + // key: 'contact',
  84 + // icon: renderIcon(ChatboxEllipsesIcon)
  85 + // },
  86 + {
  87 + type: 'divider',
  88 + key: 'd3'
  89 + },
  90 + {
  91 + label: '关闭并退出',
  92 + key: 'closePage',
  93 + icon: renderIcon(LogOutOutlineIcon)
  94 + }
  95 + // {
  96 + // label: renderLang('global.logout'),
  97 + // key: 'logout',
  98 + // icon: renderIcon(LogOutOutlineIcon)
  99 + // }
  100 +])
  101 +
  102 +// 图片渲染错误
  103 +const errorHandle = (e: Event) => {
  104 + fallback.value = true
  105 +}
  106 +
  107 +// 系统设置
  108 +const sysSetHandle = () => {
  109 + modelShow.value = true
  110 +}
  111 +
  112 +// 系统设置
  113 +const sysInfoHandle = () => {
  114 + modelShowInfo.value = true
  115 +}
  116 +
  117 +// THINGS_KIT 修改退出登录
  118 +const userStore = useUserStore()
  119 +const handleSelect = (key: string) => {
  120 + switch (key) {
  121 + case 'contact':
  122 + sysInfoHandle()
  123 + break
  124 + case 'sysSet':
  125 + sysSetHandle()
  126 + break
  127 + case 'closePage':
  128 + handleClosePage()
  129 + break
  130 + case 'logout':
  131 + // THINGS_KIT 修改退出登录
  132 + userStore.logout(true)
  133 + // logout()
  134 + break
  135 + }
  136 +}
  137 +
  138 +const handleClosePage = () => {
  139 + //先保存当前页面数据
  140 + dataSyncUpdate()
  141 + //兼容Firefox Chrome
  142 + var userAgent = navigator.userAgent
  143 + if (userAgent.indexOf('Firefox') != -1 || userAgent.indexOf('Chrome') != -1) {
  144 + window.location.href = 'about:blank'
  145 + window.close()
  146 + } else {
  147 + window.opener = null
  148 + window.open('', '_self')
  149 + window.close()
  150 + }
  151 +}
  152 +</script>
  153 +
  154 +<style lang="scss" scoped>
  155 +.user-info-box {
  156 + cursor: pointer;
  157 + transform: scale(0.7);
  158 +}
  159 +</style>
... ...
... ... @@ -17,5 +17,10 @@
17 17 </template>
18 18 <script setup lang="ts">
19 19 import { LayoutHeader } from '@/layout/components/LayoutHeader'
20   -import { GoUserInfo } from '@/components/GoUserInfo'
  20 +/**
  21 + * THINGS_KIT 升级版本这里有冲突
  22 + * 源文件 '@/components/GoUserInfo'
  23 + * 修改后 '@/components/external/GoUserInfo'
  24 + */
  25 +import { GoUserInfo } from '@/components/external/GoUserInfo'
21 26 </script>
... ...
... ... @@ -15,5 +15,5 @@ export const LeftCenterRightHeadAnimatConfig: ConfigType = {
15 15 categoryName: ChatCategoryEnumName.HEADCOMBINATION,
16 16 package: EPackagesCategoryEnum.COMPOSES,
17 17 chartFrame: ChartFrameEnum.COMMON,
18   - image: 'decorates02.png',
  18 + image: 'decorates07.png',
19 19 }
... ...
... ... @@ -13,5 +13,5 @@ export const CameraConfig: ConfigType = {
13 13 categoryName: ChatCategoryEnumName.MORE,
14 14 package: EPackagesCategoryEnum.COMPOSES,
15 15 chartFrame: ChartFrameEnum.NAIVE_UI,
16   - image: 'dateTime.png'
  16 + image: 'camera.png'
17 17 }
... ...
... ... @@ -13,5 +13,5 @@ export const Title1Config: ConfigType = {
13 13 categoryName: ChatCategoryEnumName.MORE,
14 14 package: EPackagesCategoryEnum.COMPOSES,
15 15 chartFrame: ChartFrameEnum.NAIVE_UI,
16   - image: 'dateTime.png'
  16 + image: 'title1.png'
17 17 }
... ...
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { Title2Config } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export const option = {
  7 + dataset: '我是标题',
  8 + attribute: {
  9 + linearColors: ['#21649C', '#060F1E', '#2AFFFF', '#000000', '#ffcc33', '#ffcc33'],
  10 + fontSize: 20,
  11 + fontPos: {
  12 + x: 0,
  13 + y: 20
  14 + },
  15 + fontColor: '#2AFFFF'
  16 + }
  17 +}
  18 +
  19 +export default class Config extends PublicConfigClass implements CreateComponentType {
  20 + public key = Title2Config.key
  21 + public chartConfig = cloneDeep(Title2Config)
  22 + public option = cloneDeep(option)
  23 +}
... ...
  1 +<template>
  2 + <CollapseItem name="配置" :expanded="true">
  3 + <SettingItemBox name="标题">
  4 + <SettingItem name="内容">
  5 + <n-input v-model:value="optionData.dataset" />
  6 + </SettingItem>
  7 + <SettingItem name="大小">
  8 + <n-input-number v-model:value="optionData.attribute.fontSize" />
  9 + </SettingItem>
  10 + <SettingItem name="颜色">
  11 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.attribute.fontColor"></n-color-picker>
  12 + </SettingItem>
  13 + <SettingItem name="x轴位置">
  14 + <n-input-number v-model:value="optionData.attribute.fontPos.x" />
  15 + </SettingItem>
  16 + <SettingItem name="y轴位置">
  17 + <n-input-number v-model:value="optionData.attribute.fontPos.y" />
  18 + </SettingItem>
  19 + </SettingItemBox>
  20 + <SettingItemBox
  21 + :name="`装饰颜色-${index + 1}`"
  22 + v-for="(item, index) in optionData.attribute.linearColors"
  23 + :key="index"
  24 + >
  25 + <SettingItem name="颜色">
  26 + <n-color-picker
  27 + size="small"
  28 + :modes="['hex']"
  29 + v-model:value="optionData.attribute.linearColors[index]"
  30 + ></n-color-picker>
  31 + </SettingItem>
  32 + <SettingItem>
  33 + <n-button
  34 + size="small"
  35 + @click="optionData.attribute.linearColors[index] = option.attribute.linearColors[index]"
  36 + >
  37 + 恢复默认
  38 + </n-button>
  39 + </SettingItem>
  40 + </SettingItemBox>
  41 + </CollapseItem>
  42 +</template>
  43 +
  44 +<script setup lang="ts">
  45 +import { PropType } from 'vue'
  46 +import { option } from './config'
  47 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  48 +
  49 +
  50 +defineProps({
  51 + optionData: {
  52 + type: Object as PropType<typeof option>,
  53 + required: true
  54 + }
  55 +})
  56 +</script>
... ...
  1 +import { ChartFrameEnum, ConfigType } from '@/packages/index.d'
  2 +import { EPackagesCategoryEnum } from '@/packages/components/external/types'
  3 +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
  4 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  5 +
  6 +const { key, chartKey, conKey } = useWidgetKey('Title2')
  7 +export const Title2Config: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '小标题2',
  12 + category: ChatCategoryEnum.MORE,
  13 + categoryName: ChatCategoryEnumName.MORE,
  14 + package: EPackagesCategoryEnum.COMPOSES,
  15 + chartFrame: ChartFrameEnum.NAIVE_UI,
  16 + image: 'title2.png'
  17 +}
... ...
  1 +<template>
  2 + <div class="go-content-box">
  3 + <svg :width="w" :height="h" fill="none">
  4 + <defs xmlns="http://www.w3.org/2000/svg">
  5 + <linearGradient id="linear_title2_u" x1="0%" y1="50%" x2="100%" y2="50%" gradientUnits="objectBoundingBox">
  6 + <stop offset="0" :stop-color="attribute.linearColors[0]" stop-opacity="1" />
  7 + <stop offset="1" :stop-color="attribute.linearColors[1]" stop-opacity="1" />
  8 + </linearGradient>
  9 + <linearGradient id="linear_title2_d" x1="0%" y1="50%" x2="100%" y2="50%" gradientUnits="objectBoundingBox">
  10 + <stop offset="0" :stop-color="attribute.linearColors[3]" stop-opacity="1" />
  11 + <stop offset="1" :stop-color="attribute.linearColors[4]" stop-opacity="0" />
  12 + </linearGradient>
  13 + </defs>
  14 + <g opacity="1" transform="translate(0 0) rotate(0 160 15)">
  15 + <path
  16 + id="矩形 title2_u"
  17 + fill-rule="evenodd"
  18 + fill="url(#linear_title2_u)"
  19 + transform="translate(0 0) rotate(0 160 15)"
  20 + opacity="1"
  21 + :d="`M2.77,${h} L${w},${h} L${w},0 L${
  22 + w - 11.44
  23 + },0 C10.58,0 9.81,0.55 9.54,1.37 L0.88,27.37 C0.68,27.98 0.78,28.65 1.15,29.17 C1.53,29.69 2.13,30 2.77,30 Z `"
  24 + />
  25 + <path
  26 + id="矩形 title2_u"
  27 + style="stroke: url(#linear_title2_d); stroke-width: 1; stroke-opacity: 100; stroke-dasharray: 0 0"
  28 + transform="translate(0 0) rotate(0 160 15)"
  29 + :d="`M2.77,${h} L${w},${h} L${w},0 L11.44,0 C10.58,0 9.81,0.55 9.54,1.37 L0.88,27.37 C0.68,27.98 0.78,28.65 1.15,29.17 C1.53,29.69 2.13,30 2.77,30 Z `"
  30 + />
  31 + <g opacity="1" :transform="`translate(24 ${h / 2 - 26 / 2}) rotate(0 46 13)`">
  32 + <text>
  33 + <tspan
  34 + :x="attribute.fontPos.x"
  35 + :y="attribute.fontPos.y"
  36 + :font-size="attribute.fontSize"
  37 + line-height="0"
  38 + :fill="attribute.fontColor"
  39 + opacity="1"
  40 + font-family="YouSheBiaoTiHei"
  41 + letter-spacing="0"
  42 + >
  43 + {{ dataset }}
  44 + </tspan>
  45 + </text>
  46 + </g>
  47 + <path
  48 + id="矩形 title2_d"
  49 + fill-rule="evenodd"
  50 + :style="{ fill: attribute.linearColors[4] }"
  51 + :transform="`translate(9 ${h / 2 - 15 / 2}) rotate(0 3.5 7.5)`"
  52 + opacity="1"
  53 + d="M0,15L2,15L7,0L5,0L0,15Z "
  54 + />
  55 + <path
  56 + id="矩形 title2_d"
  57 + fill-rule="evenodd"
  58 + :style="{ fill: attribute.linearColors[5] }"
  59 + :transform="`translate(15 ${h / 2 - 15 / 2}) rotate(0 3.5 7.5)`"
  60 + opacity="1"
  61 + d="M0,15L2,15L7,0L5,0L0,15Z "
  62 + />
  63 + </g>
  64 + </svg>
  65 + </div>
  66 +</template>
  67 +<script setup lang="ts">
  68 +import { PropType, toRefs } from 'vue'
  69 +import { CreateComponentType } from '@/packages/index.d'
  70 +
  71 +const props = defineProps({
  72 + chartConfig: {
  73 + type: Object as PropType<CreateComponentType>,
  74 + required: true
  75 + }
  76 +})
  77 +
  78 +const { dataset, attribute } = toRefs(props.chartConfig.option)
  79 +
  80 +const { w, h } = toRefs(props.chartConfig.attr)
  81 +</script>
  82 +
  83 +<style lang="scss" scoped>
  84 +.go-content-box {
  85 + width: v-bind('w+"px"');
  86 + height: v-bind('h+"px"');
  87 + display: flex;
  88 + align-items: center;
  89 + justify-content: center;
  90 +}
  91 +</style>
... ...
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { Title3Config } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export const option = {
  7 + dataset: '我是标题',
  8 + attribute: {
  9 + linearColors: [
  10 + '#21649C',
  11 + '#060F1E',
  12 + '#2AFFFF',
  13 + '#2AFFFF',
  14 + '#2AFFFF',
  15 + '#2AFFFF',
  16 + '#2affff',
  17 + '#16d9d9',
  18 + '#2affff',
  19 + '#2affff',
  20 + '#2affff',
  21 + '#2affff'
  22 + ],
  23 + fontSize: 20,
  24 + fontPos: {
  25 + x: 0,
  26 + y: 20
  27 + },
  28 + fontColor: '#2AFFFF'
  29 + }
  30 +}
  31 +
  32 +export default class Config extends PublicConfigClass implements CreateComponentType {
  33 + public key = Title3Config.key
  34 + public chartConfig = cloneDeep(Title3Config)
  35 + public option = cloneDeep(option)
  36 +}
... ...
  1 +<template>
  2 + <CollapseItem name="配置" :expanded="true">
  3 + <SettingItemBox name="标题">
  4 + <SettingItem name="内容">
  5 + <n-input v-model:value="optionData.dataset" />
  6 + </SettingItem>
  7 + <SettingItem name="大小">
  8 + <n-input-number v-model:value="optionData.attribute.fontSize" />
  9 + </SettingItem>
  10 + <SettingItem name="颜色">
  11 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.attribute.fontColor"></n-color-picker>
  12 + </SettingItem>
  13 + <SettingItem name="x轴位置">
  14 + <n-input-number v-model:value="optionData.attribute.fontPos.x" />
  15 + </SettingItem>
  16 + <SettingItem name="y轴位置">
  17 + <n-input-number v-model:value="optionData.attribute.fontPos.y" />
  18 + </SettingItem>
  19 + </SettingItemBox>
  20 + <SettingItemBox
  21 + :name="`装饰颜色-${index + 1}`"
  22 + v-for="(item, index) in optionData.attribute.linearColors"
  23 + :key="index"
  24 + >
  25 + <SettingItem name="颜色">
  26 + <n-color-picker
  27 + size="small"
  28 + :modes="['hex']"
  29 + v-model:value="optionData.attribute.linearColors[index]"
  30 + ></n-color-picker>
  31 + </SettingItem>
  32 + <SettingItem>
  33 + <n-button
  34 + size="small"
  35 + @click="optionData.attribute.linearColors[index] = option.attribute.linearColors[index]"
  36 + >
  37 + 恢复默认
  38 + </n-button>
  39 + </SettingItem>
  40 + </SettingItemBox>
  41 + </CollapseItem>
  42 +</template>
  43 +
  44 +<script setup lang="ts">
  45 +import { PropType } from 'vue'
  46 +import { option } from './config'
  47 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  48 +
  49 +
  50 +defineProps({
  51 + optionData: {
  52 + type: Object as PropType<typeof option>,
  53 + required: true
  54 + }
  55 +})
  56 +</script>
... ...
  1 +import { ChartFrameEnum, ConfigType } from '@/packages/index.d'
  2 +import { EPackagesCategoryEnum } from '@/packages/components/external/types'
  3 +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
  4 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  5 +
  6 +const { key, chartKey, conKey } = useWidgetKey('Title3')
  7 +export const Title3Config: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '小标题3',
  12 + category: ChatCategoryEnum.MORE,
  13 + categoryName: ChatCategoryEnumName.MORE,
  14 + package: EPackagesCategoryEnum.COMPOSES,
  15 + chartFrame: ChartFrameEnum.NAIVE_UI,
  16 + image: 'title3.png'
  17 +}
... ...
  1 +<template>
  2 + <div class="go-content-box">
  3 + <svg :width="w" :height="h" fill="none">
  4 + <defs>
  5 + <linearGradient id="linear_0" x1="0%" y1="50%" x2="100%" y2="50%" gradientUnits="objectBoundingBox">
  6 + <stop offset="0" :stop-color="attribute.linearColors[0]" stop-opacity="1" />
  7 + <stop offset="1" :stop-color="attribute.linearColors[1]" stop-opacity="1" />
  8 + </linearGradient>
  9 + <linearGradient id="linear_1" x1="0%" y1="50%" x2="100%" y2="50%" gradientUnits="objectBoundingBox">
  10 + <stop offset="0" :stop-color="attribute.linearColors[2]" stop-opacity="1" />
  11 + <stop offset="1" :stop-color="attribute.linearColors[3]" stop-opacity="0" />
  12 + </linearGradient>
  13 + <linearGradient id="linear_2" x1="0%" y1="50%" x2="100%" y2="50%" gradientUnits="objectBoundingBox">
  14 + <stop offset="0" :stop-color="attribute.linearColors[4]" stop-opacity="1" />
  15 + <stop offset="1" :stop-color="attribute.linearColors[5]" stop-opacity="0" />
  16 + </linearGradient>
  17 + </defs>
  18 + <g opacity="1" transform="translate(-6.249999273677531e-8 0.5) rotate(0 160.00000003125 15.000000000000005)">
  19 + <path
  20 + id="矩形 16"
  21 + fill-rule="evenodd"
  22 + fill="url(#linear_0)"
  23 + transform="translate(6.249999895402425e-8 2) rotate(0 160 13)"
  24 + opacity="1"
  25 + :d="`M0,${h - 5} L${w},${h - 5} L${w},0 L0,0 L0,${h - 5} Z`"
  26 + />
  27 + <g opacity="1" :transform="`translate(34.0000000625 ${h / 2 - 26 / 2}) rotate(0 46 13)`">
  28 + <text>
  29 + <tspan
  30 + :x="attribute.fontPos.x"
  31 + :y="attribute.fontPos.y"
  32 + :font-size="attribute.fontSize"
  33 + line-height="0"
  34 + :fill="attribute.fontColor"
  35 + opacity="1"
  36 + font-family="YouSheBiaoTiHei"
  37 + letter-spacing="0"
  38 + >
  39 + {{ dataset }}
  40 + </tspan>
  41 + </text>
  42 + </g>
  43 + <path
  44 + id="路径 5"
  45 + style="stroke: url(#linear_1); stroke-width: 1; stroke-opacity: 100; stroke-dasharray: 0 0"
  46 + transform="translate(6.249999895402425e-8 30) rotate(0 160 0)"
  47 + d="M0,0L320,0 "
  48 + />
  49 + <path
  50 + id="路径 5"
  51 + style="stroke: url(#linear_2); stroke-width: 1; stroke-opacity: 100; stroke-dasharray: 0 0"
  52 + transform="translate(6.249999895402425e-8 0) rotate(0 160 0)"
  53 + d="M0,0L320,0 "
  54 + />
  55 + <path
  56 + id="路径 5"
  57 + :style="{ stroke: attribute.linearColors[10], strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 0 }"
  58 + transform="translate(0 0.0005) rotate(0 2.000000062499999 0)"
  59 + d="M0,0L4,0 "
  60 + />
  61 + <path
  62 + id="路径 5"
  63 + :style="{ stroke: attribute.linearColors[11], strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 0 }"
  64 + :transform="`translate(0 ${h - 1}) rotate(0 2.000000062499999 0)`"
  65 + d="M0,0 L4,0 "
  66 + />
  67 + <g opacity="1" :transform="`translate(10.0000000625 ${h / 2 - 18 / 2}) rotate(0 8.598076211353316 9)`">
  68 + <path
  69 + id="三角形 1"
  70 + fill-rule="evenodd"
  71 + :style="{ fill: attribute.linearColors[6] }"
  72 + transform="translate(4 0) rotate(0 6.598076211353316 4.5)"
  73 + opacity="1"
  74 + d="M2.8,9L13.2,9L0,0L2.8,9Z "
  75 + />
  76 + <path
  77 + id="三角形 1"
  78 + fill-rule="evenodd"
  79 + :style="{ fill: attribute.linearColors[7] }"
  80 + transform="translate(4 9) rotate(0 6.598076211353316 4.5)"
  81 + opacity="1"
  82 + d="M13.2,0L2.8,0L0,9L13.2,0Z "
  83 + />
  84 + <path
  85 + id="圆形 7"
  86 + fill-rule="evenodd"
  87 + :style="{ fill: attribute.linearColors[8] }"
  88 + transform="translate(0 7) rotate(0 2 2)"
  89 + opacity="1"
  90 + d="M2,0C0.9,0 0,0.9 0,2C0,3.1 0.9,4 2,4C3.1,4 4,3.1 4,2C4,0.9 3.1,0 2,0Z "
  91 + />
  92 + </g>
  93 + <path
  94 + id="矩形 17"
  95 + fill-rule="evenodd"
  96 + :style="{ fill: attribute.linearColors[9] }"
  97 + transform="translate(6.249999895402425e-8 2) rotate(0 0.5 13)"
  98 + opacity="1"
  99 + :d="`M0,${h - 5}L1,${h - 5}L1,0L0,0L0,${h - 5}Z`"
  100 + />
  101 + </g>
  102 + </svg>
  103 + </div>
  104 +</template>
  105 +<script setup lang="ts">
  106 +import { PropType, toRefs } from 'vue'
  107 +import { CreateComponentType } from '@/packages/index.d'
  108 +
  109 +const props = defineProps({
  110 + chartConfig: {
  111 + type: Object as PropType<CreateComponentType>,
  112 + required: true
  113 + }
  114 +})
  115 +
  116 +const { dataset, attribute } = toRefs(props.chartConfig.option)
  117 +
  118 +const { w, h } = toRefs(props.chartConfig.attr)
  119 +</script>
  120 +
  121 +<style lang="scss" scoped>
  122 +.go-content-box {
  123 + width: v-bind('w+"px"');
  124 + height: v-bind('h+"px"');
  125 + display: flex;
  126 + align-items: center;
  127 + justify-content: center;
  128 +}
  129 +</style>
... ...
1   -import { ButtonConfig } from './Button/index'
2 1 import { Title1Config } from './Title1/index'
  2 +import { Title2Config } from './Title2/index'
  3 +import { Title3Config } from './Title3/index'
  4 +import { ButtonConfig } from './Button/index'
3 5 import { CameraConfig } from './Camera/index'
4 6
5   -export default [ButtonConfig, Title1Config, CameraConfig]
  7 +export default [Title1Config, Title2Config, Title3Config, CameraConfig, ButtonConfig]
... ...
... ... @@ -13,5 +13,5 @@ export const ClockConfig: ConfigType = {
13 13 categoryName: ChatCategoryEnumName.MORE,
14 14 package: PackagesCategoryEnum.DECORATES,
15 15 chartFrame: ChartFrameEnum.STATIC,
16   - image: 'clock.png'
  16 + image: 'clock1.png'
17 17 }
... ...
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { OverrideImageConfig } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export const option = {
  7 + // 图片路径
  8 + dataset: '',
  9 + // 适应方式
  10 + fit: 'fill',
  11 + // 圆角
  12 + borderRadius: 10
  13 +}
  14 +
  15 +export default class Config extends PublicConfigClass implements CreateComponentType {
  16 + public key = OverrideImageConfig.key
  17 + public chartConfig = cloneDeep(OverrideImageConfig)
  18 + public option = cloneDeep(option)
  19 +}
... ...
  1 +<template>
  2 + <collapse-item name="属性" :expanded="true">
  3 + <setting-item-box name="上传图片" :alone="true">
  4 + <setting-item>
  5 + <n-card class="upload-box">
  6 + <n-upload
  7 + :show-file-list="false"
  8 + v-model:file-list="uploadFileListRef"
  9 + :customRequest="customRequest"
  10 + :onBeforeUpload="beforeUploadHandle"
  11 + >
  12 + <n-upload-dragger>
  13 + <img v-if="optionData.dataset" class="upload-show" :src="optionData.dataset" alt="背景" />
  14 + <div class="upload-img" v-show="!optionData.dataset">
  15 + <img src="@/assets/images/canvas/noImage.png" />
  16 + <n-text class="upload-desc" depth="3">
  17 + 图片需小于 {{ backgroundImageSize }}M ,格式为 png/jpg/gif 的文件
  18 + </n-text>
  19 + </div>
  20 + </n-upload-dragger>
  21 + </n-upload>
  22 + </n-card>
  23 + </setting-item>
  24 + </setting-item-box>
  25 + <setting-item-box name="样式">
  26 + <setting-item name="类型">
  27 + <n-select v-model:value="optionData.fit" size="small" :options="fitList"></n-select>
  28 + </setting-item>
  29 + <setting-item name="圆角">
  30 + <n-input-number
  31 + v-model:value="optionData.borderRadius"
  32 + size="small"
  33 + :min="0"
  34 + placeholder="圆角"
  35 + ></n-input-number>
  36 + </setting-item>
  37 + </setting-item-box>
  38 + </collapse-item>
  39 +</template>
  40 +
  41 +<script setup lang="ts">
  42 +import { PropType, ref, nextTick } from 'vue'
  43 +import { option } from './config'
  44 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  45 +import { FileTypeEnum } from '@/enums/fileTypeEnum'
  46 +import { uploadFile } from '@/api/external/contentSave/content'
  47 +import { UploadCustomRequestOptions } from 'naive-ui'
  48 +import { backgroundImageSize } from '@/settings/designSetting'
  49 +import { fetchRouteParamsLocation } from '@/utils'
  50 +
  51 +const props = defineProps({
  52 + optionData: {
  53 + type: Object as PropType<typeof option>,
  54 + required: true
  55 + }
  56 +})
  57 +
  58 +const uploadFileListRef = ref()
  59 +
  60 +// 上传图片前置处理
  61 +//@ts-ignore
  62 +const beforeUploadHandle = async ({ file }) => {
  63 + uploadFileListRef.value = []
  64 + const type = file.file.type
  65 + const size = file.file.size
  66 +
  67 + if (size > 1024 * 1024 * backgroundImageSize) {
  68 + window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`)
  69 + return false
  70 + }
  71 + if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) {
  72 + window['$message'].warning('文件格式不符合,请重新上传!')
  73 + return false
  74 + }
  75 + return true
  76 +}
  77 +
  78 +// 自定义上传操作
  79 +const customRequest = (options: UploadCustomRequestOptions) => {
  80 + const { file } = options
  81 + nextTick(async () => {
  82 + if (file.file) {
  83 + // 修改名称
  84 + const newNameFile = new File([file.file], `${fetchRouteParamsLocation()}_index_background.png`, {
  85 + type: file.file.type
  86 + })
  87 + let uploadParams = new FormData()
  88 + uploadParams.append('file', newNameFile)
  89 + const uploadRes = await uploadFile(uploadParams)
  90 + if (uploadRes) {
  91 + props.optionData.dataset = uploadRes?.fileStaticUri
  92 + window['$message'].success('添加图片成功!')
  93 + }
  94 + } else {
  95 + window['$message'].error('添加图片失败,请稍后重试!')
  96 + }
  97 + })
  98 +}
  99 +
  100 +// 适应类型
  101 +const fitList = [
  102 + {
  103 + value: 'fill',
  104 + label: 'fill'
  105 + },
  106 + {
  107 + value: 'contain',
  108 + label: 'contain'
  109 + },
  110 + {
  111 + value: 'cover',
  112 + label: 'cover'
  113 + },
  114 + {
  115 + value: 'scale-down',
  116 + label: 'scale-down'
  117 + },
  118 + {
  119 + value: 'none',
  120 + label: 'none'
  121 + }
  122 +]
  123 +</script>
  124 +<style lang="scss" scoped>
  125 +$uploadHeight: 193px;
  126 +.upload-box {
  127 + cursor: pointer;
  128 + margin-bottom: 20px;
  129 + @include deep() {
  130 + .n-card__content {
  131 + padding: 0;
  132 + overflow: hidden;
  133 + }
  134 + .n-upload-dragger {
  135 + padding: 5px;
  136 + }
  137 + }
  138 + .upload-show {
  139 + width: -webkit-fill-available;
  140 + height: $uploadHeight;
  141 + border-radius: 5px;
  142 + }
  143 + .upload-img {
  144 + display: flex;
  145 + flex-direction: column;
  146 + align-items: center;
  147 + img {
  148 + height: 150px;
  149 + }
  150 + .upload-desc {
  151 + padding: 10px 0;
  152 + }
  153 + }
  154 +}
  155 +</style>
... ...
  1 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  2 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Informations/index.d'
  3 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  4 +
  5 +const { key, conKey, chartKey } = useWidgetKey('OverrideImage', true)
  6 +
  7 +export const OverrideImageConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '自定义图片',
  12 + category: ChatCategoryEnum.MORE,
  13 + categoryName: ChatCategoryEnumName.MORE,
  14 + package: PackagesCategoryEnum.INFORMATIONS,
  15 + chartFrame: ChartFrameEnum.COMMON,
  16 + image: 'photo.png'
  17 +}
... ...
  1 +<template>
  2 + <div :style="getStyle(borderRadius)">
  3 + <n-image
  4 + :object-fit="fit"
  5 + preview-disabled
  6 + :src="!option.dataset ? logo : option.dataset"
  7 + :fallback-src="requireErrorImg()"
  8 + :width="w"
  9 + :height="h"
  10 + ></n-image>
  11 + </div>
  12 +</template>
  13 +
  14 +<script setup lang="ts">
  15 +import { PropType, shallowReactive, watch, toRefs } from 'vue'
  16 +import { requireErrorImg } from '@/utils'
  17 +import { useChartDataFetch } from '@/hooks'
  18 +import { CreateComponentType } from '@/packages/index.d'
  19 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  20 +import logo from '@/assets/logo.png'
  21 +
  22 +const props = defineProps({
  23 + chartConfig: {
  24 + type: Object as PropType<CreateComponentType>,
  25 + required: true
  26 + }
  27 +})
  28 +
  29 +const { w, h } = toRefs(props.chartConfig.attr)
  30 +const { dataset, fit, borderRadius } = toRefs(props.chartConfig.option)
  31 +
  32 +const option = shallowReactive({
  33 + dataset: ''
  34 +})
  35 +
  36 +const getStyle = (radius: number) => {
  37 + return {
  38 + borderRadius: `${radius}px`,
  39 + overflow: 'hidden'
  40 + }
  41 +}
  42 +
  43 +// 编辑更新
  44 +watch(
  45 + () => props.chartConfig.option.dataset,
  46 + (newData: any) => {
  47 + option.dataset = newData
  48 + },
  49 + {
  50 + immediate: true
  51 + }
  52 +)
  53 +
  54 +// 预览更新
  55 +useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
  56 + option.dataset = newData
  57 +})
  58 +</script>
... ...
... ... @@ -2,12 +2,14 @@ import { EPackagesCategoryEnum, EPackagesType } from '@/packages/components/exte
2 2 import { ComposesList } from '@/packages/components/external/Composes'
3 3 import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
4 4 import { ClockConfig } from '@/packages/components/external/Decorates/Mores/Icon'
  5 +import { OverrideImageConfig } from '@/packages/components/external/Informations/Mores/OverrideImage'
5 6
6 7 export function useInjectLib(packagesList: EPackagesType) {
7 8
8 9 packagesList[EPackagesCategoryEnum.COMPOSES] = ComposesList
9 10
10 11 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig)
  12 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideImageConfig)
11 13 }
12 14
13 15 /**
... ...
  1 +import HeaderLeftBtn from './index.vue'
  2 +
  3 +export { HeaderLeftBtn }
... ...
  1 +<template>
  2 + <n-space class="header-left-btn" :size="25">
  3 + <n-button disabled size="small" quaternary @click="goHomeHandle()">
  4 + <template #icon>
  5 + <n-icon :depth="3">
  6 + <home-icon></home-icon>
  7 + </n-icon>
  8 + </template>
  9 + </n-button>
  10 + <n-space>
  11 + <!-- 模块展示按钮 -->
  12 + <n-tooltip v-for="item in btnList" :key="item.key" placement="bottom" trigger="hover">
  13 + <template #trigger>
  14 + <n-button size="small" ghost :type="styleHandle(item)" @click="clickHandle(item)">
  15 + <component :is="item.icon"></component>
  16 + </n-button>
  17 + </template>
  18 + <span>{{ item.title }}</span>
  19 + </n-tooltip>
  20 +
  21 + <n-divider vertical />
  22 +
  23 + <!-- 历史记录按钮 -->
  24 + <n-tooltip v-for="item in historyList" :key="item.key" placement="bottom" trigger="hover">
  25 + <template #trigger>
  26 + <n-button size="small" ghost type="primary" :disabled="!item.select" @click="clickHistoryHandle(item)">
  27 + <component :is="item.icon"></component>
  28 + </n-button>
  29 + </template>
  30 + <span>{{ item.title }}</span>
  31 + </n-tooltip>
  32 + <n-divider vertical />
  33 + <!-- 保存 -->
  34 + <n-tooltip placement="bottom" trigger="hover">
  35 + <template #trigger>
  36 + <div class="save-btn">
  37 + <n-button size="small" type="primary" ghost @click="dataSyncUpdate()">
  38 + <template #icon>
  39 + <n-icon>
  40 + <Save />
  41 + </n-icon>
  42 + </template>
  43 + </n-button>
  44 + </div>
  45 + </template>
  46 + <span>保存</span>
  47 + </n-tooltip>
  48 + </n-space>
  49 + </n-space>
  50 +</template>
  51 +
  52 +<script setup lang="ts">
  53 +import { toRefs, Ref, reactive, computed } from 'vue'
  54 +import { renderIcon, goDialog, goHome } from '@/utils'
  55 +import { icon } from '@/plugins'
  56 +import { Save } from '@vicons/carbon'
  57 +import { useRemoveKeyboard } from '../../../hooks/useKeyboard.hook'
  58 +
  59 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  60 +
  61 +import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
  62 +import { HistoryStackEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
  63 +// THINGS_KIT 修改同步逻辑
  64 +import { useSyncRemote } from '../../../hooks/external/useRemote.hook'
  65 +import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
  66 +import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
  67 +
  68 +const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon, ArrowBackIcon, ArrowForwardIcon } = icon.ionicons5
  69 +const { setItem } = useChartLayoutStore()
  70 +const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore())
  71 +const chartEditStore = useChartEditStore()
  72 +const chartHistoryStore = useChartHistoryStore()
  73 +const { dataSyncUpdate } = useSyncRemote()
  74 +
  75 +interface ItemType<T> {
  76 + key: T
  77 + select: Ref<boolean> | boolean
  78 + title: string
  79 + icon: any
  80 +}
  81 +
  82 +const btnList = reactive<ItemType<ChartLayoutStoreEnum>[]>([
  83 + {
  84 + key: ChartLayoutStoreEnum.CHARTS,
  85 + select: getCharts,
  86 + title: '图表组件',
  87 + icon: renderIcon(BarChartIcon)
  88 + },
  89 + {
  90 + key: ChartLayoutStoreEnum.LAYERS,
  91 + select: getLayers,
  92 + title: '图层控制',
  93 + icon: renderIcon(LayersIcon)
  94 + },
  95 + {
  96 + key: ChartLayoutStoreEnum.DETAILS,
  97 + select: getDetails,
  98 + title: '详情设置',
  99 + icon: renderIcon(PrismIcon)
  100 + }
  101 +])
  102 +
  103 +const isBackStack = computed(() => chartHistoryStore.getBackStack.length > 1)
  104 +
  105 +const isForwardStack = computed(() => chartHistoryStore.getForwardStack.length > 0)
  106 +
  107 +const historyList = reactive<ItemType<HistoryStackEnum>[]>([
  108 + {
  109 + key: HistoryStackEnum.BACK_STACK,
  110 + // 一定会有初始化画布
  111 + select: isBackStack,
  112 + title: '后退',
  113 + icon: renderIcon(ArrowBackIcon)
  114 + },
  115 + {
  116 + key: HistoryStackEnum.FORWARD_STACK,
  117 + select: isForwardStack,
  118 + title: '前进',
  119 + icon: renderIcon(ArrowForwardIcon)
  120 + }
  121 +])
  122 +
  123 +
  124 +// store 描述的是展示的值,所以和 ContentConfigurations 的 collapsed 是相反的
  125 +const styleHandle = (item: ItemType<ChartLayoutStoreEnum>) => {
  126 + if (item.key === ChartLayoutStoreEnum.DETAILS) {
  127 + return item.select ? '' : 'primary'
  128 + }
  129 + return item.select ? 'primary' : ''
  130 +}
  131 +
  132 +// 布局处理
  133 +const clickHandle = (item: ItemType<ChartLayoutStoreEnum>) => {
  134 + setItem(item.key, !item.select)
  135 +}
  136 +
  137 +// 历史记录处理
  138 +const clickHistoryHandle = (item: ItemType<HistoryStackEnum>) => {
  139 + switch (item.key) {
  140 + case HistoryStackEnum.BACK_STACK:
  141 + chartEditStore.setBack()
  142 + break;
  143 + case HistoryStackEnum.FORWARD_STACK:
  144 + chartEditStore.setForward()
  145 + break;
  146 + }
  147 +}
  148 +
  149 +// 返回首页
  150 +const goHomeHandle = () => {
  151 + goDialog({
  152 + message: '返回将不会保存任何操作',
  153 + isMaskClosable: true,
  154 + onPositiveCallback: () => {
  155 + goHome()
  156 + useRemoveKeyboard()
  157 + }
  158 + })
  159 +}
  160 +</script>
  161 +<style lang="scss" scoped>
  162 +.header-left-btn {
  163 + margin-left: -37px;
  164 +}
  165 +</style>
... ...
  1 +import HeaderRightBtn from './index.vue'
  2 +
  3 +export { HeaderRightBtn }
... ...
  1 +<template>
  2 + <n-space class="go-mt-0">
  3 + <n-button v-for="item in comBtnList" :key="item.title" :type="item.type" ghost @click="item.event">
  4 + <template #icon>
  5 + <component :is="item.icon"></component>
  6 + </template>
  7 + <span>{{ item.title }}</span>
  8 + </n-button>
  9 + </n-space>
  10 +</template>
  11 +
  12 +<script setup lang="ts">
  13 +import { computed } from 'vue'
  14 +import { renderIcon, goDialog, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils'
  15 +import { PreviewEnum } from '@/enums/pageEnum'
  16 +import { StorageEnum } from '@/enums/storageEnum'
  17 +import { useRoute } from 'vue-router'
  18 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  19 +import { syncData } from '../../../ContentEdit/components/EditTools/hooks/useSyncUpdate.hook'
  20 +import { icon } from '@/plugins'
  21 +import { cloneDeep } from 'lodash'
  22 +
  23 +const { BrowsersOutlineIcon, SendIcon, AnalyticsIcon } = icon.ionicons5
  24 +const chartEditStore = useChartEditStore()
  25 +
  26 +const routerParamsInfo = useRoute()
  27 +
  28 +// 预览
  29 +const previewHandle = () => {
  30 + const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
  31 + if (!path) return
  32 + const { id } = routerParamsInfo.params
  33 + // id 标识
  34 + const previewId = typeof id === 'string' ? id : id[0]
  35 + const storageInfo = chartEditStore.getStorageInfo
  36 + const sessionStorageInfo = getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || []
  37 +
  38 + if (sessionStorageInfo?.length) {
  39 + const repeateIndex = sessionStorageInfo.findIndex((e: { id: string }) => e.id === previewId)
  40 + // 重复替换
  41 + if (repeateIndex !== -1) {
  42 + sessionStorageInfo.splice(repeateIndex, 1, { id: previewId, ...storageInfo })
  43 + setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
  44 + } else {
  45 + sessionStorageInfo.push({
  46 + id: previewId,
  47 + ...storageInfo
  48 + })
  49 + setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
  50 + }
  51 + } else {
  52 + setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [{ id: previewId, ...storageInfo }])
  53 + }
  54 + // 跳转
  55 + routerTurnByPath(path, [previewId], undefined, true)
  56 +}
  57 +
  58 +// 发布
  59 +const sendHandle = () => {
  60 + goDialog({
  61 + message: '想体验发布功能,请前往 master-fetch 分支查看: https://demo.mtruning.club/#/login',
  62 + positiveText: '了然',
  63 + closeNegativeText: true,
  64 + onPositiveCallback: () => {}
  65 + })
  66 +}
  67 +
  68 +const btnList = [
  69 + {
  70 + select: true,
  71 + title: '同步内容',
  72 + type: 'primary',
  73 + icon: renderIcon(AnalyticsIcon),
  74 + event: syncData
  75 + },
  76 + {
  77 + select: true,
  78 + title: '预览',
  79 + icon: renderIcon(BrowsersOutlineIcon),
  80 + event: previewHandle
  81 + },
  82 + // {
  83 + // select: true,
  84 + // title: '发布',
  85 + // icon: renderIcon(SendIcon),
  86 + // event: sendHandle
  87 + // }
  88 +]
  89 +
  90 +const comBtnList = computed(() => {
  91 + if (chartEditStore.getEditCanvas.isCodeEdit) {
  92 + return btnList
  93 + }
  94 + const cloneList = cloneDeep(btnList)
  95 + cloneList.shift()
  96 + return cloneList
  97 +})
  98 +</script>
  99 +
  100 +<style lang="scss" scoped>
  101 +.align-center {
  102 + margin-top: -4px;
  103 +}
  104 +</style>
... ...
... ... @@ -50,9 +50,13 @@ const chartEditStore = useChartEditStore()
50 50
51 51 // 记录初始化
52 52 chartHistoryStoreStore.canvasInit(chartEditStore.getEditCanvas)
53   -
54   -const HeaderLeftBtn = loadAsyncComponent(() => import('./ContentHeader/headerLeftBtn/index.vue'))
55   -const HeaderRightBtn = loadAsyncComponent(() => import('./ContentHeader/headerRightBtn/index.vue'))
  53 +/**
  54 + * THINGS_KIT 升级版本这里有冲突
  55 + * 源文件 './ContentHeader/headerRightBtn/index.vue' ./ContentHeader/headerLeftBtn/index.vue'
  56 + * 修改后 './ContentHeader/headerRightBtn/external/index.vue' ./ContentHeader/headerLeftBtn/external/index.vue'
  57 + */
  58 +const HeaderLeftBtn = loadAsyncComponent(() => import('./ContentHeader/headerLeftBtn/external/index.vue'))
  59 +const HeaderRightBtn = loadAsyncComponent(() => import('./ContentHeader/headerRightBtn/external/index.vue'))
56 60 const HeaderTitle = loadAsyncComponent(() => import('./ContentHeader/headerTitle/index.vue'))
57 61 const ContentLayers = loadAsyncComponent(() => import('./ContentLayers/index.vue'))
58 62 const ContentCharts = loadAsyncComponent(() => import('./ContentCharts/index.vue'))
... ...