This commit is contained in:
GGJ
2025-01-09 19:02:44 +08:00
commit 92e7a7a5eb
2943 changed files with 1152283 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
<script>
export default {
name: 'app-main',
computed: {
cachedViews() {
// console.log(this.$store.state.tagsView.cachedViews)
return this.$store.state.tagsView.cachedViews
},
key() {
return this.$route.path
}
},
watch: {
$route: {
handler(to) {
if (this.$store.state.settings.tagsView || !to.name) return
this.$store.dispatch('tagsView/noTagsAddView', to)
},
immediate: true
}
},
render(h) {
const { cachedViews, key } = this
const keepAlive = (
<keep-alive include={cachedViews}>
<router-view key={key} />
</keep-alive>
)
window.__KEEP_ALIVE__ = keepAlive
return (
<section
class='app-main-container'
id='app-main'
>
<transition
name='fade-transform'
mode='out-in'
onBeforeLeave={() => this.$el.parentNode.style.overflow = 'hidden'}
onAfterEnter={() => this.$el.parentNode.style.overflow = ''}
>
{keepAlive}
</transition>
</section>
)
}
}
</script>
<style scoped>
.app-main-container {
/* BFC */
float: left;
width: 100%;
height: 100%;
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
</style>

View File

@@ -0,0 +1,65 @@
<template>
<el-breadcrumb id="breadcrumb-container" class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
const matched = this.$route.matched.filter(item => item.meta && item.meta.title)
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
margin-left: 8px;
font-size: 14px;
line-height: 50px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<div id="hamburger-container" style="padding: 0 18px;" @click="toggleClick">
<svg-icon icon-class="hamburger" class="hamburger" :class="{'is-active':isActive}" />
</div>
</template>
<script>
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
</script>
<style lang='less' scoped>
.hamburger {
display: inline-block;
width: 20px !important;
height: 20px !important;
vertical-align: middle !important;
}
.hamburger.is-active {
transform: rotate(180deg);
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<div key="collapse" class="sidebar-logo-link" to="/dashboard">
<!-- <img v-if="logo" :src="settings.title=='电能质量监测系统'?logo:logo1" class="sidebar-logo" :style="{ width: settings.layout === 'layout1' ? '35px' : '50px',height: settings.layout === 'layout1' ? '35px' : '50px'}"> -->
<img :src="logourl" class="sidebar-logo" :style="{ width: settings.layout === 'layout1' ? '35px' : '180px',height: settings.layout === 'layout1' ? '35px' : '50px'}">
<!-- <svg-icon icon-class="logo" class="sidebar-logo" /> -->
<transition name="sidebarLogoFade">
<h1
v-show="!collapse"
class="sidebar-title"
:style="{ fontSize: settings.layout === 'layout1' && settings.title.length >= 8 ? '14px' : '24px'}"
>{{ settings.title }} </h1>
</transition>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'sidebar-logo',
props: {
collapse: {
type: Boolean,
required: true
}
},
computed: {
...mapGetters(['settings'])
},
data () {
return {
// logo: require('../../assets/login/'+window.logo),
logourl:window.sessionStorage.logo
}
},
created(){
// console.log(this.logourl)
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/variables.scss";
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 60px;
padding-left: 20px;
overflow: hidden;
line-height: 60px;
& .sidebar-logo-link {
width: 100%;
height: 100%;
& .sidebar-logo {
width: 52px;
height: 52px;
margin-right: 8px;
color: #fff;
vertical-align: middle;
}
& .sidebar-title {
display: inline-block;
margin: 0;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
font-size: 18px;
font-weight: 600;
line-height: 50px;
color: #fff;
vertical-align: middle;
}
}
&.collapse {
padding-left: 10px;
}
}
</style>

View File

@@ -0,0 +1,29 @@
<script>
export default {
name: 'menu-item',
functional: true,
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
}
},
render(h, context) {
const { icon, title } = context.props
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
}
if (title) {
vnodes.push(<span>{(title)}</span>)
}
return vnodes
}
}
</script>

View File

@@ -0,0 +1,61 @@
<template>
<el-menu-item v-if="!item.children || item.children.every(it=>it.hidden)" :index="item.routePath || item.url">
<app-link :to="to(item)">
<item :icon="item.icon" :title="item.title" />
</app-link>
</el-menu-item>
<el-submenu v-else :class="{'no-arrow': item.title === '...'}" :index="item.routePath">
<span slot="title">
<item :icon="item.icon" :title="item.title" />
</span>
<navbar-item
v-for="(child,idx) in item.children"
:key="idx"
:index="idx"
:item="child"
/>
</el-submenu>
</template>
<script>
import Item from './Item.vue'
import AppLink from '../Sidebar/Link.vue'
export default {
name: 'navbar-item',
components: { Item, AppLink },
props: {
item: {
type: Object,
required: true
},
index: {
type: Number,
required: true
}
},
methods: {
to(menuItem) {
const visitedTags = this.$store.state.tagsView.visitedTags
const tag = visitedTags.find(t => t.routeName === menuItem.routeName)
return tag ? tag.routePath : menuItem.routePath
}
}
}
</script>
<style>
.el-menu--horizontal .svg-icon {
margin-right: 8px;
vertical-align: middle;
}
</style>
<style lang="scss" scoped>
.no-arrow ::v-deep .el-submenu__title {
width: 60px;
font-weight: bold;
text-align: center;
i {
display: none;
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="navbar-menu">
<el-menu
:default-active="customActiveMenu || activeMenu"
unique-opened
mode="horizontal"
>
<navbar-item v-for="(item,index) in renderMenu" :key="index" :item="item" :index="index" />
</el-menu>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import NavbarItem from './NavbarItem'
import variables from '@/styles/variables.scss'
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
export default {
components: { NavbarItem },
props: {
menus: {
type: Array,
default: []
},
customActiveMenu: {
type: String,
default: ''
}
},
data() {
return {
renderMenu: []
}
},
computed: {
...mapGetters([
'sidebar'
]),
activeMenu() {
if (this.$route.meta.activeMenu) {
return this.$route.meta.activeMenu
}
return this.$route.path
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
},
watch: {
menus() {
this.setMenus()
}
},
mounted() {
setTimeout(() => {
this.setMenus()
}, 200)
addResizeListener(this.$el, this.setMenus)
},
beforeDestroy() {
removeResizeListener(this.$el, this.setMenus)
},
methods: {
setMenus() {
const wrapperW = this.$el.offsetWidth
const topMenu = []
const otherMenu = []
let nowW = 0
this.menus.forEach(item => {
nowW += this.getItemW(item)
if (nowW + 100 > wrapperW) {
otherMenu.push(item)
} else {
topMenu.push(item)
}
})
if (otherMenu.length > 0) {
topMenu.push({
title: '...',
routePath: '/',
children: otherMenu
})
}
this.renderMenu = topMenu
},
getItemW(item) {
// 20 14 8 20
let w = 0
if (item.icon) w += 22 // 图标长度
if (item.children) w += 20 // 右边下箭头
w += item.title.length * 14 // 文字长度
w += 40 // padding
return w
}
}
}
</script>
<style lang="scss" scoped>
.navbar-menu {
flex: 1;
& ::v-deep .el-menu.el-menu--horizontal {
display: flex;
border-bottom: none;
.el-submenu__title {
border-bottom-style: solid;
border-bottom-width: 2px;
}
}
& ::v-deep .el-menu-item {
padding: 0;
& > a {
display: inline-block;
width: 100%;
height: 100%;
padding: 0 20px;
}
}
}
</style>

View File

@@ -0,0 +1,574 @@
<template>
<div class="right-menu">
<div class="menu-item">
<span style="width: 210px;font-size: 14px;font-weight: 500;color:#05fd3c">当前时间{{nowtime}}</span>
<!-- <el-button type="primary" round @click="ddd" size="mini" >关闭告警</el-button>
<el-badge :value="200" :max="99" class="item" >
<i class="el-icon-message-solid" style="font-size: 20px;" @click="message"/>
</el-badge> -->
</div>
<el-dropdown trigger="click" size="medium">
<div id="right-menu-set" class="menu-item">
<i class="el-icon-s-custom" /><span style="margin-left: 5px" v-if="flag"
>{{ userInfo.name
}}<i style="margin-left: 5px" class="el-icon-caret-bottom"
/></span>
</div>
<!-- <el-dropdown-menu slot="dropdown" id="dropdown" class="user-dropdown" :style="device==1?'zoom:1':'zoom:1.06'"> -->
<el-dropdown-menu slot="dropdown" id="dropdown" class="user-dropdown">
<!-- <el-dropdown-menu slot="dropdown" id="dropdown" class="user-dropdown"> -->
<el-dropdown-item>
<span style="display: block" @click="userclickd()">用户信息</span>
</el-dropdown-item>
<el-dropdown-item @click.native="handleResetPassword()">
<span style="display: block">修改密码</span>
</el-dropdown-item>
<el-dropdown-item>
<span style="dispblock" @click="goto">帮助文档</span>
</el-dropdown-item>
<!-- divided closelostar-->
<el-dropdown-item @click.native="logout(1)">
<span style="display: block">清理缓存</span>
</el-dropdown-item>
<el-dropdown-item @click.native="logout(0)">
<span style="display: block">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dialog
:close-on-click-modal="false"
title="修改密码"
:visible.sync="dialogVisible1"
append-to-body
width="30%"
:before-close="closepw"
>
<el-form ref="passwordform" :model="passwordform" :rules="rules" label-width="100px">
<el-form-item label="旧密码:" prop="password">
<el-input v-model="passwordform.password" type="password" placeholder="请输入旧密码"/>
</el-form-item>
<el-form-item label="新密码:" prop="newPwd" class="top" >
<el-input v-model="passwordform.newPwd" type="password" placeholder="请输入新密码"/>
</el-form-item>
<el-form-item label="确认密码:" prop="confirmPwd" class="top">
<el-input v-model="passwordform.confirmPwd" type="password" placeholder="请输入确认密码"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button
text-align="center"
@click="resetForm('passwordform')"
> </el-button
>
<el-button text-align="center" type="primary" @click="submint"
>确认</el-button
>
</span>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
title="用户信息"
append-to-body
:visible.sync="dialogVisible2"
width="30%"
>
<el-form ref="form" :model="form" label-width="100px">
<el-form-item label="用户名称:">
<el-input v-model="form.name" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="登录名称:" class="top" >
<el-input v-model="form.loginName" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="归属部门名称:" class="top" >
<el-input v-model="form.deptName" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="拥有的角色:" class="top" >
<el-input v-model="form.roles" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="电话号码:" class="top" >
<el-input v-model="form.iphone" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="电子邮箱:" class="top" >
<el-input v-model="form.emirl" :disabled="true"></el-input>
</el-form-item>
<!-- <el-form-item label="系统类型:">
<el-input v-model="form.systype" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="状态:">
<el-input v-model="form.status" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="登录时间:">
<el-input v-model="form.lofintime" :disabled="true"></el-input>
</el-form-item> -->
</el-form>
</el-dialog>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import {
add,
list,
getById,
deleteById,
dept,
edit,
detptree,
rolelist,
selecRoleDetail,
passwordConfirm,
deluser,
updatePassword,
activateUser,
exportUser,
exportUserExcel,
} from "@/api/admin/user";
import { gongkey, userfunction } from "@/api/user";
import axios from "axios";
import { sm3Digest } from "@/assets/commjs/sm3";
import { sm2, encrypt } from "@/assets/commjs/sm2.js";
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码"));
} else {
if (this.passwordform.confirmPwd !== "") {
this.$refs.passwordform.validateField("confirmPwd");
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== this.passwordform.newPwd) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
return {
flag: true,
dialogVisible2: false,
dialogVisible1: false,
passwordform: {
password: "",
newPwd:'',
confirmPwd:''
},
nowtime:'',
LocationProvince:"正在定位所在省", //给渲染层定义一个初始值
LocationCity:"正在定位所在市" , //给渲染层定义一个初始值
form: {
name: "",
deptName: "",
iphone: "",
emirl: "",
roles: '',
loginName: "",
systype: "",
status: "",
lofintime: "",
},
password3: "",
id: "",
device: 1,
pd: "",
rules: {
password: [
{ required: true, message: "请输入旧密码", trigger: "blur" },
{
min: 6,
max: 16,
message: "长度在 6 到 16 个字符",
trigger: "blur",
},
{ validator: validatePass, trigger: "blur" },
],
newPwd: [
{ required: true, message: "请输入密码", trigger: "blur" },
{
min: 6,
max: 16,
message: "长度在 6 到 16 个字符",
trigger: "blur",
},
{ validator: validatePass, trigger: "blur" },
],
confirmPwd: [
{ required: true, message: "请确认密码", trigger: "blur" },
{
min: 6,
max: 16,
message: "长度在 6 到 16 个字符",
trigger: "blur",
},
{ validator: validatePass2, trigger: "blur", required: true },
],
},
};
},
computed: {
...mapGetters(["userInfo"]),
},
created() {
this.device = window.devicePixelRatio;
},
mounted() {
// this.city() //触发获取城市方法
setInterval(()=>{
this.getnowtime()
},500)
},
methods: {
...mapActions({
changeSetting: "settings/changeSetting",
}),
async logout(t) {
this.flag = false;
await this.$store.dispatch("user/logout");
if(t==1){
this.$message({
type: "success",
message: "缓存清理成功!",
});
}else{
return
}
},
toggleSettings() {
const showSettings = this.$store.state.app.showSettings;
this.$store.state.app.showSettings = !showSettings;
},
goto(t) {
//this.$router.push({path:'/algorithm/algorithm1',query:{name:'wtzhpg',path:'/algorithm/algorithm1'}})
this.$router.push({ path: "/algorithm/algorithm1" });
},
message() {
// alert(9)
this.$router.push("/BusinessAdministrator/alarManagement/AlarmInformation");
},
ddd() {
this.$notify.closeAll();
},
closelostar() {
this.$confirm("此操作将清理所有信息缓存, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
window.localStorage.removeItem("loglevel:webpack-dev-server");
window.localStorage.removeItem("CUSTOM_SETTINGS");
//window.localStorage.clear()
//window.sessionStorage.clear()
this.$store.dispatch("user/logout");
this.$message({
type: "success",
message: "缓存清理成功!",
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消缓存清理",
});
});
},
userclickd() {
this.dialogVisible2 = true;
var info = window.sessionStorage.getItem("Info");
info = eval("(" + info + ")");
// var gNamelist=[]
// for (var i = 0; i < info.role.length; i++) {
// gNamelist.push(row.role[i]);
// }
// gNamelist.join();
this.form.name = info.name;
this.form.deptName = info.deptName;
if (info.type == 1) {
this.form.systype = "省级系统";
} else {
this.form.systype = "数据中心";
}
this.form.emirl = info.emirl;
let str = info.loginTime
let str1=str.split('T')
//console.log(str1)
this.form.lofintime = str1[0]+" "+str1[1];
// let r = ''
// info.role.forEach((m,index) => {
// r+=m+', '
// });
this.form.roles = info.role.join()
//this.form.roles =JSON.stringify(info.role);
if (info.state == 1) {
this.form.status = "正常";
}
this.form.loginName = info.loginName;
},
resetForm(formName){
this.$refs[formName].resetFields();
},
closepw(){
this.$refs.passwordform.resetFields();
this.dialogVisible1 = false;
},
handleResetPassword() {
this.passwordform.password = "";
this.passwordform.newPwd = "";
this.passwordform.confirmPwd = "";
this.pd = "pw";
this.dialogVisible1 = true;
var info = window.sessionStorage.getItem("Info");
info = eval("(" + info + ")");
this.id = info.id;
this.gongKey();
},
submint() {
this.gonxxKey();
},
gonxxKey() {
gongkey({ loginName: window.sessionStorage.cnloginname }).then((response) => {
if (response.code === "A0000") {
var publicKey = response.data;
// console.log("获取公钥后赋值" + publicKey);
var sm3Pwd = sm3Digest(this.passwordform.password); //SM3加密
var jiamipassword = "";
jiamipassword = sm2(
sm3Pwd + "|" + this.passwordform.password,
publicKey,
0
); //SM2公钥加密
this.password3 = jiamipassword;
// console.log("密码加密后hhhhh:" + jiamipassword);
// console.log("测试" + this.password3);
if (this.pd == "del") {
var password = this.password3;
this.dialogVisible1 = false;
passwordConfirm(password).then((response) => {
if (response.code === "A0000") {
this.$confirm("此操作将永久删除该用户, 是否继续?", "提示", {
confirmButtonText: "删除",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
let data = {
id: this.id,
};
deluser(data).then((response) => {
this.$message({ type: "success", message: "删除成功" });
});
this.handleQuery();
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
}
});
}
if (this.pd === "pw") {
var password = this.password3;
this.dialogVisible1 = false;
passwordConfirm(password).then((response) => {
if (response.code === "A0000") {
// this.$prompt("请输入新密码", "提示", {
// confirmButtonText: "确定",
// cancelButtonText: "取消",
// })
// .then(({ value }) => {
gongkey({loginName: window.sessionStorage.cnloginname}).then((response) => {
if (response.code === "A0000") {
var publicKey = response.data;
// console.log("获取公钥后赋值" + publicKey);
var sm3Pwd = sm3Digest(this.passwordform.newPwd); //SM3加密
var jiamipassword = "";
jiamipassword = sm2( sm3Pwd + "|" + this.passwordform.newPwd, publicKey, 0); //SM2公钥加密
let id = window.sessionStorage.getItem("Info").id;
let data = {
id: this.id,
newPassword: jiamipassword,
};
updatePassword(data).then((response) => {
if (response.code === "A0000") {
this.$message({
type: "success",
message: "修改成功",
});
}
});
}
});
// })
// .catch(() => {
// this.$message({
// type: "info",
// message: "取消输入",
// });
// });
}
});
}
}
}
);
},
getnowtime(){
const now = new Date();
const year = now.getFullYear();
const month = ('0' + (now.getMonth() + 1)).slice(-2);
const day = ('0' + now.getDate()).slice(-2);
const hours = ('0' + now.getHours()).slice(-2);
const minutes = ('0' + now.getMinutes()).slice(-2);
const seconds = ('0' + now.getSeconds()).slice(-2);
this.nowtime = year +"-"+ month +"-"+ day +":"+ hours +":"+ minutes +":"+ seconds;
},
city(){ //定义获取城市方法
const geolocation = new BMap.Geolocation();
var _this = this
geolocation.getCurrentPosition(function getinfo(position){
let city = position.address.city; //获取城市信息
let province = position.address.province; //获取省份信息
_this.LocationProvince = province
_this.LocationCity = city
}, function(e) {
_this.LocationCity = "定位失败"
}, {provider: 'baidu'});
}
// delAllCookie() {
// //清空全部cookie
// var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
// if (keys) {
// for (var i = keys.length; i--; ) {
// document.cookie =
// keys[i] + "=0;path=/;expires=" + new Date(0).toUTCString(); //清除当前域名下
// document.cookie =
// keys[i] +"=0;path=/;domain=" +document.domain +";expires=" +
// new Date(0).toUTCString();
// document.cookie =
// keys[i] +"=0;path=示例:/index.vue不同域的path,也就是你清除不了的cookie;domain=示例10.10.10.208不同域的domain,也就是你清除不了的cookie;expires=" +
// new Date(0).toUTCString();
// // **document.cookie可加多条**
// }
// }
// },
},
};
</script>
<style lang="less" scoped>
@import url("../../styles/comStyle.less");
</style>
<style lang="scss" scoped>
.item {
margin-top: 0px;
margin-right: 20px;
}
.right-menu {
display: flex;
border-left: 1px solid #d4d4d400 !important;
//background: red;
height: 100%;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
height: 100%;
padding: 0 8px;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
.menu-item {
display: flex;
align-items: center;
height: 100%;
padding: 0 10px;
color: #fff;
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 0.1);
}
.user-avatar {
width: 30px;
height: 30px;
margin-right: 8px;
border-radius: 10px;
}
.el-icon-caret-bottom {
font-size: 12px;
}
}
}
.user-dropdown {
min-width: 100px;
// display: flex;
position: absolute;
z-index: 9999999999999 !important;
left: 99%;
}
</style>
<style>
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
.v-modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.5;
/* background: #7a6464; */
}
</style>

View File

@@ -0,0 +1,189 @@
<template>
<el-drawer
title="页面设置"
:visible.sync="$store.state.app.showSettings"
direction="rtl"
append-to-body
custom-class="settings-drawer"
>
<div class="drawer-container">
<div class="drawer-item">
<span>布局类型</span>
<ul class="layout">
<li @click="changeSetting({layout:'layout1'})">
<img src="../../../assets/layout/layout1.svg" alt="layout1">
<i v-show="settings.layout === 'layout1'" class="el-icon-check" />
</li>
<li @click="changeSetting({layout:'layout2'})">
<img src="../../../assets/layout/layout2.svg" alt="layout2">
<i v-show="settings.layout === 'layout2'" class="el-icon-check" />
</li>
<li @click="changeSetting({layout:'layout3'})">
<img src="../../../assets/layout/layout3.svg" alt="layout3">
<i v-show="settings.layout === 'layout3'" class="el-icon-check" />
</li>
<li @click="changeSetting({layout:'layout4'})">
<img src="../../../assets/layout/layout4.svg" alt="layout4">
<i v-show="settings.layout === 'layout4'" class="el-icon-check" />
</li>
</ul>
</div>
<div class="drawer-item">
<span>主题色</span>
<el-color-picker
:value="settings.themeColor || '#4451B2'"
:predefine="['#4451B2', '#12B1B1', '#409EFF', '#E84B16', '#fa541c','#EBA313', '#4CB418', '#2f54eb', '#722ed1']"
popper-class="theme-picker-dropdown"
style="float: right; height: 26px; margin: -3px 8px 0 0;"
class="theme-picker"
@change="themeColor=>changeSetting({themeColor})"
/>
</div>
<div class="drawer-item">
<span>布局大小</span>
<el-select
size="mini"
class="drawer-select"
:value="settings.size"
@change="sizeChange"
>
<el-option label="小" value="mini" />
<el-option label="中" value="small" />
<el-option label="大" value="medium" />
</el-select>
</div>
<div class="drawer-item">
<span>开启页签</span>
<el-switch
:value="settings.tagsView"
class="drawer-switch"
@input="tagsView=>changeSetting({tagsView})"
/>
</div>
<div class="drawer-item">
<span>固定头部</span>
<el-switch
:value="settings.layout !== 'layout2' && settings.layout !== 'layout1' ? true : settings.fixedHeader"
class="drawer-switch"
:disabled="settings.layout !== 'layout2' && settings.layout !== 'layout1' "
@input="fixedHeader=>changeSetting({fixedHeader})"
/>
</div>
</div>
</el-drawer>
</template>
<script>
import { mapActions } from 'vuex'
import { addClass } from '@/utils/className.js'
import { removeClass } from '@/utils/className'
export default {
data() {
return {}
},
computed: {
settings() {
return this.$store.state.settings
}
},
methods: {
...mapActions({
changeSetting: 'settings/changeSetting'
}),
sizeChange(size) {
this.$ELEMENT.size = size
//console.log(`PAGE_SIZE-${this.settings.size}`)
removeClass(document.body, `PAGE-SIZE-${this.settings.size}`)
addClass(document.body, `PAGE-SIZE-${size}`)
this.changeSetting({ size })
this.refreshView()
this.$message({
message: '切换布局大小成功',
type: 'success'
})
},
refreshView() {
// In order to make the cached page re-rendered
this.$store.dispatch('tagsView/delAllTags')
const { fullPath } = this.$route
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
}
}
}
</script>
<style lang="scss" scoped>
.drawer-container {
padding: 0 24px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
.drawer-item {
padding: 12px 0;
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
.layout {
margin-top: 16px;
& > li {
position: relative;
display: inline-block;
width: 52px;
height: 45px;
margin-right: 10px;
img {
width: 100%;
height: 100%;
}
i {
position: absolute;
top: 50%;
left: 50%;
font-size: 22px;
font-weight: bold;
color: #409EFF;
transform: translate(-50%, -50%);
}
}
}
}
.drawer-switch {
float: right;
}
.drawer-select {
float: right;
width: 60px;
}
}
.theme-picker ::v-deep .el-color-picker__trigger {
width: 26px !important;
height: 26px !important;
padding: 2px;
}
</style>
<style>
.theme-picker-dropdown {
z-index: 99999 !important;
}
.settings-drawer {
width: 250px !important;
}
</style>

View File

@@ -0,0 +1,26 @@
export default {
computed: {
device() {
return this.$store.state.app.device
}
},
mounted() {
// In order to fix the click on menu on the ios device will trigger the mouseleave bug
// https://github.com/PanJiaChen/vue-element-admin/issues/1135
this.fixBugIniOS()
},
methods: {
fixBugIniOS() {
const $subMenu = this.$refs.subMenu
if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave
$subMenu.handleMouseleave = (e) => {
if (this.device === 'mobile') {
return
}
handleMouseleave(e)
}
}
}
}
}

View File

@@ -0,0 +1,29 @@
<script>
export default {
name: 'menu-item',
functional: true,
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
}
},
render(h, context) {
const { icon, title } = context.props
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
}
if (title) {
vnodes.push(<span slot='title'>{(title)}</span>)
}
return vnodes
}
}
</script>

View File

@@ -0,0 +1,36 @@
<template>
<!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)">
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils/validate'
export default {
props: {
to: {
type: String,
required: true
}
},
methods: {
linkProps(url) {
if (isExternal(url)) {
return {
is: 'a',
href: url,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'router-link',
to: url
}
}
}
}
</script>

View File

@@ -0,0 +1,57 @@
<template>
<div>
<template v-if='!item.children || item.children.every(it=>it.hidden)'>
<app-link :to='to(item)'>
<el-menu-item :index='item.routePath' :class="{'submenu-title-noDropdown':!isNest}">
<item :icon='item.icon' :title='item.title' />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref='subMenu' :index='item.routePath' popper-append-to-body>
<template slot='title'>
<item :icon='item.icon' :title='item.title' />
</template>
<sidebar-item
v-for='(child,idx) in item.children'
:key='idx'
:index='idx'
:is-nest='true'
:item='child'
class='nest-menu'
/>
</el-submenu>
</div>
</template>
<script>
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
name: 'sidebar-item',
components: { Item, AppLink },
mixins: [FixiOSBug],
props: {
item: {
type: Object,
required: true
},
index: {
type: Number,
required: true
},
isNest: {
type: Boolean,
default: false
}
},
methods: {
to(menuItem) {
const visitedTags = this.$store.state.tagsView.visitedTags
const tag = visitedTags.find(t => t.routeName === menuItem.routeName)
return tag ? tag.routePath : menuItem.routePath
}
}
}
</script>

View File

@@ -0,0 +1,50 @@
<template>
<div class="sidebar-container">
<el-menu :default-active="activeMenu" :collapse="collapse" :collapse-transition="false" unique-opened>
<sidebar-item v-for="(item, index) in menus" :key="index" :item="item" :index="index" />
</el-menu>
</div>
</template>
<script>
import SidebarItem from './SidebarItem'
export default {
components: { SidebarItem },
props: {
menus: {
type: Array,
default: []
},
collapse: {
type: Boolean,
default: false
}
},
watch: {
collapse() {
setTimeout(() => {
window.echartsArr &&
window.echartsArr.forEach(item => {
item.resize()
})
}, 200)
}
},
computed: {
activeMenu() {
if (this.$route.meta.activeMenu) {
return this.$route.meta.activeMenu
}
if(this.$route.fullPath.indexOf('iframe') > -1){
return this.$route.fullPath
}else{
return this.$route.path
}
}
}
}
</script>
<style lang="scss" scoped>
@import './sidebar.scss';
</style>

View File

@@ -0,0 +1,50 @@
.sidebar-container ::v-deep {
.svg-icon {
// margin-right: 16px;
margin-right: 5px;
}
.el-menu {
width: 100% !important;
height: 100%;
border: none;
}
& .nest-menu {
font-size: 0;
}
.el-menu>div {
padding: 5px 0;
}
.el-menu-item,
.el-submenu__title {
height: 40px;
line-height: 40px;
}
.el-submenu__title {
.el-submenu__icon-arrow {
position: relative;
right: 0;
float: right;
}
}
.el-menu--collapse {
.el-submenu {
&>.el-submenu__title {
&>span {
display: inline-block;
width: 0;
height: 0;
overflow: hidden;
visibility: hidden;
}
.el-submenu__icon-arrow {
display: none;
}
}
}
}
}
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}

View File

@@ -0,0 +1,100 @@
<template>
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
<slot />
</el-scrollbar>
</template>
<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing
export default {
name: 'scroll-pane',
props: {
watchData: {}
},
data() {
return {
left: 0
}
},
computed: {
scrollWrapper() {
return this.$refs.scrollContainer.$refs.wrap
}
},
watch: {
watchData: {
handler() {
this.$nextTick(() => {
this.$refs.scrollContainer.update()
})
},
immediate: false,
deep: true
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40
const $scrollWrapper = this.scrollWrapper
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
},
moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.scrollWrapper
const tagList = this.$parent.$refs.tag
let firstTag = null
let lastTag = null
// find first tag and last tag
if (tagList.length > 0) {
debugger
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
// find preTag and nextTag
const currentIndex = tagList.findIndex(item => item === currentTag)
const prevTag = tagList[currentIndex - 1]
const nextTag = tagList[currentIndex + 1]
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
}
}
}
}
</script>
<style lang="scss" scoped>
.scroll-container {
position: relative;
width: 100%;
overflow: hidden;
white-space: nowrap;
::v-deep {
.el-scrollbar__bar {
bottom: 0;
}
.el-scrollbar__wrap {
// height: 49px;
}
}
}
</style>

View File

@@ -0,0 +1,467 @@
<template>
<div id='tags-view-container' class='tags-view-container'>
<scroll-pane ref='scrollPane' :watch-data='visitedTags' class='tags-view-wrapper'>
<router-link v-for='tag in visitedTags' ref='tag' :key='tag.routeName'
:class="isActive(tag) ? 'active' : ''"
:to='tag.routePath' tag='span' class='tags-view-item'
@click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent.native='openMenu(tag, $event)'>
<i v-if='tag.routeName == routeName' class='el-icon-s-home'></i>
{{ tag.title }}
<span v-if='tag.routeName == routeName' />
<span v-else class='el-icon-close' @click.prevent.stop='closeSelectedTag(tag)' />
</router-link>
</scroll-pane>
<ul v-if='selectedTag.routeName==routeName' v-show='visible' :style="{ left: left + 'px', top: top + 'px' }"
class='contextmenu'>
<li @click='refreshSelectedTag(selectedTag)'><i class='el-icon-refresh-right'></i> 刷新页面</li>
<li @click='closeOthersTags(selectedTag)'><i class='el-icon-circle-close'></i> 关闭其他</li>
<li @click='closeAllTags'><i class='el-icon-circle-close'></i> 关闭所有</li>
</ul>
<ul v-else v-show='visible' :style="{ left: left + 'px', top: top + 'px' }" class='contextmenu'>
<li @click='refreshSelectedTag(selectedTag)'><i class='el-icon-refresh-right'></i> 刷新页面</li>
<li @click='closeSelectedTag(selectedTag)'><i class='el-icon-close'></i> 关闭当前</li>
<li @click='closeOthersTags(selectedTag)'><i class='el-icon-circle-close'></i> 关闭其他</li>
<li @click='closeAllTags'><i class='el-icon-circle-close'></i> 关闭所有</li>
</ul>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
import Sortable from 'sortablejs'
import { mapGetters } from 'vuex'
export default {
components: { ScrollPane },
data() {
return {
visible: false,
top: 0,
left: 0,
selectedTag: {},
affixTags: [],
menulists: [],
path: '',
routeName: '',
title: ''
}
},
computed: {
visitedTags() {
// console.log("+++++++++++++++++===================++++++++++++++++++++",this.$store.state.tagsView.visitedTags)
let arr = this.$store.state.tagsView.visitedTags
arr.filter((item, index) => arr.indexOf(item) === index)
//console.log("########----------",this.$store.state.user.userInfo.loginName)
// if(this.$store.state.user.userInfo==null){
// return arr;
// }else{
// if(this.$store.state.user.userInfo.roleCode[0]=='root'){
// // arr[0]={
// // routeName:"qualityzhilestimate",
// // routePath: "/harmonic-boot/area/powerAssessment",
// // title: "电能质量综合评估"
// // };
// // arr[0]={
// // routeName:"test",
// // routePath: "/jbei/QualityOverview",
// // title: "电能质量概览"
// // };
// arr[0]={
// routeName:"dashboard",
// routePath: "/dashboard/index",
// title: "首页概览"
// };
// }
// if(this.$store.state.user.userInfo.loginName=='njcnpqs'){
// arr[0]={
// routeName:"uesrmanagement",
// routePath: "/user-boot/user/list",
// title: "用户管理"
// };
// }else if(this.$store.state.user.userInfo.loginName=='njcnser'){
// arr[0]={
// routeName:"premage",
// routePath: "/BusinessAdministrator/TerminalManagement/FrontManagement",
// title: "前置管理"
// };
// }else if(this.$store.state.user.userInfo.loginName=='njcnsh'){
// arr[0]={
// routeName:"sys-user-boot-check",
// routePath: "/user/checkUserListview",
// title: "审核列表"
// };
// }else if(this.$store.state.user.userInfo.loginName=='njcnsj'){
// arr[0]={
// routeName:"Management",
// routePath: "/BusinessAdministrator/Audit/Operations/Management",
// title: "审计列表管理"
// };
// }else{
// arr[0]={
// routeName:"qualityzhilestimate",
// routePath: "/harmonic-boot/area/powerAssessment",
// title: "电能质量综合评估"
// };
// arr[0]={
// routeName:"test",
// routePath: "/jbei/QualityOverview",
// title: "电能质量概览"
// };
// console.log("########----------",this.$store.state.user)
// arr[0]={
// routeName:"dashboard",
// routePath: "/dashboard/index",
// title: "首页概览"
// };
this.menulists = JSON.parse(window.sessionStorage.getItem('menus'))
this.getmuen(this.menulists[0])
arr[0] = {
routeName: this.routeName,
routePath: this.path,
title: this.title
}
// }
// }
// console.log('++++++++++++', arr)
return arr
}
},
watch: {
$route() {
this.addTags()
//this.moveToCurrentTag();
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
mounted() {
this.addTags()
this.setSort()
//this.filtertag()
},
methods: {
getmuen(data) {
if (data.children.length == 0) {
this.path = data.routePath
this.routeName = data.routeName
this.title = data.title
} else {
this.getmuen(data.children[0])
}
},
filtertag() {
let arr = this.$store.state.tagsView.visitedTags
arr.filter((item, index) => arr.indexOf(item) === index)
return arr
},
isActive(tag) {
return tag.routePath === this.$route.fullPath
},
isAffix(tag) {
const { affixTags } = this.$store.state.tagsView
return affixTags.some((t) => t.routeName === tag.routeName)
},
addTags() {
// alert(0)
const { name } = this.$route
if (name) {
this.$store.dispatch('tagsView/addView', this.$route)
}
return false
},
//移除当前 标签
moveToCurrentTag() {
// alert(1)
const tags = this.$refs.tag
tags.splice(0, 1)
// this.$router.push("/Statistical-analysis/indicatorClassification");
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to === this.$route.fullPath) {
//if (tag.to !== "/Statistical-analysis/indicatorClassification") {
this.$refs.scrollPane.moveToTarget(tag)
break
//}
}
}
})
},
//刷新当前
refreshSelectedTag(tag) {
// alert(2)
const fullPath = tag.routePath
this.$store.commit('tagsView/DEL_CACHED_VIEW', tag.routeName)
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
},
open() {
},
//关闭选择
closeSelectedTag(tag) {
// console.log("+++++++++++++++++++++",tag)
if (tag.title == '算法编辑器') {
this.$confirm('此操作将关闭删除该标签,是否已保存! 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$store.dispatch('tagsView/delTag', tag).then(({ visitedTags }) => {
if (this.isActive(tag)) {
this.toLastView(visitedTags, tag)
// this.$message({
// type: 'success',
// message: '确认保存成功!'
// });
//this.$router.push("/Statistical-analysis/indicatorClassification");
// this.$nextTick(() => {
// this.$router.replace({
// path: "/Statistical-analysis/indicatorClassification",
// });
// this.$router.push("/Statistical-analysis/indicatorClassification");
// });
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消关闭'
})
})
} else {
this.$store.dispatch('tagsView/delTag', tag).then(({ visitedTags }) => {
if (this.isActive(tag)) {
this.toLastView(visitedTags, tag)
}
})
}
},
//关闭其他
closeOthersTags(tag) {
// alert(4)
this.$router.push(tag.routePath)
const fullPath = tag.routePath
this.$store.dispatch('tagsView/delOthersTags', tag)
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
this.moveToCurrentTag()
},
//关闭所有
closeAllTags() {
// alert(5)
this.$store.dispatch('tagsView/delAllTags')
//this.$router.push("/dashboard/index");
//this.$router.push("/jbei/QualityOverview");
this.$nextTick(() => {
// this.$router.replace({
// path: "/dashboard/index",
// });
//this.$router.push("/dashboard/index");
// this.$router.replace({
// path: "/jbei/QualityOverview",
// });
// this.$router.push("/jbei/QualityOverview");
})
// 关完还剩下的
const { visitedTags } = this.$store.state.tagsView
const lastTag = visitedTags[visitedTags.length - 1]
if (lastTag) {
if (this.$route.fullPath !== lastTag.routePath) {
this.$router.push(lastTag.routePath)
}
} else {
this.getmuen(this.menulists[0])
this.$router.replace({ path: '/redirect' + this.path })
//if (this.$route.name === "dashboard") {
// this.$router.replace({ path: "/redirect" + this.$route.path });
//} else {
// this.$router.push("/");
//}
}
},
toLastView(visitedTags, view) {
// alert(6)
const latestTag = visitedTags.slice(-1)[0]
if (latestTag) {
this.$router.push(latestTag.routePath)
} else {
if (view.name === 'dashboard') {
// this.$router.replace({ path: "/redirect" + view.routePath });
} else {
this.$router.push('/')
}
}
},
openMenu(tag, e) {
const menuMinWidth = 105
const windowWidth = document.documentElement.clientWidth
const maxLeft = windowWidth - menuMinWidth // left boundary
const left = e.clientX + 15 // 15: margin right
if (left > maxLeft) {
this.left = maxLeft
} else {
this.left = left
}
this.top = e.clientY
this.visible = true
this.selectedTag = tag
},
closeMenu() {
this.visible = false
},
setSort() {
const el = this.$refs.scrollPane.$el.querySelector('.el-scrollbar__view')
this.sortable = Sortable.create(el, {
ghostClass: 'tags-view-ghost', // Class name for the drop placeholder,
setData: function(dataTransfer) {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
}
})
//console.log("kjkjkjkjkjkjkjkjkjkjk",this.$router)
// this.$router.push('/Statistical-analysis/indicatorClassification')
}
}
}
</script>
<style lang='scss' scoped>
.tags-view-container {
width: 100%;
height: 34px;
background: rgb(255, 255, 255);
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {
position: relative;
display: inline-block;
height: 26px;
padding: 0 8px;
margin-top: 4px;
margin-left: 5px;
font-size: 12px;
line-height: 26px;
color: $themeColor;
cursor: pointer;
background: #fff;
border: 1px solid #d9ecff;
border-radius: 3px;
&:first-of-type {
margin-left: 15px;
}
&:last-of-type {
margin-right: 15px;
}
&.active {
color: #fff;
background-color: $themeColor;
border-color: $themeColor;
&::before {
position: relative;
//display: inline-block;
width: 8px;
height: 8px;
margin-right: 2px;
// background: #fff;
border-radius: 50%;
content: "";
}
}
}
}
.contextmenu {
position: fixed;
z-index: 3000;
padding: 5px 0;
margin: 0;
font-size: 12px;
font-weight: 400;
color: #333;
list-style-type: none;
background: #fff;
border-radius: 4px;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
padding: 7px 16px;
margin: 0;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}
</style>
<style lang='scss' scoped>
//reset element css of el-icon-close
::v-deep .el-scrollbar__wrap {
overflow: auto !important;
height: 48px !important;
margin-bottom: 0 !important;
}
::v-deep ::-webkit-scrollbar-thumb {
background: $themeColor !important;
height: 8px ! important
}
::-webkit-scrollbar {
display: block;
width: 8px !important;
height: 14px;
}
::v-deep .el-scrollbar__thumb {
position: relative;
display: block;
width: 0;
height: 0;
cursor: pointer !important;
border-radius: inherit;
background-color: $themeColor !important;
-webkit-transition: .3s background-color;
transition: .3s background-color;
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<div>
</div>
</template>
<script>
export default {
data(){
return {
}
},
created(){
// setInterval(()=>{
// this.open1()
// },60000)
setInterval(()=>{
this.ddd()
},200000)
},
mounted(){
},
methods:{
// // 打开一个新的通知
// openNotify(userNotification) {
// // userNotification 中的userNotifyId是唯一的
// const h = this.$createElement
// let notify= this.$notify.success({
// title: data.notification.notifyName,
// dangerouslyUseHTMLString: true,
// position: 'bottom-right',
// message: h(
// 'p',
// {
// // 相当于`v-bind:style`
// style: {
// color: 'red',
// fontSize: '14px'
// },
// class: 'pointer',
// on: {
// click: () => {
// this.closeNotification(
// data.notification.data.message,
// data.userNotifyId,
// )
// }
// }
// },
// data.notification.data.message
// ),
// duration: 60 * 1000
// })
// this.notifications[data.userNotifyId] = notify
// },
// //关闭单个通知
// closeNotification(message, id){
// this.notifications[id].close();
// delete this.notifications[id];
// },
// // 关闭所有通知
// closeAllNotification(){
// for (let key in this.notifications) {
// this.notifications[key].close();
// delete this.notifications[key];
// }
// }
open1() {
const h = this.$createElement
const hrender = h('p', null, [
h('div', [
h('div','终端名称:pqs600-200 '), // 传变量
h('div','告警类型:流量告警'),
h('div','告警发生时间:2022-05-25 12:25:54'),
], null),
h('button', {
class: '样式',
}, "处理"),
])
var rq = document.getElementById('ppp')
this.$notify({
title: '告警通知',
dangerouslyUseHTMLString: true,
message: hrender,
type: 'warning',
// position: 'bottom-right',
offset: 50,
duration: 0,
// showClose: false,
onClick: () => {
//this.toApproval(approvalQuery)
this.$router.push("/BusinessAdministrator/alarManagement/AlarmInformation");
},
onClose: () => {
// alert(`Notify已关闭说明异常或已查看`)
}
})
//this.yuyin()
},
ddd(){
this.$notify.closeAll()
},
yuyin(){
let utterThis = new window.SpeechSynthesisUtterance();
// utterThis.text = data.toString(); //播放内容
utterThis.text= '终端pqs9000-300空调外机启动测试发生严重告警,请尽快联系管理员处理'; //播放内容
window.speechSynthesis.speak(utterThis);
}
}
}
</script>

131
src/layout/index.vue Normal file
View File

@@ -0,0 +1,131 @@
<template>
<div :class="classObj" class="layout-wrapper">
<!-- <div id="ppp" style="right:0px;width:200px;height:90%;px;margin-top:3%;background:red;position: absolute;z-index:200 ;overflow-y: auto;"> -->
<!-- <alarmcenter style="width:100px; z-index: " ></alarmcenter> -->
<!-- </div> -->
<component :is="layout">
<div ref="appmain" id="APP_MAIN" />
</component>
<!-- 避免切换布局时app-main更新 -->
<portal :target="`.${layout} #APP_MAIN`">
<app-main />
</portal>
<settings />
<MqttWs v-if="show" />
</div>
</template>
<script>
import { mapState } from 'vuex'
import alarmcenter from '@/layout/components/alarmcenter/alarmcenter.vue'
import Layout1 from './layout1/index.vue'
import Layout2 from './layout2/index.vue'
import Layout3 from './layout3/index.vue'
import Layout4 from './layout4/index.vue'
import AppMain from './components/AppMain.vue'
import Settings from './components/Settings/index.vue'
import Portal from '@/components/Portal'
import MqttWs from '@/components/Mqtt/mqttWs.vue'
import { getThemeColor, dictypeData, getSysConfig } from '@/api/user'
import { addClass, removeClass } from '@/utils/className.js'
// 用来切换样式
const LayoutList = ['layout1', 'layout2', 'layout3', 'layout4']
export default {
name: 'layout',
components: {
Layout1,
Layout2,
Layout3,
Layout4,
AppMain,
Settings,
Portal,
alarmcenter,
MqttWs
},
data() {
return {
appwidth: null,
appheight: null,
show: false
}
},
computed: {
...mapState({
sidebar: state => state.app.sidebar,
device: state => state.app.device,
needTagsView: state => state.settings.tagsView,
fixedHeader: state => state.settings.fixedHeader
}),
classObj() {
return {
hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile',
fixedHeader: this.fixedHeader,
needTagsView: this.needTagsView
}
},
layout() {
const { layout } = this.$store.state.settings
const body = document.body
LayoutList.forEach(item => removeClass(body, item))
addClass(body, layout)
return layout
}
},
created() {
//this.getdicData()
},
mounted() {
// setTimeout(() => {
// this.appwidth = document.getElementById("app").offsetWidth -205
// this.appheight = document.getElementById("app").offsetHeight -105
// window.sessionStorage.setItem("appwidth",this.appwidth);
// window.sessionStorage.setItem("appheight", this.appheight);
// // console.log("+++++++++++++++++++++", document.getElementById("app").offsetHeight);
// }, 100);
//window.sessionStorage.setItem('appheight',this.$refs.appmain.offsetHeight)
this.show = JSON.parse(window.sessionStorage.getItem('Info')).roleCode.includes('audit_manager')
},
methods: {
getdicData() {
dictypeData().then(response => {
const dictType = response.data
window.sessionStorage.setItem('dictypeData', JSON.stringify(dictType))
// setDictype(JSON.stringify(dictType))
})
this.getSystemData()
},
getSystemData() {
getSysConfig().then(res => {
const sysdata = res.data
let systype = res.data.type
window.sessionStorage.setItem('sysdata', JSON.stringify(sysdata))
window.sessionStorage.setItem('systype', systype)
//setSysConfig()
this.SysConfig()
})
},
SysConfig() {
getSysConfig().then(res => {
if (res.code == 'A0000') {
let systype = res.data.type
window.sessionStorage.setItem('systype', systype)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.layout-wrapper {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,206 @@
.layout1-aside {
position: relative;
z-index: 10;
width: 100% !important;
height: 100%;
background-color: $themeColor;
transition: width 0.28s;
.sidebar-logo-container {
.sidebar-logo-link {
display: flex;
align-items: center;
.sidebar-title {
display: inline-block;
line-height: 18px;
flex: 1;
@include nowrap(3);
}
}
}
}
.layout1-navbar {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
padding-right: 20px;
overflow: hidden;
background: $themeColor;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.navbar-left {
display: flex;
align-items: center;
height: 100%;
}
.hamburger-container {
height: 100%;
line-height: 60px;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.right-menu .menu-item {
color: #f5f6f8 !important;
background: $themeColor !important;
&:hover {
background: rgba(0, 0, 0, 0.025) !important;
}
}
}
//布局
.layout1 {
.app-container {
//background: blue;
position: relative;
width: 100%;
height: 100%;
background: #F0F2F5;
.app-aside {
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 3;
width: $sideBarWidth;
height: 100%;
//background: yellow;
background: $themeColor;
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
transition: width 0.28s;
// 侧边栏滚动条
.el-scrollbar {
height: 100%;
}
.el-scrollbar__bar {
z-index: 10;
}
.el-scrollbar__bar.is-vertical {
right: 0;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
}
.app-main {
position: relative;
height: 100%;
margin-left: $sideBarWidth;
overflow-y: auto;
transition: margin-left 0.28s;
//z-index: 2;
.app-header-in {
//background: green;
position: absolute;
top: 0;
right: 0;
left: 0;
height: 60px;
}
.app-main-in {
position: relative;
height: calc(100% - 60px);
margin-top: 60px;
background: #F0F2F5;
}
}
}
.fixedHeader {
.app-main-in {
overflow-y: auto;
}
}
.hideSidebar {
.app-aside {
width: 54px !important;
}
.app-main {
margin-left: 54px !important;
}
}
.needTagsView {
.app-header-in {
height: 94px !important;
.layout1-navbar {
height: 60px;
}
}
.app-main-in {
height: calc(100% - 94px) !important;
margin-top: 94px !important;
}
}
//.mobile {
// .app-main {
// margin-left: 0 !important;
// .app-header-in {
// left: 0 !important;
// }
// }
//
// .app-aside {
// position: fixed;
// top: 0;
// left: 0;
// bottom: 0;
// z-index: 100;
// transition: left .28s !important;
// width: $sideBarWidth !important;
// }
// &.hideSidebar {
// .app-aside {
// left: -$sideBarWidth;
// }
// }
//}
.withoutAnimation {
.app-main {
transition: none !important;
}
.app-aside {
transition: none !important;
}
}
}
//layout1 菜单颜色样式
.layout1 .el-menu {
background-color: $themeColor;
.el-menu-item,
.el-submenu__title {
color: rgba(255, 255, 255, 0.7);
background-color: $themeColor;
.el-submenu__icon-arrow {
color: rgba(255, 255, 255, 0.5);
}
&:hover,
&:focus {
color: #fff;
background-color: rgba(0, 0, 0, 0.1);
.el-submenu__icon-arrow {
color: #fff;
}
}
&.is-active {
color: #fff;
background-color: rgba(0, 0, 0, 0.2);
}
}
.is-active .el-submenu__title {
color: #fff;
.el-submenu__icon-arrow {
color: #fff;
}
}
}

View File

@@ -0,0 +1,88 @@
<template>
<div class="app-container">
<!-- <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />-->
<div class="app-aside">
<el-scrollbar wrap-class="scrollbar-wrapper">
<div class="layout1-aside">
<logo :collapse="!sidebar.opened" />
<sidebar :collapse="isCollapse" :menus="menus" />
</div>
</el-scrollbar>
</div>
<div class="app-main">
<div class="app-header-in">
<div class="layout1-navbar">
<div class="navbar-left">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
</div>
<right-menu />
</div>
<tags-view v-if="needTagsView" />
</div>
<div class="app-main-in" id='app-main-in'>
<slot />
</div>
</div>
</div>
</template>
<script>
import Logo from '../components/Logo.vue'
import Sidebar from '../components/Sidebar/index.vue'
import TagsView from '../components/TagsView'
import RightMenu from '../components/RightMenu.vue'
import Breadcrumb from '../components/Breadcrumb'
import Hamburger from '../components/Hamburger'
import ResizeMixin from '../mixin/ResizeHandler'
export default {
name: 'layout1',
components: {
Logo,
RightMenu,
Sidebar,
Breadcrumb,
Hamburger,
TagsView
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
menus() {
return this.$store.getters.menus
},
isCollapse() {
return !this.sidebar.opened
},
needTagsView() {
return this.$store.state.settings.tagsView
},
device() {
return this.$store.state.app.device
}
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
},
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
}
}
}
</script>
<style lang="scss">
@import './index.scss';
.drawer-bg {
position: absolute;
top: 0;
z-index: 99;
width: 100%;
height: 100%;
background: #000;
opacity: 0.3;
}
</style>

View File

@@ -0,0 +1,116 @@
//头部
.layout2-navbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
padding: 0 20px;
background-color: $themeColor;
//logo
.sidebar-logo-container {
width: auto;
padding-right: 16px;
padding-left: 0;
}
// menu
.navbar-menu {
display: flex;
justify-content: center;
flex: 1;
}
}
//布局
.layout2 {
.app-container {
width: 100%;
height: 100%;
overflow-y: auto;
//background: blue;
background: $themeColor;
.app-header {
height: 60px;
background: green;
}
.app-main {
position: relative;
height: calc(100% - 60px);
background: #F0F2F5;
}
}
.fixedHeader {
.app-main {
overflow-y: auto;
}
.app-header {
position: relative;
z-index: 1;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
}
}
.needTagsView {
.app-header {
position: relative;
height: 94px !important;
box-shadow: none !important;
}
.app-main {
height: calc(100% - 94px) !important;
}
&.fixedHeader .app-main {
height: calc(100% - 94px);
}
}
}
//layout2 菜单颜色样式
.layout2 .el-menu {
background-color: $themeColor;
.el-menu-item,
.el-submenu__title {
color: rgba(255, 255, 255, 0.7);
background-color: $themeColor;
.el-submenu__icon-arrow {
color: rgba(255, 255, 255, 0.7);
}
&:hover,
&:focus {
color: #fff;
background-color: rgba(0, 0, 0, 0.1);
.el-submenu__icon-arrow {
color: #fff;
}
}
&.is-active {
color: #fff;
}
}
.is-active .el-submenu__title {
color: #fff;
.el-submenu__icon-arrow {
color: #fff;
}
}
&.el-menu--horizontal > .el-menu-item {
border: none;
&.is-active {
background-color: rgba(0, 0, 0, 0.15);
//background-color: #409EFF;
}
}
&.el-menu--horizontal > .el-submenu .el-submenu__title {
border: none;
}
&.el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
background-color: rgba(0, 0, 0, 0.15);
border: none;
//background-color: #409EFF;
}
&.el-menu--popup .el-menu-item.is-active {
//background-color: #409EFF;
background-color: rgba(0, 0, 0, 0.15);
}
&.el-menu--popup .is-active .el-submenu__title {
color: #fff;
}
}

View File

@@ -0,0 +1,45 @@
<template>
<div class="app-container">
<div class="app-header">
<div class="layout2-navbar">
<logo :collapse="false" />
<navbar :menus="menus" />
<right-menu />
</div>
<tags-view v-if="needTagsView" />
</div>
<div class="app-main" id='app-main-in'>
<slot />
</div>
</div>
</template>
<script>
import Logo from '../components/Logo.vue'
import Navbar from '../components/Navbar/index.vue'
import RightMenu from '../components/RightMenu.vue'
import TagsView from '../components/TagsView'
// import ResizeMixin from '../mixin/ResizeHandler'
export default {
components: {
Logo,
RightMenu,
Navbar,
TagsView
},
// mixins: [ResizeMixin],
computed: {
menus() {
return this.$store.getters.menus
},
needTagsView() {
return this.$store.state.settings.tagsView
}
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -0,0 +1,217 @@
.layout3-navbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
padding: 0 20px;
background-color: #fff;
border-bottom: solid 1px #e6e6e6;
//logo
.sidebar-logo-container {
width: auto;
padding-right: 16px;
padding-left: 0;
.sidebar-logo {
color: $themeColor !important;
}
.sidebar-title {
color: #333 !important;
}
}
// 菜单
.navbar-menu {
display: flex;
justify-content: center;
flex: 1;
.el-menu.el-menu--horizontal {
border-bottom: solid 1px #e6e6e6;
}
}
// 右侧按钮
.right-menu .menu-item {
color: #303133 !important;
background: #fff !important;
&:hover {
background: rgba(0, 0, 0, 0.025) !important;
}
}
}
.layout3-aside {
width: 100%;
height: 100%;
overflow-x: hidden;
background-color: #fff;
border-right: solid 1px #e6e6e6;
box-sizing: border-box;
transition: width 0.28s;
.subMenu-title {
display: flex;
align-items: center;
justify-content: space-between;
width: $sideBarWidth;
height: 60px;
padding-left: 20px;
border-bottom: solid 1px #e6e6e6;
.hamburger-container {
height: 100%;
line-height: 60px;
&:hover {
background-color: rgba(0, 0, 0, 0.025);
}
}
}
}
//布局样式
.layout3 {
.app-container {
width: 100%;
height: 100%;
.app-header {
height: 60px;
//background: red;
}
.app-container-in {
position: relative;
width: 100%;
height: calc(100% - 60px);
//background: blue;
background: #F0F2F5;
.app-aside {
//background: yellow;
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 1;
width: $sideBarWidth;
transition: width 0.28s;
// 侧边栏滚动条
.el-scrollbar {
height: calc(100% - 60px);
}
.el-scrollbar__bar {
z-index: 10;
}
.el-scrollbar__bar.is-vertical {
right: 0;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
}
.app-main {
position: relative;
width: calc(100% - #{$sideBarWidth});
height: 100%;
margin-left: $sideBarWidth;
overflow-y: auto;
background-color: #F0F2F5;
transition: margin-left 0.28s, width 0.28s;
}
}
}
.hideSidebar {
.app-aside {
width: 54px !important;
}
.app-main {
width: calc(100% - 54px) !important;
margin-left: 54px !important;
}
.subMenu-title {
padding-left: 0;
& > span {
display: none;
}
}
.app-aside .el-scrollbar {
display: none;
}
}
.nosidebar {
.app-aside {
width: 0 !important;
}
.app-main {
width: 100% !important;
margin-left: 0 !important;
transition: none !important;
}
}
.needTagsView {
.app-header {
position: relative;
height: 94px !important;
}
.app-container-in {
height: calc(100% - 94px) !important;
}
}
}
//layout3 菜单样式
.layout3 .app-aside .el-menu {
background-color: #fff;
.el-menu-item,
.el-submenu__title {
color: #999;
background-color: #fff;
.el-submenu__icon-arrow {
color: #999;
}
&:focus {
color: #fff;
.el-submenu__icon-arrow {
color: $themeColor;
}
}
&:hover,
&.is-active {
position: relative;
color: #fff;
background-color: $themeColor;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 3px;
color: #fff;
background-color: $themeColor;
content: '';
}
}
}
.is-active .el-submenu__title {
color: $themeColor;
.el-submenu__icon-arrow {
color: #999;
}
}
}
/* stylelint-disable no-duplicate-selectors */
.layout3 {
.el-menu {
&.el-menu--horizontal > .el-menu-item.is-active {
color: #fff;
background-color: $themeColor;
border-bottom-color: $themeColor;
}
&.el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
color: $themeColor;
background-color: $themeColor;
border-bottom-color: $themeColor;
}
}
.el-menu--popup.el-menu {
.el-menu-item.is-active {
color: #fff;
background-color: $themeColor-11;
}
}
}
/* stylelint-enable no-duplicate-selectors */

View File

@@ -0,0 +1,99 @@
<template>
<div class="app-container">
<div class="app-header">
<div class="layout3-navbar">
<logo :collapse="false" />
<navbar :custom-active-menu="activeMenu" :menus="menus" />
<right-menu />
</div>
<tags-view v-if="needTagsView" />
</div>
<div class="app-container-in" :class="{nosidebar: !subMenu}">
<div v-if="subMenu" class="app-aside">
<div class="layout3-aside">
<div class="subMenu-title">
<span>{{ subMenu.title }}</span>
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
</div>
<el-scrollbar wrap-class="scrollbar-wrapper">
<sidebar :menus="subMenu.children" :collapse="isCollapse" />
</el-scrollbar>
</div>
</div>
<div class="app-main" id='app-main-in'>
<slot />
</div>
</div>
</div>
</template>
<script>
import Logo from '../components/Logo.vue'
import Navbar from '../components/Navbar/index.vue'
import Sidebar from '../components/Sidebar/index.vue'
import RightMenu from '../components/RightMenu.vue'
import TagsView from '../components/TagsView'
import Hamburger from '../components/Hamburger'
import ResizeMixin from '../mixin/ResizeHandler'
export default {
components: {
Logo,
RightMenu,
Sidebar,
Navbar,
Hamburger,
TagsView
},
mixins: [ResizeMixin],
data() {
return {
menus: []
}
},
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
needTagsView() {
return this.$store.state.settings.tagsView
},
activeMenu() {
return this.subMenu ? this.subMenu.routePath : ''
},
subMenu() {
const menus = this.$store.getters.menus
const activeMenu = '/' + this.$route.path.split('/')[1]
const subMenu = menus.find(item => item.routePath === activeMenu) || {}
return subMenu.children ? subMenu : null
},
isCollapse() {
return !this.sidebar.opened
}
},
created() {
const menus = this.$store.getters.menus
this.menus = menus.map(item => ({
...item,
children: null
}))
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
},
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
}
}
}
</script>
<style lang="scss">
@import './index.scss';
.layout3 .app-aside .el-menu .el-menu-item .is-active, .layout3 .app-aside .el-menu .el-submenu__title .is-active {
position: relative;
color: $themeColor;
background-color: $themeColor;
}
</style>

View File

@@ -0,0 +1,185 @@
.layout4-aside {
position: relative;
z-index: 10;
width: 100% !important;
height: 100%;
background-color: $themeColor;
transition: width 0.28s;
}
// .el-menu .el-submenu__title {
// //background-color: #003078;
// padding-left: 10px !important;
// }
// .el-menu {
// padding-left: 10px !important;
// }
// .el-submenu__title {
// //background-color: #003078;
// padding-left: 10px !important;
// }
// .el-submenu .el-menu-item {
// padding-left: 20px !important;
// }
.layout4-navbar {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
padding-right: 20px;
overflow: hidden;
background: $themeColor;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.navbar-left {
display: flex;
align-items: center;
height: 100%;
}
.sidebar-logo-container {
.sidebar-logo-link {
.sidebar-logo {
color: #fff;
}
.sidebar-title {
color: rgb(251, 251, 251);
}
}
}
.right-menu .menu-item {
color: #fbfbfb !important;
background: $themeColor !important;
&:hover {
background: rgba(0, 0, 0, 0.025) !important;
}
}
}
//布局
.layout4 {
.app-container {
//background: blue;
position: relative;
width: 100%;
height: 100%;
background: #F0F2F5;
.app-header {
position: relative;
z-index: 99;
height: 60px;
box-shadow: 0 2px 4px 0 rgba(96, 96, 96, 0.16);
}
.app-container-in {
position: relative;
width: 100%;
height: auto;
height: calc(100% - 60px);
//background: blue;
background: #F0F2F5;
.app-aside {
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 3;
width: $sideBarWidth;
height: 100%;
//background: yellow;
background: $themeColor;
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
transition: width 0.28s;
// 侧边栏滚动条
.el-scrollbar {
height: calc(100% - 30px);
}
.el-scrollbar__bar {
z-index: 10;
}
.el-scrollbar__bar.is-vertical {
right: 0;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
.hamburger-container {
height: 30px;
line-height: 30px;
color: #fff;
text-align: center;
cursor: pointer;
background: rgba(0, 0, 0, 0.05);
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
}
}
.app-main {
position: relative;
height: 100%;
margin-left: $sideBarWidth;
transition: margin-left 0.28s;
.app-main-in {
position: relative;
height: 0%;
//overflow: auto;
background: #f8f9fa;
}
}
}
}
.hideSidebar {
.app-aside {
width: 54px !important;
}
.app-main {
margin-left: 54px !important;
}
}
.needTagsView {
.app-header-in {
height: 34px;
}
.app-main-in {
height: calc(100% - 34px) !important;
}
}
.withoutAnimation {
.app-main {
transition: none !important;
}
.app-aside {
transition: none !important;
}
}
}
//layout4 菜单颜色样式
.layout4 .el-menu {
background-color: $themeColor;
.el-menu-item,
.el-submenu__title {
color: rgba(255, 255, 255, 0.7);
background-color: $themeColor;
.el-submenu__icon-arrow {
color: rgba(255, 255, 255, 0.5);
}
&:hover,
&:focus {
color: #fff;
background-color: rgba(0, 0, 0, 0.1);
.el-submenu__icon-arrow {
color: #fff;
}
}
&.is-active {
color: rgb(0, 245, 253);
background-color: rgba(0, 0, 0, 0.2);
}
}
.is-active .el-submenu__title {
color: #fff;
.el-submenu__icon-arrow {
color: #fff;
}
}
}

View File

@@ -0,0 +1,107 @@
<template>
<div class="app-container">
<!--<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />-->
<div class="app-header">
<div class="layout4-navbar">
<div class="navbar-left">
<logo :collapse="false" />
<!--<breadcrumb class="breadcrumb-container" />-->
</div>
<right-menu />
</div>
</div>
<div class="app-container-in">
<div class="app-aside">
<el-scrollbar wrap-class="scrollbar-wrapper">
<div class="layout4-aside">
<sidebar :collapse="isCollapse" :menus="menus" />
</div>
</el-scrollbar>
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
</div>
<div class="app-main">
<div v-if="needTagsView" class="app-header-in">
<tags-view />
</div>
<div ref="getheight" id='app-main-in' class="app-main-in">
<slot />
</div>
</div>
</div>
</div>
</template>
<script>
import Logo from '../components/Logo.vue'
import Sidebar from '../components/Sidebar/index.vue'
import TagsView from '../components/TagsView'
import RightMenu from '../components/RightMenu.vue'
// import Breadcrumb from '../components/Breadcrumb'
import Hamburger from '../components/Hamburger'
//import ResizeMixin from '../mixin/ResizeHandler'
import {menulist,getMenus} from '@/api/user'
export default {
name: 'layout4',
components: {
Logo,
RightMenu,
Sidebar,
// Breadcrumb,
Hamburger,
TagsView
},
// mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
menus() {
//consloe.log(this.$store.getters.menus)
return this.$store.getters.menus
//return getMenus
},
isCollapse() {
return !this.sidebar.opened
},
needTagsView() {
return this.$store.state.settings.tagsView
},
device() {
return this.$store.state.app.device
}
},
mounted(){
// console.log(getMenus)
// console.log(this.$refs.getheight.offsetHeight)
const mainHeight = this.$refs.getheight.offsetHeight
window.sessionStorage.setItem('mainHeight',mainHeight)
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
},
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
}
}
</script>
<style lang="scss">
@import './index.scss';
.drawer-bg {
//position: absolute;
top: 0;
z-index: 10;
width: 100%;
height: 100%;
background: #000;
opacity: 0.3;
}
</style>

View File

@@ -0,0 +1,47 @@
import store from '@/store'
const { body } = document
//const WIDTH = 992 // refer to Bootstrap's responsive design
export default {
// watch: {
// $route(route) {
// if (this.device === 'mobile' && this.sidebar.opened) {
// store.dispatch('app/closeSideBar', { withoutAnimation: false })
// }
// }
// },
beforeMount() {
window.addEventListener('resize', this.$_resizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
},
mounted() {
const isMobile = this.$_isMobile()
if (isMobile) {
store.dispatch('app/toggleDevice', 'mobile')
store.dispatch('app/closeSideBar', { withoutAnimation: true })
}
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - 1 < WIDTH
},
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.$_isMobile()
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('app/closeSideBar', { withoutAnimation: true })
} else {
store.dispatch('app/openSideBar', { withoutAnimation: true })
}
}
}
}
}