Files
laravel_swoole/resources/admin/src/layouts/components/setting.vue
T

400 lines
8.2 KiB
Vue

<template>
<a-drawer
v-model:open="open"
title="布局配置"
placement="right"
:width="420"
>
<div class="setting-content">
<div class="setting-item">
<div class="setting-title">布局模式</div>
<div class="layout-mode-list">
<div
v-for="mode in layoutModes"
:key="mode.value"
class="layout-mode-item"
:class="{
active: layoutStore.layoutMode === mode.value,
}"
@click="handleLayoutChange(mode.value)"
>
<div
class="layout-preview"
:class="`preview-${mode.value}`"
>
<div class="preview-sidebar"></div>
<div
v-if="mode.value === 'default'"
class="preview-sidebar-2"
></div>
<div class="preview-content">
<div class="preview-header"></div>
<div class="preview-body"></div>
</div>
</div>
<div class="layout-name">{{ mode.label }}</div>
<CheckOutlined
v-if="layoutStore.layoutMode === mode.value"
class="check-icon"
/>
</div>
</div>
</div>
<div class="setting-item">
<div class="setting-title">主题颜色</div>
<div class="color-list">
<div
v-for="color in themeColors"
:key="color"
class="color-item"
:class="{ active: themeColor === color }"
:style="{ backgroundColor: color }"
@click="changeThemeColor(color)"
>
<CheckOutlined v-if="themeColor === color" />
</div>
</div>
</div>
<div class="setting-item">
<div class="setting-title">显示设置</div>
<div class="toggle-list">
<div class="toggle-item">
<span>显示标签栏</span>
<a-switch
v-model:checked="showTags"
@change="handleShowTagsChange"
/>
</div>
<div class="toggle-item">
<span>显示面包屑</span>
<a-switch
v-model:checked="showBreadcrumb"
@change="handleShowBreadcrumbChange"
/>
</div>
</div>
</div>
<div class="setting-item">
<div class="setting-title">其他设置</div>
<div class="action-buttons">
<a-button type="primary" block @click="handleResetSettings">
<ReloadOutlined />
重置设置
</a-button>
</div>
</div>
</div>
</a-drawer>
</template>
<script setup>
import { ref, watch, onMounted } from "vue";
import { message } from "ant-design-vue";
import { useLayoutStore } from "@/stores/modules/layout";
import { CheckOutlined, ReloadOutlined } from "@ant-design/icons-vue";
// 定义组件名称(多词命名)
defineOptions({
name: "LayoutSetting",
});
const layoutStore = useLayoutStore();
const open = ref(false);
const themeColor = ref("#1890ff");
const showTags = ref(true);
const showBreadcrumb = ref(true);
const layoutModes = [
{ value: "default", label: "默认布局" },
{ value: "menu", label: "菜单布局" },
{ value: "top", label: "顶部布局" },
];
const themeColors = [
"#1890ff",
"#f5222d",
"#fa541c",
"#faad14",
"#13c2c2",
"#52c41a",
"#2f54eb",
"#722ed1",
];
const openDrawer = () => {
open.value = true;
};
const closeDrawer = () => {
open.value = false;
};
defineExpose({
openDrawer,
closeDrawer,
});
// 切换布局
const handleLayoutChange = (mode) => {
layoutStore.setLayoutMode(mode);
const modeLabel = layoutModes.find((m) => m.value === mode)?.label || mode;
message.success(`已切换到${modeLabel}`);
};
// 切换主题颜色
const changeThemeColor = (color) => {
themeColor.value = color;
// 更新 CSS 变量
document.documentElement.style.setProperty("--primary-color", color);
message.success("主题颜色已更新");
};
// 切换标签栏显示
const handleShowTagsChange = (checked) => {
showTags.value = checked;
// 触发自定义事件或更新状态
document.documentElement.style.setProperty(
"--show-tags",
checked ? "block" : "none",
);
message.success(checked ? "标签栏已显示" : "标签栏已隐藏");
};
// 切换面包屑显示
const handleShowBreadcrumbChange = (checked) => {
showBreadcrumb.value = checked;
message.success(checked ? "面包屑已显示" : "面包屑已隐藏");
};
// 重置设置
const handleResetSettings = () => {
themeColor.value = "#1890ff";
showTags.value = true;
showBreadcrumb.value = true;
layoutStore.setLayoutMode("default");
document.documentElement.style.setProperty("--primary-color", "#1890ff");
document.documentElement.style.setProperty("--show-tags", "block");
message.success("设置已重置");
};
// 初始化
onMounted(() => {
// 从本地存储或其他地方恢复设置
const savedThemeColor = localStorage.getItem("themeColor");
if (savedThemeColor) {
themeColor.value = savedThemeColor;
document.documentElement.style.setProperty(
"--primary-color",
savedThemeColor,
);
}
const savedShowTags = localStorage.getItem("showTags");
if (savedShowTags !== null) {
showTags.value = savedShowTags === "true";
document.documentElement.style.setProperty(
"--show-tags",
savedShowTags === "true" ? "block" : "none",
);
}
});
// 监听设置变化并保存到本地存储
watch(themeColor, (newVal) => {
localStorage.setItem("themeColor", newVal);
});
watch(showTags, (newVal) => {
localStorage.setItem("showTags", String(newVal));
});
</script>
<style scoped lang="scss">
.setting-content {
.setting-item {
margin-bottom: 32px;
.setting-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 16px;
color: #333;
}
.layout-mode-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
.layout-mode-item {
position: relative;
border: 2px solid #e8e8e8;
border-radius: 8px;
padding: 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
border-color: var(--primary-color, #1890ff);
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
&.active {
border-color: var(--primary-color, #1890ff);
background-color: rgba(24, 144, 255, 0.05);
}
.layout-preview {
width: 100%;
height: 60px;
border-radius: 4px;
margin-bottom: 8px;
overflow: hidden;
display: flex;
background-color: #f0f2f5;
.preview-sidebar {
background-color: #001529;
}
.preview-sidebar-2 {
background-color: #fff;
border-left: 1px solid #e8e8e8;
}
.preview-content {
flex: 1;
padding: 4px;
.preview-header {
height: 8px;
background-color: #fff;
margin-bottom: 4px;
}
.preview-body {
height: calc(100% - 12px);
background-color: #e8e8e8;
}
}
&.preview-default {
.preview-sidebar {
width: 20px;
}
.preview-sidebar-2 {
width: 24px;
}
}
&.preview-menu {
.preview-sidebar {
width: 30px;
background-color: #fff;
border-right: 1px solid #e8e8e8;
}
}
&.preview-top {
flex-direction: column;
.preview-sidebar {
width: 100%;
height: 12px;
background-color: #fff;
}
.preview-content {
.preview-header {
display: none;
}
.preview-body {
height: 100%;
}
}
}
}
.layout-name {
font-size: 12px;
color: #666;
text-align: center;
}
.check-icon {
position: absolute;
top: 4px;
right: 4px;
color: var(--primary-color, #1890ff);
font-size: 12px;
}
}
}
.color-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
.color-item {
width: 32px;
height: 32px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
border: 2px solid transparent;
&:hover {
transform: scale(1.1);
}
&.active {
border-color: #fff;
box-shadow: 0 0 0 2px var(--primary-color, #1890ff);
.anticon {
color: #fff;
}
}
}
}
.toggle-list {
.toggle-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
span {
font-size: 14px;
color: #333;
}
}
}
.action-buttons {
:deep(.ant-btn) {
height: 40px;
font-size: 14px;
}
}
}
}
</style>