wecom-dashboards/ruoyi-ui/src/views/wecom/departmentStatistics/index.vue

485 lines
16 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>
<div class="app-container">
<el-row :gutter="20">
<el-col :span="4">
<el-card shadow="never" class="department-tree-card">
<div slot="header" class="clearfix">
<span>部门列表</span>
</div>
<el-tree
:data="departmentTree"
:props="defaultProps"
node-key="id"
:expand-on-click-node="false"
:default-expand-all="false"
@node-click="handleNodeClick"
highlight-current
>
<span class="tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
</span>
</el-tree>
</el-card>
</el-col>
<el-col :span="20">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="数据类型" prop="dataType">
<el-select
v-model="queryParams.dataType"
placeholder="请选择数据类型"
@change="handleDataTypeChange"
>
<el-option label="按天查询" value="day" />
<el-option label="自然周叠加数据" value="week" />
<el-option label="自然月叠加数据" value="month" />
<el-option label="所有数据" value="all" />
</el-select>
</el-form-item>
<el-form-item label="开始日期" prop="startDate" v-if="showDateRange">
<el-date-picker
v-model="queryParams.startDate"
type="date"
placeholder="选择开始日期"
value-format="yyyy-MM-dd"
clearable
/>
</el-form-item>
<el-form-item label="结束日期" prop="endDate" v-if="showDateRange">
<el-date-picker
v-model="queryParams.endDate"
type="date"
placeholder="选择结束日期"
value-format="yyyy-MM-dd"
clearable
/>
</el-form-item>
<el-form-item label="年份" prop="year" v-if="showYearSelect">
<el-select
v-model="queryParams.year"
placeholder="选择年份"
clearable
@change="updateWeekDateRange"
>
<el-option
v-for="item in yearOptions"
:key="item"
:label="item + '年'"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item label="周数" prop="week" v-if="showWeekSelect">
<el-select
v-model="queryParams.week"
placeholder="选择周数"
clearable
@change="updateWeekDateRange"
>
<el-option
v-for="item in weekOptions"
:key="item"
:label="item + '周'"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-if="showWeekDateRange && weekDateRange">
<el-tag type="info" size="medium">
{{ weekDateRange }}
</el-tag>
</el-form-item>
<el-form-item label="年月" prop="yearMonth" v-if="showMonthSelect">
<el-date-picker
v-model="queryParams.yearMonth"
type="month"
placeholder="选择年月"
value-format="yyyy-MM"
@change="handleYearMonthChange"
/>
</el-form-item>
<el-form-item label="部门路径" prop="departmentPath">
<el-input
v-model="queryParams.departmentPath"
placeholder="请输入部门路径"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="20" class="mb20" style="background: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px;">
<el-col :span="8">
<div class="summary-item">
<span class="label">整体成单数:</span>
<span class="value">{{ summaryData.totalOrders || 0 }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="summary-item">
<span class="label">历史整体进粉量:</span>
<span class="value">{{ summaryData.totalIns || 0 }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="summary-item">
<span class="label">渗透率</span>
<span class="value">{{ summaryData.penetrationRate + "%" }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['wecom:departmentStatistics:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange" show-summary :summary-method="getSummaries">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="日期" align="center" prop="statDate" width="120" v-if="showStatDate">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.statDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="部门路径" align="center" prop="departmentPath" width="200" :show-overflow-tooltip="true" />
<el-table-column label="当日总承接数" align="center" prop="dailyTotalAccepted" width="180" :show-overflow-tooltip="true" />
<el-table-column label="当日总成单数" align="center" prop="dailyTotalOrders" width="180" :show-overflow-tooltip="true" />
<el-table-column label="当日转化率" align="center" prop="dailyConversionRate" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.dailyConversionRate) }}</span>
</template>
</el-table-column>
<el-table-column label="当日及时单占比" align="center" prop="dailyTimelyOrderRatio" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.dailyTimelyOrderRatio) }}</span>
</template>
</el-table-column>
<el-table-column label="当日非及时单占比" align="center" prop="dailyNonTimelyOrderRatio" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.dailyNonTimelyOrderRatio) }}</span>
</template>
</el-table-column>
<el-table-column label="家长成单率" align="center" prop="dailyParentOrderRate" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.dailyParentOrderRate) }}</span>
</template>
</el-table-column>
<el-table-column label="学生成单率" align="center" prop="dailyStudentOrderRate" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.dailyStudentOrderRate) }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
</div>
</template>
<script>
import { listDepartmentStatistics, exportDepartmentStatistics, getDepartmentStatisticsSummary, getDepartmentTree } from "@/api/wecom/departmentStatistics"
export default {
name: "DepartmentStatistics",
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 部门统计数据表格数据
dataList: [],
// 汇总数据
summaryData: {
totalOrders: 0,
totalIns: 0,
penetrationRate: 0
},
// 部门树数据
departmentTree: [],
// 树形组件配置
defaultProps: {
children: 'children',
label: 'name'
},
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
startDate: undefined,
endDate: undefined,
departmentPath: undefined,
dataType: 'day',
year: undefined,
week: undefined,
month: undefined,
yearMonth: undefined
},
// 年份选项
yearOptions: [],
// 周数选项(1-53)
weekOptions: [],
// 周日期范围显示
weekDateRange: ''
}
},
computed: {
// 是否显示日期范围选择器
showDateRange() {
return this.queryParams.dataType === 'day'
},
// 是否显示年份选择器
showYearSelect() {
return this.queryParams.dataType === 'week'
},
// 是否显示周数选择器
showWeekSelect() {
return this.queryParams.dataType === 'week'
},
// 是否显示周日期范围
showWeekDateRange() {
return this.queryParams.dataType === 'week'
},
// 是否显示年月选择器
showMonthSelect() {
return this.queryParams.dataType === 'month'
},
// 是否显示日期列
showStatDate() {
return this.queryParams.dataType === 'day'
}
},
created() {
this.initYearOptions()
this.initWeekOptions()
this.getDepartmentTreeData()
this.getList()
},
methods: {
// 合计行计算方法
getSummaries(param) {
const { columns, data } = param
const sums = []
// 需要计算合计的列(非百分比列)
const sumColumns = ['dailyTotalAccepted', 'dailyTotalOrders']
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '合计'
return
}
// 检查是否是需要合计的列
if (sumColumns.includes(column.property)) {
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
const sum = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + value
} else {
return prev
}
}, 0)
sums[index] = sum
} else {
sums[index] = ''
}
} else {
// 百分比列和其他列不显示合计
sums[index] = ''
}
})
return sums
},
// 格式化百分比显示
formatPercent(value) {
if (value === null || value === undefined || value === '') {
return '-'
}
const num = parseFloat(value)
if (isNaN(num)) {
return '-'
}
return num.toFixed(2) + '%'
},
// 获取部门树数据
getDepartmentTreeData() {
getDepartmentTree().then(response => {
this.departmentTree = response.data || []
})
},
// 处理部门树节点点击
handleNodeClick(data) {
this.queryParams.departmentPath = data.ancestors
this.handleQuery()
},
initYearOptions() {
const currentYear = new Date().getFullYear()
const years = []
for (let i = currentYear; i >= currentYear - 5; i--) {
years.push(i)
}
this.yearOptions = years
},
// 初始化周数选项(1-53)
initWeekOptions() {
const weeks = []
for (let i = 1; i <= 53; i++) {
weeks.push(i)
}
this.weekOptions = weeks
},
// 数据类型变更处理
handleDataTypeChange(val) {
this.queryParams.year = undefined
this.queryParams.week = undefined
this.queryParams.month = undefined
this.queryParams.yearMonth = undefined
this.queryParams.startDate = undefined
this.queryParams.endDate = undefined
this.weekDateRange = ''
},
// 更新周日期范围显示
updateWeekDateRange() {
if (this.queryParams.year && this.queryParams.week) {
const dateRange = this.getWeekDateRange(this.queryParams.year, this.queryParams.week)
this.weekDateRange = `${dateRange.startDate} 至 ${dateRange.endDate}`
} else {
this.weekDateRange = ''
}
},
// 根据年份和周数计算日期范围
getWeekDateRange(year, week) {
const firstDayOfYear = new Date(year, 0, 1)
const daysOffset = (week - 1) * 7
let firstDayOfWeek = new Date(firstDayOfYear)
firstDayOfWeek.setDate(firstDayOfYear.getDate() + daysOffset)
const dayOfWeek = firstDayOfWeek.getDay()
const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek
firstDayOfWeek.setDate(firstDayOfWeek.getDate() + diff)
const lastDayOfWeek = new Date(firstDayOfWeek)
lastDayOfWeek.setDate(firstDayOfWeek.getDate() + 6)
const formatDate = (date) => {
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
return `${y}-${m}-${d}`
}
return {
startDate: formatDate(firstDayOfWeek),
endDate: formatDate(lastDayOfWeek)
}
},
// 年月变更处理
handleYearMonthChange(val) {
if (val) {
const [year, month] = val.split('-')
this.queryParams.year = parseInt(year)
this.queryParams.month = parseInt(month)
} else {
this.queryParams.year = undefined
this.queryParams.month = undefined
}
},
/** 查询部门统计数据列表 */
getList() {
this.loading = true
const params = { ...this.queryParams }
if (params.dataType === 'day') {
params.dataType = undefined
}
if (params.dataType === 'month' && params.yearMonth) {
const [year, month] = params.yearMonth.split('-')
params.year = parseInt(year)
params.month = parseInt(month)
}
listDepartmentStatistics(params).then(response => {
this.dataList = response.rows
this.total = response.total
this.loading = false
})
getDepartmentStatisticsSummary(params).then(response => {
this.summaryData = response.data
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.queryParams.dataType = 'day'
this.queryParams.year = undefined
this.queryParams.week = undefined
this.queryParams.month = undefined
this.queryParams.yearMonth = undefined
this.weekDateRange = ''
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 导出按钮操作 */
handleExport() {
const formattedDate = this.parseTime(new Date(), '{y}-{m}-{d}');
const params = { ...this.queryParams }
if (params.dataType === 'day') {
params.dataType = undefined
}
if (params.dataType === 'month' && params.yearMonth) {
const [year, month] = params.yearMonth.split('-')
params.year = parseInt(year)
params.month = parseInt(month)
}
this.download('/wecom/departmentStatistics/export', {
...params
}, `销售看板数据_${formattedDate}.xlsx`)
}
}
}
</script>
<style scoped>
.department-tree-card {
height: calc(100vh - 180px);
overflow-y: auto;
}
.tree-node {
font-size: 14px;
}
</style>