<template>
|
<div class="vue-project-layout">
|
<div class="layout">
|
<div class="layout-header">
|
<div class="logo">
|
<img :src="logo" />
|
</div>
|
<div class="user-dropdown">
|
<Dropdown trigger="click" @on-click="onClick">
|
<a href="javascript:void(0)">
|
<Avatar icon="ios-person" :src="avatar" size="large" />
|
<span class="username">{{ userName }}</span>
|
<Icon type="md-arrow-dropdown" size="18"></Icon>
|
</a>
|
<template #list>
|
<DropdownMenu>
|
<DropdownItem name="profile">个人中心</DropdownItem>
|
<DropdownItem name="logout">退出登录</DropdownItem>
|
</DropdownMenu>
|
</template>
|
</Dropdown>
|
</div>
|
<Skin></Skin>
|
<Locale></Locale>
|
</div>
|
<div class="layout-body">
|
<div class="menu-list">
|
<Menu
|
:active-name="activeName"
|
:open-names="openNames"
|
accordion
|
@on-select="onMenuClick"
|
ref="refMenu"
|
>
|
<template v-for="list in menuList" :key="list.id">
|
<Submenu v-if="list.isFolder" :name="list.id">
|
<template #title>
|
<Icon :custom="list.icon" />
|
{{ list.title }}
|
</template>
|
<MenuItem
|
v-for="app in list.children"
|
:key="app.id"
|
:name="app.id"
|
>
|
<Icon :custom="app.icon" />
|
{{ app.title }}</MenuItem
|
>
|
</Submenu>
|
<MenuItem v-else :name="list.id">
|
<Icon :custom="list.icon" />
|
{{ list.title }}
|
</MenuItem>
|
</template>
|
</Menu>
|
</div>
|
<div class="view">
|
<RouterView v-slot="{ Component }">
|
<template v-if="Component">
|
<KeepAlive>
|
<component :is="Component" :key="$route.fullPath" />
|
</KeepAlive>
|
</template>
|
</RouterView>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import { ref } from "vue";
|
import Skin from "./skin.vue";
|
import Locale from "./locale.vue";
|
import menuList from "./menu.json";
|
import { showError } from "@/libs/util";
|
export default {
|
name: "Layout",
|
components: {
|
Skin,
|
Locale,
|
},
|
data() {
|
return {
|
menuList,
|
activeName: "",
|
openNames: [],
|
logo: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI4AAAAcCAYAAAC3U4dAAAAAAXNSR0IArs4c6QAACSpJREFUaEPtmwesZVUVhr+FvfeusccSS+wVsStRrCD2blARKwI2REWxgQUL2Im9YCG2OIJiCPaWKFEpIorKKCAWFMss8032nuw5c8495829c53gXcnLZO7b+5y91/r3v/619n3BnJaZJwPXm/GYo4GHR8Q5Q2Myc3/gucDlBsasB14eEYfNudzV9AV5IOZ9zgTg/AzYB/h8RGT3fZl5aeCTwL2AC6yAM29EljN/GcD5l6AB9gVOjQj/T2b67ksBewDPB646Y8srxlkOHia/ZRnAcTF/B74KvBM4EdgAXBZ4KPAU4GojK14BZ3JIlzNwWcCpu/kL8CvgXODawJWAHSZsdRJwMvP6wFU6Ke+kiPjdDH118bKWKzZjfg2cXtmxJ72aUm9Y1l9/7eE4LSLWZ+ZFgesC9Zn/BE4CzupL1z3Pv0SZXzWfLH1yRPyhbx+Zec3C3u0eHOq8s4G/AX+NiD9N8PWkIcsGzqRF9QyaCpwXAnsDV26ecXBE+NkWlpmC9k7Aq4GdmgHvAV4VEacNzBOcH2/mqN2+D7w4ItZlpsXC64Bdy3yD90bgfcAZs8CTmQL5fsBLgNuU+WcCe0aE79xkmXl14GbAI4AbA3fprPfPZV2nAr8AjvLfiPj31gaizju/Aec6wLuBuwMXLJsUdDtFhCJ9M8vMywPPBl4EXLjDOILtqIj4RydYgm134CPN5wLDNPzKiDivBzgOdczBgKBcP1AoCBqLBPVgC4ItgJOZMtozgWcBMtwUe0upTgcr3CkPccz5CjhuKDP31DmdNPJB4KkRYcrYaIVtDI5McPseh30BeE5E2G5oT7lgU+zLVJp67TvAXhHxvfLsLuPU+YLnTcBh3bSTmRcD7t0DGuf2AeegAppL1i0BPwG+1SzXw2DRYcpTFnymgHsFnG7AM/NawBHAjg3rSNk7R8TxDXBMN88rPy3b1CHqsWcAR7ask5mPBQRiCwb7SwdUYA4wTh2vzngzcGhEnFWAdhHgvoX5KiDbrW0GnMwULF8rqawefvXMrhFhOqqHo2otiw/98kfg6C6L9hya0Y+2NeMoGH8DKE4VdgZQYewp1VmW41cop0KBZ6XVJ5YnaZzGYU8HXtM0FH3fx4Anl1RiGjMlmF6k/CEzOD7rRFNLZnpyrQ5v3bDNDwub/ah5/xDjtOB5G3AIIEDvIRMAdxxYSBc4+uwbwE2b8eqWxwHHKNBHIz/ngG0BHIN0CvDjIszUFm7EzeskwaSY9JRbPRgMxaxCTzF4W+Dmnby9VuB4wj4A3LNhHYG7W0Qcm5nS936mosZ/vyw6xMrsMuVzU5sd7SMi4tzMfFIRuHWalP/2wjYb+1OFQcaA4zDnCtzjylruOiOWXeDouyOLiL5QfW3xu4DS96ZPAS/LLNwWDRyV+1eALwMGwoafDDPJMvMagIETPA8oAlFmWhNwSvAeWASsrKYZWJ1t+pFt1BrSd/2dQNPpis07NAtWtzyxrEG2uUUTKDXFoyPCfzfZQKqyDXF6YasqZgWPh+xWzXTH+JmptlqfxtmtiG2Zuo2jB9fxP9f/wA+Add01TgrIjEGLAo79gy8B7yrlnumpdaSnQlHpOE9LvXoQVPZL7DO042Uiy8udS9AM/pruqkpZ+yHgwU36c10vBXbx/qx55U+LoBYYAmev0g+qoHqtPRjgDQ2DyZ5vLWyzWXk7ABwBaDoynTwEqEzRbv33gJWPrKwOmgUcr2rcm2v1oPXF8j9FIri/TwOfWlQaWwRwjgE+B3wxIuwKW7HYIPMkeHJNO7KI6cPKwd9V4OhwA+JpNKVJscfXPkMpl2/nqQa+udZLzsy0N7OuCZKs47usMGo6ErwC3lL67My8CfCOUtLXwP0WMAj1dLt+97pLRMiym9kM4FjGq6/UX3bNWz1nShGY7y+M+NFZwCl+1p/qHAW1fnr8AEnIQurMQ4HDF9EIXARw1CV2Z88p9093LifKUlewGCAZpK9ycZ9uyhPmCbbiUGR+vZyOMzNTR5tSNkSEQZ9smWmas9/ysBmTFLc27kyvgl4msD/ixay6q8/s7djgs0kooCYDJyJOKcASJHVdFg2mTist9/xIYBQ49aWlylIr3qAASAYylXXNtLWfTcrJThwYuAjg7BARGzLzRkVs3r9USpaMU64Tuks7rwjHE4APK3JlIPsuvmetG85MezTfHphnwCylX9FqscwU8HZ57zNwY29H+W5DQJ7FOAKnAFQ9p0D3ru71pr2mPF8TcBoA1YtjfX/L0mpwD9UU+3tHhMwzl80NnOIET6iNNzu3dj8XYaYxG2Y22w7ZWnFXGEsA2pbvmn0dm3wbG3edADyhaJIqoOuvZZgDTDdDQJ4CnOI32w/+eIcliDfaFMbJTLWYKfO9fX2ZzJTlrQK7IBE4drDnsrmBk5mKOXOrKWnu5/XsxrRg+jKd2FdZs2WmVYs03ZraSqcOpZua5qzO2jSrttkxIs4YWshU4MyYP8o4makOq4fBuzZLbw9Z1ZgyjunUbnQ1U/0+EfGJNTuxM2HuQGemjjS3bktTOB4UETbM1mxFtyg6H9NMPhZ41MjNudWPjTrTSrU9IkIxPWhLAs7hpUJTIMvOsk+byo2t+rCVC96T7T9rz1OduwjgjH11dOpaZo2zLyFwtopii2iXdb5bHGmFcWBEeGpnWjnZ9nEMkEWAX6cYm9PXADQd7l41zgjwpjCOVZmMafPUOAqQrqYUTKZW06Bi228K2F+b2/4vgKOXSs63cnmQpb3fPJzS08hMq0bvvrzb2jciFM1jwPG7Rl602i+qZrp9WkTYlBubLyhaVlPrmWI+252YmRYjgtlri7Zp6FDf5VWLLQ73vDBbJnBEvmV3e1elgLP8HVvHXIyzMG+tHrTJA2MBG3XVhC+r+wzLQBtlflXBuxmp086wp9/mlT2IWWtZAWc0EssdsAzgKNysaA4s3eVNDbPMtHx/QRGtQ38ao0dWwFkuLkbftgzgKES967Er2vfnMTYOrRDsNNdv7XUXvgLOaCiXO2ARwPF6oC1Xuzvw5vhlsxp4mWkH1UpCzdNnXkV4x2I5ubLtwAOLAI73LXaLt2CTsj+/EnFcRCiMe61cV3gZatOtz9RIJ0SEt7wr2w48MDdwtoM9rJbwP/DAfwEGg7pKrm8BNAAAAABJRU5ErkJggg==",
|
avatar: "http://115.29.185.26:5101/userphoto?login=sa",
|
userName: "用户名",
|
};
|
},
|
computed: {
|
viewList() {
|
let routes = [];
|
this.menuList.forEach((menu) => {
|
if (menu.isFolder) routes.push(...menu.children);
|
else routes.push(menu);
|
});
|
return routes;
|
},
|
},
|
methods: {
|
onClick(name) {
|
if (name == "logout") {
|
this.$router.push({ name: "logout" });
|
}
|
},
|
onMenuClick(id) {
|
let route = this.viewList.find((r) => r.id == id);
|
if (!route)
|
this.showError(
|
`未找到id=${id}所对应的路由,请检查菜单、路由的配置是否正确`
|
);
|
|
this.activeName = route.id;
|
this.menuList.forEach((list) => {
|
if (list.isFolder && list.children.find((l) => l.id == route.id))
|
this.openNames = [list.id];
|
else if (list.id == route.id) this.openNames = [];
|
});
|
this.$nextTick(() => {
|
this.refMenu.updateOpened();
|
});
|
|
this.$router.push({
|
name: route.name,
|
query: {
|
menuId: id,
|
},
|
});
|
},
|
showError(ex) {
|
showError(this, ex);
|
},
|
},
|
mounted() {
|
let firstRoute = this.viewList[0];
|
this.onMenuClick(firstRoute.id);
|
},
|
setup() {
|
let refMenu = ref(null);
|
return { refMenu };
|
},
|
};
|
</script>
|
|
<style lang="less">
|
.vue-project-layout {
|
width: 100%;
|
height: 100%;
|
overflow: hidden;
|
.layout {
|
width: 100%;
|
height: 100%;
|
display: flex;
|
overflow: hidden;
|
-webkit-box-orient: vertical;
|
-webkit-box-direction: normal;
|
flex-direction: column;
|
-webkit-box-flex: 1;
|
background: #f5f7f9;
|
.layout-header {
|
width: 100%;
|
padding: 0;
|
height: 64px;
|
line-height: 64px;
|
flex: 0 0 auto;
|
.logo {
|
height: 64px;
|
display: inline-block;
|
width: 240px;
|
padding: 5px 20px;
|
text-align: center;
|
transition: width 0.2sease-out;
|
img {
|
max-width: 200px;
|
max-height: 54px;
|
vertical-align: sub;
|
}
|
}
|
.user-dropdown {
|
float: right;
|
height: 64px;
|
padding-right: 40px;
|
line-height: 64px;
|
.username {
|
display: inline-block;
|
margin-left: 5px;
|
color: #fff;
|
}
|
.ivu-icon {
|
color: #fff;
|
}
|
}
|
}
|
.layout-body {
|
width: 100%;
|
height: calc(100% - 64px);
|
display: flex;
|
flex: auto;
|
.menu-list {
|
flex-shrink: 0;
|
flex-grow: 0;
|
flex-basis: 240px;
|
height: 100%;
|
border-right: 1px solid #dcdee2;
|
overflow-x: hidden;
|
overflow-y: auto;
|
.ivu-menu {
|
height: 100%;
|
}
|
|
.ivu-menu-vertical.ivu-menu-light:after {
|
display: none;
|
}
|
}
|
}
|
}
|
}
|
</style>
|