wip: 临时提交

This commit is contained in:
Ray.Hao
2025-05-25 09:35:50 +08:00
parent 5eaccf112c
commit a3e4d6b852
8 changed files with 546 additions and 148 deletions

View File

@@ -34,6 +34,6 @@ const theneList = [
]; ];
const handleDarkChange = (theme: ThemeMode) => { const handleDarkChange = (theme: ThemeMode) => {
settingsStore.changeTheme(theme); settingsStore.updateTheme(theme);
}; };
</script> </script>

View File

@@ -1,6 +1,5 @@
import { ref, onMounted, onUnmounted, watch, getCurrentInstance } from "vue"; import { ref, onMounted, onUnmounted, watch, getCurrentInstance } from "vue";
import { useStomp } from "./useStomp"; import { useStomp } from "./useStomp";
import { ElMessage } from "element-plus";
import { registerWebSocketInstance } from "@/plugins/websocket"; import { registerWebSocketInstance } from "@/plugins/websocket";
import { Auth } from "@/utils/auth"; import { Auth } from "@/utils/auth";

View File

@@ -76,5 +76,21 @@ export default {
showWatermark: "Show Watermark", showWatermark: "Show Watermark",
classicBlue: "Classic Blue", classicBlue: "Classic Blue",
minimalWhite: "Minimal White", minimalWhite: "Minimal White",
copyConfig: "Copy Config",
resetConfig: "Reset Default",
copySuccess: "Configuration copied to clipboard",
resetSuccess: "Reset to default configuration",
copyDescription:
"Copy config will generate current settings code, reset will restore all settings to default",
confirmReset: "Are you sure to reset all settings to default? This operation cannot be undone.",
applyToFile: "Apply to File",
onlyCopy: "Only Copy",
leftLayout: "Left Mode",
topLayout: "Top Mode",
mixLayout: "Mix Mode",
configManagement: "Config Management",
copyConfigDescription:
"Generate current settings code and copy to clipboard, then overwrite src/settings.ts file",
resetConfigDescription: "Restore all settings to system default values",
}, },
}; };

View File

@@ -79,5 +79,19 @@ export default {
showWatermark: "显示水印", showWatermark: "显示水印",
classicBlue: "经典蓝", classicBlue: "经典蓝",
minimalWhite: "极简白", minimalWhite: "极简白",
copyConfig: "复制配置",
resetConfig: "重置默认",
copySuccess: "配置已复制到剪贴板",
resetSuccess: "已重置为默认配置",
copyDescription: "复制配置将生成当前设置的代码,重置将恢复所有设置为默认值",
confirmReset: "确定要重置所有设置为默认值吗?此操作不可恢复。",
applyToFile: "应用到文件",
onlyCopy: "仅复制",
leftLayout: "左侧模式",
topLayout: "顶部模式",
mixLayout: "混合模式",
configManagement: "配置管理",
copyConfigDescription: "生成当前设置的代码并复制到剪贴板,然后覆盖 src/settings.ts 文件",
resetConfigDescription: "恢复所有设置为系统默认值",
}, },
}; };

View File

@@ -19,7 +19,7 @@ import variables from "@/styles/variables.module.scss";
// 缓存页面集合 // 缓存页面集合
const cachedViews = computed(() => useTagsViewStore().cachedViews); const cachedViews = computed(() => useTagsViewStore().cachedViews);
const appMainHeight = computed(() => { const appMainHeight = computed(() => {
if (useSettingsStore().tagsView) { if (useSettingsStore().showTagsView) {
return `calc(100vh - ${variables["navbar-height"]} - ${variables["tags-view-height"]})`; return `calc(100vh - ${variables["navbar-height"]} - ${variables["tags-view-height"]})`;
} else { } else {
return `calc(100vh - ${variables["navbar-height"]})`; return `calc(100vh - ${variables["navbar-height"]})`;
@@ -32,5 +32,19 @@ const appMainHeight = computed(() => {
position: relative; position: relative;
overflow-y: auto; overflow-y: auto;
background-color: var(--el-bg-color-page); background-color: var(--el-bg-color-page);
/* 布局切换动画优化 */
&.animate__animated {
animation-duration: 0.4s;
animation-fill-mode: forwards;
}
&.animate__fadeOut {
animation-timing-function: ease-in;
}
&.animate__fadeIn {
animation-timing-function: ease-out;
}
} }
</style> </style>

View File

@@ -1,102 +1,167 @@
<template> <template>
<el-drawer <el-drawer
v-model="drawerVisible" v-model="drawerVisible"
size="300" size="380"
:title="t('settings.project')" :title="t('settings.project')"
:before-close="handleCloseDrawer" :before-close="handleCloseDrawer"
class="settings-drawer"
> >
<section class="config-section"> <div class="settings-content">
<el-divider>{{ t("settings.theme") }}</el-divider> <section class="config-section">
<el-divider>{{ t("settings.theme") }}</el-divider>
<div class="flex-center"> <div class="flex-center">
<el-switch <el-switch
v-model="isDark" v-model="isDark"
active-icon="Moon" active-icon="Moon"
inactive-icon="Sunny" inactive-icon="Sunny"
@change="handleThemeChange" class="theme-switch"
/> @change="handleThemeChange"
</div> />
</section> </div>
</section>
<!-- 界面设置 --> <!-- 界面设置 -->
<section class="config-section"> <section class="config-section">
<el-divider>{{ t("settings.interface") }}</el-divider> <el-divider>{{ t("settings.interface") }}</el-divider>
<div class="config-item flex-x-between"> <div class="config-item flex-x-between">
<span class="text-xs">{{ t("settings.themeColor") }}</span> <span class="text-xs">{{ t("settings.themeColor") }}</span>
<el-color-picker <el-color-picker
v-model="selectedThemeColor" v-model="selectedThemeColor"
:predefine="colorPresets" :predefine="colorPresets"
popper-class="theme-picker-dropdown" popper-class="theme-picker-dropdown"
/> />
</div> </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>
<el-switch v-model="settingsStore.showTagsView" /> <el-switch v-model="settingsStore.showTagsView" />
</div> </div>
<div class="config-item flex-x-between"> <div class="config-item flex-x-between">
<span class="text-xs">{{ t("settings.showAppLogo") }}</span> <span class="text-xs">{{ t("settings.showAppLogo") }}</span>
<el-switch v-model="settingsStore.showAppLogo" /> <el-switch v-model="settingsStore.showAppLogo" />
</div> </div>
<div class="config-item flex-x-between"> <div class="config-item flex-x-between">
<span class="text-xs">{{ t("settings.showWatermark") }}</span> <span class="text-xs">{{ t("settings.showWatermark") }}</span>
<el-switch v-model="settingsStore.showWatermark" /> <el-switch v-model="settingsStore.showWatermark" />
</div> </div>
<div v-if="!isDark" class="config-item flex-x-between"> <div v-if="!isDark" class="config-item flex-x-between">
<span class="text-xs">{{ t("settings.sidebarColorScheme") }}</span> <span class="text-xs">{{ t("settings.sidebarColorScheme") }}</span>
<el-radio-group v-model="sidebarColor" @change="changeSidebarColor"> <el-radio-group v-model="sidebarColor" @change="changeSidebarColor">
<el-radio :value="SidebarColor.CLASSIC_BLUE"> <el-radio :value="SidebarColor.CLASSIC_BLUE">
{{ t("settings.classicBlue") }} {{ t("settings.classicBlue") }}
</el-radio> </el-radio>
<el-radio :value="SidebarColor.MINIMAL_WHITE"> <el-radio :value="SidebarColor.MINIMAL_WHITE">
{{ t("settings.minimalWhite") }} {{ t("settings.minimalWhite") }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</div> </div>
</section> </section>
<!-- 布局设置 --> <!-- 布局设置 -->
<section class="config-section"> <section class="config-section">
<el-divider>{{ t("settings.navigation") }}</el-divider> <el-divider>{{ t("settings.navigation") }}</el-divider>
<!-- 整合的布局选择器 --> <!-- 整合的布局选择器 -->
<div class="layout-select"> <div class="layout-select">
<el-tooltip <div class="layout-grid">
v-for="item in layoutOptions" <el-tooltip
:key="item.value" v-for="item in layoutOptions"
:content="item.label" :key="item.value"
placement="bottom" :content="item.label"
> placement="bottom"
<div >
role="button" <div
tabindex="0" role="button"
:class="[ tabindex="0"
'layout-item', :class="[
item.className, 'layout-item',
{ 'is-active': settingsStore.layout === item.value }, item.className,
]" {
@click="handleLayoutChange(item.value)" 'is-active': settingsStore.layout === item.value,
@keydown.enter.space="handleLayoutChange(item.value)" },
> ]"
<div class="layout-item-part" /> @click="handleLayoutChange(item.value)"
<div class="layout-item-part" /> @keydown.enter.space="handleLayoutChange(item.value)"
>
<!-- 布局预览图标 -->
<div class="layout-preview">
<div v-if="item.value !== LayoutMode.LEFT" class="layout-header"></div>
<div v-if="item.value !== LayoutMode.TOP" class="layout-sidebar"></div>
<div class="layout-main"></div>
</div>
<!-- 布局名称 -->
<div class="layout-name">{{ item.label }}</div>
<!-- 选中状态指示器 -->
<div v-if="settingsStore.layout === item.value" class="layout-check">
<el-icon><Check /></el-icon>
</div>
</div>
</el-tooltip>
</div> </div>
</el-tooltip> </div>
</section>
</div>
<!-- 操作按钮区域 - 固定到底部 -->
<div class="action-footer">
<div class="action-divider"></div>
<div class="action-card">
<div class="action-buttons">
<el-tooltip
content="复制配置将生成当前设置的代码,覆盖 src/settings.ts 下的 defaultSettings 变量"
placement="top"
>
<el-button
type="primary"
size="default"
:icon="copyIcon"
:loading="copyLoading"
class="action-btn"
@click="handleCopySettings"
>
{{ copyLoading ? "复制中..." : t("settings.copyConfig") }}
</el-button>
</el-tooltip>
<el-tooltip content="重置将恢复所有设置为默认值" placement="top">
<el-button
type="warning"
size="default"
:icon="resetIcon"
:loading="resetLoading"
class="action-btn"
@click="handleResetSettings"
>
{{ resetLoading ? "重置中..." : t("settings.resetConfig") }}
</el-button>
</el-tooltip>
</div>
</div> </div>
</section> </div>
</el-drawer> </el-drawer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { DocumentCopy, RefreshLeft, Check } from "@element-plus/icons-vue";
import { markRaw } from "vue";
const { t } = useI18n(); const { t } = useI18n();
import { LayoutMode } from "@/enums/settings/layout.enum"; import { LayoutMode } from "@/enums/settings/layout.enum";
import { ThemeMode } from "@/enums/settings/theme.enum"; import { ThemeMode } from "@/enums/settings/theme.enum";
import { SidebarColor } from "@/enums/settings/theme.enum"; import { SidebarColor } from "@/enums/settings/theme.enum";
import { useSettingsStore, usePermissionStore, useAppStore } from "@/store"; import { useSettingsStore, usePermissionStore, useAppStore } from "@/store";
// 按钮图标 - 使用markRaw避免响应式警告
const copyIcon = markRaw(DocumentCopy);
const resetIcon = markRaw(RefreshLeft);
// 加载状态
const copyLoading = ref(false);
const resetLoading = ref(false);
// 布局选项配置 // 布局选项配置
interface LayoutOption { interface LayoutOption {
value: LayoutMode; value: LayoutMode;
@@ -105,9 +170,9 @@ interface LayoutOption {
} }
const layoutOptions: LayoutOption[] = [ const layoutOptions: LayoutOption[] = [
{ value: LayoutMode.LEFT, label: "左侧模式", className: "left" }, { value: LayoutMode.LEFT, label: t("settings.leftLayout"), className: "left" },
{ value: LayoutMode.TOP, label: "顶部模式", className: "top" }, { value: LayoutMode.TOP, label: t("settings.topLayout"), className: "top" },
{ value: LayoutMode.MIX, label: "混合模式", className: "mix" }, { value: LayoutMode.MIX, label: t("settings.mixLayout"), className: "mix" },
]; ];
// 颜色预设 // 颜色预设
@@ -165,7 +230,10 @@ const changeSidebarColor = (val: any) => {
* @param layout - 布局模式 * @param layout - 布局模式
*/ */
const handleLayoutChange = (layout: LayoutMode) => { const handleLayoutChange = (layout: LayoutMode) => {
if (settingsStore.layout === layout) return;
settingsStore.updateLayout(layout); settingsStore.updateLayout(layout);
if (layout === LayoutMode.MIX && route.name) { if (layout === LayoutMode.MIX && route.name) {
const topLevelRoute = findTopLevelRoute(permissionStore.routes, route.name as string); const topLevelRoute = findTopLevelRoute(permissionStore.routes, route.name as string);
if (appStore.activeTopMenuPath !== topLevelRoute.path) { if (appStore.activeTopMenuPath !== topLevelRoute.path) {
@@ -174,6 +242,89 @@ const handleLayoutChange = (layout: LayoutMode) => {
} }
}; };
/**
* 复制当前配置
*/
const handleCopySettings = async () => {
try {
copyLoading.value = true;
// 生成配置代码
const configCode = generateSettingsCode();
// 复制到剪贴板
await navigator.clipboard.writeText(configCode);
// 显示成功消息
ElMessage.success({
message: t("settings.copySuccess"),
duration: 3000,
});
} catch {
ElMessage.error("复制配置失败");
} finally {
copyLoading.value = false;
}
};
/**
* 重置为默认配置
*/
const handleResetSettings = async () => {
resetLoading.value = true;
try {
settingsStore.resetSettings();
// 同步更新本地状态
isDark.value = settingsStore.theme === ThemeMode.DARK;
sidebarColor.value = settingsStore.sidebarColorScheme;
ElMessage.success(t("settings.resetSuccess"));
} catch {
ElMessage.error("重置配置失败");
} finally {
resetLoading.value = false;
}
};
/**
* 生成配置代码字符串
*/
const generateSettingsCode = (): string => {
const settings = {
title: "pkg.name",
version: "pkg.version",
showSettings: true,
showTagsView: settingsStore.showTagsView,
showAppLogo: settingsStore.showAppLogo,
layout: `LayoutMode.${settingsStore.layout.toUpperCase()}`,
theme: `ThemeMode.${settingsStore.theme.toUpperCase()}`,
size: "ComponentSize.DEFAULT",
language: "LanguageEnum.ZH_CN",
themeColor: `"${settingsStore.themeColor}"`,
showWatermark: settingsStore.showWatermark,
watermarkContent: "pkg.name",
sidebarColorScheme: `SidebarColor.${settingsStore.sidebarColorScheme.toUpperCase().replace("-", "_")}`,
};
return `const defaultSettings: AppSettings = {
title: ${settings.title},
version: ${settings.version},
showSettings: ${settings.showSettings},
showTagsView: ${settings.showTagsView},
showAppLogo: ${settings.showAppLogo},
layout: ${settings.layout},
theme: ${settings.theme},
size: ${settings.size},
language: ${settings.language},
themeColor: ${settings.themeColor},
showWatermark: ${settings.showWatermark},
watermarkContent: ${settings.watermarkContent},
sidebarColorScheme: ${settings.sidebarColorScheme},
};`;
};
/** /**
* 查找路由的顶层父路由 * 查找路由的顶层父路由
* *
@@ -216,109 +367,300 @@ const handleCloseDrawer = () => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 设置抽屉样式 */
.settings-drawer {
:deep(.el-drawer__body) {
position: relative;
height: 100%;
padding: 0;
overflow: hidden;
}
}
/* 设置内容区域 */
.settings-content {
height: calc(100vh - 120px); /* 减去头部和底部按钮的高度 */
padding: 20px;
padding-bottom: 20px;
overflow-y: auto;
}
/* 底部操作区域样式 */
.action-footer {
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);
.action-divider {
display: none; /* 移除重复的分割线 */
}
.action-card {
padding: 16px 20px;
margin: 0;
background: var(--el-fill-color-extra-light);
border: none;
border-radius: 0;
.action-buttons {
display: flex;
gap: 12px;
.action-btn {
flex: 1;
font-size: 14px;
border-radius: 8px;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
}
}
}
}
/* 主题切换器优化 */
.theme-switch {
transform: scale(1.2);
transition: all 0.3s ease;
&:hover {
transform: scale(1.25);
}
}
.config-section { .config-section {
margin-bottom: 24px; margin-bottom: 24px;
.config-item { .config-item {
padding: 12px 0; padding: 12px 0;
border-bottom: 1px solid var(--el-border-color-light); border-bottom: 1px solid var(--el-border-color-light);
transition: all 0.3s ease;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
&:hover {
padding-right: 8px;
padding-left: 8px;
margin: 0 -8px;
background-color: var(--el-fill-color-light);
border-radius: 6px;
}
} }
} }
/* 布局选择器样式 */ /* 布局选择器样式优化 */
.layout-select { .layout-select {
display: flex; padding: 16px 8px;
gap: 10px;
justify-content: space-evenly; .layout-grid {
padding: 10px 0; display: grid;
--layout-primary: #1b2a47; grid-template-columns: repeat(3, 1fr);
--layout-background: #f0f2f5; gap: 12px;
--layout-shadow: 0 0 8px rgba(0, 0, 0, 0.1); justify-items: center;
--layout-hover: #e3f1f9; }
} }
.layout-item { .layout-item {
position: relative; position: relative;
width: 18%; width: 70px;
height: 50px; height: 80px;
overflow: hidden;
cursor: pointer; cursor: pointer;
background: var(--layout-background); background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%);
border-radius: 8px; border: 2px solid var(--el-border-color-light);
box-shadow: var(--layout-shadow); border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform 0.2s ease,
border-color 0.2s ease,
box-shadow 0.2s ease;
&:hover { &:hover {
background-color: var(--layout-hover); background: linear-gradient(145deg, #ffffff 0%, var(--el-color-primary-light-9) 100%);
transform: scale(1.02); border-color: var(--el-color-primary-light-3);
transform: translateY(-4px) scale(1.05);
} }
&:focus-visible { &:active {
outline: 2px solid var(--el-color-primary); transform: translateY(-2px) scale(1.02);
} }
&-part { .layout-preview {
position: relative;
width: 100%;
height: 50px;
margin: 8px 0 4px 0;
}
.layout-header {
position: absolute; position: absolute;
background: var(--layout-primary); top: 0;
border-radius: 4px; right: 4px;
box-shadow: var(--layout-shadow); left: 4px;
transition: all 0.3s ease; height: 8px;
background: linear-gradient(
90deg,
var(--el-color-primary) 0%,
var(--el-color-primary-light-3) 100%
);
border-radius: 2px;
} }
.layout-sidebar {
position: absolute;
left: 4px;
width: 12px;
background: linear-gradient(
180deg,
var(--el-color-primary-dark-2) 0%,
var(--el-color-primary) 100%
);
border-radius: 2px;
}
.layout-main {
position: absolute;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
border: 1px solid var(--el-border-color-lighter);
border-radius: 2px;
}
.layout-name {
position: absolute;
right: 0;
bottom: 6px;
left: 0;
font-size: 10px;
font-weight: 500;
color: var(--el-text-color-regular);
text-align: center;
transition: color 0.3s ease;
}
.layout-check {
position: absolute;
top: 4px;
right: 4px;
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
font-size: 10px;
color: white;
background: var(--el-color-success);
border-radius: 50%;
}
// 左侧布局
&.left { &.left {
.layout-item-part { .layout-sidebar {
&:first-child { top: 4px;
width: 30%; bottom: 4px;
height: 100%; }
border-radius: 4px 0 0 4px; .layout-main {
} top: 4px;
&:last-child { right: 4px;
top: 0; bottom: 4px;
right: 0; left: 20px;
width: 70%;
height: 30%;
background: #fff;
border-radius: 0 4px 4px 0;
}
} }
} }
// 顶部布局
&.top { &.top {
.layout-item-part:first-child { .layout-header {
width: 100%; height: 12px;
height: 30%; }
border-radius: 4px 4px 0 0; .layout-main {
top: 16px;
right: 4px;
bottom: 4px;
left: 4px;
} }
} }
// 混合布局
&.mix { &.mix {
.layout-item-part { .layout-header {
&:first-child { height: 10px;
width: 100%; }
height: 30%; .layout-sidebar {
border-radius: 4px 4px 0 0; top: 14px;
} bottom: 4px;
&:last-child { }
bottom: 0; .layout-main {
left: 0; top: 14px;
width: 30%; right: 4px;
height: 70%; bottom: 4px;
border-radius: 0 0 4px 4px; left: 20px;
} }
}
&.is-active {
background: linear-gradient(
145deg,
var(--el-color-primary-light-9) 0%,
var(--el-color-primary-light-8) 100%
);
border-color: var(--el-color-primary);
transform: translateY(-2px) scale(1.08);
.layout-name {
font-weight: 600;
color: var(--el-color-primary);
} }
} }
} }
.is-active { /* 深色模式适配 */
background-color: var(--layout-hover); .dark {
border: 2px solid var(--el-color-primary); .action-footer {
transform: scale(1.05); background: var(--el-bg-color);
border-top-color: var(--el-border-color);
}
.action-card {
background: var(--el-fill-color-extra-light);
}
.layout-item {
background: linear-gradient(145deg, var(--el-bg-color) 0%, var(--el-bg-color-page) 100%);
border-color: var(--el-border-color);
&:hover {
background: linear-gradient(
145deg,
var(--el-bg-color-page) 0%,
var(--el-color-primary-light-9) 100%
);
}
&.is-active {
background: linear-gradient(
145deg,
var(--el-color-primary-light-9) 0%,
var(--el-color-primary-light-8) 100%
);
}
.layout-main {
background: linear-gradient(135deg, var(--el-fill-color) 0%, var(--el-fill-color-light) 100%);
}
}
}
/* 复制配置对话框样式 */
:deep(.copy-config-dialog) {
.el-message-box__content {
max-height: 400px;
overflow-y: auto;
}
} }
</style> </style>

View File

@@ -1,5 +1,10 @@
<template> <template>
<component :is="currentLayoutComponent" /> <div class="layout-wrapper">
<component :is="currentLayoutComponent" />
<!-- 设置面板 - 独立于布局组件 -->
<Settings v-if="isShowSettings" />
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -8,7 +13,9 @@ import { useLayout } from "./composables/useLayout";
import LeftLayout from "./views/LeftLayout.vue"; import LeftLayout from "./views/LeftLayout.vue";
import TopLayout from "./views/TopLayout.vue"; import TopLayout from "./views/TopLayout.vue";
import MixLayout from "./views/MixLayout.vue"; import MixLayout from "./views/MixLayout.vue";
import Settings from "./components/Settings/index.vue";
import { LayoutMode } from "@/enums/settings/layout.enum"; import { LayoutMode } from "@/enums/settings/layout.enum";
import defaultSettings from "@/settings";
const { currentLayout } = useLayout(); const { currentLayout } = useLayout();
@@ -24,4 +31,14 @@ const currentLayoutComponent = computed(() => {
return LeftLayout; return LeftLayout;
} }
}); });
// 是否显示设置面板
const isShowSettings = computed(() => defaultSettings.showSettings);
</script> </script>
<style lang="scss" scoped>
.layout-wrapper {
width: 100%;
height: 100%;
}
</style>

View File

@@ -6,9 +6,6 @@
<!-- 布局内容插槽 --> <!-- 布局内容插槽 -->
<slot></slot> <slot></slot>
<!-- 设置面板 -->
<Settings v-if="isShowSettings" />
<!-- 返回顶部按钮 --> <!-- 返回顶部按钮 -->
<el-backtop target=".app-main"> <el-backtop target=".app-main">
<div class="i-svg:backtop w-6 h-6" /> <div class="i-svg:backtop w-6 h-6" />
@@ -19,10 +16,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useLayout } from "../composables/useLayout"; import { useLayout } from "../composables/useLayout";
import { useLayoutResponsive } from "../composables/useLayoutResponsive"; import { useLayoutResponsive } from "../composables/useLayoutResponsive";
import Settings from "../components/Settings/index.vue";
// 布局相关 // 布局相关
const { layoutClass, isShowSettings, isSidebarOpen, closeSidebar } = useLayout(); const { layoutClass, isSidebarOpen, closeSidebar } = useLayout();
// 响应式处理 // 响应式处理
const { isMobile } = useLayoutResponsive(); const { isMobile } = useLayoutResponsive();