布局框架搭建

This commit is contained in:
2026-01-16 11:19:54 +08:00
parent f33bf735d9
commit 08f97d1a21
10 changed files with 1573 additions and 70 deletions

View File

@@ -0,0 +1,251 @@
<template>
<div class="userbar">
<a-dropdown :trigger="['click']">
<div class="user-info">
<a-avatar :size="32" :src="userStore.user?.avatar || ''">
{{ userStore.user?.username?.charAt(0)?.toUpperCase() || 'U' }}
</a-avatar>
<span class="username">{{ userStore.user?.username || 'Admin' }}</span>
<DownOutlined />
</div>
<template #overlay>
<a-menu @click="handleMenuClick">
<a-menu-item key="profile">
<UserOutlined />
<span>个人中心</span>
</a-menu-item>
<a-menu-item key="settings">
<SettingOutlined />
<span>系统设置</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout">
<LogoutOutlined />
<span>退出登录</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<a-tooltip title="全屏">
<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-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 { useRouter } from 'vue-router'
import { message, Modal } from 'ant-design-vue'
import { useUserStore } from '@/stores/modules/user'
import { useLayoutStore } from '@/stores/modules/layout'
import {
DownOutlined,
UserOutlined,
SettingOutlined,
LogoutOutlined,
FullscreenOutlined,
FullscreenExitOutlined,
CheckOutlined
} from '@ant-design/icons-vue'
const router = useRouter()
const userStore = useUserStore()
const layoutStore = useLayoutStore()
const isFullscreen = ref(false)
const showSetting = ref(false)
const themeColor = ref('#1890ff')
const themeColors = [
'#1890ff',
'#f5222d',
'#fa541c',
'#faad14',
'#13c2c2',
'#52c41a',
'#2f54eb',
'#722ed1'
]
// 切换全屏
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen()
isFullscreen.value = true
} else {
document.exitFullscreen()
isFullscreen.value = false
}
}
// 监听全屏变化
const handleFullscreenChange = () => {
isFullscreen.value = !!document.fullscreenElement
}
onMounted(() => {
document.addEventListener('fullscreenchange', handleFullscreenChange)
})
onUnmounted(() => {
document.removeEventListener('fullscreenchange', handleFullscreenChange)
})
// 处理菜单点击
const handleMenuClick = ({ key }) => {
switch (key) {
case 'profile':
router.push('/profile')
break
case 'settings':
showSetting.value = true
break
case 'logout':
handleLogout()
break
}
}
// 退出登录
const handleLogout = () => {
Modal.confirm({
title: '确认退出',
content: '确定要退出登录吗?',
okText: '确定',
cancelText: '取消',
onOk: async () => {
try {
await userStore.logout()
message.success('退出成功')
router.push('/login')
} catch (error) {
message.error('退出失败')
}
}
})
}
// 切换布局
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">
.userbar {
display: flex;
align-items: center;
gap: 12px;
.user-info {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 4px 12px;
border-radius: 4px;
transition: background-color 0.3s;
&:hover {
background-color: rgba(0, 0, 0, 0.04);
}
.username {
font-size: 14px;
color: #333;
}
}
.action-btn {
font-size: 16px;
padding: 4px 8px;
&:hover {
color: #1890ff;
}
}
}
.setting-content {
.setting-item {
margin-bottom: 24px;
.setting-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 12px;
color: #333;
}
.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;
}
}
}
}
}
}
</style>