1.布局文件
<!--
* @Description: 布局容器组件 vue-intro + intro.js 新手引导页功能 home文件
* @Version: 2.0
* @Autor: lhl
* @Date: 2020-06-11 17:54:29
* @LastEditors: lhl
* @LastEditTime: 2020-08-20 16:54:29
-->
<!-- -->
<template>
<div class="layout-box">
<el-container>
<el-header>
<topHeader />
</el-header>
<el-container>
<el-aside width="220px">
<el-scrollbar class="page-component__scroll">
<siderBar />
</el-scrollbar>
</el-aside>
<el-main>
<div class="right-view" ref="rightViewBox">
<!-- <Breadcrumb ref="bread"/> -->
<transition name="fade-transform" mode="out-in">
<keep-alive :include="keepAliveList">
<router-view></router-view>
</keep-alive>
</transition>
</div>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
// router-view 的 key 或者用路由钩子 再或者用监听路由变化
// 场景:由于 Vue 会复用相同组件, 即 /detail/1 => /detail/2 或者 /detail?id=1 => /detail?id=2 这类链接跳转时, 将不在执行created, mounted之类的钩子
// <router-view :key="$route.fullPath"></router-view>
// 这样组件的 created 和 mounted 就都会执行
import topHeader from "./topHeader";
import siderBar from "./siderBar";
import Breadcrumb from '../Breadcrumb/index'
import { mapActions, mapGetters, mapMutations } from "vuex";
// 引导
import Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from '@/common/steps'
export default {
components: {
topHeader,
siderBar,
Breadcrumb
},
data() {
//这里存放数据
return {
keepAliveList: ['baiduMapTest'] // 缓存的组件
}
},
methods: {
...mapMutations(["setContentBoxHeight", "setBtnPermitCode"]),
// 系统引导
guide() {
const driver = new Driver()
driver.defineSteps(steps)
driver.start()
},
// 获取按钮code码
getBtnCodeList(){
this.$http.user.btnList().then(res => {
console.log(res)
if(res.returnResult === 200){
this.setBtnPermitCode(res.returnData.buttonList)
}
})
}
},
computed:{
...mapGetters(['getcontentBoxHeight'])
},
created() {
this.getBtnCodeList()
},
mounted() {
this.guide();
//第二种方法
if (window.performance.navigation.type == 1) {
console.log("页面被刷新")
} else {
console.log("首次被加载")
}
window.addEventListener("resize", () => {
if (this.$refs.rightViewBox && this.$refs.rightViewBox.clientHeight) {
this.setContentBoxHeight(this.$refs.rightViewBox.clientHeight);
// console.log(this.$refs.rightViewBox.clientHeight, "rightViewBox");
}
})
if (this.$refs.rightViewBox.clientHeight) {
this.setContentBoxHeight(this.$refs.rightViewBox.clientHeight);
}
}
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.layout-box {
height: 100%;
min-width: 1200px;
.el-aside {
position: absolute;
top: 60px;
bottom: 0;
background: #3db4f8;
.el-menu{
border-right: none;
}
}
.el-main {
position: absolute;
left: 220px;
right: 0;
top: 60px;
bottom: 0;
padding: 10px;
background: #eee;
.right-view{
padding: 20px;
background: #fff;
min-height: 100%;
}
}
}
</style>
<!--
* @Description: 侧边栏组件
* @Version: 2.0
* @Autor: lhl
* @Date: 2020-06-11 17:54:54
* @LastEditors: lhl
* @LastEditTime: 2020-07-17 14:22:49
-->
<!-- -->
<template>
<div class="sider-content-box">
<el-menu
class="el-menu-vertical-demo"
background-color="#3DB4F8"
text-color="#fff"
active-text-color="#fff"
:collapse-transition="false"
router
>
<!-- 一级菜单 -->
<template v-for="(item,index) in menuData">
<el-submenu
v-if="item.children && item.children.length"
:index="item.menuUrl"
:key="item.menuUrl"
>
<template slot="title">
<i :class="item.icon"></i>
<span>{{item.name}}</span>
</template>
<!-- 二级菜单 -->
<template v-for="(itemChild,vIndex) in item.children">
<el-submenu
v-if="itemChild.children && itemChild.children.length"
:index="itemChild.menuUrl"
:key="vIndex"
>
<template slot="title">
<i :class="itemChild.icon"></i>
<span>{{itemChild.name}}</span>
</template>
<!-- 三级菜单 -->
<el-menu-item
v-for="itemChild_Child in itemChild.children"
:index="itemChild_Child.menuUrl"
:key="itemChild_Child.menuUrl"
>
<i :class="itemChild_Child.icon"></i>
<span slot="title">{{itemChild_Child.name}}</span>
</el-menu-item>
</el-submenu>
<el-menu-item v-else :index="itemChild.menuUrl" :key="itemChild.vIndex">
<i :class="itemChild.icon"></i>
<span slot="title">{{itemChild.name}}</span>
</el-menu-item>
</template>
</el-submenu>
<el-menu-item v-else :index="item.menuUrl" :key="index">
<i :class="item.icon"></i>
<span slot="title">{{item.name}}</span>
</el-menu-item>
</template>
</el-menu>
</div>
</template>
<script>
import { menuData } from '@/common/constant'
export default {
components: {},
data() {
//这里存放数据
return {
menuData
};
},
computed: {},
watch: {},
methods: {},
created() {},
mounted() {}
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
</style>
<!--
* @Description: 头部组件
* @Version: 2.0
* @Autor: lhl
* @Date: 2020-06-12 09:12:02
* @LastEditors: lhl
* @LastEditTime: 2020-08-04 17:33:36
-->
<!-- -->
<template>
<div class="header-content-box">
<div class="logo" id="logo">
<img src="@/assets/logo.png" alt />
</div>
<div id="title">vue测试管理系统</div>
<div class="top-info">
<div class="user-avatar">
<img src="@/assets/logo.png" alt />
</div>
<div class="user-down" id="user">
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link">
{{getUserInfo.userName || '默认用户'}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="baseInfo">基本资料</el-dropdown-item>
<el-dropdown-item command="changePassword">修改密码</el-dropdown-item>
<el-dropdown-item divided command="loginOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<el-dialog title="用户基本资料" :visible.sync="dialogVisibleInfo" width="50%">
<div class="user-info">
<p>id:{{getUserInfo.id}}</p>
<p>用户名:{{getUserInfo.userName}}</p>
<p>账号:{{getUserInfo.domainAccount}}</p>
<p>所在公司:{{getUserInfo.organizationName}}</p>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleInfo = false">取 消</el-button>
<el-button type="primary" @click="dialogVisibleInfo = false">确 定</el-button>
</span>
</el-dialog>
<el-dialog title="修改密码" :visible.sync="dialogVisibleChangePwd" width="50%">
<el-form
ref="ruleForm"
:model="ruleForm"
:rules="resetRules"
class="login-form"
label-width="80px"
label-position="left"
>
<el-form-item label="密码" prop="passwd">
<el-input
type="password"
v-model="ruleForm.passwd"
placeholder="请输入密码"
autocomplete="off"
show-password
></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input
type="password"
v-model="ruleForm.checkPass"
placeholder="请输入确认密码"
autocomplete="off"
show-password
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="resetForm('ruleForm')">取 消</el-button>
<el-button type="primary" @click="submitForm('ruleForm')">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { mapMutations, mapGetters } from "vuex";
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码"));
} else {
if (this.ruleForm.checkPass !== "") {
this.$refs.ruleForm.validateField("checkPass");
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== this.ruleForm.passwd) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
//这里存放数据
return {
dialogVisibleInfo: false, // 用户信息
dialogVisibleChangePwd: false, // 修改用户密码
ruleForm: {
passwd: "",
checkPass: "",
},
resetRules: {
passwd: [{ validator: validatePass, trigger: "blur" }],
checkPass: [{ validator: validatePass2, trigger: "blur" }],
},
};
},
computed: {
...mapGetters(["getUserInfo"]),
},
created() {},
methods: {
...mapMutations(["loginOut"]),
handleCommand(command) {
console.log(command);
if (command === "baseInfo") {
this.dialogVisibleInfo = true;
} else if (command === "changePassword") {
this.dialogVisibleChangePwd = true;
} else if (command === "loginOut") {
this.$confirm("您确定要退出吗?", "退出管理平台", {
confirmButtonText: "确定",
cancelButtonText: "取消",
})
.then(() => {
this.loginOut();
this.$router.push(`/login?redirect=${this.$route.fullPath}`);
})
.catch(() => {});
}
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
// 调用重置密码的接口
alert("重置密码成功!");
this.dialogVisibleChangePwd = false;
} else {
// console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.dialogVisibleChangePwd = false;
this.$refs[formName].resetFields();
},
},
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.header-content-box {
height: 60px;
display: flex;
justify-content: space-between;
align-items: center;
background: url("~@/assets/img/bg.png");
.logo {
img {
width: 40px;
height: 40px;
}
}
.top-info {
display: flex;
justify-content: center;
align-items: center;
.user-avatar {
border-radius: 50%;
img {
width: 40px;
height: 40px;
border-radius: 50%;
}
}
}
}
</style>
2.公共样式
// reset.scss
@import './transition.scss';
@import './public.scss';
@import './elementreset.scss';
*{
margin: 0;
padding: 0;
list-style: none;
}
/** 清除内外边距 **/
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */
dl, dt, dd, ul, ol, li,/* list elements 列表元素 */
pre, /* text formatting elements 文本格式元素 */
form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */
th, td /* table elements 表格元素 */ {
margin: 0;
padding: 0;
}
body,#app{
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
html{
height:100%;
}
body{
height:100%;
margin:0px;
padding:0px;
}
address, cite, dfn, em, var ,i{
font-style: normal;
}
/* for ie6 */
fieldset, img { border: 0; }
/* img :让链接里的 img 无边框 */
button, input, select, textarea { font-size: 100%;}
/* 使得表单元素在 ie 下能继承字体大小 *//* 注:optgroup 无法扶正 */ /** 重置表格元素 **/
table { border-collapse: collapse; border-spacing: 0; }
label {
font-weight: 700;
}
html *{
font-size: 14px;
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
// a:focus,
// a:active {
// outline: none;
// }
// a,
// a:focus,
// a:hover {
// cursor: pointer;
// text-decoration: none;
// }
div:focus {
outline: none;
}
.fr {
float: right;
}
.fl {
float: left;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
.md{
display: inline-block;
vertical-align: middle;
}
/*去掉type为number的箭头*/
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type="number"] {
-moz-appearance: textfield;
}
// Dialog 对话框居中显示
.dialog-box-center{
text-align: center;
&:after {
content: "";
display: inline-block;
height: 100%;
width: 0;
vertical-align: middle;
}
.el-dialog{
text-align: left;
display: inline-block;
margin: 0 !important;
vertical-align: middle;
min-width: 800px;
}
}
// transition.scss
/*fade*/
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/*fade-transform*/
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .8s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
// public.scss
.pd10{
padding: 10px;
}
.ptb10{
padding: 10px 0;
}
.mt10{
margin-top: 10px;
}
.md {
display: inline-block;
vertical-align: middle;
}
// 进度条颜色
#nprogress .bar {
background: red !important; //自定义颜色
}
.flex-column {
display: flex;
justify-content: center;
flex-direction: column;
}
/*滚动条样式*/
.page-component__scroll {
height: 100%;
.el-scrollbar__wrap {
overflow-x: auto;
}
}
/*滚动条颜色*/
.scroll-bar-color {
.el-scrollbar__thumb {
background-color: rgba(118, 118, 119, 0.8);
}
}
// elementreset.scss
// 分页样式
.el-pagination {
text-align: right;
margin-top: 10px;
}
// element ui表头换行
.el-table .cell {
white-space: pre-line;
}
// tooltip样式更改 固定宽高换行显示
.el-tooltip__popper {
max-width: 400px;
line-height: 180%;
}
// 重置表单样式 element ui 下拉、输入、级联、日期
.reset-form {
.el-input,
.el-select,
.el-cascader,
.el-range-editor {
width: 100% !important;
}
.el-pagination {
.el-input {
width: 50px !important;
}
.el-select {
.el-input {
width: 100px !important;
}
100px !important;
}
}
}
.el-table {
.el-date-editor.el-input,
.el-date-editor.el-input__inner {
width: 100% !important;
}
}
// to fixed https://github.com/ElemeFE/element/issues/2461
// Dialog内select tree等组件在点击箭头时有虚晃
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
// 当前行选中颜色高亮
.el-table--striped .el-table__body tr.el-table__row--striped.current-row td,
.el-table__body tr.current-row>td {
background-color: #ffec8b;
}
.el-loading-mask {
background-color: rgba(0, 0, 0, .3);
}
// 菜单样式
/* 非折叠的竖直菜单处理方式和上方的水平菜单一样,只不过需要将horizontal改为vertical
Element-UI中,在没有折叠的菜单中,无论是水平还是竖直菜单,子菜单是放在菜单栏所在的div中的,
只是将display属性设置为了none。
而在折叠菜单中,虽然菜单项也是放在页面上,但不在是放在菜单栏所在的div中,而是放在了页面底部的单独div中,
类属性值是el-menu--popup,因为是单独的div,所以在处理弹出菜单时就比较方便,直接处理el-submenu__title
和el-menu-item就可以,而不必区分是几级菜单项(如果不同级别菜单项要分别处理,需要使用“>”等操作分别处理) */
/*处理竖直菜单收齐后的弹出菜单中的子菜单 */
.el-menu--vertical .el-menu--popup .el-submenu__title {
height: 40px;
line-height: 40px;
}
.el-menu--vertical .el-menu--popup .el-menu-item {
height: 40px;
line-height: 40px;
}
.el-menu--vertical .el-menu--popup .el-icon-arrow-right:before {
color: #fff;
}
3.面包屑组件
<!--
* @Description: 全局面包屑组件 Breadcrumb/index.vue
* @Version: 2.0
* @Autor: lhl
* @Date: 2020-08-04 17:51:48
* @LastEditors: lhl
* @LastEditTime: 2020-08-20 16:57:07
-->
<template>
<div class="breadcrumb-box">
<!-- separator-class="el-icon-arrow-right" 图标分隔符 class -->
<el-breadcrumb separator="/">
<el-breadcrumb-item
v-for="item in breadList"
:to="{path: item.path }"
:key="item.name"
>{{item.meta.title}}</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
<script>
export default {
created() {
this.getBreadcrumb();
},
data() {
return {
breadList: [],
};
},
watch: {
$route() {
this.getBreadcrumb();
},
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter((item) => item.name);
// console.log(matched,'matched')
this.breadList = matched;
},
},
};
</script>
<style lang='scss' scoped>
//@import url(); 引入公共css类
.breadcrumb-box{
padding: 10px 0;
background: #fff;
border-bottom: 1px solid #333;
}
</style>
以上代码本人项目实测!!!真实可靠,请勿随意转载~转载请注明出处~~~谢谢合作!