Files
dvcp_v2_webapp/project/sass/apps/Announce/AppAnnounceStatistics/AppAnnounceStatistics.vue
2022-07-25 10:50:59 +08:00

699 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<ai-list class="AppAnnounceStatistics">
<template slot="content">
<div class="statistics-content">
<ai-title title="宣发日历"></ai-title>
<div class="flex-content">
<div class="flex-left">
<div class="date-header">
<p>{{chooseYear}}{{chooseMonth}}</p>
<div>
<el-date-picker size="small"
v-model="searchMonth"
type="month" value-format="yyyy-MM"
placeholder="选择日期" @change="searchMonthChange">
</el-date-picker>
</div>
</div>
<el-calendar v-model="calendarDate">
<template
slot="dateCell"
slot-scope="{date, data}" >
<div class="flex-date">
<span>{{Number(data.day.substring(8, 10))}}</span>
<span class="tips" v-if="data.day.substring(5, 7) == chooseMonth && dateList[Number(data.day.substring(8, 10))] && dateList[Number(data.day.substring(8, 10))].taskList.length">{{dateList[Number(data.day.substring(8, 10))].taskList.length}}</span>
</div>
</template>
</el-calendar>
</div>
<div class="flex-right">
<div class="title">{{chooseMonth}}{{chooseDay}}日宣发内容</div>
<div class="list-content">
<el-timeline>
<el-timeline-item v-for="(item, index) in taskList" :key="index">
<el-card>
<div class="flex-between">
<p class="item-title">{{item.taskTitle}}</p>
<span class="item-time" v-if="item.createTime">{{item.createTime.substring(10, 16)}}</span>
</div>
<div class="item-info item-created">创建人
<ai-open-data type="userName" :openid="item.createUserId" class="name"></ai-open-data>
</div>
<div class="item-info">创建部门
<ai-open-data type="departmentName" :openid="item.createUserDept"></ai-open-data>
</div>
<div class="flex-between">
<!-- <div class="item-info">群发类型<span>{{$dict.getLabel('mstSendType', item.sendType) || ''}}</span></div> -->
<span class="item-btn">详情</span>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
<ai-empty v-if="!taskList.length" />
</div>
</div>
</div>
</div>
<div class="statistics-content">
<div class="flex-between mar-b16">
<ai-title title="宣发效果"></ai-title>
<div class="right-search">
<div class="time-select" :class="effectType == index ? 'active' : ''" v-for="(item, index) in dateTypeList" :key="index" @click="changeEffectType(index)">{{item}}</div>
<ai-picker :instance="instance" v-model="deptList" @change="getEffect">
<div class="time-select">
<span class="dept-name" style="color:#999;" v-if="deptList && !deptList.length">宣发部门</span>
<ai-open-data class="dept-name" type="departmentName" :openid="deptList[0]" v-else/>
<i class="el-icon-arrow-down"></i>
</div>
</ai-picker>
</div>
</div>
<div class="line-content">
<div class="flex1">
<div class="header">
<p>累计创建宣发任务数</p>
<h2>{{effectData.createCount}}</h2>
</div>
<div class="chart-content">
<div class="chart-title">宣发任务数</div>
<div class="chart-box" id="createChart"></div>
</div>
</div>
<div class="flex1">
<div class="header">
<p>累计执行宣发次数</p>
<h2>{{effectData.executeCount}}</h2>
</div>
<div class="chart-content">
<div class="chart-title">宣发次数</div>
<div class="chart-box" id="executeChart"></div>
</div>
</div>
<div class="flex1 mar-r0">
<div class="header">
<p>累计触达人次</p>
<h2>{{effectData.receiveCount}}</h2>
</div>
<div class="chart-content">
<div class="chart-title">触达人次</div>
<div class="chart-box" id="receiveChart"></div>
</div>
</div>
</div>
</div>
<div class="statistics-content">
<div class="flex-between mar-b16">
<ai-title title="宣发明细"></ai-title>
<div class="right-search">
<div class="time-select" :class="departType == index ? 'active' : ''" v-for="(item, index) in dateTypeList" :key="index" @click="changeDepartType(index)">{{item}}</div>
</div>
</div>
<div id="departBarChart">
<ai-empty v-if="!isDepartData"></ai-empty>
</div>
</div>
<ai-dialog :visible.sync="dialogDate" title="选择时间" width="500px" customFooter :show-close="false">
<el-date-picker v-model="timeList" size="small" type="daterange" value-format="yyyy-MM-dd"
range-separator="" start-placeholder="开始日期" end-placeholder="结束日期">
</el-date-picker>
<el-button slot="footer" @click="selectDete" type="primary">确认</el-button>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import * as echarts from "echarts";
import { mapActions, mapState } from 'vuex';
export default {
name: 'AppAnnounceStatistics',
label: '协同宣发统计',
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
calendarDate: new Date(),
dateList: {},
chooseYear: '',
chooseMonth: '',
chooseDay: '',
searchMonth: '',
taskList: [],
effectType: 0, // 宣发效果类型 0近七天、1近30天、2近一年、3自定义
effectData: {},
createChart: null,
executeChart: null,
receiveChart: null,
departType: 0, // 宣发明细类型 0近七天、1近30天、2近一年、3自定义
dateTypeList: ['近7天', '近30天', '近1年', '自定义'],
departData: {},
departBarChart: null,
dialogDate: false,
timeList: '',
deptList: [],
selectDeptName: '',
isDepartData: true,
type: '',
}
},
computed: {
...mapState(['user']),
},
watch: {
calendarDate: function() {
var year = '' , month = '', date = ''
if(this.calendarDate.length == 9) { // 月份选择器触发
year = this.calendarDate.substring(0, 4)
month = this.calendarDate.substring(5, 7)
date = this.calendarDate.substring(8, 10)
}else { // 日历点击
year = this.calendarDate.getFullYear();
month = this.calendarDate.getMonth() + 1;
date = this.calendarDate.getDate()
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if(this.chooseMonth != month) { // 日历点击不同月
this.searchMonth = ''
}
}
this.chooseDay = date
if(this.chooseMonth != month || this.chooseYear != year) { // 不同年/不同月重新请求日历列表
this.getCalendarList(year, month)
} else {
this.getTaskList(date)
}
this.chooseMonth = month
this.chooseYear = year
}
},
created() {
var year = this.calendarDate.getFullYear();
var month = this.calendarDate.getMonth() + 1;
var date = this.calendarDate.getDate()
if (month >= 1 && month <= 9) {
month = "0" + month;
}
this.chooseMonth = month
this.chooseYear = year
this.chooseDay = date
this.getCalendarList(year, month)
this.getEffect()
this.getDepart()
this.dict.load(['mstSendType'])
},
methods: {
...mapActions(['initOpenData', 'transCanvas']),
selectDete() {
if(!this.timeList || !this.timeList.length) {
return this.$message.error('请选择自定义时间');
}
if(this.effectType == 3) { //宣发效果
this.getEffect()
}
if(this.departType == 3) { //宣发明细
this.getDepart()
}
this.dialogDate = false
},
searchMonthChange() {
this.calendarDate = this.searchMonth + '-1'
},
getCalendarList(year, month){
this.instance.post(`/app/appmasssendingtask/statisticsCalendar?yyyyMM=${year}${month}`).then(res => {
if (res.code == 0) {
this.dateList = res.data
this.getTaskList(this.chooseDay)
}
})
},
getTaskList(day) {
this.taskList = this.dateList[day].taskList
},
changeEffectType(type) {
this.effectType = type
if(type == 3) {
this.dialogDate = true
this.timeList = []
}else {
this.getEffect()
}
},
getEffect() {
var startTime = this.timeList[0] || '' , endTime = this.timeList[1] || '', departId = this.deptList[0] || ''
this.instance.post(`/app/appmasssendingtask/statisticsEffect?type=${this.effectType}&startTime=${startTime}&endTime=${endTime}&departId=${departId}`).then(res => {
if (res.code == 0) {
this.effectData = res.data
var xData = [], createData = [], executeData = [], receiveData = []
res.data.trend.map(e => {
xData.push(e.ymd)
createData.push(e.createCount)
executeData.push(e.executeCount)
receiveData.push(e.receiveCount)
})
this.setLineChart(xData, createData, 'createChart', ['#2891FF'])
this.setLineChart(xData, executeData, 'executeChart', ['#FFB865'])
this.setLineChart(xData, receiveData, 'receiveChart', ['#26D52B'])
}
})
},
setLineChart(xData, yData, id, colorList) {
this[id] = echarts.init(document.querySelector(`#${id}`))
var option = {
xAxis: {
type: 'category',
data: xData
},
yAxis: {
type: 'value'
},
grid: {
left: '10px',
right: '28px',
bottom: '14px',
top: '30px',
containLabel: true
},
tooltip: {
trigger: 'axis'
},
legend: {
type: "plain"
},
color: colorList,
series: [
{
data: yData,
type: 'line'
}
]
}
this[id].setOption(option)
},
changeDepartType(type) {
this.departType = type
if(type == 3) {
this.dialogDate = true
this.timeList = []
}else {
this.getDepart()
}
},
getDepart() {
var startTime = this.timeList[0] || '' , endTime = this.timeList[1] || ''
this.instance.post(`/app/appmasssendingtask/statisticsDepart?type=${this.departType}&startTime=${startTime}&endTime=${endTime}`).then(res => {
if (res.code == 0) {
if(res.data && res.data.length) {
this.isDepartData = true
var items = [], xData = [], yData = []
res.data.map((item) => {
var i = {type: 'departmentName', id: item.deptId, corpid: this.user.info.corpId}
items.push(i)
yData.push(item.taskCount)
})
this.initOpenData({canvas:true})
this.transCanvas(items).then((data) => {
data.items.map((i) => {
xData.push(i.data)
})
this.setBarChart(xData, yData)
})
}else {
this.isDepartData = false
}
}
})
},
setBarChart(xData, yData) {
this.departBarChart = echarts.init(document.querySelector(`#departBarChart`))
var option = {
color: ['#2891FF'],
grid: {
top: '10%',
left: '2%',
right: '2%',
bottom: 90,
containLabel: true
},
// toolbox: {
// feature: {
// dataZoom: {
// yAxisIndex: false
// },
// saveAsImage: {
// pixelRatio: 2
// }
// }
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
dataZoom: [
{
type: 'inside'
},
{
type: 'slider'
}
],
xAxis: {
data: xData,
silent: false,
splitLine: {
show: false
},
splitArea: {
show: false
}
},
yAxis: {
splitArea: {
show: false
}
},
series: [
{
type: 'bar',
data: yData,
barWidth: 20,
barGap: '250%',
large: true
}
]
};
// {
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'shadow'
// }
// },
// grid: {
// top: '10%',
// left: '2%',
// right: '2%',
// bottom: '2%',
// containLabel: true
// },
// color: ['#2891FF'],
// xAxis: {
// type: 'category',
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
// },
// yAxis: {
// type: 'value'
// },
// series: [
// {
// data: [120, 200, 150, 80, 70, 110, 130],
// type: 'bar',
// barWidth: 20,
// barGap: '250%',
// }
// ]
// };
this.departBarChart.setOption(option)
}
}
}
</script>
<style lang="scss" scoped>
.AppAnnounceStatistics {
height: 100%;
.flex-between{
display: flex;
justify-content: space-between;
}
.mar-b16{
margin-bottom: 16px;
}
.mar-r0{
margin-right: 0!important;
}
.statistics-content{
padding: 0 24px 24px;
background-color: #fff;
box-shadow: 0px 4px 6px -2px rgba(15,15,21,0.1500);
border-radius: 4px;
margin-bottom: 20px;
.flex-content{
width: 100%;
display: flex;
margin-top: 16px;
.flex-left{
width: 50%;
.date-header{
padding: 12px 16px;
border: 1px solid #eee;
display: flex;
justify-content: space-between;
p{
line-height: 32px;
}
}
.flex-date{
display: flex;
justify-content: space-between;
}
.tips{
display: inline-block;
padding: 0 4px;
height: 16px;
line-height: 16px;
border-radius: 8px;
background: #2891FF;
font-size: 12px;
font-family: ArialMT;
color: #FFF;
margin-top: 8px;
}
}
.flex-right{
width: 50%;
margin-left: 16px;
border: 1px solid #eee;
.title{
line-height: 56px;
border-bottom: 1px solid #EEE;
padding-left: 16px;
font-size: 16px;
font-family: MicrosoftYaHeiSemibold;
color: #333;
}
.list-content{
padding: 16px;
height: 339px;
box-sizing: border-box;
overflow-y: scroll;
background-color: #F9F9F9;
.item-title{
width: calc(100% - 100px);
word-break: break-all;
margin-bottom: 8px;
font-size: 16px;
font-family: MicrosoftYaHeiSemibold;
color: #222;
line-height: 24px;
}
.item-time{
width: 100px;
text-align: right;
font-size: 16px;
font-family: ArialMT;
color: #888;
line-height: 24px;
}
.item-info{
display: inline-block;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #888;
line-height: 22px;
span{
color: #222;
}
}
.item-created{
width: 152px;
margin-bottom: 4px;
}
.item-btn{
color: #26f;
cursor: pointer;
}
}
}
}
.right-search{
margin-top: 10px;
div{
display: inline-block;
}
.time-select{
font-size: 14px;
font-family: MicrosoftYaHei;
color: #222;
line-height: 22px;
padding: 6px 12px;
border-radius: 2px;
border: 1px solid #D0D4DC;
margin-right: 8px;
box-sizing: border-box;
cursor: pointer;
.dept-name{
display: inline-block;
width: 200px;
}
}
.active{
border: 1px solid #26f;
color: #26f;
}
}
.line-content{
display: flex;
.flex1{
flex: 1;
margin-right: 16px;
.header{
padding: 16px;
width: 100%;
height: 90px;
background: #F9F9F9;
border-radius: 2px;
box-sizing: border-box;
margin-bottom: 16px;
p{
font-size: 14px;
font-family: MicrosoftYaHeiSemibold;
color: #222;
line-height: 22px;
margin-bottom: 4px;
}
h2{
font-size: 24px;
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #26F;
line-height: 32px;
}
}
.chart-content{
width: 100%;
padding: 16px;
background: #F9F9F9;
border-radius: 2px;
box-sizing: border-box;
.chart-title{
font-size: 16px;
font-family: MicrosoftYaHeiSemibold;
color: #333;
line-height: 24px;
}
.chart-box{
width: 100%;
height: 280px;
}
}
}
}
#departBarChart{
width: 100%;
height: 300px;
}
}
::v-deep .el-calendar-table:not(.is-range) td.next,
::v-deep .el-calendar-table:not(.is-range) td.prev {
color: #ccc;
}
::v-deep .el-calendar-table .el-calendar-day{
height: 48px;
line-height: 32px;
padding-left: 12px;
font-size: 14px;
font-family: ArialMT;
}
.el-calendar-table:not(.is-range) td .current{
color: #888;
}
::v-deep .el-calendar__header{
display: none;
}
::v-deep .el-calendar__body{
padding: 0;
}
::v-deep .el-calendar-table thead th:nth-of-type(1){
border-left: 1px solid #eee;
}
::v-deep .el-calendar-table thead th:nth-of-type(7){
border-right: 1px solid #eee;
}
::v-deep .el-calendar-table tr td:first-child {
border-left: 1px solid #eee;
}
::v-deep .el-calendar-table tr:first-child td {
border-top: 1px solid #eee;
}
::v-deep .el-calendar-table td {
border-bottom: 1px solid #eee;
border-right: 1px solid #eee;
}
::v-deep .el-timeline-item__timestamp.is-top{
margin-bottom: 0;
padding-top: 0;
}
::v-deep .el-timeline-item__node{
background-color: #26F;
width: 8px;
height: 8px;
border-radius: 50%;
left: 1px;
}
::v-deep .el-card{
border: none;
}
::v-deep .el-card__body{
padding: 8px;
}
}
::v-deep .ai-list__content {
padding: 0!important;
.ai-list__content--right-wrapper {
background: transparent!important;
box-shadow: none!important;
margin: 0!important;
padding: 0 0 0!important;
}
}
::v-deep .AiPicker{
display: inline-block;
}
</style>