你可以下载代码: github.com/jiangyuzhen…
1.在项目文件夹src中新建pages和style
pages是存放页面的
style是存放样式的

2.style文件夹新建flex.css和common.css
由于页面是使用flex布局的,所以我就直接手写一个flex公共样式
把这两个引入到main.js项目入口文件

flex.css
/* // 默认flex水平排列 */
.flex {
display: flex;
}
/* // flex垂直排列 */
.direction-clo {
display: flex;
flex-direction: column;
}
/* 垂直水平居中 */
.center-align {
display: flex;
justify-content: center;
align-items: center;
}
/* // 向左 */
.justify-l {
display: flex;
justify-content: flex-start;
}
/* // 向右 */
.justify-r {
display: flex;
justify-content: flex-end;
}
/* // 两边 */
.justify-b {
display: flex;
justify-content: space-between;
}
/* // 向上 */
.align-t {
display: flex;
align-items: flex-start;
}
/* // 向上 */
.align-c {
display: flex;
align-items: center;
}
/* // 向下 */
.align-b {
display: flex;
align-items: flex-end;
}
/* // 占剩下的全部 配合父级的 */
.flex-all {
flex: 1;
}
common.css
/* 单行省略 */
.line-1 {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}
2.在pages中建立文件夹
分析头部菜单


在每一个对应的文件夹都建立index.vue页面,并使用快捷键建立vue模板文件代码

<template>
<div>
活动管理
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
再在pages里新建一个layout.vue布局页面

3.把pages里建立的文件夹index.vue,注册到路由
import Vue from 'vue'
import Router from 'vue-router'
import layout from '@/pages/layout'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: '首页',
component: layout,
redirect: '/workbench',
children: [
{
path: 'workbench',
name: '工作台',
component: () => import(/* webpackChunkName:"workbench" */ '@/pages/workbench/index')
},
{
path: 'activity',
name: '活动管理',
component: () => import(/* webpackChunkName:"activity" */ '@/pages/activity/index')
},
{
path: 'evaluate',
name: '评估中心',
component: () => import(/* webpackChunkName:"evaluate" */ '@/pages/evaluate/index')
},
{
path: 'label',
name: '标签管理',
component: () => import(/* webpackChunkName:"label" */ '@/pages/label/index')
},
{
path: 'labour',
name: '劳动竞赛',
component: () => import(/* webpackChunkName:"labour" */ '@/pages/labour/index')
},
{
path: 'system',
name: '系统管理',
component: () => import(/* webpackChunkName:"system" */ '@/pages/system/index')
},
{
path: 'model',
name: '模型管理',
component: () => import(/* webpackChunkName:"model" */ '@/pages/model/index')
},
{
path: 'user',
name: '个人中心',
component: () => import(/* webpackChunkName:"user" */ '@/pages/user/index')
}
]
}
]
})
编辑页面
layout.vue
主要的布局是使用antDesign-vue的loyout布局

<template>
<a-layout id="components-layout-demo-fixed">
<a-layout-header :style="{ position: 'fixed', zIndex: 1, width: '100%', display: 'flex'}">
<div class="logo flex center-align">
<div>
<a-icon type="dribbble" style="color: #fff;font-size: 25px" title="大数据精准营销系统"/>
</div>
<div class="flex direction-clo justify-l" style="margin-left: 15px" v-show="showLogo">
<div style="font-size: 1.2rem;line-height:25px;" class="line-1" title="大数据精准营销系统">大数据精准营销系统</div>
<div style="font-size: 0.65rem;line-height:15px;" class="line-1" title="Big data Precision Markenting System">Big data Precision Markenting System</div>
</div>
</div>
<!-- 菜单 -->
<a-menu
theme="light"
mode="horizontal"
:default-selected-keys="defaultSelect"
:style="{ lineHeight: '64px', flex: '1', display: 'flex', alignItems: 'center', justifyContent: 'center'}"
>
<a-menu-item :key="item.key" v-for="item in menu" @click="jumpRouter(item)">
{{item.name}}
</a-menu-item>
</a-menu>
<div>
<a-dropdown class="dropdow flex center-align" >
<a class="ant-dropdown-link" @click="e => e.preventDefault()">
<a-icon type="user" /><span style="margin: 0 10px">admin </span><a-icon type="down" />
</a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;">登录</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">个人中心</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</div>
</a-layout-header>
<a-layout-content :style="{marginTop: '64px',display: 'flex',flexDirection: 'column' }">
<!-- <a-breadcrumb :style="{ margin: '16px 0' , display: 'flex', }">
<a-breadcrumb-item>Home</a-breadcrumb-item>
<a-breadcrumb-item>List</a-breadcrumb-item>
<a-breadcrumb-item>App</a-breadcrumb-item>
</a-breadcrumb> -->
<!-- 页面内容 -->
<div :style="{ background: '#f4f4f4', padding: '0',flex: '1', display: 'flex',}">
<router-view></router-view>
</div>
</a-layout-content>
</a-layout>
</template>
<script>
export default {
data () {
return {
showLogo: true,
defaultSelect: ['1'],
menu: [
{
name: '工作台',
key: '1',
routerPath: '/workbench'
},
{
name: '标签管理',
key: '2',
routerPath: '/label'
},
{
name: '模型管理',
key: '3',
routerPath: '/model'
},
{
name: '活动管理',
key: '4',
routerPath: '/activity'
},
{
name: '评估中心',
key: '5',
routerPath: '/evaluate'
},
{
name: '劳动竞赛',
key: '6',
routerPath: '/labour'
},
{
name: '系统管理',
key: '7',
routerPath: '/system'
}
]
}
},
created () {
this.defaultSelect = ['1']
},
mounted () {
const _this = this
window.addEventListener('resize', () => {
_this.onresize()
})
this.onresize()
},
methods: {
/**
* 窗口缩放事件
* @method onresize
*/
onresize () {
// this.parentDom.offsetWidth 父元素的宽
console.log()
if (document.body.offsetWidth > 1000) {
this.showLogo = true
} else {
this.showLogo = false
}
},
/**
* 跳转路由
* @method jumpRouter
*/
jumpRouter (item) {
this.$router.push({ path: item.routerPath })
}
}
}
</script>
<style scoped>
#components-layout-demo-fixed .logo {
/* background: rgba(255, 255, 255, 0.2); */
margin: 16px 24px 16px 0;
justify-content: flex-start;
align-items: center;
display: flex;
color: #fff;
line-height: 15px;
}
.ant-layout-header {
background: linear-gradient(#304678 ,#172b5a);
}
.ant-menu {
background: none;
}
.ant-menu-horizontal > .ant-menu-item:hover, .ant-menu-horizontal > .ant-menu-submenu:hover, .ant-menu-horizontal > .ant-menu-item-active, .ant-menu-horizontal > .ant-menu-submenu-active, .ant-menu-horizontal > .ant-menu-item-open, .ant-menu-horizontal > .ant-menu-submenu-open, .ant-menu-horizontal > .ant-menu-item-selected, .ant-menu-horizontal > .ant-menu-submenu-selected {
color: #fff;
background: rgba(255,255,255,0.2);
}
.ant-menu-horizontal > .ant-menu-item, .ant-menu-horizontal > .ant-menu-submenu {
color: #fff;
margin-bottom: 3px;
}
.ant-layout-header {
padding: 0 20px;
}
.ant-menu-horizontal {
border: none;
}
.dropdow {
color: #fff;
}
.dropdow:hover{
color: #00a2ff;
}
</style>
workbench文件夹下的index.vue
<template>
<div class="page-box flex" id="sale-states" >
<div class="flex direction-clo flex-all">
<Atitle title="我参与的"></Atitle>
<div class="flex center-align">
<a-card class="flex-all car" hoverable>
<h3 style="color: #fff">8月战狼行动</h3>
<p>超级日租卡用户获取活动,1天1元800M循环叠加,全国流量不封顶!</p>
<a-progress
:percent="55"
:stroke-color="{
'0%': '#fff',
'100%': '#fff',
}"
/>
</a-card>
<a-card class="flex-all car back-blue" hoverable>
<h3 style="color: #fff">3G升4G大冲锋</h3>
<p>超级日租卡用户获取活动,1天1元800M循环叠加,全国流量不封顶!</p>
<a-progress
:percent="55"
:stroke-color="{
'0%': '#fff',
'100%': '#fff',
}"
/>
</a-card>
<a-card class="flex-all car back-yellow" hoverable>
<h3 style="color: #fff">异网获取,“飓风”活动</h3>
<p>超级日租卡用户获取活动,1天1元800M循环叠加,全国流量不封顶!</p>
<a-progress
:percent="55"
:stroke-color="{
'0%': '#fff',
'100%': '#fff',
}"
/>
</a-card>
</div>
<Atitle title="统计概览" class="title-margin"></Atitle>
<div class="flex direction-clo flex-all" style="width:100%;height:100%; margin-bottom: 15px;">
<a-card class="flex-all direction-clo" hoverable>
<h3>销售目标完成情况</h3>
<div class="chart-box flex-all direction-clo" style="width:100%;height:100%;">
<evaChart
style="width:100%;height:100%"
:parentId="'sale-states'"
:option="saleStateOption"
>
</evaChart>
</div>
</a-card>
</div>
</div>
<div style="margin-left: 15px; width: 20%; background: #eee">
<!-- 更多消息 -->
<div class="flex center-align justify-l message-list">
<a-avatar style="backgroundColor:rgb(254, 91, 12)" icon="bell" />
<span style="margin: 0 8px;">我的消息</span>
<a-badge
count="12"
:number-style="{
backgroundColor: '#fff',
color: 'rgb(234, 83, 35)',
borderColor: 'rgb(234, 83, 35)'
}"
/>
<div class="flex-all" style="text-align: right; cursor:pointer">更多 >></div>
</div>
<!-- 我的待办 -->
<div>
<h3>我的待办</h3>
<div id="reviewBox">
<div id="comment1">
<div class="car-line flex center-align justify-l" v-for="(item,index) in myWaitList" :key="index">
<div class="border-br"></div>
<div class="car-content flex direction-clo">
<span class="car-content-text"> {{ item.title }} </span>
<span style="margin-top: 3%;color: rgb(173, 170, 170);font-size: 12px">{{ item.time }}</span>
</div>
</div>
</div>
<div id="comment2"></div>
</div>
</div>
<!-- 其他 -->
<div style="padding: 5%">
<a-tabs default-active-key="1" @change="tabChange">
<a-tab-pane key="1" tab="公告">
<div>
<div v-for="(item, index) in noticeList" :key="index" class="flex center-align">
<div class="flex flex-all">
<a-badge status="default" /> <span class="flex-all tab-li-title">{{item.title}} </span><span style="margin-left: 5%;color: rgb(173, 170, 170);font-size: 12px">{{ item.time }}</span>
</div>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="动态" force-render>
<div>
<div v-for="(item, index) in activeList" :key="index" class="flex center-align">
<div class="flex flex-all">
<a-badge status="default" /> <span class="flex-all tab-li-title">{{item.title}} </span><span style="margin-left: 5%;color: rgb(173, 170, 170);font-size: 12px">{{ item.time }}</span>
</div>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
</div>
</div>
</template>
<script>
const saleStateOption = {
color: ['#8486dc', '#f49181'],
// title: {
// text: '销售目标完成情况'
// // x:'center'
// },
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
legend: {
data: ['目标销售额', '当前销售额'],
align: 'right'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
axisTick: {
show: false
},
axisLine: {
show: false
},
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
}
],
yAxis: [
{
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
// 使用深浅的间隔色
color: ['#eee']
}
}
}
],
series: [
{
name: '目标销售额',
type: 'bar',
barWidth: 15,
barMinWidth: 9,
barMaxWidth: 25,
data: [320, 332, 301, 334, 390, 330, 320, 301, 334, 390, 330, 320]
},
{
name: '当前销售额',
type: 'bar',
stack: '广告',
barWidth: 15,
barMinWidth: 9,
barMaxWidth: 25,
data: [120, 32, 31, 54, 80, 300, 10, 201, 84, 390, 30, 99]
}
]
}
const myWaitList = [
{
title: '地市约谈:广州市电信分公司,关于省公司集约活动参与情况积极性问题约谈',
time: '2020-08-11 15:32:05'
},
{
title: '场地预约:办公室场地申请',
time: '2020-05-23 15:32:05'
},
{
title: 'ESIM办理预约:联通ESIM办理,关于省公司集约活动参与情况积极性问题约谈',
time: '2020-05-24 15:32:05'
},
{
title: '节目筹备:六一儿童节水上乐园节日狂欢筹备',
time: '2020-06-01 15:32:05'
},
{
title: '记者会召开:关于澄清月季话费费用事项的记者招待会',
time: '2020-05-26 15:32:05'
}
]
const noticeList = [
{
title: '超级日租卡用户获取活动',
time: '08-11'
},
{
title: '宽带装新限时优惠活动',
time: '05-23'
},
{
title: '政企专线提速',
time: '05-24'
},
{
title: '第三季度各地市销售额季度排名',
time: '06-01'
},
{
title: '明日活动发布事项,通知如下',
time: '05-26'
},
{
title: '超级日租卡用户获取活动',
time: '08-11'
},
{
title: '宽带装新限时优惠活动',
time: '05-23'
},
{
title: '政企专线提速',
time: '05-24'
}
]
const activeList = [
{
title: '宽带装新限时优惠活动',
time: '05-23'
},
{
title: '政企专线提速',
time: '05-24'
},
{
title: '第三季度各地市销售额季度排名',
time: '06-01'
},
{
title: '明日活动发布事项,通知如下',
time: '05-26'
},
{
title: '超级日租卡用户获取活动',
time: '08-11'
},
{
title: '超级日租卡用户获取活动',
time: '08-11'
},
{
title: '宽带装新限时优惠活动',
time: '05-23'
},
{
title: '政企专线提速',
time: '05-24'
}
]
export default {
data () {
return {
collapsed: false,
saleStateOption,
myWaitList,
noticeList,
activeList,
timer: undefined
}
},
created () {
this.$nextTick(() => {
if (document.getElementById('comment1') && document.getElementById('comment2') && document.getElementById('reviewBox')) {
this.roll(150)
} else {
if (this.timer) {
clearInterval(this.timer)
}
}
})
},
methods: {
tabChange (val) {
// console.log(val)
if (this.timer) {
clearInterval(this.timer)
}
},
roll (t) {
if (document.getElementById('comment1') && document.getElementById('comment2') && document.getElementById('reviewBox')) {
clearInterval(this.timer)
let ul1 = document.getElementById('comment1')
let ul2 = document.getElementById('comment2')
let ulbox = document.getElementById('reviewBox')
if (!ul1.innerHTML) {
ul1.innerHTML = ul2.innerHTML
} else {
ul2.innerHTML = ul1.innerHTML
}
ulbox.scrollTop = 0 // 开始无滚动时设为0
this.timer = setInterval(this.rollStart, t) // 设置定时器,参数t用在这为间隔时间(单位毫秒),参数t越小,滚动速度越快
// 鼠标移入div时暂停滚动
ulbox.onmouseover = () => {
clearInterval(this.timer)
}
// 鼠标移出div后继续滚动
ulbox.onmouseout = () => {
this.timer = setInterval(this.rollStart, t)
}
}
},
/**
* 开始滚动函数
*/
rollStart () {
// 上面声明的DOM对象为局部对象需要再次声明
if (document.getElementById('comment1') && document.getElementById('comment2') && document.getElementById('reviewBox')) {
let ul1 = document.getElementById('comment1')
let ulbox = document.getElementById('reviewBox')
if (ulbox.scrollTop >= ul1.scrollHeight) {
ulbox.scrollTop = 0
} else {
ulbox.scrollTop++
}
}
}
}
}
</script>
<style scoped>
.page-box {
flex: 1;
padding: 15px 0 0 15px;
background: #f4f4f4;
}
.content-box {
flex: 1;
margin-right: 15px;
}
.title-margin {
margin: 10px 0;
}
.car {
color: #fff;
background: linear-gradient(45deg, #a4a7ea ,#8486dc);
}
.car.back-blue{
background: linear-gradient(45deg, #7cc0fd ,#769dfc);
}
.car.back-yellow{
background: linear-gradient(45deg, #fbb173 ,#f49181);
}
.car:nth-child(2n){
margin: 1%;
}
/deep/ .ant-progress-inner {
background-color: rgba(0, 0, 0, 0.1) !important;
}
/deep/ .ant-progress-text {
color: #fff;
}
.chart-box {
flex: 1;
}
/* 消息 */
.message-list {
background: #fff;
width: 100%;
height: 50px;
padding: 0 5%;
white-space: nowrap;
}
/* 我的待办 */
.car-line {
height: 70px;
background: #fff;
margin-top: 5px;
border-radius: 4px;
overflow: hidden;
}
.car-content {
padding: 5%;
box-sizing: border-box;
width: 100%;
}
.car-content-text {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.border-br {
height: 100%;
border: 2px solid #e1e2e3;
}
.car-line:hover .border-br {
border: 2px solid #f99b50;
}
.car-line:hover .car-content{
background: rgba(255, 240, 233, 0.5);
}
/deep/ .ant-card-body {
height: 100%;
display: flex;
flex-direction: column;
}
/* 预览滚动的高度 */
#review_box, #reviewBox{
height: 250px;
overflow: hidden;
}
/* tab */
.tab-li-title {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}
/deep/ .ant-tabs .ant-tabs-top-content.ant-tabs-content-animated, .ant-tabs .ant-tabs-bottom-content.ant-tabs-content-animated {
max-height: 280px;
}
</style>
其中我封装了标题和echart

a-title.vue
!<template>
<div class="flex justify-l align-c">
<span class="title-br"></span>
<slot name="title"> <span class="a-title">{{ title }} </span></slot>
</div>
</template>
<script>
export default {
props: {
title: {
default: '标题'
}
}
}
</script>
<style scoped>
.title-br {
display: flex;
width: 4px;
height: 25px;
border-radius: 8px;
background: #547cd8;
margin-right: 10px;
}
.a-title {
color: #547cd8 !important;
font-size: 15px;
}
</style>
eva-chart.vue
<template>
<div :id="id" style="height:100%;margin: 0 auto;width: 100%" class="chartH"></div>
</template>
<script>
const option = {
title: {
text: '销售目标完成情况'
// x:'center'
},
color: ['#3398DB'],
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '直接访问',
type: 'bar',
barWidth: '60%',
data: [10, 52, 200, 334, 390, 330, 220]
}
]
}
export default {
name: 'HelloWorld',
props: {
id: {
default: 'chartEle'
},
parentId: {},
option: {
type: Object,
default () {
return option
}
}
},
data () {
return {
Chart: undefined
}
},
created () {
// 监听父元素的宽高变化
// window.addEventListener('resize', this.onresize())
// this.$nextTick(() => {
// let targetNode = document.getElementById(this.parentId)
// let observer = new MutationObserver((mutations) => {
// this.onresize(mutations)
// })
// observer.observe(targetNode, { attributes: true, childList: false, subtree: false })
// // document.getElementById(this.parentId).addEventListener('resize', this.onresize())
// })
},
mounted () {
// 基于准备好的dom,初始化echarts实例
this.initChart() // 重点
this.renderChart()
const _this = this
window.addEventListener('resize', () => {
_this.onresize()
})
this.onresize()
},
methods: {
/**
* 初始化表格
* @method initChart
*/
initChart () {
if (this.Chart) {
this.Chart = null
this.Chart = this.$echarts.init(document.getElementById('chartEle'))
} else {
this.Chart = this.$echarts.init(document.getElementById('chartEle'))
this.Chart = null
this.Chart = this.$echarts.init(document.getElementById('chartEle'))
}
},
/**
* 窗口缩放事件
* @method onresize
*/
onresize () {
// this.parentDom.offsetWidth 父元素的宽
if (this.Chart) {
this.Chart.resize() // 自适应表格
}
this.$emit('resize', this.parentDom) // 设置屏幕自适应
},
/**
* 渲染表格数据
* @method renderChart
* @param {Object} option
*/
renderChart (option) {
this.onresize()
if (this.Chart) {
this.Chart.setOption(option || this.option, true)
} else {
this.initChart()
this.Chart.setOption(option || this.option, true)
}
// setOption:设置图表实例的配置项以及数据,万能接口,所有参数和数据的修改都可以通过它完成,ECharts会合并新的参数和数据,然后刷新图表。
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.chartH > div {
height: 100%;
}
</style>