|
|
@@ -0,0 +1,433 @@
|
|
|
+<template>
|
|
|
+ <div class="content-container">
|
|
|
+ <el-row>
|
|
|
+ <v-chart ref="myDayChart" autoresize style="width: 100%;height: 160px;" :options="chartOptions" />
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row class="tool-bar">
|
|
|
+ <el-col :span="12" class="left">
|
|
|
+ <div class="block-title">
|
|
|
+ {{ viewData.year }}年{{ viewData.month }}月 <span v-if="viewData.day>0">{{ viewData.day }}日</span>
|
|
|
+ <span class="block-note"> (从上面图表中可选择目标日期)</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12" class="right">
|
|
|
+ <el-button class="filter-item" type="primary" icon="el-icon-document" size="mini" @click="handleExport">导出</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row class="content-body">
|
|
|
+ <el-table
|
|
|
+ v-loading="listLoading"
|
|
|
+ class="page-table"
|
|
|
+ border
|
|
|
+ :data="dataList"
|
|
|
+ :max-height="maxHeight"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" />
|
|
|
+ <el-table-column type="index" label="序号" align="center" header-align="center" width="60" />
|
|
|
+
|
|
|
+ <el-table-column label="部门" align="center" header-align="center" width="200">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <span>{{ row.groupName }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="" align="right" header-align="right" width="80">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <el-avatar v-if="row.accountAvatar" :size="30" :src="row.accountAvatar" />
|
|
|
+ <svg-icon v-else icon-class="user" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="人员" align="left" header-align="left" :min-width="150">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <span>{{ row.accountName }}</span>
|
|
|
+ -
|
|
|
+ <span>{{ row.positionName }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="打卡次数" align="center" header-align="center" :min-width="80">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <span><el-tag type="warning">{{ row.dutyCount }}</el-tag></span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="最早打卡" align="left" header-align="left" :min-width="300">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <div class="flex">
|
|
|
+ <div class="flex-1">
|
|
|
+ <p v-if="row.dutyStartTime"><i class="el-icon-time" />{{ row.dutyStartTime }}</p>
|
|
|
+ <p v-if="row.dutyStartLocation"><i class="el-icon-location" />{{ row.dutyStartLocation }}</p>
|
|
|
+ </div>
|
|
|
+ <div v-if="row.dutyStartIcon" class="image__preview">
|
|
|
+ <el-image
|
|
|
+ :src="row.dutyStartIcon"
|
|
|
+ :preview-src-list="[row.dutyStartPhoto]"
|
|
|
+ fit="contain"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="最晚打卡" align="left" header-align="left" :min-width="300">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <div class="flex">
|
|
|
+ <div class="flex-1">
|
|
|
+ <p v-if="row.dutyLastTime"><i class="el-icon-time" />{{ row.dutyLastTime }}</p>
|
|
|
+ <p v-if="row.dutyLastLocation"><i class="el-icon-location" />{{ row.dutyLastLocation }}</p>
|
|
|
+ </div>
|
|
|
+ <div v-if="row.dutyLastIcon" class="image__preview">
|
|
|
+ <el-image
|
|
|
+ :src="row.dutyLastIcon"
|
|
|
+ :preview-src-list="[row.dutyLastPhoto]"
|
|
|
+ fit="contain"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="操作" header-align="center" align="center" width="100">
|
|
|
+ <template v-slot="{row}">
|
|
|
+ <el-button size="mini" type="primary" @click="handleDetail(row)">详情</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="pagination-container" style="float:right;">
|
|
|
+ <pagination v-show="total > 0" :total="total" :page.sync="viewData.page" :limit.sync="viewData.limit" @pagination="getDutyRecord" />
|
|
|
+ </div>
|
|
|
+ </el-row>
|
|
|
+ <duty-record-item-list ref="DutyRecordItemList" />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import Pagination from '@/components/Pagination/index.vue'
|
|
|
+import { getDutyRecordByPage, getDutyRecordByList } from '@/api/aqpt/dutyRecordApi'
|
|
|
+import { counterByDay } from '@/api/aqpt/dutyRecordStatisAPi'
|
|
|
+import DutyRecordItemList from './DutyRecordItemList.vue'
|
|
|
+import 'echarts/lib/chart/line'
|
|
|
+import { formatDateByYmd, parseDate } from '@/utils'
|
|
|
+import exportExcel from '@/utils/excel-export'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'DayCounterRecord',
|
|
|
+ components: {
|
|
|
+ Pagination,
|
|
|
+ DutyRecordItemList
|
|
|
+ },
|
|
|
+ filters: {},
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ listLoading: false,
|
|
|
+ ops: {
|
|
|
+ bar: {
|
|
|
+ keepShow: false,
|
|
|
+ background: 'rgba(144, 147, 153, 0.4)',
|
|
|
+ onlyShowBarOnScroll: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ time_option: {
|
|
|
+ disabledDate(time) {
|
|
|
+ return time.getTime() > Date.now()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ chartOptions: {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['人数', '次数'],
|
|
|
+ textStyle: {
|
|
|
+ color: '#fefefe'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ splitLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#1F3C51'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ show: true,
|
|
|
+ color: '#FFF',
|
|
|
+ fontSize: '8'
|
|
|
+ },
|
|
|
+ data: []
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ show: true,
|
|
|
+ color: '#FFF',
|
|
|
+ fontSize: '8'
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: false,
|
|
|
+ lineStyle: {
|
|
|
+ color: '#FFF'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: '20px',
|
|
|
+ bottom: '10px',
|
|
|
+ left: '25px',
|
|
|
+ right: '10px',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '人数',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 5,
|
|
|
+ // sampling: 'average',
|
|
|
+ itemStyle: {
|
|
|
+ normal: {
|
|
|
+ color: '#00f693',
|
|
|
+ label: {
|
|
|
+ show: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ data: []
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '次数',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 5,
|
|
|
+ // sampling: 'average',
|
|
|
+ itemStyle: {
|
|
|
+ normal: {
|
|
|
+ color: '#b9b3a9',
|
|
|
+ label: {
|
|
|
+ show: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ data: []
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ maxHeight: 0,
|
|
|
+ conditions: {},
|
|
|
+ dayCounter: undefined,
|
|
|
+ viewData: {
|
|
|
+ page: 1,
|
|
|
+ limit: 10,
|
|
|
+ keywords: '',
|
|
|
+ groupId: undefined,
|
|
|
+ year: undefined,
|
|
|
+ month: undefined,
|
|
|
+ day: 0
|
|
|
+ },
|
|
|
+ total: 0,
|
|
|
+ dataList: [],
|
|
|
+ exportItems: []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+
|
|
|
+ // 加载数据
|
|
|
+ loadData(params) {
|
|
|
+ this.conditions = params
|
|
|
+ this.viewData.keywords = params.keywords
|
|
|
+ this.viewData.page = 1
|
|
|
+ this.viewData.limit = 10
|
|
|
+ this.viewData.groupId = params.groupId
|
|
|
+ this.viewData.year = params.year
|
|
|
+ this.viewData.month = params.month
|
|
|
+ this.viewData.day = new Date().getDate()
|
|
|
+ this.getData()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 请求数据
|
|
|
+ getData() {
|
|
|
+ this.getDayCountOfMonth()
|
|
|
+ this.getDutyRecord()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 按月请求统计
|
|
|
+ getDayCountOfMonth() {
|
|
|
+ this.listLoading = true
|
|
|
+ counterByDay(this.conditions).then((resp) => {
|
|
|
+ const { data } = resp
|
|
|
+ this.listLoading = false
|
|
|
+ this.dayCounter = data
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.maxHeight = window.innerHeight - 470
|
|
|
+ this.initCharts()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 请求用户记录
|
|
|
+ getDutyRecord() {
|
|
|
+ this.listLoading = true
|
|
|
+ const { keywords, page, limit, groupId, year, month, day } = this.viewData
|
|
|
+ const params = {
|
|
|
+ keywords: keywords,
|
|
|
+ page: page,
|
|
|
+ limit: limit,
|
|
|
+ groupId: groupId,
|
|
|
+ dutyDate: formatDateByYmd(year, month, day)
|
|
|
+ }
|
|
|
+ getDutyRecordByPage(params).then((resp) => {
|
|
|
+ const { data, total } = resp
|
|
|
+ this.listLoading = false
|
|
|
+ this.total = total
|
|
|
+ this.dataList = data
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ initCharts() {
|
|
|
+ const keys = []
|
|
|
+ const vals1 = []
|
|
|
+ const vals2 = []
|
|
|
+ const dayNum = this.dayCounter.days
|
|
|
+ for (let i = 1; i <= dayNum; i++) {
|
|
|
+ const dayData = this.dayCounter['d' + i]
|
|
|
+ keys.push(i)
|
|
|
+ vals1.push(dayData.nou)
|
|
|
+ vals2.push(dayData.tou)
|
|
|
+ }
|
|
|
+ if (this.chartOptions.title) this.chartOptions.title.text = this.viewData.year + '年' + this.viewData.month + '月'
|
|
|
+ this.chartOptions.xAxis.data = keys
|
|
|
+ this.chartOptions.series[0].data = vals1
|
|
|
+ this.chartOptions.series[1].data = vals2
|
|
|
+ const self = this
|
|
|
+ this.$refs.myDayChart.chart.on('click', function(params) {
|
|
|
+ self.$emit('on-change', params)
|
|
|
+ self.viewData.day = params.name
|
|
|
+ self.getDutyRecord()
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 显示详情
|
|
|
+ handleDetail(data) {
|
|
|
+ const { accountId, dutyStartTime } = data
|
|
|
+ const dutyDate = parseDate(dutyStartTime)
|
|
|
+ this.$refs['DutyRecordItemList'].showView(accountId, dutyDate)
|
|
|
+ },
|
|
|
+
|
|
|
+ // 导出
|
|
|
+ handleExport() {
|
|
|
+ this.listLoading = true
|
|
|
+ const { groupId, year, month, day } = this.viewData
|
|
|
+ const params = {
|
|
|
+ groupId: groupId,
|
|
|
+ dutyDate: formatDateByYmd(year, month, day)
|
|
|
+ }
|
|
|
+ getDutyRecordByList(params).then((resp) => {
|
|
|
+ const { data } = resp
|
|
|
+ this.exportToExcel(data)
|
|
|
+ this.listLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 导出Excel
|
|
|
+ exportToExcel(data) {
|
|
|
+ const headers = [
|
|
|
+ { title: '部门', dataIndex: 'groupName', width: 150 },
|
|
|
+ { title: '人员', dataIndex: 'accountName', width: 60 },
|
|
|
+ { title: '岗位', dataIndex: 'positionName', width: 60 },
|
|
|
+ { title: '打卡次数', dataIndex: 'dutyCount', width: 50 },
|
|
|
+ { title: '首次打卡时间', dataIndex: 'dutyStartTime', width: 150 },
|
|
|
+ { title: '首次打卡地点', dataIndex: 'dutyStartLocation', width: 150 },
|
|
|
+ { title: '最后打卡时间', dataIndex: 'dutyLastTime', width: 150 },
|
|
|
+ { title: '最后打卡地点', dataIndex: 'dutyLastTime', width: 150 }
|
|
|
+ ]
|
|
|
+
|
|
|
+ let t1 = this.viewData.year + '年' + this.viewData.month + '月'
|
|
|
+ if (this.viewData.day > 0) {
|
|
|
+ t1 += this.viewData.day + '日'
|
|
|
+ }
|
|
|
+ t1 += '-人员在岗记录'
|
|
|
+
|
|
|
+ const options = [
|
|
|
+ { title: t1 }
|
|
|
+ ]
|
|
|
+
|
|
|
+ const type = { bookType: 'xlsx', bookSST: true, type: 'binary', cellStyles: true }
|
|
|
+ exportExcel(headers, data, options, type, t1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.content-container {
|
|
|
+ margin: 0 10px 0 10px ;
|
|
|
+ padding-top: 0;
|
|
|
+ height: calc(100vh - 120px);
|
|
|
+ background-color: #193142FF;
|
|
|
+ p {
|
|
|
+ margin: 0;
|
|
|
+ line-height: 2em;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tool-bar {
|
|
|
+ padding: 5px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .block-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #fff;
|
|
|
+
|
|
|
+ .block-note {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #bdb7b7;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-body {
|
|
|
+ .flex {
|
|
|
+ width: 240px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ text-align: left;
|
|
|
+
|
|
|
+ .flex-1 {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .image__preview {
|
|
|
+ width: 60px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|