egg-jianghu
Version:
egg-jianghu
307 lines (287 loc) • 10.1 kB
HTML
<template id="jhLayout">
<v-app id="inspire" mobile-breakpoint="sm">
<div v-if="layoutLoading" class="text-center mt-10">
<v-progress-circular
:size="70"
:width="7"
color="success"
indeterminate
></v-progress-circular>
</div>
<template v-else>
<v-navigation-drawer
v-model="drawer"
app
clipped
v-if="isMobile"
>
<v-list>
<v-list-item-group
v-model="selectedItem"
color="success"
>
<v-list-item
v-for="item in inMenuList"
:class="{'pa-2': !isMobile}"
:key="item.path"
@click="jump(item.path, item.query)"
>
<v-list-item-content :class="{'pl-3': isMobile, 'pa-0': isMobile}">
<v-list-item-title :style="{'font-weight': isMobile ? 'bold' : 'normal'}">
{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-navigation-drawer>
<v-app-bar
app
color="secondary"
clipped-left
dark
height="55"
flat
>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" v-if="isMobile"></v-app-bar-nav-icon>
<v-toolbar-title ref="toolbarTitle" class="mr-8 align-center" style="font-size: 14px" :style="{'flex': isMobile ? 1 : 'none'}">
<span class="title" style="font-size: 1rem!important;">{{ appTitle }}</span>
</v-toolbar-title>
<v-tabs
v-model="selectedItem"
v-if="!isMobile"
show-arrows
slider-size="5"
:style="{maxWidth: tabsMaxWidth}"
>
<v-tabs-slider color="success"></v-tabs-slider>
<template v-for="item in inMenuList">
<v-tab
class="pl-3 pr-3"
:key="item.path"
@click="jump(item.path, item.query)"
>
{{ item.title }}
</v-tab>
<v-divider
style="max-height: 35px;min-height: 35px;align-self: center;"
v-if="item.path.includes('operationManual')"
vertical
></v-divider>
</template>
</v-tabs>
<div style="white-space: nowrap">
<v-menu offset-y>
<template v-slot:activator="{ on }">
<template v-if="!isMobile">
<v-btn disabled text class="ml-1 text-none" style="color: #ffffff!important;" v-on="on">
{{ userInfo.user.username }}
</v-btn>
</template>
<v-btn icon small class="ml-1" v-on="on">
<v-icon>mdi-account-circle</v-icon>
</v-btn>
</template>
<v-list nav dense>
<v-list-item
class="mt-2"
>
<v-list-item-icon class="mr-2">
<v-icon>mdi-account</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ userInfo.user.userId }}</v-list-item-title>
<v-list-item-subtitle>{{ userInfo.user.username }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item
v-for="(item, index) in profileMenus"
:key="index"
:href="item.path"
class="mt-2"
>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item v-for="avatarMenu of inAvatarMenuList" :key="avatarMenu.path" :href="avatarMenu.path">
<v-list-item-icon class="mr-2">
<v-icon>mdi-account-cog-outline</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ avatarMenu.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item @click="logout">
<v-list-item-icon class="mr-2">
<v-icon>mdi-logout</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>登出</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</div>
</v-app-bar>
<v-main>
<slot></slot>
</v-main>
</template>
</v-app>
</template>
<!--jhLayout.html start-->
<script>
Vue.component("jh-layout", {
vuetify: new Vuetify(),
name: 'jh-layout',
props: {},
data: () => ({
// 是否与登录
layoutLoading: true,
selectedItem: -1,
selectItemTitle: '',
drawer: null,
appDirectoryLink: '<$ ctx.app.config.appDirectoryLink $>',
appType: '<$ ctx.app.config.appType $>',
appTitle: '<$ ctx.app.config.appTitle $>',
userInfo: {
user: {}
},
// 用户菜单
inMenuList: [],
inAvatarMenuList: [],
profileMenus: [],
tabsMaxWidth: 'calc(100vw - 320px)'
}),
watch: {},
computed: {
isMobile() {
return window.innerWidth < 600;
}
},
async created() {
await this.getLoginUserInfo();
this.computedMenuList();
this.locateCurrentMenuItem();
this.getTabsMaxWidth();
},
methods: {
// 动态计算菜单栏目的最大宽度,按照实际的标题宽度计算
getTabsMaxWidth() {
this.$nextTick(() => {
if(this.$refs.toolbarTitle) {
this.tabsMaxWidth = 'calc(100vw - ' + (this.$refs.toolbarTitle.offsetWidth + 200) + 'px)';
}
})
},
// 跳转链接
jump(url, queryParams) {
if (queryParams) {
const queryStrings = Object.keys(queryParams)
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(queryParams[k]))
.join('&');
window.location.href = url + '?' + queryStrings;
} else {
window.location.href = url;
}
},
// 定位当前页面在属于哪个菜单
locateCurrentMenuItem() {
// 遍历菜单 path 进行匹配
const index = _.findIndex(this.inMenuList, {path: location.pathname});
if (index > -1) {
// 设置标题、菜单选中
this.selectedItem = index;
this.selectItemTitle = this.inMenuList[index].title;
document.title = this.appTitle + (this.selectItemTitle ? " - " + this.selectItemTitle : "")
}
},
// 获取用户信息
async getLoginUserInfo() {
const userInfo = await window.jianghuAxios({
data: {
appData: {
pageId: 'allPage',
actionId: 'userInfo',
actionData: {},
}
}
}).then(response => {
return response.data.appData.resultData;
})
this.userInfo = userInfo;
this.$emit('layoutData', { userInfo: this.userInfo })
this.layoutLoading = false;
},
computedMenuList() {
const urlPathList = window.location.pathname.split('/');
const currentPageId = urlPathList && urlPathList[urlPathList.length - 1];
const appType = '<$ ctx.app.config.appType $>';
const urlParams = new URLSearchParams(location.search);
let title = urlParams.get('title');
// 处理过长的 title
if (title && title.length > 10) {
title = `${title.slice(0, 5)}...${title.slice(title.length - 4, title.length)}`
}
this.inMenuList = _
.chain(this.userInfo.allowPageList)
.filter(page => page.pageType === 'showInMenu' || (_.includes(['dynamicInMenu', 'avatarInMenu'], page.pageType) && currentPageId === page.pageId))
.map((page) => {
return {
path: `/${window.appInfo.appId}/page/${page.pageId}`,
title: title && page.pageType === 'dynamicInMenu' ? `${page.pageName}【${title}】` : page.pageName,
sort: parseInt(page.sort)
};
})
.orderBy(['sort'], ['asc'])
.value();
if (appType === 'multiApp' && this.appDirectoryLink) {
console.log(this.appDirectoryLink)
this.inMenuList.unshift({ path: this.appDirectoryLink, title: '回到目录' });
}
this.inAvatarMenuList = _
.chain(this.userInfo.allowPageList)
.filter(['pageType', 'showInAvatarMenu'])
.map((page) => {
return {
path: `/${window.appInfo.appId}/page/${page.pageId}`,
title: page.pageName,
sort: parseInt(page.sort)
};
})
.orderBy(['sort'], ['asc'])
.value();
},
// 登出
async logout() {
try {
await window.jianghuAxios({
data: {
appData: {
pageId: 'allPage',
actionId: 'logout'
}
}
})
vtoast.success('注销成功');
localStorage.removeItem(`${window.appInfo.appId}_authToken`);
setTimeout(() => {
location.href = `/${window.appInfo.appId}/page/login`;
}, 700);
} catch (error) {
vtoast.fail(error.errorReason);
localStorage.removeItem(`${window.appInfo.appId}_authToken`);
setTimeout(() => {
location.href = `/${window.appInfo.appId}/page/login`;
}, 700);
}
}
},
template: '#jhLayout',
})
</script>
<!--jhLayout.html end-->