登录模块
views>login>index.vue
<template>
<div>
<el-card class="login-form-layout">
<el-form autoComplete="on"
:model="loginForm"
:rules="loginRules"
ref="loginForm"
label-position="left">
<div style="text-align: center">
<svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon>
</div>
<h2 class="login-title color-main">mall-admin-web</h2>
<el-form-item prop="username">
<el-input name="username"
type="text"
v-model="loginForm.username"
autoComplete="on"
placeholder="请输入用户名">
<span slot="prefix">
<svg-icon icon-class="user" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input name="password"
:type="pwdType"
@keyup.enter.native="handleLogin"
v-model="loginForm.password"
autoComplete="on"
placeholder="请输入密码">
<span slot="prefix">
<svg-icon icon-class="password" class="color-main"></svg-icon>
</span>
<span slot="suffix" @click="showPwd">
<svg-icon icon-class="eye" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<el-form-item style="margin-bottom: 60px">
<el-button style="width: 100%" type="primary" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
</el-form-item>
</el-form>
</el-card>
<img :src="login_center_bg" class="login-center-layout">
<el-dialog
title="特别赞助"
:visible.sync="supportDialogVisible"
width="30%">
<span>mall项目已由CODING特别赞助,点击去支持,页面加载完后点击<span class="color-main font-medium">免费体验</span>按钮即可完成支持,谢谢!</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogCancel">残忍拒绝</el-button>
<el-button type="primary" @click="dialogConfirm">去支持</el-button>
</span>
</el-dialog>
<el-dialog
title="公众号二维码"
:visible.sync="dialogVisible"
:show-close="false"
:center="true"
width="30%">
<div style="text-align: center">
<span>mall全套学习教程连载中<span class="color-main font-medium">关注公众号</span>第一时间获取</span>
<img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg" width="150" height="150" style="margin-top: 10px">
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogConfirm">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {isvalidUsername} from '@/utils/validate';
import {setSupport,getSupport,SupportUrl} from '@/utils/support';
import login_center_bg from '@/assets/images/login_center_bg.png'
export default {
name: 'login',
data() {
const validateUsername = (rule, value, callback) => {
if (!isvalidUsername(value)) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
};
const validatePass = (rule, value, callback) => {
if (value.length < 3) {
callback(new Error('密码不能小于3位'))
} else {
callback()
}
};
return {
loginForm: {
username: 'admin',
password: '123456',
},
loginRules: {
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePass}]
},
loading: false,
pwdType: 'password',
login_center_bg,
dialogVisible:false,
supportDialogVisible:false
}
},
methods: {
showPwd() {
if (this.pwdType === 'password') {
this.pwdType = ''
} else {
this.pwdType = 'password'
}
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
let isSupport = getSupport();
if(isSupport===undefined||isSupport==null){
this.dialogVisible =true;
return;
}
this.loading = true;
this.$store.dispatch('Login', this.loginForm).then(() => {
this.loading = false;
this.$router.push({path: '/'})
}).catch(() => {
this.loading = false
})
} else {
console.log('参数验证不合法!');
return false
}
})
},
dialogConfirm(){
this.dialogVisible =false;
setSupport(true);
// window.location.href=SupportUrl;
},
dialogCancel(){
this.dialogVisible = false;
setSupport(false);
}
}
}
</script>
<style scoped>
.login-form-layout {
position: absolute;
left: 0;
right: 0;
width: 360px;
margin: 140px auto;
border-top: 10px solid #409EFF;
}
.login-title {
text-align: center;
}
.login-center-layout {
background: #409EFF;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
margin-top: 200px;
}
</style>
前端代码主要由三部分组成:
<template>
</template>
<script>
</script>
<style scoped>
</style>
个人理解:
- 其中template主要用来绘制内容,大多是用户可以看见的内容。
- 而style是用来修饰template的,比如颜色的选择,某块放置的位置,透明度,背景,动画等样式处理;这里有一个scope,是为了防止样式的应用到了其他页面,scope将其渲染的效果局限于当前页面,建议最好都加上
- script主要用来方式其内部逻辑,比如被点击后的响应,向后端发送请求,存储该页面展示的数据等功能
我们发现template之中,主要有这些标签:
<div>
</div>
<span>
</span>
<img>
以上这三种都是html自有的标签
<el-dialog>
</el-dialog>
<el-button>
</el-button>
而这些都是element-ui的组件
这些组件只要引入以后就可以在自己的项目中使用,兼容性很好
现在来分析下这个登录按钮的代码
<el-button style="width: 100%" type="primary" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
这里有一个@click.native.prevent的触发事件
具体解释如下:
@click-native-prevent click事件的native修饰
handleLogin的具体代码:
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
let isSupport = getSupport();
if (isSupport === undefined || isSupport == null) {
this.dialogVisible = true;
return;
}
this.loading = true;
this.$store.dispatch('Login', this.loginForm).then(() => {
this.loading = false;
this.$router.push({path: '/'})
}).catch(() => {
this.loading = false
})
} else {
console.log('参数验证不合法!');
return false
}
})
},
这个方法里使用了这些技术:
- js-cookie:
- js-cookie中文文档
- 主要是实现二维码页面的是否跳出
- vuex:
这里首先实现验证输入是否合法,然后
首页模块
全部代码如下:
<template>
<div class="app-container">
<div class="address-layout">
<el-row :gutter="20">
<el-col :span="6">
<div class="out-border">
<div class="layout-title">后台项目</div>
<div class="color-main address-content">
<a href="https://github.com/macrozheng/mall">mall</a>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="out-border">
<div class="layout-title">前端项目</div>
<div class="color-main address-content">
<a href="https://github.com/macrozheng/mall-admin-web">mall-admin-web</a>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="out-border">
<div class="layout-title">学习教程</div>
<div class="color-main address-content">
<a href="https://github.com/macrozheng/mall-learning">mall-learning</a>
</div>
</div>
</el-col>
</el-row>
</div>
<div class="total-layout">
<el-row :gutter="20">
<el-col :span="6">
<div class="total-frame">
<img :src="img_home_order" class="total-icon">
<div class="total-title">今日订单总数</div>
<div class="total-value">200</div>
</div>
</el-col>
<el-col :span="6">
<div class="total-frame">
<img :src="img_home_today_amount" class="total-icon">
<div class="total-title">今日销售总额</div>
<div class="total-value">¥5000.00</div>
</div>
</el-col>
<el-col :span="6">
<div class="total-frame">
<img :src="img_home_yesterday_amount" class="total-icon">
<div class="total-title">昨日销售总额</div>
<div class="total-value">¥5000.00</div>
</div>
</el-col>
<!--<el-col :span="6">-->
<!--<div class="total-frame">-->
<!--<svg-icon icon-class="total-week" class="total-icon">-->
<!--</svg-icon>-->
<!--<div class="total-title">近7天销售总额</div>-->
<!--<div class="total-value">¥50000.00</div>-->
<!--</div>-->
<!--</el-col>-->
</el-row>
</div>
<el-card class="mine-layout">
<div style="text-align: center">
<img width="150px" height="150px" src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg">
</div>
<div style="text-align: center">mall全套学习教程连载中!</div>
<div style="text-align: center;margin-top: 5px"><span class="color-main">关注公号</span>,第一时间获取。</div>
</el-card>
<div class="un-handle-layout">
<div class="layout-title">待处理事务</div>
<div class="un-handle-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">待付款订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">已完成订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">待确认收货订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">待发货订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">新缺货登记</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">待处理退款申请</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">已发货订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">待处理退货订单</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
<el-col :span="8">
<div class="un-handle-item">
<span class="font-medium">广告位即将到期</span>
<span style="float: right" class="color-danger">(10)</span>
</div>
</el-col>
</el-row>
</div>
</div>
<div class="overview-layout">
<el-row :gutter="20">
<el-col :span="12">
<div class="out-border">
<div class="layout-title">商品总览</div>
<div style="padding: 40px">
<el-row>
<el-col :span="6" class="color-danger overview-item-value">100</el-col>
<el-col :span="6" class="color-danger overview-item-value">400</el-col>
<el-col :span="6" class="color-danger overview-item-value">50</el-col>
<el-col :span="6" class="color-danger overview-item-value">500</el-col>
</el-row>
<el-row class="font-medium">
<el-col :span="6" class="overview-item-title">已下架</el-col>
<el-col :span="6" class="overview-item-title">已上架</el-col>
<el-col :span="6" class="overview-item-title">库存紧张</el-col>
<el-col :span="6" class="overview-item-title">全部商品</el-col>
</el-row>
</div>
</div>
</el-col>
<el-col :span="12">
<div class="out-border">
<div class="layout-title">用户总览</div>
<div style="padding: 40px">
<el-row>
<el-col :span="6" class="color-danger overview-item-value">100</el-col>
<el-col :span="6" class="color-danger overview-item-value">200</el-col>
<el-col :span="6" class="color-danger overview-item-value">1000</el-col>
<el-col :span="6" class="color-danger overview-item-value">5000</el-col>
</el-row>
<el-row class="font-medium">
<el-col :span="6" class="overview-item-title">今日新增</el-col>
<el-col :span="6" class="overview-item-title">昨日新增</el-col>
<el-col :span="6" class="overview-item-title">本月新增</el-col>
<el-col :span="6" class="overview-item-title">会员总数</el-col>
</el-row>
</div>
</div>
</el-col>
</el-row>
</div>
<div class="statistics-layout">
<div class="layout-title">订单统计</div>
<el-row>
<el-col :span="4">
<div style="padding: 20px">
<div>
<div style="color: #909399;font-size: 14px">本月订单总数</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">10000</div>
<div>
<span class="color-success" style="font-size: 14px">+10%</span>
<span style="color: #C0C4CC;font-size: 14px">同比上月</span>
</div>
</div>
<div style="margin-top: 20px;">
<div style="color: #909399;font-size: 14px">本周订单总数</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">1000</div>
<div>
<span class="color-danger" style="font-size: 14px">-10%</span>
<span style="color: #C0C4CC;font-size: 14px">同比上周</span>
</div>
</div>
<div style="margin-top: 20px;">
<div style="color: #909399;font-size: 14px">本月销售总额</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">100000</div>
<div>
<span class="color-success" style="font-size: 14px">+10%</span>
<span style="color: #C0C4CC;font-size: 14px">同比上月</span>
</div>
</div>
<div style="margin-top: 20px;">
<div style="color: #909399;font-size: 14px">本周销售总额</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">50000</div>
<div>
<span class="color-danger" style="font-size: 14px">-10%</span>
<span style="color: #C0C4CC;font-size: 14px">同比上周</span>
</div>
</div>
</div>
</el-col>
<el-col :span="20">
<div style="padding: 10px;border-left:1px solid #DCDFE6">
<el-date-picker
style="float: right;z-index: 1"
size="small"
v-model="orderCountDate"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="handleDateChange"
:picker-options="pickerOptions">
</el-date-picker>
<div>
<ve-line
:data="chartData"
:legend-visible="false"
:loading="loading"
:data-empty="dataEmpty"
:settings="chartSettings"></ve-line>
</div>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import {str2Date} from '@/utils/date';
import img_home_order from '@/assets/images/home_order.png';
import img_home_today_amount from '@/assets/images/home_today_amount.png';
import img_home_yesterday_amount from '@/assets/images/home_yesterday_amount.png';
const DATA_FROM_BACKEND = {
columns: ['date', 'orderCount','orderAmount'],
rows: [
{date: '2018-11-01', orderCount: 10, orderAmount: 1093},
{date: '2018-11-02', orderCount: 20, orderAmount: 2230},
{date: '2018-11-03', orderCount: 33, orderAmount: 3623},
{date: '2018-11-04', orderCount: 50, orderAmount: 6423},
{date: '2018-11-05', orderCount: 80, orderAmount: 8492},
{date: '2018-11-06', orderCount: 60, orderAmount: 6293},
{date: '2018-11-07', orderCount: 20, orderAmount: 2293},
{date: '2018-11-08', orderCount: 60, orderAmount: 6293},
{date: '2018-11-09', orderCount: 50, orderAmount: 5293},
{date: '2018-11-10', orderCount: 30, orderAmount: 3293},
{date: '2018-11-11', orderCount: 20, orderAmount: 2293},
{date: '2018-11-12', orderCount: 80, orderAmount: 8293},
{date: '2018-11-13', orderCount: 100, orderAmount: 10293},
{date: '2018-11-14', orderCount: 10, orderAmount: 1293},
{date: '2018-11-15', orderCount: 40, orderAmount: 4293}
]
};
export default {
name: 'home',
data() {
return {
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
let start = new Date();
start.setFullYear(2018);
start.setMonth(10);
start.setDate(1);
end.setTime(start.getTime() + 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一月',
onClick(picker) {
const end = new Date();
let start = new Date();
start.setFullYear(2018);
start.setMonth(10);
start.setDate(1);
end.setTime(start.getTime() + 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}]
},
orderCountDate: '',
chartSettings: {
xAxisType: 'time',
area:true,
axisSite: { right: ['orderAmount']},
labelMap: {'orderCount': '订单数量', 'orderAmount': '订单金额'}},
chartData: {
columns: [],
rows: []
},
loading: false,
dataEmpty: false,
img_home_order,
img_home_today_amount,
img_home_yesterday_amount
}
},
created(){
this.initOrderCountDate();
this.getData();
},
methods:{
handleDateChange(){
this.getData();
},
initOrderCountDate(){
let start = new Date();
start.setFullYear(2018);
start.setMonth(10);
start.setDate(1);
const end = new Date();
end.setTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
this.orderCountDate=[start,end];
},
getData(){
setTimeout(() => {
this.chartData = {
columns: ['date', 'orderCount','orderAmount'],
rows: []
};
for(let i=0;i<DATA_FROM_BACKEND.rows.length;i++){
let item=DATA_FROM_BACKEND.rows[i];
let currDate=str2Date(item.date);
let start=this.orderCountDate[0];
let end=this.orderCountDate[1];
if(currDate.getTime()>=start.getTime()&&currDate.getTime()<=end.getTime()){
this.chartData.rows.push(item);
}
}
this.dataEmpty = false;
this.loading = false
}, 1000)
}
}
}
</script>
<style scoped>
.app-container {
margin-top: 40px;
margin-left: 120px;
margin-right: 120px;
}
.address-layout {
}
.total-layout {
margin-top: 20px;
}
.total-frame {
border: 1px solid #DCDFE6;
padding: 20px;
height: 100px;
}
.total-icon {
color: #409EFF;
width: 60px;
height: 60px;
}
.total-title {
position: relative;
font-size: 16px;
color: #909399;
left: 70px;
top: -50px;
}
.total-value {
position: relative;
font-size: 18px;
color: #606266;
left: 70px;
top: -40px;
}
.un-handle-layout {
margin-top: 20px;
border: 1px solid #DCDFE6;
}
.layout-title {
color: #606266;
padding: 15px 20px;
background: #F2F6FC;
font-weight: bold;
}
.un-handle-content {
padding: 20px 40px;
}
.un-handle-item {
border-bottom: 1px solid #EBEEF5;
padding: 10px;
}
.overview-layout {
margin-top: 20px;
}
.overview-item-value {
font-size: 24px;
text-align: center;
}
.overview-item-title {
margin-top: 10px;
text-align: center;
}
.out-border {
border: 1px solid #DCDFE6;
}
.statistics-layout {
margin-top: 20px;
border: 1px solid #DCDFE6;
}
.mine-layout {
position: absolute;
right: 140px;
top: 107px;
width: 250px;
height: 235px;
}
.address-content{
padding: 20px;
font-size: 18px
}
</style>
首先我们关注布局,先忽略样式,主要是template这块
一共有6个部分,分别对应如下图
那我们便会存在疑问,他的侧边栏和上方导航栏是在哪呢?
ps:
chrome浏览器长截图方法:
f12->crtl+shift+p(windows)->capture full size即可
这里有个图表,使用的是v-charts技术:
v-charts
vue中使用v-charts
这里就用到组件了:
我们首先看下它的路由是如何配置的:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
export const constantRouterMap = [
{path: '/login', component: () => import('@/views/login/index'), hidden: true},
{path: '/404', component: () => import('@/views/404'), hidden: true},
{
path: '',
component: Layout,
redirect: '/home',
children: [{
path: 'home',
name: 'home',
component: () => import('@/views/home/index'),
meta: {title: '首页', icon: 'home'}
}]
}
...
{path: '*', redirect: '/404', hidden: true}
]
export default new Router({
// mode: 'history', //后端支持可开
scrollBehavior: () => ({y: 0}),
routes: constantRouterMap
})
我们发现在home里面注册了一个叫Layout的组件
/* Layout */
import Layout from '../views/layout/Layout'
{
path: '',
component: Layout,
redirect: '/home',
children: [{
path: 'home',
name: 'home',
component: () => import('@/views/home/index'),
meta: {title: '首页', icon: 'home'}
}]
}
Layout.vue:
<template>
<div class="app-wrapper" :class="classObj">
<sidebar class="sidebar-container"></sidebar>
<div class="main-container">
<navbar></navbar>
<app-main></app-main>
</div>
</div>
</template>
<script>
import { Navbar, Sidebar, AppMain } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
}
</style>
Layout组件实现了将页面合理布局的功能,实现了将页面划分为如下:
- 侧边栏
- 主体
- 导航栏
- 主体
同时这里的Layout也使用了组件
import { Navbar, Sidebar, AppMain } from './components'
这里我主要分析下Sidebar组件的实现,就是侧边栏的实现
Sidebar>index.vue:
<template>
<scroll-bar>
<el-menu
mode="vertical"
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<sidebar-item :routes="routes"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
import ScrollBar from '@/components/ScrollBar'
export default {
components: { SidebarItem, ScrollBar },
computed: {
...mapGetters([
'sidebar'
]),
routes() {
return this.$router.options.routes
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
-
这里用了 vuex,具体讲解如下:
Vuex入门(2)—— state,mapState,…mapState对象展开符详解
-
SidebarItem.vue:
<template>
<div class="menu-wrapper">
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
:key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link>
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<template slot="title">
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
},
isNest: {
type: Boolean,
default: false
}
},
methods: {
hasOneShowingChildren(children) {
const showingChildren = children.filter(item => {
return !item.hidden
})
if (showingChildren.length === 1) {
return true
}
return false
}
}
}
</script>
看代码的主要意思就是,在for循环下,一级菜单的就如此显示:
<router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
:key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link>
二级菜单的就是:
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<template slot="title">
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
- ScrollBar
就是为了实现侧边栏的滚动
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
return {
top: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import '../../styles/variables.scss';
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: $menuBg;
.scroll-wrapper {
position: absolute;
width: 100%!important;
}
}
</style>
最后,这里我们再来看下什么是svg-icon
<svg-icon :icon-class="item.meta.icon"></svg-icon>
svg-sprite-loader 使用教程
懒人神器:svg-sprite-loader实现自己的Icon组件