From 012fc44f08c9e87cf34be482d417c431e7c55911 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 28 Oct 2024 10:48:17 +0800 Subject: [PATCH 01/22] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E4=BE=9D=E8=B5=96=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 34 +++++++++++++++++----------------- src/main.ts | 7 +++---- vite.config.ts | 14 +------------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 71bbe0a8..76e7b0c8 100644 --- a/package.json +++ b/package.json @@ -16,31 +16,31 @@ "@antv/g6": "~4.8.24", "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-yaml": "^6.1.1", - "@codemirror/merge": "^6.6.3", + "@codemirror/merge": "^6.7.2", "@codemirror/theme-one-dark": "^6.1.2", - "@tato30/vue-pdf": "^1.10.0", - "@vueuse/core": "~10.10.1", + "@tato30/vue-pdf": "^1.11.2", + "@vueuse/core": "^11.1.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", - "ant-design-vue": "^3.2.20", - "antdv-pro-layout": "~3.3.5", - "antdv-pro-modal": "^3.1.0", + "ant-design-vue": "^4.2.5", + "antdv-pro-layout": "^4.1.8", + "antdv-pro-modal": "^4.0.5", "codemirror": "^6.0.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.11", "echarts": "~5.5.0", "file-saver": "^2.0.5", "grid-layout-plus": "^1.0.5", - "intl-tel-input": "^23.8.1", + "intl-tel-input": "^24.6.0", "js-base64": "^3.7.7", "js-cookie": "^3.0.5", "localforage": "^1.10.0", "nprogress": "^0.2.0", "p-queue": "~8.0.1", - "pinia": "^2.1.7", - "vue": "~3.3.13", - "vue-i18n": "^9.13.1", - "vue-router": "^4.4.0", + "pinia": "^2.2.4", + "vue": "^3.5.12", + "vue-i18n": "^10.0.4", + "vue-router": "^4.4.5", "vue3-smooth-dnd": "^0.0.6", "xlsx": "~0.18.5" }, @@ -48,14 +48,14 @@ "@types/crypto-js": "^4.2.2", "@types/file-saver": "^2.0.7", "@types/js-cookie": "^3.0.6", - "@types/node": "^18.0.0", + "@types/node": "^22.7.7", "@types/nprogress": "^0.2.3", - "@vitejs/plugin-vue": "^5.0.5", + "@vitejs/plugin-vue": "^5.1.4", "less": "^4.2.0", - "typescript": "~5.4.5", - "unplugin-vue-components": "~0.26.0", - "vite": "~5.3.1", + "typescript": "^5.6.3", + "unplugin-vue-components": "^0.27.4", + "vite": "5.4.10", "vite-plugin-compression": "~0.5.1", - "vue-tsc": "~2.0.22" + "vue-tsc": "^2.1.8" } } diff --git a/src/main.ts b/src/main.ts index 358d7988..f770c37a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,16 +4,15 @@ import App from './App.vue'; import router from './router'; import directive from './directive'; import i18n from './i18n'; -import ProModal from "antdv-pro-modal"; -import 'antdv-pro-layout/dist/style.css'; + import 'antdv-pro-modal/dist/style.css'; -import 'ant-design-vue/dist/antd.variable.min.css'; +import 'antdv-pro-layout/dist/style.css'; +import 'ant-design-vue/dist/reset.css'; const app = createApp(App); app.use(store); app.use(router); app.use(directive); app.use(i18n); -app.use(ProModal); app.mount('#app'); diff --git a/vite.config.ts b/vite.config.ts index bd38b8ef..ac1af06f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -41,18 +41,6 @@ export default defineConfig(({ mode }) => { }, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], }, - css: { - preprocessorOptions: { - less: { - // DO NOT REMOVE THIS LINE - javascriptEnabled: true, - modifyVars: { - // hack: `true; @import 'ant-design-vue/dist/antd.variable.less'`, - // '@primary-color': '#eb2f96', // 全局主色 - }, - }, - }, - }, build: { target: 'esnext', // Use 'esnext' to support the latest features sourcemap: false, @@ -94,7 +82,7 @@ export default defineConfig(({ mode }) => { AntDesignVueResolver({ importStyle: false, resolveIcons: true, - cjs: true, // 避免es模块打包缺失 + cjs: false, // 统一使用es模块打包 }), ], }), From 327e82e05772e6f31165da4f0be3f7d858f431c0 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 28 Oct 2024 10:51:11 +0800 Subject: [PATCH 02/22] =?UTF-8?q?fix:=20=E5=B8=83=E5=B1=80=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=8D=87=E7=BA=A7=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 119 ++++++++---------- src/assets/background_dark.jpg | Bin 0 -> 114 bytes .../{background.jpg => background_light.jpg} | Bin src/assets/svg/dark.svg | 1 + src/assets/svg/light.svg | 1 + src/hooks/useTheme.ts | 2 +- src/i18n/locales/en-US.ts | 19 ++- src/i18n/locales/zh-CN.ts | 23 +++- src/layouts/BasicLayout.vue | 48 ++++--- src/layouts/components/RightContent.vue | 56 +++++++-- src/layouts/components/Tabs.vue | 17 ++- src/store/modules/layout.ts | 103 +++++++++++++-- src/store/modules/user.ts | 10 +- 13 files changed, 279 insertions(+), 120 deletions(-) create mode 100644 src/assets/background_dark.jpg rename src/assets/{background.jpg => background_light.jpg} (100%) create mode 100644 src/assets/svg/dark.svg create mode 100644 src/assets/svg/light.svg diff --git a/src/App.vue b/src/App.vue index 020167b8..fc9f4331 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,22 +1,48 @@ diff --git a/src/assets/background_dark.jpg b/src/assets/background_dark.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b64e7dff794db8d1a9744d940d72c7f66d3ddac GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*z3?w}c`(6i9Ea{HEjtmSN`?>!lvI6;R0X`wF zCT3Q81}5%N4AwvqaZeY=5RU7~KmPy!&(3zKgZbML!6mbJ7@h~Gem(Un^)yfkgQu&X J%Q~loCIFL(AJzZ> literal 0 HcmV?d00001 diff --git a/src/assets/background.jpg b/src/assets/background_light.jpg similarity index 100% rename from src/assets/background.jpg rename to src/assets/background_light.jpg diff --git a/src/assets/svg/dark.svg b/src/assets/svg/dark.svg new file mode 100644 index 00000000..9b27cc61 --- /dev/null +++ b/src/assets/svg/dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/light.svg b/src/assets/svg/light.svg new file mode 100644 index 00000000..161b3bd9 --- /dev/null +++ b/src/assets/svg/light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts index 4ae9108c..e9cd450d 100644 --- a/src/hooks/useTheme.ts +++ b/src/hooks/useTheme.ts @@ -1,5 +1,5 @@ import { onBeforeMount } from 'vue'; -import { ConfigProvider, message } from 'ant-design-vue/lib'; +import { ConfigProvider, message } from 'ant-design-vue/es'; import { CACHE_LOCAL_PRIMARY_COLOR } from '@/constants/cache-keys-constants'; import { localGet, localSet } from '@/utils/cache-local-utils'; diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index aad058da..a277f380 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -192,6 +192,7 @@ export default { lockPasswd: "Unlock Password", lockPasswdTip: "No password can be set", fullscreen: "Full Screen", + theme: "Theme light/dark mode", logout: "Logout", profile: "Profile", settings: "Settings", @@ -317,6 +318,8 @@ export default { color: "Style color scheme", colorActions: "Overall style color scheme setting", colorRandomly: "Randomization", + theme: "Theme dark and light modes", + themeActions: "Toggle light/dark mode", navTheme: "Dark Menu", navThemeActions: "Menus that can only change the navigation mode", fixedHeader: "Fixed top navigation bar", @@ -1072,7 +1075,19 @@ export default { element:'Element', granularity:'Granularity', unit:'Unit', - } + }, + kpiKeyTarget:{ + "fullWidthLayout":"Full Width", + "twoColumnLayout":"Two Column", + "saveLayout": "Save Layout", + "restoreSaved": "Restore Layout", + "saveSuccess": " '{name}' saved successfully", + "restoreSavedSuccess": " '{name}' restored successfully", + "noSavedLayout": "No saved layout found for '{name}'", + "layout1": "Layout 1", + "layout2": "Layout 2", + "layout3": "Layout 3" + }, }, traceManage: { analysis: { @@ -1288,7 +1303,7 @@ export default { }, exportFile:{ fileName:'File Source', - downTip: "Confirm the download file name is [{fileName}] File?", + downTip: "Confirm the download file name is [{fileName}] File?", downTipErr: "Failed to get file", deleteTip: "Confirm the delete file name is [{fileName}] File?", deleteTipErr: "Failed to delete file", diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 3f878c47..bce5a90c 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -192,6 +192,7 @@ export default { lockPasswd: "解锁密码", lockPasswdTip: "可不设置密码", fullscreen: "全屏显示", + theme: "主题明/暗模式", logout: "退出登录", profile: "个人中心", settings: "个人设置", @@ -317,6 +318,8 @@ export default { color: "风格配色", colorActions: "整体风格配色设置", colorRandomly: "随机", + theme: "主题明暗模式", + themeActions: "切换浅色/暗黑模式", navTheme: "深色菜单", navThemeActions: "只能改变导航模式的菜单", fixedHeader: "固定顶部导航栏", @@ -1072,7 +1075,19 @@ export default { element:'元素', granularity:'颗粒度', unit:'单位', - } + }, + kpiKeyTarget:{ + "fullWidthLayout":"全宽布局", + "twoColumnLayout":"两列布局", + "saveLayout": "保存布局", + "restoreSaved": "恢复布局", + "saveSuccess": " {name} 保存成功", + "restoreSavedSuccess": " {name} 恢复成功", + "noSavedLayout": "没有找到保存的布局 {name}", + "layout1": "布局1", + "layout2": "布局2", + "layout3": "布局3" + }, }, traceManage: { analysis: { @@ -1127,7 +1142,7 @@ export default { stopNotRun: "{title} 任务未运行", }, task: { - traceId: '跟踪编号', + traceId: '跟踪编号', trackType: '跟踪类型', trackTypePlease: '请选择跟踪类型', creater: '创建人', @@ -1288,7 +1303,7 @@ export default { }, exportFile:{ fileName:'文件来源', - downTip: "确认下载文件名为 【{fileName}】 文件?", + downTip: "确认下载文件名为 【{fileName}】 文件?", downTipErr: "文件获取失败", deleteTip: "确认删除文件名为 【{fileName}】 文件?", deleteTipErr: "文件删除失败", @@ -2110,7 +2125,7 @@ export default { hostSelectMore: "加载更多 {num}", hostSelectHeader: "主机列表", }, - ps:{ + ps:{ realTimeHigh:"高", realTimeLow:"低", realTimeRegular:"常规", diff --git a/src/layouts/BasicLayout.vue b/src/layouts/BasicLayout.vue index 9a3f9f4d..2d5dddcc 100644 --- a/src/layouts/BasicLayout.vue +++ b/src/layouts/BasicLayout.vue @@ -1,10 +1,9 @@ - + - + @@ -364,7 +362,7 @@ onUnmounted(() => { - + diff --git a/src/layouts/components/Tabs.vue b/src/layouts/components/Tabs.vue index b72f87b9..341bcd01 100644 --- a/src/layouts/components/Tabs.vue +++ b/src/layouts/components/Tabs.vue @@ -4,9 +4,9 @@ import { computed, watch } from 'vue'; import { useRouter } from 'vue-router'; import useTabsStore from '@/store/modules/tabs'; import useI18n from '@/hooks/useI18n'; -const { t } = useI18n(); const tabsStore = useTabsStore(); const router = useRouter(); +const { t } = useI18n(); defineProps({ /**标签栏宽度 */ @@ -112,7 +112,7 @@ watch(router.currentRoute, v => tabsStore.tabOpen(v), { immediate: true }); tabsStore.tabOpen(v), { immediate: true }); - + @@ -199,7 +199,18 @@ watch(router.currentRoute, v => tabsStore.tabOpen(v), { immediate: true }); } } +[data-theme='dark'] .tabs { + background: #141414; +} + .tabs :deep(.ant-tabs-nav:before) { border-bottom: none; } +.tabs :deep(.ant-tabs-nav-list .ant-tabs-tab) { + border-radius: 8px; +} +.tabs :deep(.ant-tabs-nav-list .ant-tabs-tab.ant-tabs-tab-active) { + border-bottom-right-radius: unset; + border-bottom-left-radius: unset; +} diff --git a/src/store/modules/layout.ts b/src/store/modules/layout.ts index c297498b..21c9cf34 100644 --- a/src/store/modules/layout.ts +++ b/src/store/modules/layout.ts @@ -1,16 +1,23 @@ -import { CACHE_LOCAL_PROCONFIG } from '@/constants/cache-keys-constants'; -import { localGetJSON, localSetJSON } from '@/utils/cache-local-utils'; +import { theme } from 'ant-design-vue/es'; +import type { ThemeConfig } from 'ant-design-vue/es/config-provider/context'; import { defineStore } from 'pinia'; +import { + CACHE_LOCAL_PRIMARY_COLOR, + CACHE_LOCAL_PROCONFIG, +} from '@/constants/cache-keys-constants'; +import { + localGet, + localGetJSON, + localSetJSON, +} from '@/utils/cache-local-utils'; /**布局参数类型 */ type LayoutStore = { - /**布局设置抽屉显示 */ - visible: boolean; /**布局配置 */ proConfig: { /**导航布局 */ layout: 'side' | 'top' | 'mix'; - /**全局主题色,需要导入样式文件 */ + /**全局主题色*/ theme: 'dark' | 'light'; /**菜单导航主题色 */ menuTheme: 'dark' | 'light'; @@ -29,10 +36,33 @@ type LayoutStore = { /**内容区域-导航标签项 */ tabRender: any | boolean | undefined; }; + /**主题配置 */ + themeConfig: ThemeConfig; /**水印内容 */ waterMarkContent: string; }; +/** + * 获取随机颜色范围 + * @returns 颜色 + */ +function getRandomColor(): string { + const colors: string[] = [ + '#f5222d', + '#fa541c', + '#fa8c16', + '#a0d911', + '#13c2c2', + '#1890ff', + '#722ed1', + '#eb2f96', + '#faad14', + '#52c41a', + ]; + const i = Math.floor(Math.random() * 10); + return colors[i]; +} + /**判断是否关闭内容区域 */ const proRender = (render: any) => (render === false ? false : undefined); @@ -40,7 +70,7 @@ const proRender = (render: any) => (render === false ? false : undefined); const proConfigLocal: LayoutStore['proConfig'] = localGetJSON( CACHE_LOCAL_PROCONFIG ) || { - layout: 'mix', + layout: 'side', theme: 'light', menuTheme: 'light', fixSiderbar: true, @@ -50,7 +80,6 @@ const proConfigLocal: LayoutStore['proConfig'] = localGetJSON( const useLayoutStore = defineStore('layout', { state: (): LayoutStore => ({ - visible: false, proConfig: { layout: proConfigLocal.layout, theme: proConfigLocal.theme, @@ -63,13 +92,27 @@ const useLayoutStore = defineStore('layout', { menuHeaderRender: proRender(proConfigLocal.menuHeaderRender), tabRender: proRender(proConfigLocal.tabRender), }, + themeConfig: { + algorithm: [theme.darkAlgorithm], + // algorithm: themeColor["dark"], + token: { + // colorBgContainer: "#fff", + colorPrimary: localGet(CACHE_LOCAL_PRIMARY_COLOR) || '#1890ff', + // borderRadius: 6, + }, + }, waterMarkContent: import.meta.env.VITE_APP_NAME, }), - actions: { - /**改变显示状态 */ - changeVisibleLayoutSetting() { - this.visible = !this.visible; + getters: { + getColorPrimary(): string { + let color = '#1890ff'; + if (this.themeConfig.token) { + color = this.themeConfig.token.colorPrimary || color; + } + return color; }, + }, + actions: { /**修改水印文字 */ changeWaterMark(text: string) { this.waterMarkContent = text; @@ -77,10 +120,48 @@ const useLayoutStore = defineStore('layout', { /**修改布局设置 */ changeConf(key: string, value: boolean | string | number | undefined) { if (Reflect.has(this.proConfig, key)) { + console.log(key, value); + if (key === 'theme') { + // const themeColor = { + // light: theme.defaultAlgorithm, + // compact: theme.compactAlgorithm, + // dark: theme.darkAlgorithm, + // }; + if (value === 'dark') { + document.documentElement.setAttribute('data-theme', 'dark'); + this.themeConfig.algorithm = [theme.darkAlgorithm]; + } else { + document.documentElement.setAttribute('data-theme', 'light'); + this.themeConfig.algorithm = [theme.defaultAlgorithm]; + } + } Reflect.set(this.proConfig, key, value); localSetJSON(CACHE_LOCAL_PROCONFIG, this.proConfig); } }, + /**主题色初始化 */ + initPrimaryColor() { + // 主题色初始化 + this.changePrimaryColor(this.getColorPrimary); + // 明暗模式初始化 + const themeMode = this.proConfig.theme; + document.documentElement.setAttribute('data-theme', themeMode); + this.changeConf('theme', themeMode); + }, + /** + * 主题色变更 + * @param color 颜色 + */ + changePrimaryColor(color?: string) { + if (!color) { + color = getRandomColor(); + } + + if (this.themeConfig && this.themeConfig.token) { + this.themeConfig.token.colorPrimary = color; + localStorage.setItem(CACHE_LOCAL_PRIMARY_COLOR, color); + } + }, }, }); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index 1a9b1932..a7bb65a6 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -1,7 +1,7 @@ import defaultAvatar from '@/assets/images/default_avatar.png'; import useLayoutStore from './layout'; import { login, logout, getInfo } from '@/api/login'; -import { getToken, setToken, removeToken } from '@/plugins/auth-token'; +import { setToken, removeToken } from '@/plugins/auth-token'; import { defineStore } from 'pinia'; import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; @@ -133,10 +133,10 @@ const useUserStore = defineStore('user', { } // 水印文字信息=用户昵称 手机号 - let waterMarkContent = this.userName; - if (this.phonenumber) { - waterMarkContent = `${this.userName} ${this.phonenumber}`; - } + // let waterMarkContent = this.userName; + // if (this.phonenumber) { + // waterMarkContent = `${this.userName} ${this.phonenumber}`; + // } // useLayoutStore().changeWaterMark(waterMarkContent); useLayoutStore().changeWaterMark(''); } From 3c058ec107f971da5f9b6fac5b628b50497f881e Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 28 Oct 2024 10:53:03 +0800 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20PCF=E8=A1=A5=E5=85=85=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0online=E5=92=8Coffline=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E6=9C=89=E5=8F=96=E6=B6=88=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/neUser/pcf/index.vue | 112 ++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 44 deletions(-) diff --git a/src/views/neUser/pcf/index.vue b/src/views/neUser/pcf/index.vue index 00831faa..94771746 100644 --- a/src/views/neUser/pcf/index.vue +++ b/src/views/neUser/pcf/index.vue @@ -1,9 +1,10 @@ @@ -56,13 +66,31 @@ function fnColorChange(e: Event) { + + {{ t('views.account.settings.theme') }} + + + {{ t('views.account.settings.navTheme') }} diff --git a/src/views/dashboard/overview/components/Topology/index.vue b/src/views/dashboard/overview/components/Topology/index.vue index 8c2116e1..54c57cc5 100644 --- a/src/views/dashboard/overview/components/Topology/index.vue +++ b/src/views/dashboard/overview/components/Topology/index.vue @@ -2,7 +2,7 @@ import { onMounted, ref } from 'vue'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { listAllNeInfo } from '@/api/ne/neInfo'; -import { message } from 'ant-design-vue/lib'; +import { message } from 'ant-design-vue/es'; import { getGraphData } from '@/api/monitor/topology'; import { Graph, GraphData, Tooltip } from '@antv/g6'; import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge'; diff --git a/src/views/dashboard/smscCDR/index.vue b/src/views/dashboard/smscCDR/index.vue index 30fc6a5b..0c160722 100644 --- a/src/views/dashboard/smscCDR/index.vue +++ b/src/views/dashboard/smscCDR/index.vue @@ -1,10 +1,10 @@ +const isSaving = ref(false); +const isRestoring = ref(false); +//保存布局按钮 +const handleSaveLayout = async (info: MenuInfo) => { + if (typeof info.key === 'string' && !isSaving.value) { + isSaving.value = true; + isRestoring.value = true; + try { + await saveCurrentLayout(info.key); + } finally { + setTimeout(()=>{ + isSaving.value = false; + isRestoring.value = false; + },700) + } + } else { + console.error('Invalid layout key or operation in progress'); + } +}; +//恢复布局按钮 +const handleRestoreLayout = async (info: MenuInfo) => { + if (typeof info.key === 'string' && !isRestoring.value) { + isRestoring.value = true; + isSaving.value = true; + try { + await restoreSavedLayout(info.key); + } finally { + setTimeout(()=>{ + isSaving.value = false; + isRestoring.value = false; + },700) + } + } else { + console.error('Invalid layout key or operation in progress'); + } +}; + +// 保存当前布局 +const saveCurrentLayout = async (layoutName: string) => { + const savedLayouts = JSON.parse(localStorage.getItem('savedLayouts') || '{}'); + savedLayouts[layoutName] = { + layout: chartOrder.value, + selectedTypes: selectedNeTypes.value + }; + localStorage.setItem('savedLayouts', JSON.stringify(savedLayouts)); + message.success(t('views.perfManage.kpiKeyTarget.saveSuccess', { name: t(`views.perfManage.kpiKeyTarget.${layoutName}`)})); +}; +//恢复已保存的布局 +const restoreSavedLayout = async (layoutName: string) => { + const savedLayouts = JSON.parse(localStorage.getItem('savedLayouts') || '{}'); + const savedLayout = savedLayouts[layoutName]; + if (savedLayout && Array.isArray(savedLayout.selectedTypes) && Array.isArray(savedLayout.layout)) { + selectedNeTypes.value = savedLayout.selectedTypes; + networkElementTypes.value = savedLayout.selectedTypes; + + // 更新布局 + chartOrder.value = savedLayout.layout.filter((item: LayoutItem) => + savedLayout.selectedTypes.includes(item.i) + ); + + // 如果有当前选中的网元类型不在保存的布局中,添加它们 + const missingTypes = savedLayout.selectedTypes.filter((type: AllChartType) => + !chartOrder.value.some(item => item.i === type) + ); + missingTypes.forEach((type: AllChartType) => { + chartOrder.value.push({ + x: (chartOrder.value.length % 2) * 6, + y: Math.floor(chartOrder.value.length / 2) * 4, + w: 6, + h: 4, + i: type, + }); + }); + + localStorage.setItem('chartOrder', JSON.stringify(chartOrder.value)); + localStorage.setItem('selectedNeTypes', JSON.stringify(selectedNeTypes.value)); + + await nextTick(); + await initCharts(); + + message.success(t('views.perfManage.kpiKeyTarget.restoreSavedSuccess', { name: t(`views.perfManage.kpiKeyTarget.${layoutName}`)})); + } else { + message.warning(t('views.perfManage.kpiKeyTarget.noSavedLayout', { name: t(`views.perfManage.kpiKeyTarget.${layoutName}`)})); + } +}; + +// 应用全宽布局 +const applyFullWidthLayout = () => { + isManuallyUpdating.value = true; + const newLayout = selectedNeTypes.value.map((type, index) => ({ + x: 0, + y: index * 8, + w: 12, + h: 8, + i: type, + })); + + nextTick(() => { + Object.assign(chartOrder.value, newLayout) + // chartOrder.value = newLayout; + localStorage.setItem('chartOrder', JSON.stringify(newLayout)); + initCharts(); + // 直接更新图表,不调用 handleLayoutUpdated + chartOrder.value.forEach((item) => { + const state = chartStates[item.i]; + if (state?.chart.value) { + state.chart.value.resize(); + renderChart(item.i); + } + }); + isManuallyUpdating.value = false; + }); +}; + +// 应用两列布局 +const applyTwoColumnLayout = () => { + isManuallyUpdating.value = true; + const newLayout = selectedNeTypes.value.map((type, index) => ({ + x: (index % 2) * 6, + y: Math.floor(index / 2) * 4, + w: 6, + h: 4, + i: type, + })); + nextTick(() => { + // chartOrder.value = newLayout; + Object.assign(chartOrder.value, newLayout) + localStorage.setItem('chartOrder', JSON.stringify(newLayout)); + initCharts(); + // 直接更新图表,不调用 handleLayoutUpdated + chartOrder.value.forEach((item) => { + const state = chartStates[item.i]; + if (state?.chart.value) { + state.chart.value.resize(); + renderChart(item.i); + } + }); + isManuallyUpdating.value = false; + }); +}; +