更新
This commit is contained in:
@@ -4,6 +4,24 @@ export default {
|
||||
login: 'Login',
|
||||
logout: 'Logout',
|
||||
register: 'Register',
|
||||
searchMenu: 'Search Menu',
|
||||
messages: 'Messages',
|
||||
tasks: 'Tasks',
|
||||
clearAll: 'Clear All',
|
||||
noMessages: 'No Messages',
|
||||
noTasks: 'No Tasks',
|
||||
fullscreen: 'Fullscreen',
|
||||
personalCenter: 'Personal Center',
|
||||
systemSettings: 'System Settings',
|
||||
searchEmpty: 'Please enter search content',
|
||||
searching: 'Searching: ',
|
||||
cleared: 'Cleared',
|
||||
languageChanged: 'Language Changed',
|
||||
settingsDeveloping: 'System settings feature is under development',
|
||||
logoutSuccess: 'Logout Successful',
|
||||
logoutFailed: 'Logout Failed',
|
||||
confirmLogout: 'Confirm Logout',
|
||||
logoutConfirm: 'Are you sure you want to logout?',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
confirmPassword: 'Confirm Password',
|
||||
|
||||
@@ -4,6 +4,24 @@ export default {
|
||||
login: '登录',
|
||||
logout: '退出登录',
|
||||
register: '注册',
|
||||
searchMenu: '搜索菜单',
|
||||
messages: '消息',
|
||||
tasks: '任务',
|
||||
clearAll: '清空全部',
|
||||
noMessages: '暂无消息',
|
||||
noTasks: '暂无任务',
|
||||
fullscreen: '全屏',
|
||||
personalCenter: '个人中心',
|
||||
systemSettings: '系统设置',
|
||||
searchEmpty: '请输入搜索内容',
|
||||
searching: '正在搜索:',
|
||||
cleared: '已清空',
|
||||
languageChanged: '语言已切换',
|
||||
settingsDeveloping: '系统设置功能开发中',
|
||||
logoutSuccess: '退出成功',
|
||||
logoutFailed: '退出失败',
|
||||
confirmLogout: '确认退出',
|
||||
logoutConfirm: '确定要退出登录吗?',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
confirmPassword: '确认密码',
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<a-breadcrumb class="breadcrumb">
|
||||
<a-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path">
|
||||
<span v-if="index === breadcrumbList.length - 1" class="no-redirect">
|
||||
<component :is="item.meta?.icon || 'FileTextOutlined'" />
|
||||
{{ item.meta.title }}
|
||||
</span>
|
||||
<a v-else @click.prevent="handleLink(item)">
|
||||
<component :is="item.meta?.icon || 'HomeOutlined'" />
|
||||
{{ item.meta.title }}
|
||||
</a>
|
||||
</a-breadcrumb-item>
|
||||
|
||||
@@ -1,9 +1,293 @@
|
||||
<template>
|
||||
<a-drawer v-model:open="open" title="布局配置"></a-drawer>
|
||||
<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 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>
|
||||
</div>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, defineExpose } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useLayoutStore } from '@/stores/modules/layout'
|
||||
import { CheckOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const layoutStore = useLayoutStore()
|
||||
|
||||
const open = ref(false)
|
||||
const themeColor = ref('#1890ff')
|
||||
const showTags = 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
|
||||
// 这里可以实现主题切换逻辑
|
||||
message.success('主题颜色已更新')
|
||||
}
|
||||
|
||||
// 切换标签栏显示
|
||||
const handleShowTagsChange = (checked) => {
|
||||
// 这里可以实现标签栏显示/隐藏逻辑
|
||||
console.log('showTags:', checked)
|
||||
}
|
||||
</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: #1890ff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.layout-preview {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
background-color: #f0f2f5;
|
||||
|
||||
&.preview-default {
|
||||
.preview-sidebar {
|
||||
width: 20px;
|
||||
background-color: #001529;
|
||||
}
|
||||
|
||||
.preview-sidebar-2 {
|
||||
width: 24px;
|
||||
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-menu {
|
||||
.preview-sidebar {
|
||||
width: 30px;
|
||||
background-color: #fff;
|
||||
border-right: 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-top {
|
||||
flex-direction: column;
|
||||
|
||||
.preview-sidebar {
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
flex: 1;
|
||||
padding: 4px;
|
||||
|
||||
.preview-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-body {
|
||||
height: 100%;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-name {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
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: transform 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&.active {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -153,7 +153,7 @@ onMounted(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.side-menu {
|
||||
height: calc(100% - 64px);
|
||||
height: calc(100% - 60px);
|
||||
border-right: none;
|
||||
overflow-y: auto;
|
||||
|
||||
|
||||
@@ -1,18 +1,87 @@
|
||||
<template>
|
||||
<div class="userbar">
|
||||
<a-tooltip title="全屏">
|
||||
<!-- 菜单搜索 -->
|
||||
<a-input-search v-model:value="searchKeyword" :placeholder="$t('common.searchMenu')" enter-button
|
||||
@search="handleSearch" class="search-input" allow-clear>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input-search>
|
||||
|
||||
<!-- 消息通知 -->
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-badge :count="messageCount" :offset="[-5, 5]">
|
||||
<a-button type="text" class="action-btn">
|
||||
<BellOutlined />
|
||||
</a-button>
|
||||
</a-badge>
|
||||
<template #overlay>
|
||||
<a-card class="dropdown-card" :title="$t('common.messages')" :bordered="false">
|
||||
<template #extra>
|
||||
<a @click="clearMessages">{{ $t('common.clearAll') }}</a>
|
||||
</template>
|
||||
<div class="message-list">
|
||||
<div v-for="msg in messages" :key="msg.id" class="message-item" :class="{ unread: !msg.read }">
|
||||
<div class="message-content">
|
||||
<div class="message-title">{{ msg.title }}</div>
|
||||
<div class="message-time">{{ msg.time }}</div>
|
||||
</div>
|
||||
<a-badge v-if="!msg.read" dot />
|
||||
</div>
|
||||
<a-empty v-if="messages.length === 0" :description="$t('common.noMessages')" />
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-badge :count="taskCount" :offset="[-5, 5]">
|
||||
<a-button type="text" class="action-btn">
|
||||
<CheckSquareOutlined />
|
||||
</a-button>
|
||||
</a-badge>
|
||||
<template #overlay>
|
||||
<a-card class="dropdown-card" :title="$t('common.tasks')" :bordered="false">
|
||||
<template #extra>
|
||||
<a @click="clearTasks">{{ $t('common.clearAll') }}</a>
|
||||
</template>
|
||||
<div class="task-list">
|
||||
<div v-for="task in tasks" :key="task.id" class="task-item">
|
||||
<a-checkbox :checked="task.completed" @change="toggleTask(task)">
|
||||
<span :class="{ completed: task.completed }">{{ task.title }}</span>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<a-empty v-if="tasks.length === 0" :description="$t('common.noTasks')" />
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 语言切换 -->
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-button type="text" class="action-btn">
|
||||
<GlobalOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleLanguageChange">
|
||||
<a-menu-item v-for="locale in i18nStore.availableLocales" :key="locale.value"
|
||||
:disabled="i18nStore.currentLocale === locale.value">
|
||||
<span>{{ locale.label }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 全屏 -->
|
||||
<a-tooltip :title="$t('common.fullscreen')">
|
||||
<a-button type="text" @click="toggleFullscreen" class="action-btn">
|
||||
<FullscreenOutlined v-if="!isFullscreen" />
|
||||
<FullscreenExitOutlined v-else />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip title="布局设置">
|
||||
<a-button type="text" @click="showSetting = true" class="action-btn">
|
||||
<SettingOutlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<a-dropdown :trigger="['click']">
|
||||
<div class="user-info">
|
||||
<a-avatar :size="32" :src="userStore.user?.avatar || ''">
|
||||
@@ -25,82 +94,69 @@
|
||||
<a-menu @click="handleMenuClick">
|
||||
<a-menu-item key="profile">
|
||||
<UserOutlined />
|
||||
<span>个人中心</span>
|
||||
<span>{{ $t('common.personalCenter') }}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="settings">
|
||||
<SettingOutlined />
|
||||
<span>系统设置</span>
|
||||
<span>{{ $t('common.systemSettings') }}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item key="logout">
|
||||
<LogoutOutlined />
|
||||
<span>退出登录</span>
|
||||
<span>{{ $t('common.logout') }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 布局设置抽屉 -->
|
||||
<a-drawer v-model:open="showSetting" title="布局设置" placement="right" :width="280">
|
||||
<div class="setting-content">
|
||||
<div class="setting-item">
|
||||
<div class="setting-title">布局模式</div>
|
||||
<a-radio-group v-model:value="layoutStore.layoutMode" @change="handleLayoutChange">
|
||||
<a-radio value="default">默认布局</a-radio>
|
||||
<a-radio value="menu">菜单布局</a-radio>
|
||||
<a-radio value="top">顶部布局</a-radio>
|
||||
</a-radio-group>
|
||||
</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>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import { useLayoutStore } from '@/stores/modules/layout'
|
||||
import { useI18nStore } from '@/stores/modules/i18n'
|
||||
import {
|
||||
DownOutlined,
|
||||
UserOutlined,
|
||||
SettingOutlined,
|
||||
LogoutOutlined,
|
||||
FullscreenOutlined,
|
||||
FullscreenExitOutlined,
|
||||
CheckOutlined
|
||||
BellOutlined,
|
||||
CheckSquareOutlined,
|
||||
GlobalOutlined,
|
||||
SearchOutlined,
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const layoutStore = useLayoutStore()
|
||||
const i18nStore = useI18nStore()
|
||||
|
||||
const isFullscreen = ref(false)
|
||||
const showSetting = ref(false)
|
||||
const themeColor = ref('#1890ff')
|
||||
const searchKeyword = ref('')
|
||||
|
||||
const themeColors = [
|
||||
'#1890ff',
|
||||
'#f5222d',
|
||||
'#fa541c',
|
||||
'#faad14',
|
||||
'#13c2c2',
|
||||
'#52c41a',
|
||||
'#2f54eb',
|
||||
'#722ed1'
|
||||
]
|
||||
// 消息数据
|
||||
const messages = ref([
|
||||
{ id: 1, title: '系统通知:新版本已发布', time: '10分钟前', read: false },
|
||||
{ id: 2, title: '任务提醒:请完成待审核的用户', time: '30分钟前', read: false },
|
||||
{ id: 3, title: '安全警告:检测到异常登录', time: '1小时前', read: true },
|
||||
{ id: 4, title: '数据备份已完成', time: '2小时前', read: true }
|
||||
])
|
||||
|
||||
const messageCount = computed(() => messages.value.filter((m) => !m.read).length)
|
||||
|
||||
// 任务数据
|
||||
const tasks = ref([
|
||||
{ id: 1, title: '完成用户审核', completed: false },
|
||||
{ id: 2, title: '更新系统文档', completed: false },
|
||||
{ id: 3, title: '优化数据库查询', completed: true }
|
||||
])
|
||||
|
||||
const taskCount = computed(() => tasks.value.filter((t) => !t.completed).length)
|
||||
|
||||
// 切换全屏
|
||||
const toggleFullscreen = () => {
|
||||
@@ -126,6 +182,40 @@ onUnmounted(() => {
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
||||
})
|
||||
|
||||
// 菜单搜索
|
||||
const handleSearch = (value) => {
|
||||
if (!value.trim()) {
|
||||
message.warning(t('common.searchEmpty'))
|
||||
return
|
||||
}
|
||||
// 这里可以实现实际的搜索逻辑,比如跳转到搜索页面或显示搜索结果
|
||||
message.info(t('common.searching') + value)
|
||||
searchKeyword.value = ''
|
||||
}
|
||||
|
||||
// 清除消息
|
||||
const clearMessages = () => {
|
||||
messages.value = []
|
||||
message.success(t('common.cleared'))
|
||||
}
|
||||
|
||||
// 清除任务
|
||||
const clearTasks = () => {
|
||||
tasks.value = []
|
||||
message.success(t('common.cleared'))
|
||||
}
|
||||
|
||||
// 切换任务状态
|
||||
const toggleTask = (task) => {
|
||||
task.completed = !task.completed
|
||||
}
|
||||
|
||||
// 切换语言
|
||||
const handleLanguageChange = ({ key }) => {
|
||||
i18nStore.setLocale(key)
|
||||
message.success(t('common.languageChanged'))
|
||||
}
|
||||
|
||||
// 处理菜单点击
|
||||
const handleMenuClick = ({ key }) => {
|
||||
switch (key) {
|
||||
@@ -133,7 +223,8 @@ const handleMenuClick = ({ key }) => {
|
||||
router.push('/profile')
|
||||
break
|
||||
case 'settings':
|
||||
showSetting.value = true
|
||||
// 系统设置功能暂未实现
|
||||
message.info(t('common.settingsDeveloping'))
|
||||
break
|
||||
case 'logout':
|
||||
handleLogout()
|
||||
@@ -144,34 +235,21 @@ const handleMenuClick = ({ key }) => {
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
Modal.confirm({
|
||||
title: '确认退出',
|
||||
content: '确定要退出登录吗?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
title: t('common.confirmLogout'),
|
||||
content: t('common.logoutConfirm'),
|
||||
okText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
await userStore.logout()
|
||||
message.success('退出成功')
|
||||
message.success(t('common.logoutSuccess'))
|
||||
router.push('/login')
|
||||
} catch (error) {
|
||||
message.error('退出失败')
|
||||
message.error(t('common.logoutFailed'))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 切换布局
|
||||
const handleLayoutChange = (e) => {
|
||||
layoutStore.setLayoutMode(e.target.value)
|
||||
message.success(`已切换到${e.target.value === 'default' ? '默认' : e.target.value === 'menu' ? '菜单' : '顶部'}布局`)
|
||||
}
|
||||
|
||||
// 切换主题颜色
|
||||
const changeThemeColor = (color) => {
|
||||
themeColor.value = color
|
||||
// 这里可以实现主题切换逻辑
|
||||
message.success('主题颜色已更新')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -180,6 +258,15 @@ const changeThemeColor = (color) => {
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.search-input {
|
||||
width: 240px;
|
||||
margin-right: 8px;
|
||||
|
||||
:deep(.ant-input) {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -202,48 +289,75 @@ const changeThemeColor = (color) => {
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.lang-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-content {
|
||||
.setting-item {
|
||||
margin-bottom: 24px;
|
||||
.dropdown-card {
|
||||
width: 320px;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
|
||||
.setting-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12px;
|
||||
color: #333;
|
||||
:deep(.ant-card-head) {
|
||||
padding: 12px 16px;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.color-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
:deep(.ant-card-body) {
|
||||
padding: 12px 16px;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.color-item {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
.message-list,
|
||||
.task-list {
|
||||
|
||||
.message-item,
|
||||
.task-item {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.unread {
|
||||
background-color: rgba(24, 144, 255, 0.04);
|
||||
padding: 10px;
|
||||
margin: 0 -10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.message-content {
|
||||
.message-title {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.anticon {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.message-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.task-item {
|
||||
.completed {
|
||||
text-decoration: line-through;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
<a-layout class="main-layout">
|
||||
<a-layout-header class="app-header">
|
||||
<div class="header-left">
|
||||
<a-button
|
||||
v-if="selectedParentMenu && selectedParentMenu.children && selectedParentMenu.children.length > 0"
|
||||
type="text" :icon="sidebarCollapsed ? h(MenuUnfoldOutlined) : h(MenuFoldOutlined)"
|
||||
@click="toggleSidebar" class="collapse-btn" />
|
||||
<breadcrumb />
|
||||
</div>
|
||||
<userbar />
|
||||
@@ -42,8 +38,9 @@
|
||||
|
||||
<!-- Menu布局:左侧菜单栏布局 -->
|
||||
<template v-else-if="layoutMode === 'menu'">
|
||||
<a-layout-sider theme="light" :collapsed="sidebarCollapsed" :collapsible="true" @collapse="handleCollapse"
|
||||
class="full-menu-sidebar" width="200" :collapsed-width="64">
|
||||
<a-layout-sider theme="light" style="border-right: 1px solid #f0f0f0" :collapsed="sidebarCollapsed"
|
||||
:collapsible="true" @collapse="handleCollapse" class="full-menu-sidebar" width="200"
|
||||
:collapsed-width="64">
|
||||
<div class="logo-box-full">
|
||||
<span v-if="!sidebarCollapsed" class="logo-text">VUE ADMIN</span>
|
||||
<span v-else class="logo-text-mini">V</span>
|
||||
@@ -53,8 +50,6 @@
|
||||
<a-layout class="main-layout">
|
||||
<a-layout-header class="app-header">
|
||||
<div class="header-left">
|
||||
<a-button type="text" :icon="sidebarCollapsed ? h(MenuUnfoldOutlined) : h(MenuFoldOutlined)"
|
||||
@click="toggleSidebar" class="collapse-btn" />
|
||||
<breadcrumb />
|
||||
</div>
|
||||
<userbar />
|
||||
@@ -90,6 +85,16 @@
|
||||
</router-view>
|
||||
</a-layout-content>
|
||||
</template>
|
||||
|
||||
<!-- 漂浮的设置按钮 -->
|
||||
<a-float-button type="primary" @click="openSetting">
|
||||
<template #icon>
|
||||
<SettingOutlined />
|
||||
</template>
|
||||
</a-float-button>
|
||||
|
||||
<!-- 布局设置组件 -->
|
||||
<setting ref="settingRef" />
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
@@ -97,7 +102,7 @@
|
||||
import { computed, ref, h } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useLayoutStore } from '@/stores/modules/layout'
|
||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons-vue'
|
||||
import { SettingOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
import userbar from './components/userbar.vue'
|
||||
import breadcrumb from './components/breadcrumb.vue'
|
||||
@@ -106,10 +111,13 @@ import topMenu from './components/topMenu.vue'
|
||||
import sideMenu from './components/sideMenu.vue'
|
||||
import level1Menu from './components/level1Menu.vue'
|
||||
import level2Menu from './components/level2Menu.vue'
|
||||
import setting from './components/setting.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const layoutStore = useLayoutStore()
|
||||
|
||||
const settingRef = ref(null)
|
||||
|
||||
const layoutMode = computed(() => layoutStore.layoutMode)
|
||||
const sidebarCollapsed = computed(() => layoutStore.sidebarCollapsed)
|
||||
const selectedParentMenu = computed(() => layoutStore.selectedParentMenu)
|
||||
@@ -131,15 +139,15 @@ const layoutClass = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 切换侧边栏
|
||||
const toggleSidebar = () => {
|
||||
layoutStore.toggleSidebar()
|
||||
}
|
||||
|
||||
// 处理折叠
|
||||
const handleCollapse = (collapsed) => {
|
||||
layoutStore.sidebarCollapsed = collapsed
|
||||
}
|
||||
|
||||
// 打开设置抽屉
|
||||
const openSetting = () => {
|
||||
settingRef.value?.openDrawer()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -155,6 +163,7 @@ const handleCollapse = (collapsed) => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
height: 60px;
|
||||
z-index: 9;
|
||||
@@ -230,12 +239,11 @@ const handleCollapse = (collapsed) => {
|
||||
&.layout-menu {
|
||||
.full-menu-sidebar {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.1);
|
||||
z-index: 10;
|
||||
|
||||
.logo-box-full {
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
Reference in New Issue
Block a user