355 lines
7.9 KiB
Vue
355 lines
7.9 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>
|