first commit
This commit is contained in:
@@ -0,0 +1,548 @@
|
||||
<!--#
|
||||
layout("/layouts/v4/baseLayout.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.sub-app-container {
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
min-height: calc(100vh - 64px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
width: 250px;
|
||||
background: rgb(35 85 120);
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
height: calc(100vh - 64px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
padding: 20px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-scrollbar {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-scrollbar .el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-submenu__title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-submenu__title i,
|
||||
#sidebar-menu .el-menu .el-menu-item i {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-submenu__title:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-menu-item {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-menu-item.is-active {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-menu-item:focus,
|
||||
.el-menu-item:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#sidebar-menu .el-menu .el-menu-item a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sub-app-container-main-content {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
.content-header {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content-subtitle {
|
||||
color: #7f8c8d;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.content-body {
|
||||
min-height: 400px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
/* 悬浮菜单按钮样式 */
|
||||
.menu-toggle-btn {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(64 109 157);
|
||||
color: white;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-toggle-btn:hover {
|
||||
background-color: rgb(54 99 147);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.menu-toggle-btn.active {
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
/* 菜单遮罩层样式 */
|
||||
.menu-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-menu {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
left: -100%;
|
||||
width: 40%;
|
||||
z-index: 999;
|
||||
transition: left 0.3s ease;
|
||||
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.sidebar-menu.show {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.menu-toggle-btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu-overlay.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sub-app-container" id="sub-app-container">
|
||||
<!-- 菜单遮罩层 -->
|
||||
<div class="menu-overlay" id="menu-overlay"></div>
|
||||
|
||||
<!-- 左侧菜单区域 -->
|
||||
<nav class="sidebar-menu" id="sidebar-menu">
|
||||
<div class="menu-header">
|
||||
<h2 class="menu-title">子系统</h2>
|
||||
</div>
|
||||
|
||||
<el-scrollbar>
|
||||
<el-menu unique-opened :default-active="activeMenuIndex" :default-openeds="openedMenus"
|
||||
@select="menuSelect">
|
||||
<menu-list :menus="leftMenus" :depth="0"></menu-list>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</nav>
|
||||
|
||||
<!-- 右侧主体区域 -->
|
||||
<main class="sub-app-container-main-content" id="sub-app-container-main-content">
|
||||
<div class="content-body" id="sub-app-container-main-content-body">${layoutContent!}</div>
|
||||
</main>
|
||||
|
||||
<!-- 悬浮菜单按钮 -->
|
||||
<div class="menu-toggle-btn" id="menu-toggle-btn">
|
||||
<i class="fa fa-bars"></i>
|
||||
</div>
|
||||
|
||||
<!-- 子应用ID -->
|
||||
<div id="sub-app-id" style="display: none"></div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
// 初始化pjax加载
|
||||
;(function initPjax() {
|
||||
$(document).pjax("#sidebar-menu a[data-pjax]", "#sub-app-container-main-content-body", {
|
||||
maxCacheLength: 0,
|
||||
push: true,
|
||||
replace: false,
|
||||
fragment: "#sub-app-container-main-content-body",
|
||||
timeout: 8000
|
||||
})
|
||||
|
||||
$(document).on("pjax:send", function () {
|
||||
NProgress.configure({parent: ".sub-app-container-main-content"})
|
||||
NProgress.start()
|
||||
$("#sub-app-container-main-content").hide()
|
||||
})
|
||||
|
||||
$(document).on("pjax:complete", function () {
|
||||
NProgress.done()
|
||||
$("#sub-app-container-main-content").show()
|
||||
})
|
||||
|
||||
|
||||
try {
|
||||
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
|
||||
$("#sub-app-container #sidebar-menu .menu-header .menu-title").text(app?.name)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
})()
|
||||
|
||||
new Vue({
|
||||
el: "#sidebar-menu",
|
||||
components: {
|
||||
"menu-list": {
|
||||
name: "menu-list",
|
||||
template:
|
||||
/*language=HTML*/
|
||||
`
|
||||
<div>
|
||||
<template v-for="(item, index) in menus">
|
||||
<el-submenu :index="item.id" v-if="item.children && item.children.length > 0">
|
||||
<template #title>
|
||||
<i :class="item.icon" v-if="item.icon" :size="18"></i>
|
||||
<i v-else class="el-icon-paperclip"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
<menu-list :menus="item.children" :depth="depth + 1"/>
|
||||
</el-submenu>
|
||||
<el-menu-item :index="item.id" v-else>
|
||||
<a :href="item.href" data-pjax>
|
||||
<i :class="item.icon" v-if="item.icon" :size="18"></i>
|
||||
<i v-else :class="item.icon ? item.icon : 'el-icon-menu'"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</a>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
props: ["menus", "depth"]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 菜单数据
|
||||
menus: [],
|
||||
// 当前激活的菜单索引
|
||||
activeMenuIndex: null,
|
||||
// 当前打开的菜单索引
|
||||
openedMenus: [],
|
||||
// 默认选中菜单是否加载中
|
||||
defaultSelectLoading: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
leftMenus() {
|
||||
if (this.menus) {
|
||||
return this.menus
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(window.sessionStorage.getItem("zhgh_sub_app_menus"))
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
menuSelect(index, indexPath) {
|
||||
try {
|
||||
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
|
||||
window.sessionStorage.setItem("zhgh_sub_app_left_menu_active_index-" + app.id, index)
|
||||
window.sessionStorage.setItem("zhgh_sub_app_left_menu_opens-" + app.id, JSON.stringify(indexPath))
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
// 获取菜单
|
||||
getMenus(appId) {
|
||||
if (!appId) return
|
||||
this.$axios.post("/platform/sys/user/subAppMenus", {appId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.menus = res.data
|
||||
window.sessionStorage.setItem("zhgh_sub_app_menus", JSON.stringify(res.data))
|
||||
this.echoMenus()
|
||||
} else {
|
||||
this.menus = []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 设置应用信息
|
||||
setAppInfo(app) {
|
||||
if (app) {
|
||||
// 储存到缓存
|
||||
window.sessionStorage.setItem("zhgh_sub_app", JSON.stringify(app))
|
||||
// 储存到div中
|
||||
document.getElementById("sub-app-id").innerText = app.id
|
||||
try {
|
||||
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
|
||||
$("#sub-app-container #sidebar-menu .menu-header .menu-title").text(app?.name)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
// 菜单回显
|
||||
echoMenus() {
|
||||
try {
|
||||
const app = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
|
||||
this.activeMenuIndex = window.sessionStorage.getItem("zhgh_sub_app_left_menu_active_index-" + app.id)
|
||||
this.openedMenus = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app_left_menu_opens-" + app.id)) || []
|
||||
|
||||
// !this.activeMenuIndex &&
|
||||
if ('/platform/v4/subApp' === getBaseSubAppPath()) {
|
||||
this.defaultSelect()
|
||||
} else {
|
||||
this.hrefSelect()
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
|
||||
// 地址选中
|
||||
hrefSelect() {
|
||||
const targetHref = this.getMenuOwnerPath()
|
||||
function findMenuPath(menus, targetHref) {
|
||||
function search(items, path) {
|
||||
for (const item of items) {
|
||||
// 如果当前项匹配 href,返回路径(包含自己)
|
||||
if (item.href === targetHref) {
|
||||
return [...path, item];
|
||||
}
|
||||
// 如果有子菜单,递归查找
|
||||
if (item.children?.length) {
|
||||
const result = search(item.children, [...path, item]);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return search(menus, []);
|
||||
}
|
||||
|
||||
const result = findMenuPath(this.menus, targetHref)
|
||||
if(result){
|
||||
this.activeMenuIndex = result[result.length-1]['id']
|
||||
this.openedMenus = result.map(v=>v.id)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.menuSelect(this.activeMenuIndex, this.openedMenus)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 默认选中
|
||||
getMenuOwnerPath() {
|
||||
const pathname = window.location.pathname
|
||||
if (pathname === "/platform/qsv/survey/reportPage") {
|
||||
return "/platform/qsv/survey"
|
||||
}
|
||||
if (pathname === "/platform/qsv/new") {
|
||||
return "/platform/qsv/activity"
|
||||
}
|
||||
return pathname
|
||||
},
|
||||
|
||||
getMenuOwnerFullPath() {
|
||||
if (window.location.pathname === "/platform/qsv/survey/reportPage") {
|
||||
return "/platform/qsv/survey"
|
||||
}
|
||||
if (window.location.pathname === "/platform/qsv/new") {
|
||||
return "/platform/qsv/activity"
|
||||
}
|
||||
return getFullSubAppPath()
|
||||
},
|
||||
|
||||
defaultSelect() {
|
||||
// 查找第一个有href的子菜单
|
||||
function findFirstChildWithHref(menus) {
|
||||
for (const menu of menus) {
|
||||
// 如果当前菜单有href,返回它
|
||||
if (menu.href) {
|
||||
return {
|
||||
menu: menu,
|
||||
parentIds: [menu.parentId]
|
||||
};
|
||||
}
|
||||
|
||||
// 如果有子菜单,递归查找
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
const result = findFirstChildWithHref(menu.children);
|
||||
if (result) {
|
||||
// 添加当前菜单的ID到父ID集合
|
||||
result.parentIds.push(menu.id);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = findFirstChildWithHref(this.menus)
|
||||
if (result) {
|
||||
this.activeMenuIndex = result.menu.id
|
||||
this.openedMenus = result.parentIds
|
||||
this.$nextTick(() => {
|
||||
this.menuSelect(this.activeMenuIndex, this.openedMenus)
|
||||
// 使用pjax跳转
|
||||
commonUtil.pjaxPush(getBaseSubAppPath(result.menu.href))
|
||||
|
||||
/*const tempLink = document.createElement('a')
|
||||
tempLink.href = result.menu.href
|
||||
tempLink.setAttribute('data-pjax', '')
|
||||
tempLink.style.display = 'none'
|
||||
document.body.appendChild(tempLink)
|
||||
tempLink.click()
|
||||
document.body.removeChild(tempLink)*/
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 没有已授权的可用子菜单时,才使用当前应用自身配置的地址作为兜底入口。
|
||||
try {
|
||||
const rootApp = JSON.parse(window.sessionStorage.getItem("zhgh_sub_app"))
|
||||
const currentAppId = GetQueryString("appId")
|
||||
const hasRootHref = rootApp && typeof rootApp.href === "string" && rootApp.href.trim() !== ""
|
||||
// 校验缓存应用与当前URL一致,避免复用旧标签页缓存时跳转到其他应用。
|
||||
if (hasRootHref && String(rootApp.id) === currentAppId) {
|
||||
this.activeMenuIndex = rootApp.id
|
||||
this.openedMenus = []
|
||||
this.$nextTick(() => {
|
||||
this.menuSelect(this.activeMenuIndex, this.openedMenus)
|
||||
commonUtil.pjaxPush(getBaseSubAppPath(rootApp.href))
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// 根菜单缓存缺失或格式异常时保持当前页面,不执行无权限地址跳转。
|
||||
}
|
||||
},
|
||||
|
||||
init() {
|
||||
// 页面加载时获取到正确的菜单
|
||||
const pathname = window.location.pathname
|
||||
if (!pathname.startsWith("/platform/v4/subApp")) {
|
||||
const path = this.getMenuOwnerFullPath()
|
||||
this.$axios.post("/platform/sys/user/rootMenuByPath", {pathname: path}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.setAppInfo(res.data)
|
||||
this.getMenus(res.data?.id)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const appId = GetQueryString("appId")
|
||||
if (!appId) return
|
||||
this.getMenus(appId)
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
|
||||
|
||||
// 移动端布局
|
||||
const sidebarMenu = document.getElementById("sidebar-menu")
|
||||
const menuToggleBtn = document.getElementById("menu-toggle-btn")
|
||||
const menuOverlay = document.getElementById("menu-overlay")
|
||||
|
||||
// 初始化菜单状态
|
||||
if (window.innerWidth <= 768) {
|
||||
menuToggleBtn.style.display = "block"
|
||||
} else {
|
||||
menuToggleBtn.style.display = "none"
|
||||
}
|
||||
|
||||
// 菜单按钮点击事件
|
||||
menuToggleBtn.addEventListener("click", function () {
|
||||
sidebarMenu.classList.toggle("show")
|
||||
menuOverlay.classList.toggle("show")
|
||||
})
|
||||
|
||||
|
||||
// 遮罩层点击事件
|
||||
menuOverlay.addEventListener("click", function () {
|
||||
sidebarMenu.classList.remove("show")
|
||||
menuOverlay.classList.remove("show")
|
||||
})
|
||||
|
||||
window.addEventListener("resize", function () {
|
||||
if (window.innerWidth <= 768) {
|
||||
menuToggleBtn.style.display = "block"
|
||||
} else {
|
||||
menuToggleBtn.style.display = "none"
|
||||
sidebarMenu.classList.remove("show")
|
||||
menuOverlay.classList.remove("show")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user