271 lines
6.9 KiB
Vue
Raw Normal View History

2025-04-14 10:57:27 +08:00
<template>
<a-layout style="min-height: 100vh">
<a-layout-sider
v-model:collapsed="collapsed"
:trigger="null"
collapsible
>
<div class="logo">
<img
src="../../assets/logo.png"
style="height: 30px"
/><span
style="
font-weight: 700;
font-size: 16px;
color: #32b768;
opacity: 1;
transition: opacity 1s;
animation: fade 1s;
"
v-if="!collapsed"
>景上出行</span>
2025-04-14 10:57:27 +08:00
</div>
<a-menu
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
theme="dark"
:items="items"
@click="handleClick"
/>
</a-layout-sider>
<a-layout>
<a-layout-header style="background: #fff; padding: 0">
<!-- 菜单折叠 -->
<menu-unfold-outlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<menu-fold-outlined
v-else
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<!-- <a-button
type="primary"
@click="goBack"
>
返回
</a-button> -->
<!-- 登录 -->
<div class="topAvatar">
2025-04-27 18:22:46 +08:00
<template v-if="userdfrname != '' && username != 'admin'">
<a-dropdown>
<a class="ant-dropdown-link" @click.prevent>
当前区域{{ userdfrname }} <DownOutlined />
</a>
<template #overlay>
<a-menu @click="onOperationClick">
<a-menu-item v-for="(item) in useroperation" :key="item.regionId" :data="item">{{ item.regionName }}</a-menu-item>
</a-menu>
</template>
</a-dropdown>
2025-04-23 18:08:45 +08:00
<a-divider type="vertical" />
<UserOutlined style="margin: 0px 5px;" />
</template>
2025-04-14 10:57:27 +08:00
<a-dropdown>
<a
class="ant-dropdown-link"
@click.prevent
>
{{ username }}
<DownOutlined />
</a>
<template #overlay>
<a-menu>
<!-- <a-menu-item>
<a href="javascript:;">个人中心</a>
</a-menu-item> -->
<a-popconfirm
placement="topRight"
@confirm="confirm"
@cancel="cancel"
title="确认退出吗?"
confirm-button-text="确认"
cancel-button-text="取消"
ok-text="是"
cancel-text="否"
>
<a-menu-item v-if="isLogin">退出登录</a-menu-item>
</a-popconfirm>
<a-menu-item
v-if="!isLogin"
@click="$router.push('/login')"
>去登录</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</a-layout-header>
<a-layout-content :style="{ margin: '24px 16px', padding: '24px', background: '#fff', minHeight: '280px' }">
<!-- 二级路由出口(home等等通常放在main主区域) -->
<router-view />
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup>
import {
MenuFoldOutlined,
MenuUnfoldOutlined
} from '@ant-design/icons-vue'
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { message } from 'ant-design-vue'
import { getMenus } from '../../pages';
import { getLogoutAPI } from '@/apis/login.js'
import { getCache, setCache, clearStorage } from '@/utils/authority';
2025-04-14 10:57:27 +08:00
const selectedKeys = ref(['1'])
const openKeys = ref(['1'])
const collapsed = ref(false)
const items = ref([])
2025-04-14 10:57:27 +08:00
//路由切换
const router = useRouter()
// 处理页面刷新之后高亮菜单和显示的组件不一致的情况
// 登录状态
const isLogin = ref(false)
const username = ref('')
const useroperation = ref([])
const userdfrname = ref('')
2025-04-14 10:57:27 +08:00
onMounted(() => {
isLogin.value = getCache('ebike-token') ? true : false
//跳转到登录页面
if (!isLogin.value) {
router.push('/login');
return;
}
console.log('isLogin', isLogin.value)
// 获取用户名
const userinfo = getCache('ebike-userinfo');
console.log('userinfo', userinfo)
username.value = userinfo.username ? userinfo.username : 'X'
//获取用户区域
const cuseroperation = getCache('ebike-useroperation');
if(cuseroperation){
useroperation.value = cuseroperation;
}
//获取默认区域
const cuserdefultoperation = getCache('ebike-userdefultoperation');
if(cuserdefultoperation){
userdfrname.value = cuserdefultoperation.regionName ? cuserdefultoperation.regionName : '';
2025-04-23 18:08:45 +08:00
}
const menus = getMenus()
items.value = menus
2025-04-14 10:57:27 +08:00
const route = useRoute()
let path = route.path;
const nodes = findNodeWithParents(items.value, 'path', path);
if (nodes.length > 0) {
const lastNode = nodes[nodes.length - 1];
selectedKeys.value = [lastNode.key];
openKeys.value = nodes.slice(1).map(node => node.key);
}
})
const handleClick = (e) => {
const nodes = findNodeWithParents(items.value, 'key', e.key);
if (nodes.length > 0) {
const lastNode = nodes[nodes.length - 1];
router.push(lastNode.path);
}
}
const findNodeWithParents = (obj, targetKey, targetValue, parents = []) => {
for (const [key, value] of Object.entries(obj)) {
if (key === targetKey && value === targetValue) {
return [...parents, obj];
}
if (typeof value === 'object' && value !== null) {
const result = findNodeWithParents(value, targetKey, targetValue, [...parents, obj]);
if (result.length > 0) {
return result;
}
}
}
return [];
}
const confirm = async (e) => {
const res = await getLogoutAPI()
if (res.code != 200) {
message.error(res.message)
return
}
clearStorage();
router.push('/login');
2025-04-14 10:57:27 +08:00
return
}
const cancel = (e) => {
console.log(e)
return
}
const onOperationClick = (e) => {
if(e.item && e.item.data){
const regionName = e.item.data.regionName;
userdfrname.value = regionName;
//存储默认区域
setCache('ebike-userdefultoperation', e.item.data);
}
2025-04-14 10:57:27 +08:00
}
</script>
<style>
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
/*background: rgba(255, 255, 255, 0.3);*/
margin: 16px;
color: #fff;
display: flex;
gap: 10px;
align-items: center;
padding-left: 10px;
}
.topAvatar {
font-size: 14px;
2025-04-14 10:57:27 +08:00
float: right;
margin-right: 40px;
div {
display: inline-block;
}
.el-dropdown-link {
color: #fff;
}
.el-avatar {
margin: 0 20px;
}
.logoutLink {
text-decoration: none;
}
}
@keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>