diff --git a/frontend/src/lang/package/en.ts b/frontend/src/lang/package/en.ts index b3301b0c..2b3df172 100644 --- a/frontend/src/lang/package/en.ts +++ b/frontend/src/lang/package/en.ts @@ -172,6 +172,7 @@ export default { interface: "Interface", navigation: "Navigation", themeColor: "Theme Color", + customColor: "Custom Color", showTagsView: "Show Tags View", showAppLogo: "Show App Logo", sidebarColorScheme: "Sidebar Color Scheme", diff --git a/frontend/src/lang/package/zh-cn.ts b/frontend/src/lang/package/zh-cn.ts index 6c3ce54a..30894cce 100644 --- a/frontend/src/lang/package/zh-cn.ts +++ b/frontend/src/lang/package/zh-cn.ts @@ -174,6 +174,7 @@ export default { navigation: "导航主题", themeColor: "主题颜色", themeColorTip: "主题颜色", + customColor: "自定义颜色", darkMode: "暗黑模式", layoutSetting: "布局设置", sidebarColorScheme: "侧边栏配色", diff --git a/frontend/src/layouts/components/Settings/index.vue b/frontend/src/layouts/components/Settings/index.vue index 846ce964..5ec90a7d 100644 --- a/frontend/src/layouts/components/Settings/index.vue +++ b/frontend/src/layouts/components/Settings/index.vue @@ -336,24 +336,25 @@ const handleCloseDrawer = () => { height: 100%; padding: 0; overflow: hidden; - display: flex; - flex-direction: column; } } /* 设置内容区域 */ .settings-content { - flex: 1; + height: calc(100vh - 120px); /* 减去头部和底部按钮的高度 */ padding: 20px; padding-bottom: 20px; overflow-y: auto; - min-height: 0; /* 确保内容区域可以收缩 */ } /* 底部操作区域样式 */ .action-footer { - flex-shrink: 0; - margin-top: auto; /* 将底部操作区域推到最下方 */ + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 10; + padding: 0; background: var(--el-bg-color); border-top: 1px solid var(--el-border-color-light); diff --git a/frontend/src/layouts/components/TagsView/index.vue b/frontend/src/layouts/components/TagsView/index.vue index 858eb862..c4c06ca6 100644 --- a/frontend/src/layouts/components/TagsView/index.vue +++ b/frontend/src/layouts/components/TagsView/index.vue @@ -13,6 +13,7 @@ (null); // 滚动条引用 const scrollbarRef = ref(); +// 标签切换来源跟踪 +const tagSwitchSource = ref<'menu' | 'tab' | null>(null); + // 路由映射缓存,提升查找性能 const routePathMap = computed(() => { const map = new Map(); @@ -236,7 +240,6 @@ const extractAffixTags = (routes: RouteRecordRaw[], basePath = "/"): TagView[] = fullPath, name: String(route.name || ""), title: route.meta.title || "no-name", - icon: (route as any).icon || route.meta?.icon, affix: true, keepAlive: route.meta.keepAlive || false, }); @@ -272,20 +275,21 @@ const initAffixTags = () => { const addCurrentTag = () => { if (!route.meta?.title) return; - // 检查标签是否已存在 const existingTag = visitedViews.value.find(tag => tag.path === route.path); - + if (existingTag) { - // 如果标签已存在,将其移动到最新位置(除非是固定标签) + // 如果标签已存在,根据来源决定是否移动位置 if (!existingTag.affix) { - // 从当前位置移除 - const index = visitedViews.value.findIndex(tag => tag.path === route.path); - if (index !== -1) { - const tag = visitedViews.value.splice(index, 1)[0]; - // 添加到末尾 - visitedViews.value.push(tag); + if (tagSwitchSource.value === 'menu') { + // 通过菜单点击:移动到最新位置 + const index = visitedViews.value.findIndex(tag => tag.path === route.path); + if (index !== -1) { + const tag = visitedViews.value.splice(index, 1)[0]; + visitedViews.value.push(tag); + } } + // 通过标签容器点击:不移动位置,只激活 } } else { // 添加新标签 @@ -301,11 +305,17 @@ const addCurrentTag = () => { }); } + // 根据来源决定是否滚动 + if (tagSwitchSource.value === 'menu') { + // 通过菜单点击:滚动到最新标签 + nextTick(() => { + autoScrollToLatestTag(); + }); + } + // 通过标签容器点击:不滚动,保持当前位置 - // 检查是否需要自动滚动到最新标签 - nextTick(() => { - autoScrollToLatestTag(); - }); + // 重置来源状态 + tagSwitchSource.value = null; }; /** @@ -330,6 +340,14 @@ const updateCurrentTag = () => { }); }; +/** + * 处理标签点击 + */ +const handleTabClick = (tag: TagView) => { + // 设置标签切换来源为标签容器点击 + tagSwitchSource.value = 'tab'; +}; + /** * 处理中键点击 */ @@ -390,6 +408,10 @@ const closeSelectedTag = (tag: TagView | null) => { if (tagsViewStore.isActive(currentTag)) { tagsViewStore.toLastView(result.visitedViews, currentTag); } + // 关闭标签后重置滚动状态,以便下次可以重新判断是否需要滚动 + nextTick(() => { + resetScrollState(); + }); }); }; @@ -405,6 +427,10 @@ const closeLeftTags = () => { if (!hasCurrentRoute) { tagsViewStore.toLastView(result.visitedViews); } + // 关闭标签后重置滚动状态 + nextTick(() => { + resetScrollState(); + }); }); }; @@ -420,6 +446,10 @@ const closeRightTags = () => { if (!hasCurrentRoute) { tagsViewStore.toLastView(result.visitedViews); } + // 关闭标签后重置滚动状态 + nextTick(() => { + resetScrollState(); + }); }); }; @@ -432,6 +462,10 @@ const closeOtherTags = () => { router.push(selectedTag.value); tagsViewStore.delOtherViews(selectedTag.value).then(() => { updateCurrentTag(); + // 关闭标签后重置滚动状态 + nextTick(() => { + resetScrollState(); + }); }); }; @@ -441,6 +475,10 @@ const closeOtherTags = () => { const closeAllTags = (tag: TagView | null) => { tagsViewStore.delAllViews().then((result: any) => { tagsViewStore.toLastView(result.visitedViews, tag || undefined); + // 关闭所有标签后重置滚动状态 + nextTick(() => { + resetScrollState(); + }); }); }; @@ -545,8 +583,27 @@ const scrollRight = () => { const maxScrollLeft = scrollWrapper.scrollWidth - scrollWrapper.clientWidth const newScrollLeft = Math.min(maxScrollLeft, scrollWrapper.scrollLeft + 200) scrollbarRef.value.setScrollLeft(newScrollLeft) + + // 如果滚动到最右边,重置滚动状态以允许下次自动滚动 + if (newScrollLeft >= maxScrollLeft - 1) { + scrollState.value.hasScrolledToLatest = false + } } +/** + * 重置滚动状态 + */ +const resetScrollState = () => { + scrollState.value.hasScrolledToLatest = false + scrollState.value.isContainerFull = false +} + +// 滚动状态跟踪 +const scrollState = ref({ + hasScrolledToLatest: false, // 是否已经滚动到最新标签 + isContainerFull: false // 容器是否已满 +}) + /** * 自动滚动到最新标签 */ @@ -558,21 +615,34 @@ const autoScrollToLatestTag = () => { const containerWidth = scrollWrapper.clientWidth const contentWidth = scrollWrapper.scrollWidth - // 如果内容宽度超过容器宽度,需要滚动 - if (contentWidth > containerWidth) { + // 判断容器是否已满(内容宽度是否超过容器宽度) + const isContainerFull = contentWidth > containerWidth + + // 如果容器已满且还没有滚动到最新标签,则滚动到最右边 + if (isContainerFull && !scrollState.value.hasScrolledToLatest) { // 计算需要滚动到的位置,确保最新标签在右侧可见 const maxScrollLeft = contentWidth - containerWidth scrollbarRef.value.setScrollLeft(maxScrollLeft) - } else { + scrollState.value.hasScrolledToLatest = true + scrollState.value.isContainerFull = true + } else if (!isContainerFull) { // 如果内容宽度不超过容器宽度,滚动到最左边 scrollbarRef.value.setScrollLeft(0) + // 重置滚动状态 + scrollState.value.hasScrolledToLatest = false + scrollState.value.isContainerFull = false } + // 如果容器已满且已经滚动过,则保持当前位置 } // 监听路由变化 watch( route, () => { + // 如果没有设置来源,则默认为菜单点击 + if (tagSwitchSource.value === null) { + tagSwitchSource.value = 'menu'; + } addCurrentTag(); updateCurrentTag(); }, @@ -582,25 +652,28 @@ watch( // 监听容器大小变化 let resizeObserver: ResizeObserver | null = null; -// 监听标签数量变化,自动滚动到最新标签 +// 监听标签数量变化,自动滚动到最新标签(新标签添加时) watch( () => visitedViews.value.length, () => { - nextTick(() => { - autoScrollToLatestTag(); - }); + // 只有在通过菜单添加新标签时才滚动 + if (tagSwitchSource.value === 'menu') { + nextTick(() => { + autoScrollToLatestTag(); + }); + } } ); // 监听当前路由变化,确保点击隐藏标签时滚动到最新位置 -watch( - () => route.path, - () => { - nextTick(() => { - autoScrollToLatestTag(); - }); - } -); +// watch( +// () => route.path, +// () => { +// nextTick(() => { +// autoScrollToLatestTag(); +// }); +// } +// ); // 初始化 onMounted(() => { diff --git a/frontend/src/settings.ts b/frontend/src/settings.ts index ab84d461..87b6b9c1 100644 --- a/frontend/src/settings.ts +++ b/frontend/src/settings.ts @@ -37,17 +37,55 @@ export const defaultSettings: AppSettings = { guideVisible: false, }; -// 主题色预设 - 经典配色方案 +// 主题色预设 - 现代化配色方案 // 注意:修改默认主题色时,需要同步修改 src/styles/variables.scss 中的 primary.base 值 export const themeColorPresets = [ + // === 蓝色系 - 科技与专业 === "#4080FF", // Arco Design 蓝 - 现代感强 "#1890FF", // Ant Design 蓝 - 经典商务 "#409EFF", // Element Plus 蓝 - 清新自然 - "#FA8C16", // 活力橙 - 温暖友好 - "#722ED1", // 优雅紫 - 高端大气 - "#13C2C2", // 青色 - 科技感 - "#52C41A", // 成功绿 - 活力清新 - "#F5222D", // 警示红 - 醒目强烈 "#2F54EB", // 深蓝 - 稳重专业 + "#1E40AF", // 深蓝色 - 商务精英 + "#1D4ED8", // 皇家蓝 - 高端商务 + + // === 绿色系 - 自然与活力 === + "#52C41A", // 成功绿 - 活力清新 + "#10B981", // 翠绿色 - 清新自然 + "#059669", // 森林绿 - 生态环保 + "#16A34A", // 草绿色 - 健康活力 + "#15803D", // 深绿色 - 稳重大气 + + // === 紫色系 - 创意与优雅 === + "#722ED1", // 优雅紫 - 高端大气 + "#7C3AED", // 紫罗兰 - 创意无限 + "#8B5CF6", // 浅紫色 - 时尚现代 + "#6D28D9", // 深紫色 - 神秘高端 + "#5B21B6", // 皇家紫 - 王者风范 + + // === 橙色系 - 温暖与活力 === + "#FA8C16", // 活力橙 - 温暖友好 + "#F97316", // 火橙色 - 热情奔放 + "#EA580C", // 深橙色 - 阳光活力 + "#DC2626", // 珊瑚红 - 温暖亲切 + + // === 青色系 - 科技与清新 === + "#13C2C2", // 青色 - 科技感 + "#0891B2", // 天蓝色 - 清新自然 + "#0E7490", // 深青色 - 专业科技 + "#06B6D4", // 青蓝色 - 海洋清新 + + // === 红色系 - 激情与警示 === + "#F5222D", // 警示红 - 醒目强烈 + "#DC2626", // 猩红色 - 激情四射 + "#B91C1C", // 深红色 - 庄重严肃 + + // === 粉色系 - 温柔与时尚 === "#EB2F96", // 品红 - 时尚个性 + "#EC4899", // 玫瑰粉 - 浪漫温馨 + "#F472B6", // 浅粉色 - 柔美可爱 + + // === 灰色系 - 简约与现代 === + "#6B7280", // 经典灰 - 简约现代 + "#4B5563", // 深灰色 - 商务专业 + "#374151", // 石板灰 - 高端商务 ]; diff --git a/frontend/src/types/auto-imports.d.ts b/frontend/src/types/auto-imports.d.ts index 96afad15..d800967e 100644 --- a/frontend/src/types/auto-imports.d.ts +++ b/frontend/src/types/auto-imports.d.ts @@ -587,6 +587,7 @@ declare module 'vue' { readonly useThrottleFn: UnwrapRef readonly useThrottledRefHistory: UnwrapRef readonly useTimeAgo: UnwrapRef + readonly useTimeAgoIntl: UnwrapRef readonly useTimeout: UnwrapRef readonly useTimeoutFn: UnwrapRef readonly useTimeoutPoll: UnwrapRef diff --git a/frontend/src/utils/quickStartManager.ts b/frontend/src/utils/quickStartManager.ts index 3a66c740..85c1ce86 100644 --- a/frontend/src/utils/quickStartManager.ts +++ b/frontend/src/utils/quickStartManager.ts @@ -118,8 +118,6 @@ class QuickStartManager { routeIcon = routeIcon.charAt(0).toUpperCase() + routeIcon.slice(1); } - console.log("routeIcon",routeIcon) - // 确定最终使用的标题 - 优先使用route.title const finalTitle = customTitle || route.title || route.name || '未命名页面'; diff --git a/frontend/src/views/dashboard/components/QuickStart.vue b/frontend/src/views/dashboard/components/QuickStart.vue index 8e8cf7f9..3f299f68 100644 --- a/frontend/src/views/dashboard/components/QuickStart.vue +++ b/frontend/src/views/dashboard/components/QuickStart.vue @@ -197,28 +197,29 @@ onUnmounted(() => {