|
|
@@ -0,0 +1,179 @@
|
|
|
+<template>
|
|
|
+ <BasicModal
|
|
|
+ v-model:visible="visible"
|
|
|
+ title="首页风格"
|
|
|
+ :ok-text="'确认'"
|
|
|
+ :cancel-text="'取消'"
|
|
|
+ :mask-closable="false"
|
|
|
+ :can-fullscreen="false"
|
|
|
+ width="480px"
|
|
|
+ @ok="handleConfirm"
|
|
|
+ >
|
|
|
+ <div class="home-style-select">
|
|
|
+ <p class="home-style-tip">选择后系统将应用对应的首页风格并切换主题。</p>
|
|
|
+ <a-select v-model:value="selectedId" :options="selectOptions" placeholder="请选择首页风格" :loading="loading" style="width: 100%" />
|
|
|
+ <p v-if="currentOption" class="home-style-detail">首页路由:{{ currentOption.route }} 主题:{{ currentOption.theme }}</p>
|
|
|
+ </div>
|
|
|
+ <!-- 默认 footer 顺序:取消 → centerFooter(恢复默认) → 确认 -->
|
|
|
+ <template #centerFooter>
|
|
|
+ <a-button @click="handleRestore">恢复默认</a-button>
|
|
|
+ </template>
|
|
|
+ </BasicModal>
|
|
|
+</template>
|
|
|
+<script lang="ts" setup>
|
|
|
+ // 首页风格切换弹窗:下拉框选择首页风格,右下角 取消 / 恢复默认 / 确认。
|
|
|
+ // 选项动态获取自 /sys/permissionNew/list(getPermissionList),component 以 vent/home/ 开头视为已注册首页;
|
|
|
+ // 主题按标题关键词推导(绿→GREEN、蓝→DEEPBLUE、5.5→DEEPBLUE+theme5_5,其余→VENT1);本地存储见 /@/utils/homeStyle。
|
|
|
+ import { computed, ref } from 'vue';
|
|
|
+
|
|
|
+ import { BasicModal } from '/@/components/Modal';
|
|
|
+ import { ThemeEnum } from '/@/enums/appEnum';
|
|
|
+ import { PageEnum } from '/@/enums/pageEnum';
|
|
|
+ import { useGlobSetting } from '/@/hooks/setting';
|
|
|
+ import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
|
|
+ import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
+ import { useGo } from '/@/hooks/web/usePage';
|
|
|
+ import { ThemeModel } from '/@/layouts/default/layout.data';
|
|
|
+ import { updateDarkTheme } from '/@/logics/theme/dark';
|
|
|
+ import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
|
|
|
+
|
|
|
+ import { getPermissionList } from '/@/api/sys/menu';
|
|
|
+ import { getSavedHomeStyle, saveHomeStyle } from '/@/utils/homeStyle';
|
|
|
+ import type { HomeStyleOption } from '/@/utils/homeStyle';
|
|
|
+
|
|
|
+ const visible = ref(false);
|
|
|
+ const selectedId = ref<string>('');
|
|
|
+ const loading = ref(false);
|
|
|
+ const options = ref<HomeStyleOption[]>([]);
|
|
|
+
|
|
|
+ const { createMessage } = useMessage();
|
|
|
+ const go = useGo();
|
|
|
+ const glob = useGlobSetting();
|
|
|
+ const { setDarkMode } = useRootSetting();
|
|
|
+
|
|
|
+ /** 递归展平菜单树(兼容 children 嵌套) */
|
|
|
+ function flattenMenu(nodes: any[], list: any[] = []): any[] {
|
|
|
+ nodes?.forEach((node) => {
|
|
|
+ list.push(node);
|
|
|
+ if (Array.isArray(node.children)) flattenMenu(node.children, list);
|
|
|
+ });
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 是否已注册首页:component 以 vent/home/ 开头且配置了路由 */
|
|
|
+ function isRegisteredHome(item: any): boolean {
|
|
|
+ return !!item && !!item.component && String(item.component).startsWith('vent/home/') && !!item.url;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 由菜单节点生成首页风格选项,主题按标题关键词推导 */
|
|
|
+ function toHomeStyleOption(item: any): HomeStyleOption {
|
|
|
+ const route: string = item.url;
|
|
|
+ const name: string = item.title || item.name || route;
|
|
|
+ const is55 = name.includes('5.5') || route.includes('modelchannel/model3D');
|
|
|
+ let theme = ThemeEnum.VENT1;
|
|
|
+ if (name.includes('绿')) theme = ThemeEnum.GREEN;
|
|
|
+ else if (name.includes('蓝') || is55) theme = ThemeEnum.DEEPBLUE;
|
|
|
+ return { id: String(item.id), name, route, theme, cssVars: is55 ? ThemeModel.theme5_5 : undefined };
|
|
|
+ }
|
|
|
+
|
|
|
+ // Promise 记忆化缓存:同一会话只请求一次,失败清空可重试
|
|
|
+ let homeStyleOptionsPromise: Promise<HomeStyleOption[]> | null = null;
|
|
|
+
|
|
|
+ /** 动态获取已注册首页选项 */
|
|
|
+ function getHomeStyleOptions(): Promise<HomeStyleOption[]> {
|
|
|
+ if (!homeStyleOptionsPromise) {
|
|
|
+ homeStyleOptionsPromise = getPermissionList()
|
|
|
+ .then((res: any) => {
|
|
|
+ const optionMap = new Map<string, HomeStyleOption>();
|
|
|
+ flattenMenu(Array.isArray(res) ? res : [])
|
|
|
+ .filter(isRegisteredHome)
|
|
|
+ .forEach((item) => {
|
|
|
+ const option = toHomeStyleOption(item);
|
|
|
+ // 2D/3D 去重:按归一化路由去重,同键优先 3D 项(2D 由路由守卫自动改写)
|
|
|
+ const key = `${item.component}|${option.route.replace('micro-vent-2dModal', 'micro-vent-3dModal')}`;
|
|
|
+ const existed = optionMap.get(key);
|
|
|
+ if (!existed || (!existed.route.includes('micro-vent-3dModal') && option.route.includes('micro-vent-3dModal'))) {
|
|
|
+ optionMap.set(key, option);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return [...optionMap.values()];
|
|
|
+ })
|
|
|
+ .catch((e) => {
|
|
|
+ console.error('获取首页风格列表失败:', e);
|
|
|
+ homeStyleOptionsPromise = null;
|
|
|
+ return [];
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return homeStyleOptionsPromise;
|
|
|
+ }
|
|
|
+
|
|
|
+ const selectOptions = computed(() => options.value.map((o) => ({ value: o.id, label: o.name })));
|
|
|
+ const currentOption = computed(() => options.value.find((o) => o.id === selectedId.value) || null);
|
|
|
+
|
|
|
+ /** 打开弹窗:获取选项后按 已保存 → 系统默认首页 → 第一项 预选 */
|
|
|
+ async function show() {
|
|
|
+ visible.value = true;
|
|
|
+ loading.value = true;
|
|
|
+ options.value = await getHomeStyleOptions();
|
|
|
+ loading.value = false;
|
|
|
+ // 本地选择已失效时清理
|
|
|
+ const saved = getSavedHomeStyle();
|
|
|
+ const savedValid = !!saved && options.value.some((o) => o.id === saved.id);
|
|
|
+ if (saved && !savedValid) saveHomeStyle(null);
|
|
|
+ const savedId = saved && savedValid ? saved.id : '';
|
|
|
+ selectedId.value = savedId || options.value.find((o) => o.route === glob.homePath)?.id || options.value[0]?.id || '';
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 仅应用主题(模式 + 样式变量),不处理持久化 */
|
|
|
+ function applyTheme({ theme, cssVars }: Pick<HomeStyleOption, 'theme' | 'cssVars'>) {
|
|
|
+ setDarkMode(theme);
|
|
|
+ updateDarkTheme(theme);
|
|
|
+ updateHeaderBgColor();
|
|
|
+ updateSidebarBgColor();
|
|
|
+ if (cssVars) {
|
|
|
+ Object.entries(cssVars).forEach(([key, value]) => document.body.style.setProperty(`--${key}`, value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 应用首页风格:主题 + 持久化 + 提示 + 跳转 */
|
|
|
+ function applyStyle(option: HomeStyleOption) {
|
|
|
+ applyTheme(option);
|
|
|
+ saveHomeStyle(option);
|
|
|
+ createMessage.success(`已切换首页风格为「${option.name}」,正在应用新首页…`);
|
|
|
+ visible.value = false;
|
|
|
+ go(option.route);
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleConfirm() {
|
|
|
+ if (currentOption.value) applyStyle(currentOption.value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 恢复默认:本地存储首页置为 null,应用系统默认首页主题并跳转 */
|
|
|
+ function handleRestore() {
|
|
|
+ const defaultOption = options.value.find((o) => o.route === glob.homePath);
|
|
|
+ saveHomeStyle(null);
|
|
|
+ selectedId.value = defaultOption?.id || '';
|
|
|
+ applyTheme(defaultOption ?? { theme: ThemeEnum.VENT1 });
|
|
|
+ createMessage.success('已恢复默认首页风格,正在应用系统默认首页…');
|
|
|
+ visible.value = false;
|
|
|
+ go(glob.homePath || PageEnum.BASE_HOME);
|
|
|
+ }
|
|
|
+
|
|
|
+ defineExpose({ show });
|
|
|
+</script>
|
|
|
+<style lang="less" scoped>
|
|
|
+ .home-style-select {
|
|
|
+ .home-style-tip {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ color: #999;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .home-style-detail {
|
|
|
+ margin-top: 12px;
|
|
|
+ color: #666;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.6;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|