主页日历图表
首页的页面结构
目标
: 实现系统首页的页面结构 目前,我们的页面还剩下首页,这里我们可以按照如图实现一下的结构
- 首页页面结构,src/views/dashboard/index.vue
<template>
<div class="dashboard-container">
<!-- 头部内容 -->
<el-card class="header-card">
<div>
<div class="fl headL">
<div class="headImg">
<img src="@/assets/common/head.jpg">
</div>
<div class="headInfoTip">
<p class="firstChild">早安,管理员,祝你开心每一天!</p>
<p class="lastChild">早安,管理员,祝你开心每一天!</p>
</div>
</div>
<div class="fr" />
</div>
</el-card>
<!-- 主要内容 -->
<el-row type="flex" justify="space-between">
<!-- 左侧内容 -->
<el-col :span="13" style="padding-right:26px">
<!-- 工作日历 -->
<el-card class="box-card">
<div slot="header" class="header">
<span>工作日历</span>
</div>
<!-- 放置日历组件 -->
</el-card>
<!-- 公告 -->
<el-card class="box-card">
<div class="advContent">
<div class="title"> 公告</div>
<div class="contentItem">
<ul class="noticeList">
<li>
<div class="item">
<img src="@/assets/common/img.jpeg" alt="">
<div>
<p><span class="col">朱继柳</span> 发布了 第1期“传智大讲堂”互动讨论获奖名单公布</p>
<p>2018-07-21 15:21:38</p>
</div>
</div>
</li>
<li>
<div class="item">
<img src="@/assets/common/img.jpeg" alt="">
<div>
<p><span class="col">朱继柳</span> 发布了 第2期“传智大讲堂”互动讨论获奖名单公布</p>
<p>2018-07-21 15:21:38</p>
</div>
</div>
</li>
<li>
<div class="item">
<img src="@/assets/common/img.jpeg" alt="">
<div>
<p><span class="col">朱继柳</span> 发布了 第3期“传智大讲堂”互动讨论获奖名单公布</p>
<p>2018-07-21 15:21:38</p>
</div>
</div>
</li>
</ul>
</div>
</div>
</el-card>
</el-col>
<!-- 右侧内容 -->
<el-col :span="11">
<el-card class="box-card">
<div class="header headTit">
<span>流程申请</span>
</div>
<div class="sideNav">
<el-button class="sideBtn">加班离职</el-button>
<el-button class="sideBtn">请假调休</el-button>
<el-button class="sideBtn">审批列表</el-button>
<el-button class="sideBtn">我的信息</el-button>
</div>
</el-card>
<!-- 绩效指数 -->
<el-card class="box-card">
<div slot="header" class="header">
<span>绩效指数</span>
</div>
<!-- 放置雷达图 -->
</el-card>
<!-- 帮助连接 -->
<el-card class="box-card">
<div class="header headTit">
<span>帮助链接</span>
</div>
<div class="sideLink">
<el-row>
<el-col :span="8">
<a href="#">
<span class="icon iconGuide" />
<p>入门指南</p>
</a>
</el-col>
<el-col :span="8">
<a href="#">
<span class="icon iconHelp" />
<p>在线帮助手册</p>
</a>
</el-col>
<el-col :span="8">
<a href="#">
<span class="icon iconTechnology" />
<p>联系技术支持</p>
</a>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'Dashboard',
computed: {
...mapGetters([
'name'
])
}
}
</script>
<style lang="scss" scoped>
.dashboard-container {
margin: 10px;
li {
list-style: none;
}
.headImg {
float: left;
width: 100px;
height: 100px;
border-radius: 50%;
background: #999;
img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.headInfoTip {
padding: 25px 0 0;
margin-left: 120px;
p {
padding: 0 0 15px;
margin: 0;
&.firstChild {
font-size: 24px;
}
&.lastChild {
font-size: 20px;
color: #7f8c8d;
}
}
}
}
.box-card {
padding: 5px 10px;
margin-top: 20px;
.header {
span {
color: #2c3e50;
font-size: 24px;
}
.item {
color: #97a8be;
float: right;
padding: 3px 0;
}
}
.headTit {
span {
border-bottom: 4px solid #8a97f8;
padding-bottom: 10px;
}
}
}
.header-card{
position: relative;
.header {
position: absolute;
right: 20px;
top: 15px;
z-index: 1;
}
}
.advContent {
background: #fff;
border-radius: 5px 5px 0px 0px;
.title {
font-size: 16px;
padding: 20px;
font-weight: bold;
border-bottom: solid 1px #ccc;
}
.contentItem {
padding: 0 30px;
min-height: 350px;
.item {
display: flex;
padding:18px 0 10px;
border-bottom: solid 1px #ccc;
.col {
color: #8a97f8;
}
img {
width: 56px;
height: 56px;
border-radius: 50%;
margin-right: 10px;
}
p{
padding: 0 0 8px;
}
}
}
}
.noticeList {
margin: 0;
padding: 0;
}
.sideNav,
.sideLink {
padding: 30px 0 12px;
.sideBtn {
padding: 16px 26px;
font-size:16px;
margin: 10px 5px;
}
}
.sideLink {
text-align: center;
.icon {
display: inline-block;
width: 76px;
height: 76px;
background: url('./../../assets/common/icon.png') no-repeat;
}
.iconGuide {
background-position: 0 0;
}
.iconHelp {
background-position: -224px 0;
}
.iconTechnology {
background-position: -460px 0;
}
}
</style>
通过上面的代码,我们得到了如下的页面 我们预留了**
工作日历
和绩效指数
**两个组件,我们会在后续的组件中进行开发
首页用户资料显示
目标
:将首页的信息换成真实的用户资料 直接获取Vuex的用户资料即可
<script>
import { mapGetters } from 'vuex'
export default {
name: 'Dashboard',
data() {
return {
defaultImg: 'https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2344451607,2404623174&fm=111&gp=0.jpg'
}
},
computed: {
...mapGetters(['name', 'avatar'])
}
}
</script>
- 在 vue视图中绑定
<div class="headImg">
<img v-imgerror="defaultImg" :src="userInfo.staffPhoto">
</div>
<div class="headInfoTip">
<p class="firstChild">早安,{{ userInfo.username }},祝你开心每一天!</p>
<p class="lastChild">早安,{{ userInfo.username }} | {{ userInfo.companyName }} | {{ userInfo.departmentName }}</p>
</div>
总结:通过store获取用户的头像和名称
工作日历组件封装
**
目标
**封装一个工作日历组件在首页中展示
新建工作日历组件结构
工作日历的要求很简单,显示每个月的日期,可以设定日期的范围
基本代码结构如下 src/views/dashboard/components/work-calendar.vue
<template>
<div>
<div class="select-box">
<el-select v-model="currentYear" size="small" style="width: 120px; margin-right: 10px">
<!-- 年份取给定年份的, 前五年 + 后五年 -->
<el-option v-for="item in yearList" :key="item" :label="item" :value="item" />
</el-select>
<el-select v-model="currentMonth" size="small" style="width: 120px;">
<el-option v-for="item in 12" :key="item" :label="item" :value="item" />
</el-select>
</div>
<el-calendar />
</div>
</template>
<script>
export default {
name: 'WorkCalendar',
props: {
date: {
type: Object,
default: new Date()
}
},
computed: {
// 生成年份
yearList () {
// 生成一个年份列表
const years = []
// 获取当前的年份
const year = new Date().getFullYear()
// 当前年份前面添加5个后面添加5个
for (let i = year - 5; i < year + 5; i++) {
years.push(i)
}
return years
}
},
data () {
return {
// 日历当前选中的年份
currentYear: '',
// 日历当前选中的月份
currentMonth: ''
}
},
created () {
this.currentYear = this.date.getFullYear()
this.currentMonth = this.date.getMonth() + 1
}
}
</script>
<style lang="scss" scoped>
.select-box {
display: flex;
justify-content: flex-end;
}
</style>
动态计算 yearList
这里的 yearList 我们是写死的空数组, 应该是基于当前currentYear 动态计算的, 应该是一个计算属性!
Array.from
还可以接受第二个参数,作用类似于数组的map
方法,
用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(new Array(10), (item, index) => index)
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
计算设置 yearList
computed: {
// 要遍历的年的数组
yearList() {
return Array.from(new Array(10), (item, index) => index + this.currentYear - 5)
}
},
created() {
this.currentYear = this.date.getFullYear() // 得到当前年份
this.currentMonth = this.date.getMonth() + 1 // 当前月份
}
使用日历组件
<el-calendar />
样式:
<style lang="scss" scoped>
.select-box {
display: flex;
justify-content: flex-end;
}
::v-deep .el-calendar-day {
height: auto;
}
::v-deep .el-calendar-table__row td::v-deep .el-calendar-table tr td:first-child, ::v-deep .el-calendar-table__row td.prev{
border:none;
}
.date-content {
height: 40px;
text-align: center;
line-height: 40px;
font-size: 14px;
}
.date-content .rest {
color: #fff;
border-radius: 50%;
background: rgb(250, 124, 77);
width: 20px;
height: 20px;
line-height: 20px;
display: inline-block;
font-size: 12px;
margin-left: 10px;
}
.date-content .text{
width: 20px;
height: 20px;
line-height: 20px;
display: inline-block;
}
::v-deep .el-calendar-table td.is-selected .text{
background: #409eff;
color: #fff;
border-radius: 50%;
}
::v-deep .el-calendar__header {
display: none
}
</style>
总结
- 封装日历组件
- 动态生成年份列表 yearList
- 基于Array.from 生成年份列表
- 组件的属性类型检测函数值用法
定制日历内容 (插槽)
<el-calendar v-model="currentDate">
<template #dateCell="{ data }">
{{ data.day | getDay }}
</template>
</el-calendar>
过滤器去掉年月, 去掉 0
filters: {
getDay(value) {
return value.split('-')[2] // 11, 02
}
},
加类优化结构
<el-calendar v-model="currentDate">
<template #dateCell="{ date, data }">
<div class="date-content">
<span class="text">{{ data.day | getDay }}</span>
<span v-if="isWeek(date)" class="rest">休</span>
</div>
</template>
</el-calendar>
判断是否是周末
isWeek(date) {
return date.getDay() === 6 || date.getDay() === 0
}
总结:通过作用域插槽定制休息日的效果
实现工作日历选择逻辑
绑定变量
<el-calendar v-model="currentDate"/>
data () {
return {
// 日历的当前日期
currentDate: new Date(),
// 日历当前选中的年份
currentYear: '',
// 日历当前选中的月份
currentMonth: ''
}
},
改变年份, 月份, 日历都要更新 => 注册change事件
<el-select v-model="currentYear" size="small" style="width: 120px; margin-right: 10px" @change="handleChange"><el-select v-model="currentYear" size="small" style="width: 120px; margin-right: 10px" @change="handleChange">
created() {
this.currentYear = this.startDate.getFullYear() // 得到当前年份
this.currentMonth = this.startDate.getMonth() + 1 // 当前月份
this.handleChange()
},
methods: {
handleChange () {
// 生成新的日期
this.currentDate = new Date(this.currentYear + '-' + this.currentMonth)
}
}
总结:下拉年份和月份需要和日历的当前日期进行联动 当前触发下拉事件时监听change事件,事件函数中组合年份和月份形成一个日期对象,然后更新日历的当前日期。
watch 监视日历同步
需求:点击日历的单元格,导致的月份和年份的改变同步到下拉列表中
watch: {
currentDate(newValue) {
// console.log(newValue)
this.currentYear = newValue.getFullYear()
this.currentMonth = newValue.getMonth() + 1
}
},
总结:通过侦听器监听当前日历的日期,根据监听的日期变化修改下拉列表的当前年份和月份。
- 日历组件的基本使用
- Array.from的用法
- 组件封装的属性检测,对象类型的要求必须是函数
- 定制日历的单元格(作用域插槽)
- 表格列
- 树形组件的节点
- 日历的单元格
- 父子组件之间的数据交互
- 侦听器的用法
- 上传用户头像
- 日历组件
- 上传Excel
- 组织架构组件
- 频道组件
- 文章封面组件
- 下拉列表组件
封装雷达图插件
- echarts基本使用流程
- 安装依赖包
- 导入echarts
- 准备一个图表的填充位置
- 调用echarts提供的方法初始化图表到填充位
- 图表的效果由options提供(来源于官方案例)
目标
:封装一个echarts中的雷达图表显示在首页的绩效指数的位置 首页中,还有一个绩效指数的位置需要放置一个雷达图的图表,我们可以采用百度的echarts进行封装
第一步, 安装echarts图表
$ npm install echarts
echarts是一个很大的包,里面包含了众多图形,假设我们只使用雷达图,可以做按需加载
第二步, 新建雷达图组件,src/views/dashboard/components/work-chart.vue
<template>
<!-- 雷达图 图表必须给高和宽度-->
<div ref="workChart" class="work-chart" />
</template>
<script>
// 完整加载过程
// import * as echarts from 'echarts'
// 按需加载
import * as echarts from 'echarts/core' // 引入主模块
import { RadarChart } from 'echarts/charts' // 引入雷达图
// 引入提示框和标题组件
import { TitleComponent, TooltipComponent } from 'echarts/components'
// 引入canvas渲染器
import { CanvasRenderer } from 'echarts/renderers'
// 注册必须的组件
echarts.use(
[TitleComponent, TooltipComponent, RadarChart, CanvasRenderer]
)
export default {
// 页面渲染完毕事件
mounted() {
const myChart = echarts.init(this.$refs.myDiv) // 得到图表实例
myChart.setOption({
title: {
text: '基础雷达图'
},
tooltip: {},
legend: {
data: ['平均水平', '个人价值']
},
radar: {
// shape: 'circle',
name: {
textStyle: {
color: '#fff',
backgroundColor: '#999',
borderRadius: 3,
padding: [3, 5]
}
},
indicator: [
{ name: '工作效率', max: 100 },
{ name: '考勤', max: 100 },
{ name: '积极性', max: 100 },
{ name: '帮助同事', max: 100 },
{ name: '自主学习', max: 100 },
{ name: '正确率', max: 100 }
]
},
series: [{
name: '平均 vs 个人',
type: 'radar',
// areaStyle: {normal: {}},
data: [
{
value: [80, 80, 80, 80, 80, 80],
name: '平均水平'
},
{
value: [90, 75, 95, 90, 95, 90],
name: '个人价值'
}
]
}]
})
}
}
</script>
<style>
.work-chart {
width: 600px;
height: 400px;
}
</style>
总结:
- echarts如何在Vue脚手架环境下使用
- 需要使用mounted生命周期(保证可以获取DOM元素填充位)
- 新版本的echarts按需导入的新的用法