refactor(layout): 优化后台布局样式与主题适配
- 优化顶部工具栏、通知入口和页签操作按钮的图标颜色继承 - 调整混合布局菜单激活态与暗黑模式下的视觉表现 - 重构设置面板分组与底部操作区,精简无效配置文案 - 统一列表页公共布局样式,优化表格和页面容器边界 - 优化控制台卡片、图表和暗黑模式背景表现 - 移除布局中重复的页签高度覆盖和无用 mixin
This commit is contained in:
1 parent
0e992ae16f
commit
03a9c03401
15 files changed
+357
-280
No files matched your search
@@ -122,6 +122,7 @@ function handleVisibleChange(visible: boolean) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__trigger {
|
&__trigger {
|
||||||
@@ -130,6 +131,7 @@ function handleVisibleChange(visible: boolean) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
@@ -139,17 +141,20 @@ function handleVisibleChange(visible: boolean) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
color: currentColor;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__bell {
|
&__bell {
|
||||||
color: var(--el-text-color-regular);
|
--color: currentColor;
|
||||||
|
|
||||||
|
color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__pulse {
|
&__pulse {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 4px;
|
top: 3px;
|
||||||
right: 5px;
|
right: 4px;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
background: var(--el-color-danger);
|
background: var(--el-color-danger);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"interface": "Interface",
|
"interface": "Interface",
|
||||||
"navigation": "Navigation",
|
"navigation": "Navigation",
|
||||||
|
"assist": "Assist",
|
||||||
"themePalette": "Color System",
|
"themePalette": "Color System",
|
||||||
"customColors": "Custom Colors",
|
"customColors": "Custom Colors",
|
||||||
"customPalette": "Custom",
|
"customPalette": "Custom",
|
||||||
@@ -102,6 +103,8 @@
|
|||||||
"sidebarColorScheme": "Sidebar Color Scheme",
|
"sidebarColorScheme": "Sidebar Color Scheme",
|
||||||
"showWatermark": "Show Watermark",
|
"showWatermark": "Show Watermark",
|
||||||
"pageSwitchingAnimation": "Page Switching Animation",
|
"pageSwitchingAnimation": "Page Switching Animation",
|
||||||
|
"grayMode": "Gray Mode",
|
||||||
|
"colorWeak": "Color Weak",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"fade": "Fade",
|
"fade": "Fade",
|
||||||
"fade-slide": "Fade Slide",
|
"fade-slide": "Fade Slide",
|
||||||
|
|||||||
@@ -104,6 +104,8 @@
|
|||||||
"showAppLogo": "显示Logo",
|
"showAppLogo": "显示Logo",
|
||||||
"showWatermark": "显示水印",
|
"showWatermark": "显示水印",
|
||||||
"pageSwitchingAnimation": "页面切换动画",
|
"pageSwitchingAnimation": "页面切换动画",
|
||||||
|
"grayMode": "灰色模式",
|
||||||
|
"colorWeak": "色弱模式",
|
||||||
"none": "无动画",
|
"none": "无动画",
|
||||||
"fade": "淡入淡出",
|
"fade": "淡入淡出",
|
||||||
"fade-slide": "平滑切换",
|
"fade-slide": "平滑切换",
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
<main
|
<main
|
||||||
class="layout__main"
|
class="layout__main"
|
||||||
:class="{
|
:class="{
|
||||||
hasTagsView: showTagsView,
|
|
||||||
'layout__main--collapsed': !isSidebarOpen,
|
'layout__main--collapsed': !isSidebarOpen,
|
||||||
'layout__main--fullscreen': appStore.contentFullscreen,
|
'layout__main--fullscreen': appStore.contentFullscreen,
|
||||||
}"
|
}"
|
||||||
@@ -84,8 +83,6 @@ const { MenuIcon, topMenuItems, activeTopMenuPath, sideMenuRoutes, handleTopMenu
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use "@/styles/mixins" as *;
|
|
||||||
|
|
||||||
$double-sidebar-width: $sidebar-primary-width + $sidebar-secondary-width;
|
$double-sidebar-width: $sidebar-primary-width + $sidebar-secondary-width;
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
@@ -305,12 +302,6 @@ $double-sidebar-width: $sidebar-primary-width + $sidebar-secondary-width;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasTagsView {
|
|
||||||
:deep(.app-main) {
|
|
||||||
@include app-main-height-with-tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
.layout__double-sidebar {
|
.layout__double-sidebar {
|
||||||
width: $double-sidebar-width;
|
width: $double-sidebar-width;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
<div
|
<div
|
||||||
class="layout__main"
|
class="layout__main"
|
||||||
:class="{
|
:class="{
|
||||||
hasTagsView: showTagsView,
|
|
||||||
'layout__main--collapsed': !isSidebarOpen,
|
'layout__main--collapsed': !isSidebarOpen,
|
||||||
'layout__main--fullscreen': appStore.contentFullscreen,
|
'layout__main--fullscreen': appStore.contentFullscreen,
|
||||||
}"
|
}"
|
||||||
@@ -130,10 +129,4 @@ const appStore = useAppStore();
|
|||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasTagsView {
|
|
||||||
:deep(.app-main) {
|
|
||||||
@include app-main-height-with-tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
+38
-25
@@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
<!-- 主内容区 -->
|
||||||
<div :class="{ hasTagsView: showTagsView }" class="layout__main">
|
<div class="layout__main">
|
||||||
<LayoutTagsView v-if="showTagsView" />
|
<LayoutTagsView v-if="showTagsView" />
|
||||||
<LayoutMain />
|
<LayoutMain />
|
||||||
</div>
|
</div>
|
||||||
@@ -171,27 +171,43 @@ const useMenuColors = computed(
|
|||||||
.el-menu-item {
|
.el-menu-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0 12px;
|
padding: 0 14px;
|
||||||
line-height: $navbar-height;
|
line-height: $navbar-height;
|
||||||
border-bottom: none;
|
border-bottom: none !important;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
transition:
|
transition:
|
||||||
color 0.16s ease,
|
color 0.16s ease,
|
||||||
background-color 0.16s ease,
|
opacity 0.16s ease;
|
||||||
border-color 0.16s ease;
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 12px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 12px;
|
||||||
|
height: 2px;
|
||||||
|
content: "";
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
border-radius: 999px 999px 0 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scaleX(0.4);
|
||||||
|
transition:
|
||||||
|
opacity 0.16s ease,
|
||||||
|
transform 0.16s ease;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--menu-hover) !important;
|
color: var(--el-color-primary) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
color: var(--el-color-primary) !important;
|
color: var(--el-color-primary) !important;
|
||||||
background-color: color-mix(
|
background-color: transparent !important;
|
||||||
in srgb,
|
}
|
||||||
var(--el-color-primary) 10%,
|
|
||||||
var(--el-bg-color-overlay)
|
&.is-active::after {
|
||||||
) !important;
|
opacity: 1;
|
||||||
border-bottom: 2px solid var(--el-color-primary);
|
transform: scaleX(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,23 +305,20 @@ const useMenuColors = computed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.hasTagsView) {
|
|
||||||
.app-main {
|
|
||||||
@include app-main-height-with-tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(html.dark),
|
:global(html.dark),
|
||||||
:global(html.sidebar-color-blue) {
|
:global(html.sidebar-color-blue) {
|
||||||
.layout__header-menu {
|
.layout__header-menu {
|
||||||
:deep(.el-menu--horizontal) {
|
:deep(.el-menu--horizontal) {
|
||||||
.el-menu-item.is-active {
|
.el-menu-item {
|
||||||
color: var(--menu-active-text) !important;
|
&:hover {
|
||||||
background-color: color-mix(
|
color: color-mix(in srgb, var(--el-color-primary) 24%, var(--el-color-white)) !important;
|
||||||
in srgb,
|
background-color: transparent !important;
|
||||||
var(--el-color-primary) 18%,
|
}
|
||||||
var(--menu-background)
|
|
||||||
) !important;
|
&.is-active {
|
||||||
|
color: color-mix(in srgb, var(--el-color-primary) 38%, var(--el-color-white)) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
<!-- 主内容区 -->
|
||||||
<div :class="{ hasTagsView: showTagsView }" class="layout__main">
|
<div class="layout__main">
|
||||||
<LayoutTagsView v-if="showTagsView" />
|
<LayoutTagsView v-if="showTagsView" />
|
||||||
<LayoutMain />
|
<LayoutMain />
|
||||||
</div>
|
</div>
|
||||||
@@ -149,10 +149,4 @@ const isLogoCollapsed = computed(() => width.value < 768);
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasTagsView {
|
|
||||||
:deep(.app-main) {
|
|
||||||
@include app-main-height-with-tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
>
|
>
|
||||||
<div class="settings-content">
|
<div class="settings-content">
|
||||||
<section class="config-section">
|
<section class="config-section">
|
||||||
<el-divider>{{ t("settings.theme") }}</el-divider>
|
<div class="section-title">{{ t("settings.theme") }}</div>
|
||||||
|
|
||||||
<div class="flex-center">
|
<div class="theme-mode">
|
||||||
<el-radio-group v-model="themeMode" class="theme-mode-group">
|
<el-radio-group v-model="themeMode">
|
||||||
<el-radio-button :value="ThemeMode.LIGHT">
|
<el-radio-button :value="ThemeMode.LIGHT">
|
||||||
{{ t("login.light") }}
|
{{ t("login.light") }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-block config-block--tight">
|
<div class="config-card">
|
||||||
<div class="config-item__header">
|
<div class="config-card__header">
|
||||||
<span class="text-xs">{{ t("settings.themePalette") }}</span>
|
<span>{{ t("settings.themePalette") }}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
:class="['custom-color-trigger', { 'is-open': isCustomColorsOpen }]"
|
:class="['custom-color-trigger', { 'is-open': isCustomColorsOpen }]"
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="config-section">
|
<section class="config-section">
|
||||||
<el-divider>{{ t("settings.navigation") }}</el-divider>
|
<div class="section-title">{{ t("settings.navigation") }}</div>
|
||||||
|
|
||||||
<div class="layout-select">
|
<div class="layout-select">
|
||||||
<div class="layout-grid">
|
<div class="layout-grid">
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="sidebarColor"
|
v-model="sidebarColor"
|
||||||
class="config-item__control"
|
class="config-item__control"
|
||||||
@change="changeSidebarColor"
|
@change="setSidebarColor"
|
||||||
>
|
>
|
||||||
<el-radio :value="SidebarColor.MINIMAL_WHITE">
|
<el-radio :value="SidebarColor.MINIMAL_WHITE">
|
||||||
{{ t("settings.minimalWhite") }}
|
{{ t("settings.minimalWhite") }}
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="config-section">
|
<section class="config-section">
|
||||||
<el-divider>{{ t("settings.interface") }}</el-divider>
|
<div class="section-title">{{ t("settings.interface") }}</div>
|
||||||
|
|
||||||
<div class="config-item flex-x-between">
|
<div class="config-item flex-x-between">
|
||||||
<span class="text-xs">{{ t("settings.showTagsView") }}</span>
|
<span class="text-xs">{{ t("settings.showTagsView") }}</span>
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="settingsStore.showTagsView" class="config-item config-item--block">
|
<div v-if="settingsStore.showTagsView" class="config-item config-item--block">
|
||||||
<div class="config-item__header">
|
<div class="config-card__header">
|
||||||
<span class="text-xs">{{ t("settings.tagsViewStyle") }}</span>
|
<span class="text-xs">{{ t("settings.tagsViewStyle") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tags-style-grid">
|
<div class="tags-style-grid">
|
||||||
@@ -191,11 +191,6 @@
|
|||||||
<el-switch v-model="settingsStore.showAppLogo" />
|
<el-switch v-model="settingsStore.showAppLogo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-item flex-x-between">
|
|
||||||
<span class="text-xs">{{ t("settings.showWatermark") }}</span>
|
|
||||||
<el-switch v-model="settingsStore.showWatermark" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config-item flex-x-between">
|
<div class="config-item flex-x-between">
|
||||||
<span class="text-xs">{{ t("settings.pageSwitchingAnimation") }}</span>
|
<span class="text-xs">{{ t("settings.pageSwitchingAnimation") }}</span>
|
||||||
<el-select v-model="settingsStore.pageSwitchingAnimation" style="width: 150px">
|
<el-select v-model="settingsStore.pageSwitchingAnimation" style="width: 150px">
|
||||||
@@ -207,43 +202,46 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="config-section">
|
||||||
|
<div class="section-title">{{ t("settings.assist") }}</div>
|
||||||
|
|
||||||
<div class="config-item flex-x-between">
|
<div class="config-item flex-x-between">
|
||||||
<span class="text-xs">灰色模式</span>
|
<span class="text-xs">{{ t("settings.showWatermark") }}</span>
|
||||||
|
<el-switch v-model="settingsStore.showWatermark" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-item flex-x-between">
|
||||||
|
<span class="text-xs">{{ t("settings.grayMode") }}</span>
|
||||||
<el-switch v-model="settingsStore.grayMode" />
|
<el-switch v-model="settingsStore.grayMode" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-item flex-x-between">
|
<div class="config-item flex-x-between">
|
||||||
<span class="text-xs">色弱模式</span>
|
<span class="text-xs">{{ t("settings.colorWeak") }}</span>
|
||||||
<el-switch v-model="settingsStore.colorWeak" />
|
<el-switch v-model="settingsStore.colorWeak" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="action-buttons">
|
<div class="settings-footer">
|
||||||
<el-tooltip content="复制当前 defaults 片段,方便覆盖到 src/settings.ts" placement="top">
|
<el-button
|
||||||
<el-button
|
type="primary"
|
||||||
type="primary"
|
:icon="copyIcon"
|
||||||
size="default"
|
:loading="copyLoading"
|
||||||
:icon="copyIcon"
|
@click="copyCurrentSettings"
|
||||||
:loading="copyLoading"
|
>
|
||||||
@click="handleCopySettings"
|
{{ copyLoading ? "复制中..." : t("settings.copyConfig") }}
|
||||||
>
|
</el-button>
|
||||||
{{ copyLoading ? "复制中..." : t("settings.copyConfig") }}
|
<el-button
|
||||||
</el-button>
|
type="default"
|
||||||
</el-tooltip>
|
:icon="resetIcon"
|
||||||
<el-tooltip content="重置将恢复所有设置为默认值" placement="top">
|
:loading="resetLoading"
|
||||||
<el-button
|
@click="resetSettingsToDefault"
|
||||||
type="warning"
|
>
|
||||||
size="default"
|
{{ resetLoading ? "重置中..." : t("settings.resetConfig") }}
|
||||||
:icon="resetIcon"
|
</el-button>
|
||||||
:loading="resetLoading"
|
|
||||||
@click="handleResetSettings"
|
|
||||||
>
|
|
||||||
{{ resetLoading ? "重置中..." : t("settings.resetConfig") }}
|
|
||||||
</el-button>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
@@ -331,19 +329,23 @@ const drawerVisible = computed({
|
|||||||
set: (value) => (settingsStore.settingsVisible = value),
|
set: (value) => (settingsStore.settingsVisible = value),
|
||||||
});
|
});
|
||||||
|
|
||||||
const getPaletteColors = (colors: ThemeColorMap) => colorOptions.map((item) => colors[item.name]);
|
function getPaletteColors(colors: ThemeColorMap) {
|
||||||
|
return colorOptions.map((item) => colors[item.name]);
|
||||||
|
}
|
||||||
|
|
||||||
const getPaletteName = (palette: ThemePalettePreset) => {
|
function getPaletteName(palette: ThemePalettePreset) {
|
||||||
const key = paletteI18nKeys[palette.id];
|
const key = paletteI18nKeys[palette.id];
|
||||||
return key ? t(`settings.themePalettes.${key}.name`) : palette.name;
|
return key ? t(`settings.themePalettes.${key}.name`) : palette.name;
|
||||||
};
|
}
|
||||||
|
|
||||||
const getPaletteDescription = (palette: ThemePalettePreset) => {
|
function getPaletteDescription(palette: ThemePalettePreset) {
|
||||||
const key = paletteI18nKeys[palette.id];
|
const key = paletteI18nKeys[palette.id];
|
||||||
return key ? t(`settings.themePalettes.${key}.description`) : palette.description;
|
return key ? t(`settings.themePalettes.${key}.description`) : palette.description;
|
||||||
};
|
}
|
||||||
|
|
||||||
const getColorLabel = (name: ThemeColorName) => t(`settings.themeColorNames.${name}`);
|
function getColorLabel(name: ThemeColorName) {
|
||||||
|
return t(`settings.themeColorNames.${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
const activePaletteName = computed(() =>
|
const activePaletteName = computed(() =>
|
||||||
settingsStore.activeThemePalette
|
settingsStore.activeThemePalette
|
||||||
@@ -351,32 +353,47 @@ const activePaletteName = computed(() =>
|
|||||||
: t("settings.customPalette")
|
: t("settings.customPalette")
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleCustomColors = () => {
|
/**
|
||||||
|
* 展开或收起自定义颜色
|
||||||
|
*/
|
||||||
|
function toggleCustomColors(): void {
|
||||||
isCustomColorsOpen.value = !isCustomColorsOpen.value;
|
isCustomColorsOpen.value = !isCustomColorsOpen.value;
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleThemeColorChange = (name: ThemeColorName, color: string | null) => {
|
/**
|
||||||
|
* 更新单个主题色
|
||||||
|
*/
|
||||||
|
function handleThemeColorChange(name: ThemeColorName, color: string | null): void {
|
||||||
if (!color) return;
|
if (!color) return;
|
||||||
settingsStore.updateThemeColor(name, color);
|
settingsStore.updateThemeColor(name, color);
|
||||||
};
|
}
|
||||||
|
|
||||||
const changeSidebarColor = (val: string | number | boolean | undefined) => {
|
/**
|
||||||
if (val !== SidebarColor.CLASSIC_BLUE && val !== SidebarColor.MINIMAL_WHITE) return;
|
* 切换侧边栏配色
|
||||||
|
*/
|
||||||
|
function setSidebarColor(value: string | number | boolean | undefined): void {
|
||||||
|
if (value !== SidebarColor.CLASSIC_BLUE && value !== SidebarColor.MINIMAL_WHITE) return;
|
||||||
|
|
||||||
settingsStore.sidebarColorScheme = val;
|
settingsStore.sidebarColorScheme = value;
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLayoutChange = (layout: LayoutMode) => {
|
/**
|
||||||
|
* 切换导航布局
|
||||||
|
*/
|
||||||
|
function handleLayoutChange(layout: LayoutMode): void {
|
||||||
if (settingsStore.layout === layout) return;
|
if (settingsStore.layout === layout) return;
|
||||||
|
|
||||||
settingsStore.layout = layout;
|
settingsStore.layout = layout;
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCopySettings = async () => {
|
/**
|
||||||
|
* 复制当前 settings 默认配置片段
|
||||||
|
*/
|
||||||
|
async function copyCurrentSettings(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
copyLoading.value = true;
|
copyLoading.value = true;
|
||||||
|
|
||||||
const configCode = generateSettingsCode();
|
const configCode = buildDefaultsCode();
|
||||||
|
|
||||||
await navigator.clipboard.writeText(configCode);
|
await navigator.clipboard.writeText(configCode);
|
||||||
|
|
||||||
@@ -389,26 +406,36 @@ const handleCopySettings = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
copyLoading.value = false;
|
copyLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleResetSettings = async () => {
|
|
||||||
resetLoading.value = true;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复所有设置为默认值
|
||||||
|
*/
|
||||||
|
async function resetSettingsToDefault(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
await ElMessageBox.confirm(t("settings.confirmReset"), t("settings.resetConfig"), {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
|
||||||
|
resetLoading.value = true;
|
||||||
settingsStore.resetSettings();
|
settingsStore.resetSettings();
|
||||||
|
|
||||||
sidebarColor.value = settingsStore.sidebarColorScheme;
|
sidebarColor.value = settingsStore.sidebarColorScheme;
|
||||||
|
|
||||||
ElMessage.success(t("settings.resetSuccess"));
|
ElMessage.success(t("settings.resetSuccess"));
|
||||||
} catch {
|
} catch {
|
||||||
ElMessage.error("重置配置失败");
|
// 用户取消时不提示
|
||||||
} finally {
|
} finally {
|
||||||
resetLoading.value = false;
|
resetLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 这里只生成 defaults,避免复制出来的代码和 src/settings.ts 的真实结构对不上。
|
/**
|
||||||
const generateSettingsCode = (): string => {
|
* 生成 src/settings.ts 中 defaults 的配置片段
|
||||||
|
*/
|
||||||
|
function buildDefaultsCode(): string {
|
||||||
const themeColorsCode = JSON.stringify(settingsStore.themeColors, null, 4)
|
const themeColorsCode = JSON.stringify(settingsStore.themeColors, null, 4)
|
||||||
.replace(/"([^"]+)":/g, "$1:")
|
.replace(/"([^"]+)":/g, "$1:")
|
||||||
.replace(/^/gm, " ");
|
.replace(/^/gm, " ");
|
||||||
@@ -445,11 +472,11 @@ const generateSettingsCode = (): string => {
|
|||||||
showSettings: ${settings.showSettings},
|
showSettings: ${settings.showSettings},
|
||||||
watermarkContent: ${settings.watermarkContent},
|
watermarkContent: ${settings.watermarkContent},
|
||||||
} as const;`;
|
} as const;`;
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCloseDrawer = () => {
|
function handleCloseDrawer(): void {
|
||||||
settingsStore.settingsVisible = false;
|
settingsStore.settingsVisible = false;
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -463,51 +490,38 @@ const handleCloseDrawer = () => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-drawer__footer) {
|
||||||
|
padding: 12px 18px 16px;
|
||||||
|
border-top: 1px solid var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-content {
|
.settings-content {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
padding: 16px 18px;
|
padding: 16px 18px 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
& > .el-button {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: box-shadow 0.16s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-section {
|
.config-section {
|
||||||
margin-bottom: 16px;
|
& + & {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.config-item {
|
.config-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 10px 0;
|
min-height: 42px;
|
||||||
border-bottom: 1px solid var(--el-border-color-light);
|
padding: 8px 0;
|
||||||
transition: background-color 0.16s ease;
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--el-fill-color-light);
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .text-xs {
|
> .text-xs {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
@@ -519,9 +533,9 @@ const handleCloseDrawer = () => {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto minmax(0, 1fr);
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
column-gap: 14px;
|
column-gap: 14px;
|
||||||
padding: 14px 12px;
|
padding: 12px;
|
||||||
margin-top: 4px;
|
margin-top: 12px;
|
||||||
background: var(--el-fill-color-lighter);
|
background: var(--el-fill-color-extra-light);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
@@ -555,32 +569,62 @@ const handleCloseDrawer = () => {
|
|||||||
|
|
||||||
.config-item--block {
|
.config-item--block {
|
||||||
display: block;
|
display: block;
|
||||||
|
min-height: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
&:hover {
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
margin: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-block {
|
.section-title {
|
||||||
margin-top: 12px;
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
content: "";
|
||||||
|
background: var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-block--tight {
|
.theme-mode {
|
||||||
margin-top: 10px;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-item__header {
|
.config-card {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-card__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
|
||||||
> .text-xs {
|
> span {
|
||||||
color: var(--el-text-color-regular);
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-footer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,22 +41,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
<!-- 右侧操作 -->
|
|
||||||
<div class="tags-actions">
|
<div class="tags-actions">
|
||||||
<!-- 刷新当前 -->
|
<button
|
||||||
<span class="tags-actions__btn" @click="refreshSelectedTag(currentTag)">
|
type="button"
|
||||||
|
class="tags-actions__btn"
|
||||||
|
aria-label="刷新当前"
|
||||||
|
title="刷新当前"
|
||||||
|
@click="refreshSelectedTag(currentTag)"
|
||||||
|
>
|
||||||
<el-icon :size="16"><Refresh /></el-icon>
|
<el-icon :size="16"><Refresh /></el-icon>
|
||||||
</span>
|
</button>
|
||||||
<!-- 内容全屏 -->
|
<button
|
||||||
<span class="tags-actions__btn" @click="appStore.toggleContentFullscreen()">
|
type="button"
|
||||||
|
class="tags-actions__btn"
|
||||||
|
aria-label="内容全屏"
|
||||||
|
title="内容全屏"
|
||||||
|
@click="appStore.toggleContentFullscreen()"
|
||||||
|
>
|
||||||
<div v-if="!appStore.contentFullscreen" class="i-svg:fullscreen icon-16" />
|
<div v-if="!appStore.contentFullscreen" class="i-svg:fullscreen icon-16" />
|
||||||
<div v-else class="i-svg:fullscreen-exit icon-16" />
|
<div v-else class="i-svg:fullscreen-exit icon-16" />
|
||||||
</span>
|
</button>
|
||||||
<!-- 下拉菜单 -->
|
|
||||||
<el-dropdown trigger="click" @command="handleActionCommand">
|
<el-dropdown trigger="click" @command="handleActionCommand">
|
||||||
<span class="tags-actions__btn">
|
<button type="button" class="tags-actions__btn" aria-label="页签操作" title="页签操作">
|
||||||
<el-icon :size="16"><ArrowDown /></el-icon>
|
<el-icon :size="16"><ArrowDown /></el-icon>
|
||||||
</span>
|
</button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item command="refresh">
|
<el-dropdown-item command="refresh">
|
||||||
@@ -174,10 +182,8 @@ const tagsStyleClass = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 图标兼容:判断是否为 Element Plus 图标 (el-icon-xxx)
|
|
||||||
const isEpIcon = (icon: string) => icon.startsWith("el-icon");
|
const isEpIcon = (icon: string) => icon.startsWith("el-icon");
|
||||||
|
|
||||||
// 图标兼容:el-icon-xxx → PascalCase 组件名
|
|
||||||
const toEpIconName = (icon: string) =>
|
const toEpIconName = (icon: string) =>
|
||||||
icon.replace("el-icon-", "").replace(/(^|-)\w/g, (s) => s.slice(-1).toUpperCase());
|
icon.replace("el-icon-", "").replace(/(^|-)\w/g, (s) => s.slice(-1).toUpperCase());
|
||||||
|
|
||||||
@@ -202,7 +208,6 @@ const contextMenuStyle = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 标签栏右侧操作按钮分发
|
|
||||||
const handleActionCommand = (command: string) => {
|
const handleActionCommand = (command: string) => {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case "refresh":
|
case "refresh":
|
||||||
@@ -226,7 +231,6 @@ const handleActionCommand = (command: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// path → TagView 映射,用于快速查找标签
|
|
||||||
const routePathMap = computed(() => {
|
const routePathMap = computed(() => {
|
||||||
const map = new Map<string, TagView>();
|
const map = new Map<string, TagView>();
|
||||||
visitedViews.value.forEach((tag) => {
|
visitedViews.value.forEach((tag) => {
|
||||||
@@ -235,7 +239,6 @@ const routePathMap = computed(() => {
|
|||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 判断右键选中标签是否为最左侧(不可关闭左侧)
|
|
||||||
const isFirstView = computed(() => {
|
const isFirstView = computed(() => {
|
||||||
if (!selectedTag.value) return false;
|
if (!selectedTag.value) return false;
|
||||||
return (
|
return (
|
||||||
@@ -244,13 +247,11 @@ const isFirstView = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 判断右键选中标签是否为最右侧(不可关闭右侧)
|
|
||||||
const isLastView = computed(() => {
|
const isLastView = computed(() => {
|
||||||
if (!selectedTag.value) return false;
|
if (!selectedTag.value) return false;
|
||||||
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1]?.fullPath;
|
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1]?.fullPath;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 从路由表中提取所有 affix 固定标签
|
|
||||||
const extractAffixTags = (routes: RouteRecordRaw[], basePath = "/"): TagView[] => {
|
const extractAffixTags = (routes: RouteRecordRaw[], basePath = "/"): TagView[] => {
|
||||||
const affixTags: TagView[] = [];
|
const affixTags: TagView[] = [];
|
||||||
|
|
||||||
@@ -341,7 +342,6 @@ const closeContextMenu = () => {
|
|||||||
contextMenu.visible = false;
|
contextMenu.visible = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 鼠标滚轮横向滚动(标签栏超出可视区时生效)
|
|
||||||
const handleScroll = (event: WheelEvent) => {
|
const handleScroll = (event: WheelEvent) => {
|
||||||
closeContextMenu();
|
closeContextMenu();
|
||||||
|
|
||||||
@@ -358,7 +358,6 @@ const handleScroll = (event: WheelEvent) => {
|
|||||||
scrollbarRef.value.setScrollLeft(newScrollLeft);
|
scrollbarRef.value.setScrollLeft(newScrollLeft);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 刷新指定标签页:移除缓存后跳转 redirect 中转页重新加载
|
|
||||||
const refreshSelectedTag = (tag: TagView | null) => {
|
const refreshSelectedTag = (tag: TagView | null) => {
|
||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
|
|
||||||
@@ -368,7 +367,6 @@ const refreshSelectedTag = (tag: TagView | null) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭指定标签页,若关闭的是当前激活标签则跳转到最后一个
|
|
||||||
const closeSelectedTag = (tag: TagView | null) => {
|
const closeSelectedTag = (tag: TagView | null) => {
|
||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
|
|
||||||
@@ -379,18 +377,10 @@ const closeSelectedTag = (tag: TagView | null) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 批量关闭标签(左/右/其它方向)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按方向批量关闭标签的通用执行器。
|
* 关闭指定方向的标签
|
||||||
*
|
|
||||||
* closeLeftTagsForActive / closeRightTagsForActive / closeLeftTags /
|
|
||||||
* closeRightTags 四个公开函数均委托此函数,仅 source(标签来源 ref)
|
|
||||||
* 和 batchFn(调用的 store 方法)不同。
|
|
||||||
*
|
|
||||||
* 关闭后若当前路由对应的标签已被移除,自动跳转到最后一个剩余标签。
|
|
||||||
*/
|
*/
|
||||||
function closeDirectional(
|
function closeDirectionalTags(
|
||||||
source: Ref<TagView | null>,
|
source: Ref<TagView | null>,
|
||||||
batchFn: (tag: TagView) => Promise<{ visitedViews: TagView[] }>
|
batchFn: (tag: TagView) => Promise<{ visitedViews: TagView[] }>
|
||||||
) {
|
) {
|
||||||
@@ -406,7 +396,7 @@ function closeDirectional(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭当前激活标签之外的其它标签。
|
* 关闭当前激活标签之外的其它标签
|
||||||
*/
|
*/
|
||||||
const closeOtherTagsForActive = () => {
|
const closeOtherTagsForActive = () => {
|
||||||
if (!currentTag.value) return;
|
if (!currentTag.value) return;
|
||||||
@@ -415,16 +405,11 @@ const closeOtherTagsForActive = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭左侧(当前激活标签)
|
const closeLeftTagsForActive = () => closeDirectionalTags(currentTag, tagsViewStore.delLeftViews);
|
||||||
const closeLeftTagsForActive = () => closeDirectional(currentTag, tagsViewStore.delLeftViews);
|
const closeRightTagsForActive = () => closeDirectionalTags(currentTag, tagsViewStore.delRightViews);
|
||||||
// 关闭右侧(当前激活标签)
|
const closeLeftTags = () => closeDirectionalTags(selectedTag, tagsViewStore.delLeftViews);
|
||||||
const closeRightTagsForActive = () => closeDirectional(currentTag, tagsViewStore.delRightViews);
|
const closeRightTags = () => closeDirectionalTags(selectedTag, tagsViewStore.delRightViews);
|
||||||
// 关闭左侧(右键选中标签)
|
|
||||||
const closeLeftTags = () => closeDirectional(selectedTag, tagsViewStore.delLeftViews);
|
|
||||||
// 关闭右侧(右键选中标签)
|
|
||||||
const closeRightTags = () => closeDirectional(selectedTag, tagsViewStore.delRightViews);
|
|
||||||
|
|
||||||
// 关闭右键选中标签以外的其他标签
|
|
||||||
const closeOtherTags = () => {
|
const closeOtherTags = () => {
|
||||||
if (!selectedTag.value) return;
|
if (!selectedTag.value) return;
|
||||||
router.push(selectedTag.value);
|
router.push(selectedTag.value);
|
||||||
@@ -433,14 +418,12 @@ const closeOtherTags = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭所有标签页
|
|
||||||
const closeAllTags = (tag: TagView | null) => {
|
const closeAllTags = (tag: TagView | null) => {
|
||||||
tagsViewStore.delAllViews().then((result) => {
|
tagsViewStore.delAllViews().then((result) => {
|
||||||
tagsViewStore.toLastView(result.visitedViews, tag || undefined);
|
tagsViewStore.toLastView(result.visitedViews, tag || undefined);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 右键菜单生命周期:显示时监听外部点击关闭,卸载时移除监听
|
|
||||||
const useContextMenuManager = () => {
|
const useContextMenuManager = () => {
|
||||||
const handleOutsideClick = () => {
|
const handleOutsideClick = () => {
|
||||||
closeContextMenu();
|
closeContextMenu();
|
||||||
@@ -459,7 +442,6 @@ const useContextMenuManager = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 路由变化时同步添加标签并更新激活状态
|
|
||||||
watch(
|
watch(
|
||||||
route,
|
route,
|
||||||
() => {
|
() => {
|
||||||
@@ -469,7 +451,6 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
// 初始化固定标签(affix)
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initAffixTags();
|
initAffixTags();
|
||||||
});
|
});
|
||||||
@@ -708,22 +689,36 @@ useContextMenuManager();
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
padding: 0;
|
||||||
|
font: inherit;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
appearance: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
transition: background-color 0.15s ease;
|
transition:
|
||||||
|
color 0.15s ease,
|
||||||
|
background-color 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--el-fill-color);
|
color: var(--el-color-primary);
|
||||||
|
background-color: color-mix(in srgb, var(--el-color-primary) 10%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-icon),
|
&:focus-visible {
|
||||||
:deep(div) {
|
outline: 2px solid var(--el-color-primary-light-5);
|
||||||
opacity: 0.45;
|
outline-offset: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover :deep(.el-icon),
|
:deep(.el-icon) {
|
||||||
&:hover :deep(div) {
|
color: currentcolor;
|
||||||
opacity: 0.7;
|
--color: currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-16 {
|
||||||
|
color: currentcolor;
|
||||||
|
background-color: currentcolor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<!-- 桌面端工具项 -->
|
<!-- 桌面端工具项 -->
|
||||||
<template v-if="isDesktop">
|
<template v-if="isDesktop">
|
||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<div class="navbar-actions__item">
|
<div class="navbar-actions__item navbar-actions__item--search">
|
||||||
<CommandPalette />
|
<CommandPalette />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 用户菜单 -->
|
<!-- 用户菜单 -->
|
||||||
<div class="navbar-actions__item">
|
<div class="navbar-actions__item navbar-actions__item--profile">
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
<div style="width: 28px; height: 28px; overflow: hidden; border-radius: 50%">
|
<div style="width: 28px; height: 28px; overflow: hidden; border-radius: 50%">
|
||||||
@@ -110,8 +110,9 @@ function handleTenantChange(tenantId: number) {
|
|||||||
ElMessage.success("切换租户成功");
|
ElMessage.success("切换租户成功");
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: unknown) => {
|
||||||
ElMessage.error(error.message || "切换租户失败");
|
const message = error instanceof Error ? error.message : "切换租户失败";
|
||||||
|
ElMessage.error(message);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -177,6 +178,10 @@ function handleSettingsClick() {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.navbar-actions {
|
.navbar-actions {
|
||||||
|
--navbar-action-color: var(--el-text-color-secondary);
|
||||||
|
--navbar-action-hover-color: var(--el-color-primary);
|
||||||
|
--navbar-action-hover-bg: var(--el-fill-color-light);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -190,9 +195,10 @@ function handleSettingsClick() {
|
|||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
|
color: var(--navbar-action-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
transition:
|
transition:
|
||||||
background-color 0.16s,
|
background-color 0.16s,
|
||||||
color 0.16s;
|
color 0.16s;
|
||||||
@@ -212,6 +218,14 @@ function handleSettingsClick() {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tooltip__trigger),
|
||||||
|
:deep(.fullscreen-trigger),
|
||||||
|
:deep(.size-trigger),
|
||||||
|
:deep(.notice__trigger) {
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.i-svg\:language) {
|
:deep(.i-svg\:language) {
|
||||||
@@ -223,20 +237,40 @@ function handleSettingsClick() {
|
|||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图标样式
|
:deep([class*="i-svg:"]),
|
||||||
:deep([class*="i-svg:"]) {
|
:deep(.el-icon) {
|
||||||
|
--color: currentColor;
|
||||||
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: var(--el-text-color-secondary);
|
color: currentColor !important;
|
||||||
transition: color 0.2s;
|
transition: color 0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep([class*="i-svg:"]) {
|
||||||
|
background-color: currentColor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--el-fill-color);
|
color: var(--navbar-action-hover-color);
|
||||||
|
background: var(--navbar-action-hover-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep([class*="i-svg:"]) {
|
&__item--search {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
|
||||||
|
&:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item--profile {
|
||||||
|
padding-right: 4px;
|
||||||
|
padding-left: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +291,7 @@ function handleSettingsClick() {
|
|||||||
&__name {
|
&__name {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--el-text-color-regular);
|
color: currentColor;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
}
|
}
|
||||||
@@ -266,73 +300,55 @@ function handleSettingsClick() {
|
|||||||
|
|
||||||
// 白色文字样式(用于深色背景:暗黑主题、顶部布局、混合布局等)
|
// 白色文字样式(用于深色背景:暗黑主题、顶部布局、混合布局等)
|
||||||
.navbar-actions--white-text {
|
.navbar-actions--white-text {
|
||||||
.navbar-actions__item {
|
--navbar-action-color: var(--menu-text);
|
||||||
:deep([class*="i-svg:"]) {
|
--navbar-action-hover-color: var(--menu-active-text);
|
||||||
color: color-mix(in srgb, var(--el-color-white) 85%, transparent);
|
--navbar-action-hover-bg: var(--menu-hover);
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: color-mix(in srgb, var(--el-color-white) 10%, transparent);
|
|
||||||
|
|
||||||
:deep([class*="i-svg:"]) {
|
|
||||||
color: var(--el-color-white);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile__name {
|
.user-profile__name {
|
||||||
color: color-mix(in srgb, var(--el-color-white) 85%, transparent);
|
color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 租户选择器在白色文字模式下的样式
|
// 租户选择器在白色文字模式下的样式
|
||||||
::v-deep(.tenant-switcher__trigger) {
|
::v-deep(.tenant-switcher__trigger) {
|
||||||
color: color-mix(in srgb, var(--el-color-white) 85%, transparent);
|
color: currentColor;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
|
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
|
||||||
color: color-mix(in srgb, var(--el-color-white) 85%, transparent);
|
color: currentColor;
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger:hover) {
|
::v-deep(.tenant-switcher__trigger:hover) {
|
||||||
color: var(--el-color-white);
|
color: var(--navbar-action-hover-color);
|
||||||
background: color-mix(in srgb, var(--el-color-white) 10%, transparent);
|
background: var(--navbar-action-hover-bg);
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
|
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
|
||||||
color: var(--el-color-white);
|
color: currentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 深色文字样式(用于浅色背景:明亮主题下的左侧布局等)
|
// 深色文字样式(用于浅色背景:明亮主题下的左侧布局等)
|
||||||
.navbar-actions--dark-text {
|
.navbar-actions--dark-text {
|
||||||
.navbar-actions__item {
|
--navbar-action-color: var(--el-text-color-secondary);
|
||||||
:deep([class*="i-svg:"]) {
|
--navbar-action-hover-color: var(--el-color-primary);
|
||||||
color: var(--el-text-color-secondary) !important;
|
--navbar-action-hover-bg: var(--el-fill-color-light);
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.04);
|
|
||||||
|
|
||||||
:deep([class*="i-svg:"]) {
|
|
||||||
color: var(--el-color-primary) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile__name {
|
.user-profile__name {
|
||||||
color: var(--el-text-color-regular) !important;
|
color: var(--el-text-color-regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 租户选择器在深色文字模式下的样式
|
// 租户选择器在深色文字模式下的样式
|
||||||
::v-deep(.tenant-switcher__trigger) {
|
::v-deep(.tenant-switcher__trigger) {
|
||||||
color: var(--el-text-color-regular) !important;
|
color: var(--el-text-color-regular);
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
|
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
|
||||||
color: var(--el-text-color-regular) !important;
|
color: currentColor;
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger:hover) {
|
::v-deep(.tenant-switcher__trigger:hover) {
|
||||||
color: var(--el-color-primary) !important;
|
color: var(--navbar-action-hover-color);
|
||||||
background: var(--el-fill-color-light);
|
background: var(--navbar-action-hover-bg);
|
||||||
}
|
}
|
||||||
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
|
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
|
||||||
color: var(--el-color-primary) !important;
|
color: currentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-table {
|
.el-table {
|
||||||
--el-table-border-color: var(--border-color);
|
--el-table-border-color: var(--el-border-color-extra-light);
|
||||||
--el-table-header-bg-color: #f8fafc;
|
--el-table-header-bg-color: #f8fafc;
|
||||||
--el-table-row-hover-bg-color: #f8fbff;
|
--el-table-row-hover-bg-color: #f8fbff;
|
||||||
--el-table-current-row-bg-color: #eef5ff;
|
--el-table-current-row-bg-color: #eef5ff;
|
||||||
@@ -164,27 +164,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.el-table--border::after {
|
&.el-table--border::after {
|
||||||
display: block;
|
width: 1px;
|
||||||
background-color: var(--border-color);
|
background-color: var(--el-table-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table__inner-wrapper::before {
|
.el-table__inner-wrapper::before {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-table__inner-wrapper::after {
|
||||||
|
background-color: var(--el-table-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
.el-table__header th {
|
.el-table__header th {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
background: var(--el-table-header-bg-color);
|
background: var(--el-table-header-bg-color);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--el-table-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table__body td {
|
.el-table__body td {
|
||||||
height: 46px;
|
height: 46px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: 1px solid #eef1f6;
|
border-bottom: 1px solid var(--el-table-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
@@ -192,7 +196,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-table__fixed-right {
|
.el-table__fixed-right {
|
||||||
box-shadow: -1px 0 0 var(--border-color);
|
box-shadow: -1px 0 0 var(--el-table-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table-fixed-column--left.is-last-column::before,
|
||||||
|
.el-table-fixed-column--right.is-first-column::before {
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-empty {
|
&.is-empty {
|
||||||
|
|||||||
@@ -4,10 +4,6 @@
|
|||||||
|
|
||||||
@use "variables" as *;
|
@use "variables" as *;
|
||||||
|
|
||||||
@mixin app-main-height-with-tags {
|
|
||||||
height: calc(100vh - $navbar-height - $tags-view-height) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin app-main-height {
|
@mixin app-main-height {
|
||||||
height: calc(100vh - $navbar-height);
|
height: calc(100vh - $navbar-height);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-2
@@ -68,26 +68,43 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-table-wrapper {
|
.page-table-wrapper {
|
||||||
|
--page-table-border-color: var(--el-border-color-extra-light);
|
||||||
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--content-bg);
|
background: var(--content-bg);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--page-table-border-color);
|
||||||
border-radius: var(--card-radius);
|
border-radius: var(--card-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-table {
|
.page-table {
|
||||||
|
--el-table-border-color: var(--page-table-border-color);
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
||||||
&.el-table--border {
|
&.el-table--border {
|
||||||
border: 0;
|
border: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table__inner-wrapper {
|
.el-table__inner-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&.el-table--border::before,
|
||||||
|
&.el-table--border::after,
|
||||||
|
&.el-table--border .el-table__inner-wrapper::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__border-left-patch,
|
||||||
|
.el-table__border-right-patch,
|
||||||
|
.el-table__border-bottom-patch {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-pagination {
|
.page-pagination {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ html.sidebar-color-blue {
|
|||||||
--menu-background: #304156;
|
--menu-background: #304156;
|
||||||
--menu-text: #bfcbd9;
|
--menu-text: #bfcbd9;
|
||||||
--menu-active-text: #ffffff;
|
--menu-active-text: #ffffff;
|
||||||
--menu-active-bg: rgb(22 93 255 / 18%);
|
--menu-active-bg: rgb(64 158 255 / 22%);
|
||||||
--menu-hover: rgb(255 255 255 / 7%);
|
--menu-hover: rgb(255 255 255 / 7%);
|
||||||
--menu-border: rgb(15 23 42 / 22%);
|
--menu-border: rgb(15 23 42 / 22%);
|
||||||
--sidebar-logo-background: #2d3748;
|
--sidebar-logo-background: #2d3748;
|
||||||
|
|||||||
@@ -594,8 +594,7 @@ $pad: 10px;
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $gap;
|
gap: $gap;
|
||||||
padding: $pad;
|
padding: $pad;
|
||||||
background:
|
background: var(--page-bg);
|
||||||
linear-gradient(180deg, rgb(255 255 255 / 62%), rgb(255 255 255 / 0%) 240px), var(--page-bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
|
|||||||
Reference in new issue
Block a user