修改layouts插件
This commit is contained in:
parent
7ef85d5361
commit
fd3668c7bf
|
|
@ -0,0 +1,49 @@
|
|||
<!--
|
||||
* @MoudelName: 模块名称
|
||||
* @Company: 湖南xx科技有限公司
|
||||
* @Author: LJ
|
||||
* @Date: 2024-06-26 16:33:03
|
||||
-->
|
||||
<!-- 模块 -->
|
||||
<template>
|
||||
<div class="layout-content" :class="tabHeight ? 'tabHeight' : ''">
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<keep-alive :exclude="['addressListHome', 'CviIndex', 'Zbzxzbtj','Qxzxjsgl','Qxzxyyjsgl']">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue'
|
||||
const props = defineProps({
|
||||
tabHeight: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabHeight {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!-- 模块 -->
|
||||
<template>
|
||||
<div class="content-container">
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<transition appear :name="route.meta.transition">
|
||||
<!-- 这里暂时调整为所有页面都缓存 -->
|
||||
<keep-alive>
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</RouterView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.cvi-tabs + & {
|
||||
--cvi-tabs-height: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<template>
|
||||
<div class="notification-bell">
|
||||
<el-badge :value="unreadCount" :max="99" :hidden="unreadCount === 0" class="item" @click="handleClick">
|
||||
<i class="iconfont icon-yjpt_lxyj" title="消息通知" style="color: #ADD8E6; font-size: 20px;"></i>
|
||||
</el-badge>
|
||||
|
||||
<!-- 消息下拉面板 -->
|
||||
<el-popover v-model:visible="popoverVisible" placement="bottom-end" :width="250" trigger="click">
|
||||
<template #reference>
|
||||
<div style="display: inline-block"></div>
|
||||
</template>
|
||||
|
||||
<div class="notification-panel">
|
||||
<el-scrollbar max-height="300px">
|
||||
<div v-for="(msg, index) in messages" :key="index" class="message-item" :class="{ 'unread': !msg.read }"
|
||||
@click="readMessage(msg)">
|
||||
<div class="message-content">
|
||||
<div class="message-title">{{ msg.content }}</div>
|
||||
<div class="message-time">{{ msg.createTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="messages.length === 0" description="暂无消息"/>
|
||||
</el-scrollbar>
|
||||
<!-- <div class="panel-footer">-->
|
||||
<!-- <el-button :disabled="unreadCount === 0" type="text" @click="viewAll">查看全部</el-button>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import {onMounted, onUnmounted, reactive, toRefs} from 'vue'
|
||||
import notifications from "@/api/lawenforcement/Notification.js"
|
||||
import messagesApi from "@/api/lawenforcement/Message.js"
|
||||
import { useRouter } from 'vue-router'
|
||||
const route = useRouter()
|
||||
|
||||
const state = reactive({
|
||||
apiConfig: {
|
||||
api: notifications
|
||||
},
|
||||
unreadCount: 0,
|
||||
messages: [],
|
||||
popoverVisible: false
|
||||
})
|
||||
|
||||
const {unreadCount, messages, popoverVisible} = toRefs(state)
|
||||
|
||||
// 点击图标
|
||||
const handleClick = () => {
|
||||
popoverVisible.value = !popoverVisible.value
|
||||
}
|
||||
|
||||
// 查看全部
|
||||
const viewAll = () => {
|
||||
popoverVisible.value = false
|
||||
}
|
||||
|
||||
let timer = null
|
||||
const getMsg = () => {
|
||||
messagesApi.getUserMessages({status: 'UNREAD', page: 0, size: 100}).then(rs => {
|
||||
messages.value = rs.data
|
||||
unreadCount.value = rs.data.length
|
||||
})
|
||||
}
|
||||
|
||||
function readMessage(msg) {
|
||||
messagesApi.updateMessageStatus(msg.id, {status: 'READ'}).then(rs => {
|
||||
if (rs.success) {
|
||||
getMsg()
|
||||
}
|
||||
})
|
||||
route.push(msg.routeUrl)
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getMsg()
|
||||
timer = setInterval(getMsg, 60 * 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notification-bell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-bell .el-icon {
|
||||
color: #606266;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.notification-bell .el-icon:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.notification-panel {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
|
||||
.message-item {
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
transition: background-color 0.3s;
|
||||
color: #373E52;
|
||||
}
|
||||
|
||||
.message-item.unread {
|
||||
background-color: #f6faff;
|
||||
}
|
||||
|
||||
.message-item:hover {
|
||||
cursor: pointer;
|
||||
background-color: #3C86FF;
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<div>表头</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
onMounted(() => { })
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped></style>
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
<template>
|
||||
<div class="header-container">
|
||||
<div class="header-left" :class="{'header-height': projectName != 'gzt'}">
|
||||
<img class="logo" :src="logo" alt=""/>
|
||||
<div class="header-title">
|
||||
<div class="main-title">{{ titleMain || '西安智慧应急' }}</div>
|
||||
<template v-if="subTitle">
|
||||
<div class="subtitle">{{ subTitle }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-conten" v-if="projectName == 'gzt'">
|
||||
<el-dropdown v-for="(item, index) in MyApplocation" :key="index" size="large">
|
||||
<span class="el-dropdown-link">
|
||||
<span class="profile-name fs-base">{{ item.name }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu :class="`menuTop-list${index} menuTop-public`">
|
||||
<template v-for="(childItem, childIndex) in item.list">
|
||||
<div :class="`menuTop-list${index}-item menuTop-public-item`" @click="handleGO(childItem)">
|
||||
<img :src="childItem.avatar" alt=""/>
|
||||
<div class="menuTop-item-text ccw fs-base" :title="childItem.name">{{ childItem.name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="menu-conten" v-if="topMenuList[projectName].length">
|
||||
<div class="menu-conten-item" v-for="(item, index) in topMenuList[projectName]" :key="index" :title="item.name"
|
||||
@click="handleTo(item)" :class="{ isActive: item.path == topActive }">
|
||||
<span>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-profile">
|
||||
<span class="zyt" @click="skipPage"><i>综合执法一张图</i></span>
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar :src="userAvatar" class="profile-avatar">
|
||||
{{ username.slice(0, 1) }}
|
||||
</el-avatar>
|
||||
<span class="profile-name fs-base">{{ username }}</span>
|
||||
</span>
|
||||
<i class="iconfont icon-zngzt_zfdc" v-if="projectName == 'gzt'" title="编辑桌面"></i>
|
||||
<i class="iconfont icon-zngzt_syfw" v-if="projectName != 'gzt'" @click="handleTo({ path: 'gzt' })" title="首页"></i>
|
||||
<NotificationBell/>
|
||||
<public-full-screen></public-full-screen>
|
||||
<i class="iconfont icon-zngzt_tcdl" @click="handleLogout" title="退出"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {onMounted, reactive, toRefs, watch} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
import {useUserStore} from '@/stores/modules/user'
|
||||
import {ElLoading, ElMessage} from 'element-plus'
|
||||
import PublicFullScreen from '@/components/PublicFullScreen/index.vue'
|
||||
import NotificationBell from './NotificationBell.vue'
|
||||
|
||||
import logo from '@/assets/image/logo.png'
|
||||
import {useRouteStore} from '@/stores/modules/router'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const routerStore = useRouteStore()
|
||||
const currentPageURL = window.location.href
|
||||
const urlParts = currentPageURL.split('/')
|
||||
const projectName = urlParts[3]
|
||||
const baseURL = import.meta.env.VITE_BASE_URL
|
||||
|
||||
const state = reactive({
|
||||
topActive: '',
|
||||
topMenuList: userStore.topMenu,
|
||||
titleMain: import.meta.env.VITE_APP_TITLE,
|
||||
subTitle: import.meta.env.VITE_APP_SUB_TITLE,
|
||||
version: import.meta.env.VITE_APP_VERSION,
|
||||
MyApplocation: userStore.myAppLocations,
|
||||
username: userStore.username,
|
||||
userAvatar: userStore.userAvatar
|
||||
})
|
||||
|
||||
const {topActive, topMenuList, titleMain, subTitle, version, MyApplocation, username, userAvatar} = toRefs(state)
|
||||
|
||||
const handleGO = (data) => {
|
||||
console.log(data)
|
||||
if (data.isTrue) {
|
||||
window.open(data.url)
|
||||
} else if (data.isImg) {
|
||||
userStore.imageIndex = data.url
|
||||
window.open(data.url)
|
||||
} else if (data.router) {
|
||||
router.push({
|
||||
path: data.url,
|
||||
})
|
||||
} else {
|
||||
ElMessage.error('对不起,您没有该应用权限。')
|
||||
}
|
||||
}
|
||||
|
||||
const handleTo = (data) => {
|
||||
topActive.value = data.path
|
||||
if (data.router) {
|
||||
router.push({
|
||||
path: data.path,
|
||||
})
|
||||
} else {
|
||||
window.open(`${baseURL}/${data.path}/index.html#/`)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = () => {
|
||||
userStore.isDraggable = true
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await userStore.logout()
|
||||
}
|
||||
|
||||
const skipPage = () => {
|
||||
router.push('/big-screen')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(val) => {
|
||||
if (val) {
|
||||
topActive.value = router.currentRoute.value.path
|
||||
}
|
||||
},
|
||||
{immediate: true}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
window.document.documentElement.setAttribute('data-theme', 'whiteblueTheme')
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.menuTop-public {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 896px;
|
||||
padding: 15px !important;
|
||||
|
||||
.menuTop-public-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(25% - 56px) !important;
|
||||
height: 45px;
|
||||
padding: 16px;
|
||||
margin: 12px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.MyApplocation-item-text {
|
||||
margin-left: 16px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* .menuTop-list0 {
|
||||
width: 896px;
|
||||
.menuTop-list0-item {
|
||||
width: calc(25% - 56px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menuTop-list4 {
|
||||
width: 1120px;
|
||||
flex-wrap: nowrap;
|
||||
.menuTop-list4-item {
|
||||
width: calc(20% - 56px) !important;
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.header-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
.header-left {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 100%;
|
||||
|
||||
.logo {
|
||||
height: 30px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
height: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
font-size: 24px;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-family: 'AlimamaShuHeiTi-Bold';
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-left: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
margin-top: 0px; /* 减小与主标题的间距 */
|
||||
text-align: center;
|
||||
line-height: 1; /* 减小行高 */
|
||||
}
|
||||
|
||||
.main-title {
|
||||
text-align: center;
|
||||
line-height: 1.2; /* 减小行高 */
|
||||
margin-bottom: 0; /* 移除底部边距 */
|
||||
padding-bottom: 0; /* 移除底部内边距 */
|
||||
}
|
||||
|
||||
.logo-right {
|
||||
position: absolute;
|
||||
right: -15px;
|
||||
bottom: 6px;
|
||||
width: 58px;
|
||||
height: 10px;
|
||||
background-image: url('@/assets/image/logobg.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-conten {
|
||||
width: calc(85% - 460px);
|
||||
display: flex;
|
||||
margin-left: 50px;
|
||||
color: #fff;
|
||||
|
||||
.el-dropdown {
|
||||
align-items: center;
|
||||
padding: 0 30px;
|
||||
|
||||
.el-dropdown-link {
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-conten-item {
|
||||
// min-width: 80px;
|
||||
min-width: auto;
|
||||
padding: 0 18px;
|
||||
line-height: 56px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 2px;
|
||||
|
||||
span {
|
||||
/* 使用flex布局确保文本居中 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 为悬浮和选中状态准备的样式 */
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to top, rgba(255, 255, 255, 0.4), transparent) !important;
|
||||
color: #fff !important;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
font-family: 'OPlusSans3-Bold';
|
||||
}
|
||||
|
||||
/* 悬浮时显示底部装饰线 */
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 15%;
|
||||
width: 70%;
|
||||
height: 4px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-conten-item.isActive {
|
||||
background: linear-gradient(to top, rgba(255, 255, 255, 0.4), transparent) !important;
|
||||
color: #fff !important;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
font-family: 'OPlusSans3-Bold';
|
||||
}
|
||||
|
||||
/* 选中状态显示底部装饰线 */
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 15%;
|
||||
width: 70%;
|
||||
height: 4px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-profile {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
width: 26%;
|
||||
height: 100%;
|
||||
|
||||
.profile-name {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: auto;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.system {
|
||||
margin-right: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
> .iconfont {
|
||||
margin-right: 14px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 22px;
|
||||
color: #ADD8E6; // 修改图标颜色为淡蓝色
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.right-profile .zyt {
|
||||
display: flex;
|
||||
margin: 6px 64px 0px 0px;
|
||||
width: 207px;
|
||||
height: 43px;
|
||||
background-image: url('@/assets/image/yztan.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.right-profile .zyt i {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
transform: none;
|
||||
justify-content: center;
|
||||
line-height: 40px;
|
||||
position: absolute;
|
||||
left: 6.5%;
|
||||
}
|
||||
|
||||
.right-profile .el-dropdown-link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
<!--
|
||||
* @MoudelName: 基本视图
|
||||
* @Company: 湖南xx科技有限公司
|
||||
* @Author: LJ
|
||||
* @Date: 2024-04-08 17:27:06
|
||||
-->
|
||||
<template>
|
||||
<div class="cvi-layout-container">
|
||||
<el-header>
|
||||
<Header />
|
||||
</el-header>
|
||||
<el-main
|
||||
v-if="
|
||||
projectName == 'tyrz' ||
|
||||
projectName == 'zbzx' ||
|
||||
projectName == 'rwzx' ||
|
||||
projectName == 'jqzx' ||
|
||||
projectName == 'zhgl' ||
|
||||
projectName == 'zdry' ||
|
||||
projectName == 'jcjcgk' ||
|
||||
projectName == 'zygl'
|
||||
"
|
||||
>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
background-color="#545c64"
|
||||
text-color="#ffffff"
|
||||
active-text-color="#ffffff"
|
||||
:unique-opened="true"
|
||||
:default-openeds="defaultOpends"
|
||||
>
|
||||
<template v-for="item in allMenuData[projectName]">
|
||||
<MenuItem v-if="!item.hidden" @select="handleSelect" :key="item.gncdbh" :item="item" />
|
||||
</template>
|
||||
</el-menu>
|
||||
<div class="cvi-left">
|
||||
<ElTabs :tabsdata="tabsData" @selected-data="handleTabClick" @tab-remove="removeTab"></ElTabs>
|
||||
<Content :tabHeight="true" />
|
||||
</div>
|
||||
</el-main>
|
||||
<el-main v-else>
|
||||
<Content />
|
||||
</el-main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, defineEmits, defineComponent } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import { useRouteStore } from '@/stores/modules/router'
|
||||
import MenuItem from '@/components/ElMenu/index.vue'
|
||||
import ElTabs from '@/components/ElTabs/index.vue'
|
||||
import Header from './header/index-cvi.vue'
|
||||
import Content from './content/index-cvi.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const routerStore = useRouteStore()
|
||||
|
||||
const allMenuData = routerStore.allMenuData
|
||||
const currentPageURL = window.location.href
|
||||
const urlParts = currentPageURL.split('/')
|
||||
const projectName = urlParts[3]
|
||||
const activeMenu = ref('')
|
||||
const tabsData = ref([])
|
||||
const defaultOpends = ref([])
|
||||
|
||||
const handleSelect = (item) => {
|
||||
activeMenu.value = item.path
|
||||
userStore.frameUrl = item.frameUrl
|
||||
router.push({ path: item.path, query: { frameUrl: item.frameUrl, label: item.label } })
|
||||
const selectedItem = findItemByRoute(allMenuData[projectName], item.path)
|
||||
addTab(selectedItem)
|
||||
}
|
||||
const findItemByRoute = (items, path) => {
|
||||
for (const item of items) {
|
||||
if (item.path === path) {
|
||||
return item
|
||||
}
|
||||
if (item.children) {
|
||||
const found = findItemByRoute(item.children, path)
|
||||
if (found) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
const addTab = (item, isActive) => {
|
||||
const existingTab = tabsData.value.find((tab) => tab.path === item.path)
|
||||
if (!existingTab) {
|
||||
if (tabsData.value.length >= 10) {
|
||||
tabsData.value.splice(0, 1)
|
||||
}
|
||||
tabsData.value.push({
|
||||
path: item.path,
|
||||
label: item.label,
|
||||
name: item.name,
|
||||
icon: item.icon,
|
||||
noClosable: item.noClosable,
|
||||
frameUrl: item.frameUrl,
|
||||
query: item.query,
|
||||
})
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
if (isActive) return
|
||||
activeMenu.value = item.path
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
} else {
|
||||
activeMenu.value = item.path
|
||||
routerStore.setCurrentRoute(item.path)
|
||||
}
|
||||
}
|
||||
const handleTabClick = (data) => {
|
||||
const currentRoute = tabsData.value.find((item) => item.path == data.props.name)
|
||||
activeMenu.value = data.props.name
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
router.push({ path: data.props.name, query: currentRoute.query })
|
||||
}
|
||||
|
||||
const removeTab = (name, type) => {
|
||||
const index = tabsData.value.findIndex((item) => item.path === name)
|
||||
if (index !== -1) {
|
||||
if (type == 'current') {
|
||||
if (tabsData.value.length > 1) {
|
||||
tabsData.value.splice(index, 1)
|
||||
if (activeMenu.value === name) {
|
||||
activeMenu.value = tabsData.value.length > 0 ? tabsData.value[tabsData.value.length - 1].path : ''
|
||||
const currentRoute = tabsData.value.find((item) => item.path == activeMenu.value)
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
router.push({
|
||||
path: activeMenu.value,
|
||||
query: { frameUrl: currentRoute.frameUrl, label: currentRoute.label },
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = tabsData.value.length - 1; i >= 0; i--) {
|
||||
if (tabsData.value[i].path !== name && !tabsData.value[i].noClosable) {
|
||||
tabsData.value.splice(i, 1)
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initNoClosableTabs = (routes) => {
|
||||
routes.forEach((route) => {
|
||||
if (route.noClosable) addTab(route, true)
|
||||
if (route.children) initNoClosableTabs(route.children)
|
||||
})
|
||||
}
|
||||
|
||||
const initTags = () => {
|
||||
activeMenu.value = router.currentRoute.value.path
|
||||
routerStore.setCurrentRoute(router.currentRoute.value.path)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
initTags()
|
||||
const projectNameArr = ['tyrz', 'rwzx', 'jqzx', 'zhgl', 'zdry', 'zygl']
|
||||
if (projectNameArr.indexOf(projectName) != -1) {
|
||||
activeMenu.value = routerStore.currentRoute
|
||||
tabsData.value = routerStore.visitedRoutes
|
||||
}
|
||||
if (projectName == 'zbzx') {
|
||||
initNoClosableTabs(allMenuData[projectName])
|
||||
activeMenu.value = allMenuData[projectName][0].children[1].path
|
||||
activeMenu.value = routerStore.currentRoute
|
||||
tabsData.value = routerStore.visitedRoutes
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cvi-layout-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 100% 100%;
|
||||
transition: all 0.4s;
|
||||
|
||||
.el-header {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: calc(100% - 56px);
|
||||
padding: 0;
|
||||
|
||||
.cvi-left {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cvi-left::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.newHeight {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
<template>
|
||||
<div class="layout-container">
|
||||
<el-header>
|
||||
<Header />
|
||||
</el-header>
|
||||
<el-main v-if="menuList.length">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
|
||||
|
||||
:unique-opened="true"
|
||||
:default-openeds="defaultOpends"
|
||||
>
|
||||
<el-scrollbar class="menu-scrollbar" height="calc(100% - 80px)" :min-size="0.2">
|
||||
<template v-for="item in menuList">
|
||||
<MenuItem v-if="!item.hidden" @select="handleSelect" :key="item.gncdbh" :item="item" />
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</el-menu>
|
||||
|
||||
<div class="cvi-left svi-new-card-public">
|
||||
<!-- <ElTabs :tabsdata="tabsData" @selected-data="handleTabClick" @tab-remove="removeTab"></ElTabs> -->
|
||||
<Content :tabHeight="true" />
|
||||
</div>
|
||||
</el-main>
|
||||
<el-main v-else>
|
||||
<Content class="svi"/>
|
||||
</el-main>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref, watch, reactive, toRefs } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import { useRouteStore } from '@/stores/modules/router'
|
||||
|
||||
import Header from './header/index-svi.vue'
|
||||
import Content from './content/index-svi.vue'
|
||||
import MenuItem from '@/components/ElMenu/index.vue'
|
||||
import ElTabs from '@/components/ElTabs/index.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const routerStore = useRouteStore()
|
||||
|
||||
const allMenuData = userStore.allMenus
|
||||
console.log(allMenuData, 'allMenuData')
|
||||
const currentPageURL = window.location.href
|
||||
const urlParts = currentPageURL.split('/')
|
||||
let projectName = urlParts[3]
|
||||
const state = reactive({
|
||||
activeMenu: '',
|
||||
routerName: '',
|
||||
currentProjectName: '',
|
||||
tabsData: [],
|
||||
menuList: [],
|
||||
defaultOpends: [],
|
||||
})
|
||||
|
||||
const { activeMenu, routerName, currentProjectName, menuList, tabsData, defaultOpends } = toRefs(state)
|
||||
|
||||
const handleSelect = (item) => {
|
||||
activeMenu.value = item.path
|
||||
userStore.frameUrl = item.frameUrl
|
||||
router.push({ path: item.path, query: { frameUrl: item.frameUrl, label: item.label } })
|
||||
// const selectedItem = findItemByRoute(allMenuData[projectName], item.path)
|
||||
// 临时处理显示出完整效果,修复标签栏显示。 todo 等后续对接实际用户登录信息生成具体的数据时在做调整。
|
||||
const items = Object.values(allMenuData[projectName]).reduce((acc, value) => {
|
||||
return acc.concat(Array.isArray(value) ? value : [value])
|
||||
}, [])
|
||||
const selectedItem = findItemByRoute(items, item.path)
|
||||
addTab(selectedItem)
|
||||
}
|
||||
const findItemByRoute = (items, path) => {
|
||||
console.log(items, path)
|
||||
|
||||
for (const item of items) {
|
||||
if (item.path === path) {
|
||||
return item
|
||||
}
|
||||
if (item.children) {
|
||||
const found = findItemByRoute(item.children, path)
|
||||
if (found) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
const addTab = (item, isActive) => {
|
||||
const existingTab = tabsData.value.find((tab) => tab.path === item.path)
|
||||
if (!existingTab) {
|
||||
if (tabsData.value.length >= 10) {
|
||||
tabsData.value.splice(0, 1)
|
||||
}
|
||||
tabsData.value.push({
|
||||
path: item.path,
|
||||
label: item.label,
|
||||
name: item.name,
|
||||
icon: item.icon,
|
||||
noClosable: item.noClosable,
|
||||
frameUrl: item.frameUrl,
|
||||
query: item.query,
|
||||
})
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
if (isActive) return
|
||||
activeMenu.value = item.path
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
} else {
|
||||
activeMenu.value = item.path
|
||||
routerStore.setCurrentRoute(item.path)
|
||||
}
|
||||
}
|
||||
const handleTabClick = (data) => {
|
||||
const currentRoute = tabsData.value.find((item) => item.path == data.props.name)
|
||||
activeMenu.value = data.props.name
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
router.push({ path: data.props.name, query: currentRoute.query })
|
||||
}
|
||||
|
||||
const removeTab = (name, type) => {
|
||||
const index = tabsData.value.findIndex((item) => item.path === name)
|
||||
if (index !== -1) {
|
||||
if (type == 'current') {
|
||||
if (tabsData.value.length > 1) {
|
||||
tabsData.value.splice(index, 1)
|
||||
if (activeMenu.value === name) {
|
||||
activeMenu.value = tabsData.value.length > 0 ? tabsData.value[tabsData.value.length - 1].path : ''
|
||||
const currentRoute = tabsData.value.find((item) => item.path == activeMenu.value)
|
||||
routerStore.setCurrentRoute(activeMenu.value)
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
router.push({
|
||||
path: activeMenu.value,
|
||||
query: { frameUrl: currentRoute.frameUrl, label: currentRoute.label },
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = tabsData.value.length - 1; i >= 0; i--) {
|
||||
if (tabsData.value[i].path !== name && !tabsData.value[i].noClosable) {
|
||||
tabsData.value.splice(i, 1)
|
||||
routerStore.setVisitedRoutes(tabsData.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initNoClosableTabs = (routes) => {
|
||||
routes.forEach((route) => {
|
||||
if (route.noClosable) addTab(route, true)
|
||||
if (route.children) initNoClosableTabs(route.children)
|
||||
})
|
||||
}
|
||||
|
||||
const initTags = () => {
|
||||
activeMenu.value = router.currentRoute.value.path
|
||||
tabsData.value = routerStore.visitedRoutes
|
||||
routerStore.setCurrentRoute(router.currentRoute.value.path)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(val) => {
|
||||
if (val) {
|
||||
const projectNameArr = ['kfpt', 'yjzy', 'gmzx', 'zhzf']
|
||||
let currentPath = router.currentRoute.value.path
|
||||
const routeArr = currentPath.split('/')
|
||||
if (projectNameArr.indexOf(projectName) != -1 && currentProjectName.value != routeArr[1]) {
|
||||
initTags()
|
||||
tabsData.value = []
|
||||
initNoClosableTabs(allMenuData[projectName][routeArr[1]])
|
||||
activeMenu.value = router.currentRoute.value.path
|
||||
menuList.value = allMenuData[projectName][routeArr[1]]
|
||||
routerName.value = routeArr[1]
|
||||
currentProjectName.value = routeArr[1]
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
initTags()
|
||||
const projectNameArr = ['kfpt', 'yjzy', 'gmzx', 'zhzf']
|
||||
if (projectNameArr.indexOf(projectName) != -1) {
|
||||
let currentPath = router.currentRoute.value.path
|
||||
console.log(currentPath)
|
||||
const routeArr = currentPath.split('/')
|
||||
console.log(routeArr)
|
||||
initNoClosableTabs(allMenuData[projectName][routeArr[1]])
|
||||
activeMenu.value = router.currentRoute.value.path
|
||||
menuList.value = allMenuData[projectName][routeArr[1]]
|
||||
routerName.value = routeArr[1]
|
||||
currentProjectName.value = routeArr[1]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-dropdown {
|
||||
color: #fff !important;
|
||||
|
||||
.iconfont {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-container {
|
||||
--el-menu-width: 256px;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.el-header {
|
||||
height: 64px;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
width: 100%;
|
||||
height: calc(100%);
|
||||
padding: 0;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
margin-right: 0;
|
||||
|
||||
.svi-new-card-public {
|
||||
margin: 40px 20px 40px 20px;
|
||||
padding: 10px 15px 10px 15px;
|
||||
height: calc(100%);
|
||||
box-sizing: border-box;
|
||||
border-radius: 0.25rem;
|
||||
border-radius: 1.125rem;
|
||||
border: 0.0625rem solid #a9c7da;
|
||||
background: #ffffff38;
|
||||
backdrop-filter: blur(1rem);
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
width: var(--el-menu-width);
|
||||
flex: 0 0 var(--el-menu-width);
|
||||
height: calc(100%);
|
||||
padding: 40px 8px 40px 0;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
border-left: 0;
|
||||
// border-radius: 0 1.125rem 1.125rem 0;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
:deep .el-menu {
|
||||
width: var(--el-menu-width);
|
||||
--el-menu-text-color: #fff !important;
|
||||
--el-menu-hover-text-color: #fff !important;
|
||||
--el-menu-bg-color: transparent !important;
|
||||
}
|
||||
|
||||
/* 确保子菜单和父菜单宽度一致 */
|
||||
:deep(.el-sub-menu) {
|
||||
width: calc(var(--el-menu-width) - 8px - var(--el-border-width)) !important;
|
||||
|
||||
.el-sub-menu__icon-arrow {
|
||||
width: 14px !important;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.el-menu.el-menu--inline {
|
||||
--el-menu-text-color: rgba(255, 255, 255, 0.6) !important;
|
||||
--el-menu-hover-text-color: rgba(255, 255, 255, 0.6) !important;
|
||||
|
||||
width: calc(var(--el-menu-width) - 8px - var(--el-border-width));
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.cvi-left {
|
||||
flex: 1;
|
||||
height: calc(100% - 130px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; /* 防止内容溢出 */
|
||||
}
|
||||
|
||||
.cvi-left::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.svi {
|
||||
flex: 1;
|
||||
margin: 40px 20px 40px 20px;
|
||||
padding: 10px 15px 10px 15px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue