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"
|
2025-04-27 15:46:17 +08:00
|
|
|
|
>景上出行</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'">
|
2025-04-27 15:46:17 +08:00
|
|
|
|
<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';
|
2025-04-17 18:31:22 +08:00
|
|
|
|
import { getLogoutAPI } from '@/apis/login.js'
|
2025-04-27 15:46:17 +08:00
|
|
|
|
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)
|
2025-04-17 18:31:22 +08:00
|
|
|
|
|
|
|
|
|
|
const items = ref([])
|
2025-04-14 10:57:27 +08:00
|
|
|
|
//路由切换
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
// 处理页面刷新之后高亮菜单和显示的组件不一致的情况
|
2025-04-17 18:31:22 +08:00
|
|
|
|
|
|
|
|
|
|
// 登录状态
|
|
|
|
|
|
const isLogin = ref(false)
|
|
|
|
|
|
const username = ref('')
|
2025-04-27 15:46:17 +08:00
|
|
|
|
const useroperation = ref([])
|
2025-04-21 14:20:20 +08:00
|
|
|
|
const userdfrname = ref('')
|
2025-04-17 18:31:22 +08:00
|
|
|
|
|
2025-04-14 10:57:27 +08:00
|
|
|
|
onMounted(() => {
|
2025-04-17 18:31:22 +08:00
|
|
|
|
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)
|
2025-04-21 14:20:20 +08:00
|
|
|
|
username.value = userinfo.username ? userinfo.username : 'X'
|
|
|
|
|
|
|
2025-04-27 15:46:17 +08:00
|
|
|
|
//获取用户区域
|
|
|
|
|
|
const cuseroperation = getCache('ebike-useroperation');
|
|
|
|
|
|
if(cuseroperation){
|
|
|
|
|
|
useroperation.value = cuseroperation;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-21 14:20:20 +08:00
|
|
|
|
//获取默认区域
|
2025-04-27 15:46:17 +08:00
|
|
|
|
const cuserdefultoperation = getCache('ebike-userdefultoperation');
|
|
|
|
|
|
if(cuserdefultoperation){
|
|
|
|
|
|
userdfrname.value = cuserdefultoperation.regionName ? cuserdefultoperation.regionName : '';
|
2025-04-23 18:08:45 +08:00
|
|
|
|
}
|
2025-04-17 18:31:22 +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) => {
|
2025-04-17 18:31:22 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-27 15:46:17 +08:00
|
|
|
|
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 {
|
2025-04-21 14:20:20 +08:00
|
|
|
|
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>
|