HR-11-主页日历图表

212 阅读5分钟

主页日历图表

首页的页面结构

目标: 实现系统首页的页面结构 目前,我们的页面还剩下首页,这里我们可以按照如图实现一下的结构


image-20210421085908420.png

  • 首页页面结构,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>

总结

  1. 封装日历组件
  2. 动态生成年份列表 yearList
  3. 基于Array.from 生成年份列表
  4. 组件的属性类型检测函数值用法

定制日历内容 (插槽)

<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>

总结:

  1. echarts如何在Vue脚手架环境下使用
  2. 需要使用mounted生命周期(保证可以获取DOM元素填充位)
  3. 新版本的echarts按需导入的新的用法