修改回访统计和预警统计

This commit is contained in:
lxd 2025-06-20 11:16:20 +08:00
parent 4be453f04a
commit a70671c16d
4 changed files with 495 additions and 73 deletions

View File

@ -54,4 +54,8 @@ export const RecordApi = {
getRecordStatistics: async (params: any) => {
return await request.get({ url: `/system/record/statistics`, params })
},
//获取会员回访记录统计
getStatistics: async (orgid: number,startDate:string,endDate:string) => {
return await request.get({ url: `/system/record/getstatistics?orgid=` + orgid + `&startDate=` + startDate + `&endDate=` + endDate })
},
}

View File

@ -27,13 +27,22 @@
</el-card>
<!-- 统计图表卡片下方主内容区域 -->
<el-card class="member-stat-card stat-card" shadow="hover">
<div id="main" style="width:100%;height:580px;"></div>
<!-- 无数据提示 -->
<div v-if="chartData.length === 0" class="no-data-container">
<el-empty
description="当前时间段无数据"
:image-size="120"
class="no-data-empty"
/>
</div>
<!-- 图表容器 -->
<div v-else id="main" style="width:100%;height:580px;"></div>
</el-card>
</ContentWrap>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { ref, onMounted, watch, nextTick } from 'vue'
import * as echarts from 'echarts'
import ShortcutDateRangePicker from '@/components/ShortcutDateRangePicker/index.vue'
import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
@ -49,12 +58,29 @@ const dateRange = ref<[string, string]>([
dayjs().endOf('day').format('YYYY-MM-DD 23:59:59')
])
//
let myChart: echarts.ECharts | null = null
// echarts
const renderChart = () => {
//
if (chartData.value.length === 0) {
if (myChart) {
myChart.dispose()
myChart = null
}
return
}
const dom = document.getElementById('main')
if (!dom) return
const myChart = echarts.init(dom)
//
if (myChart) {
myChart.dispose()
}
myChart = echarts.init(dom)
myChart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 40, right: 20, top: 40, bottom: 40 },
@ -106,15 +132,17 @@ const onDateChange = (range: [string, string]) => {
//
watch([chartData, chartType], () => {
renderChart()
nextTick(() => {
renderChart()
})
})
//
onMounted(() => {
fetchData()
setTimeout(() => {
nextTick(() => {
renderChart()
}, 300)
})
})
</script>
@ -145,4 +173,23 @@ onMounted(() => {
.stat-card {
min-height: 320px; /* 统计图卡片主内容区高度,可根据实际需求调整 */
}
/* 无数据提示样式 */
.no-data-container {
display: flex;
justify-content: center;
align-items: center;
height: 580px; /* 与图表容器保持一致的高度 */
width: 100%;
}
.no-data-empty {
text-align: center;
}
.no-data-empty :deep(.el-empty__description) {
color: #909399;
font-size: 16px;
margin-top: 16px;
}
</style>

View File

@ -13,6 +13,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
@change="onDateChange"
style="width: 300px;"
clearable
/>
</el-form-item>
<el-form-item label="图表类型">
@ -22,6 +23,9 @@
<el-button :type="chartType === 'bar' ? 'primary' : 'default'" @click="chartType = 'bar'">
柱状图
</el-button>
<el-button :type="chartType === 'stack' ? 'primary' : 'default'" @click="chartType = 'stack'">
堆叠图
</el-button>
</el-form-item>
</el-form>
</el-card>
@ -43,10 +47,18 @@ import { RecordApi } from '@/api/record'
import dayjs from 'dayjs'
import { getUserProfile } from '@/api/system/user/profile'
// 'line' 线'bar'
const chartType = ref<'line' | 'bar'>('line')
//
const chartData = ref<{ date: string; totalCount: number; readCount: number; unreadCount: number }[]>([])
// 'line' 线'bar' 'stack'
const chartType = ref<'line' | 'bar' | 'stack'>('bar')
// 访
const chartData = ref<{
date: string;
totalCount: number;
visitedCount: number;
unvisitedCount: number;
satisfiedCount: number;
normalCount: number;
unsatisfiedCount: number;
}[]>([])
//
const dateRange = ref<[string, string]>([
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
@ -58,17 +70,55 @@ const renderChart = () => {
const dom = document.getElementById('main')
if (!dom) return
const myChart = echarts.init(dom)
myChart.setOption({
//
if (!chartData.value || chartData.value.length === 0) {
myChart.setOption(
{
title: {
text: '当前时间段无数据',
left: 'center',
top: 'center',
textStyle: { color: '#909399', fontSize: 16 }
},
// 线
xAxis: { show: false },
yAxis: { show: false },
series: []
},
true
) // trueoption
return
}
const option: any = {
title: {
text: '电话回访统计',
left: 'center',
top: 0,
textStyle: {
fontSize: 18,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'axis',
formatter: function(params: any) {
const data = params[0]
const date = data.name
const value = data.value
return `${date}<br/>预警信息数量: ${value}`
const date = params[0].name
let result = `${date}<br/>`
params.forEach((param: any) => {
result += `${param.seriesName}: ${param.value}<br/>`
})
return result
}
},
grid: { left: 40, right: 20, top: 60, bottom: 40 },
legend: {
data: ['总回访数', '已回访', '未回访', '满意', '一般', '不满意'],
top: 30
},
grid: { left: 40, right: 20, top: 80, bottom: 40 },
xAxis: {
type: 'category',
data: chartData.value.map(item => item.date),
@ -81,15 +131,59 @@ const renderChart = () => {
axisLine: { lineStyle: { color: '#dcdfe6' } },
axisLabel: { color: '#666', fontSize: 14 }
},
series: [
series: []
}
if (chartType.value === 'stack') {
// 访
option.series = [
{
name: '预警信息数量',
name: '已回访',
type: 'bar',
stack: 'status',
data: chartData.value.map(item => item.visitedCount),
itemStyle: { color: '#67C23A' }
},
{
name: '未回访',
type: 'bar',
stack: 'status',
data: chartData.value.map(item => item.unvisitedCount),
itemStyle: { color: '#909399' }
},
{
name: '满意',
type: 'bar',
stack: 'result',
data: chartData.value.map(item => item.satisfiedCount),
itemStyle: { color: '#409EFF' }
},
{
name: '一般',
type: 'bar',
stack: 'result',
data: chartData.value.map(item => item.normalCount),
itemStyle: { color: '#E6A23C' }
},
{
name: '不满意',
type: 'bar',
stack: 'result',
data: chartData.value.map(item => item.unsatisfiedCount),
itemStyle: { color: '#F56C6C' }
}
]
} else {
// 线
option.series = [
{
name: '总回访数',
type: chartType.value,
data: chartData.value.map(item => item.totalCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: chartType.value === 'line' ? '#409EFF' : '#67C23A',
color: '#409EFF',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
@ -97,22 +191,83 @@ const renderChart = () => {
color: '#409EFF'
},
areaStyle: chartType.value === 'line' ? { color: 'rgba(64,158,255,0.15)' } : undefined
},
{
name: '已回访',
type: chartType.value,
data: chartData.value.map(item => item.visitedCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#67C23A',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#67C23A'
}
},
{
name: '未回访',
type: chartType.value,
data: chartData.value.map(item => item.unvisitedCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#909399',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#909399'
}
}
]
})
}
myChart.setOption(option)
}
//
const renderPieChart = () => {
const dom = document.getElementById('pieChart')
if (!dom) return
//
const readCount = chartData.value.reduce((sum, item) => sum + item.readCount, 0)
const unreadCount = chartData.value.reduce((sum, item) => sum + item.unreadCount, 0)
const pieChart = echarts.init(dom)
//
if (!chartData.value || chartData.value.length === 0) {
pieChart.setOption(
{
title: {
text: '当前时间段无数据',
left: 'center',
top: 'center',
textStyle: { color: '#909399', fontSize: 16 }
},
series: []
},
true
)
return
}
//
const totalVisited = chartData.value.reduce((sum, item) => sum + item.visitedCount, 0)
const totalUnvisited = chartData.value.reduce((sum, item) => sum + item.unvisitedCount, 0)
const totalSatisfied = chartData.value.reduce((sum, item) => sum + item.satisfiedCount, 0)
const totalNormal = chartData.value.reduce((sum, item) => sum + item.normalCount, 0)
const totalUnsatisfied = chartData.value.reduce((sum, item) => sum + item.unsatisfiedCount, 0)
pieChart.setOption({
title: {
text: '回访状态分布',
left: 'center',
top: 0,
textStyle: {
fontSize: 16,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
@ -124,10 +279,10 @@ const renderPieChart = () => {
},
series: [
{
name: '预警信息',
name: '回访状态',
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '60%'],
center: ['65%', '50%'],
avoidLabelOverlap: false,
label: {
show: false,
@ -144,8 +299,8 @@ const renderPieChart = () => {
show: false
},
data: [
{ value: readCount, name: '已读', itemStyle: { color: '#67C23A' } },
{ value: unreadCount, name: '未读', itemStyle: { color: '#E6A23C' } }
{ value: totalVisited, name: '已回访', itemStyle: { color: '#67C23A' } },
{ value: totalUnvisited, name: '未回访', itemStyle: { color: '#909399' } }
]
}
]
@ -158,30 +313,46 @@ const params = ref({
orgid: 0,
})
//
// 访
const fetchData = async () => {
try {
const userProfile = await getUserProfile()
params.value.orgid = userProfile.dept.id
const res = await RecordApi.getRecordStatistics(params.value)
// 访API
const res = await RecordApi.getStatistics(
params.value.orgid,
params.value.startTime,
params.value.endTime
)
console.log(res)
//
if (res && res.dailyData && Array.isArray(res.dailyData)) {
chartData.value = res.dailyData.map((item: any) => {
// date [year, month, day]
let dateStr = item.date
if (Array.isArray(item.date) && item.date.length >= 3) {
if (res && Array.isArray(res)) {
chartData.value = res.map((item: any) => {
// date
let dateStr = ''
if (typeof item.date === 'number') {
//
dateStr = dayjs(item.date).format('YYYY-MM-DD')
} else if (Array.isArray(item.date) && item.date.length >= 3) {
// [year, month, day]
const [year, month, day] = item.date
// 1JavaScriptDate0
const date = new Date(year, month - 1, day)
dateStr = dayjs(date).format('YYYY-MM-DD')
} else {
//
dateStr = item.date
}
return {
date: dateStr,
totalCount: item.totalCount || 0,
readCount: item.readCount || 0,
unreadCount: item.unreadCount || 0
visitedCount: item.visitedCount || 0,
unvisitedCount: item.unvisitedCount || 0,
satisfiedCount: item.satisfiedCount || 0,
normalCount: item.normalCount || 0,
unsatisfiedCount: item.unsatisfiedCount || 0
}
})
} else {
@ -190,15 +361,23 @@ const fetchData = async () => {
console.log('处理后的图表数据:', chartData.value)
} catch (error) {
console.error('获取预警统计数据失败:', error)
console.error('获取回访统计数据失败:', error)
chartData.value = []
}
}
//
const onDateChange = (range: [string, string]) => {
dateRange.value = range
fetchData()
const onDateChange = (range: [string, string] | null) => {
// range
if (range && range.length === 2) {
dateRange.value = range
params.value.startTime = range[0]
params.value.endTime = range[1]
fetchData()
} else {
//
chartData.value = []
}
}
//

View File

@ -13,6 +13,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
@change="onDateChange"
style="width: 300px;"
clearable
/>
</el-form-item>
<el-form-item label="图表类型">
@ -22,6 +23,9 @@
<el-button :type="chartType === 'bar' ? 'primary' : 'default'" @click="chartType = 'bar'">
柱状图
</el-button>
<el-button :type="chartType === 'stack' ? 'primary' : 'default'" @click="chartType = 'stack'">
堆叠图
</el-button>
</el-form-item>
</el-form>
</el-card>
@ -43,10 +47,18 @@ import { AlertMessageApi } from '@/api/alertmessage'
import dayjs from 'dayjs'
import { getUserProfile } from '@/api/system/user/profile'
// 'line' 线'bar'
const chartType = ref<'line' | 'bar'>('line')
// 'line' 线'bar' 'stack'
const chartType = ref<'line' | 'bar' | 'stack'>('bar')
//
const chartData = ref<{ date: string; totalCount: number; readCount: number; unreadCount: number }[]>([])
const chartData = ref<{
date: string;
sosCount: number;
analysisCount: number;
sosReadCount: number;
analysisReadCount: number;
sosUnreadCount: number;
analysisUnreadCount: number;
}[]>([])
//
const dateRange = ref<[string, string]>([
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
@ -58,17 +70,55 @@ const renderChart = () => {
const dom = document.getElementById('main')
if (!dom) return
const myChart = echarts.init(dom)
myChart.setOption({
//
if (!chartData.value || chartData.value.length === 0) {
myChart.setOption(
{
title: {
text: '当前时间段无数据',
left: 'center',
top: 'center',
textStyle: { color: '#909399', fontSize: 16 }
},
// 线
xAxis: { show: false },
yAxis: { show: false },
series: []
},
true
) // trueoption
return
}
const option: any = {
title: {
text: '预警信息统计',
left: 'center',
top: 0,
textStyle: {
fontSize: 18,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'axis',
formatter: function(params: any) {
const data = params[0]
const date = data.name
const value = data.value
return `${date}<br/>预警信息数量: ${value}`
const date = params[0].name
let result = `${date}<br/>`
params.forEach((param: any) => {
result += `${param.seriesName}: ${param.value}<br/>`
})
return result
}
},
grid: { left: 40, right: 20, top: 60, bottom: 40 },
legend: {
data: ['总预警数', 'SOS预警', '分析预警', '已读', '未读'],
top: 30
},
grid: { left: 40, right: 20, top: 80, bottom: 40 },
xAxis: {
type: 'category',
data: chartData.value.map(item => item.date),
@ -81,15 +131,52 @@ const renderChart = () => {
axisLine: { lineStyle: { color: '#dcdfe6' } },
axisLabel: { color: '#666', fontSize: 14 }
},
series: [
series: []
}
if (chartType.value === 'stack') {
//
option.series = [
{
name: '预警信息数量',
name: 'SOS预警',
type: 'bar',
stack: 'type',
data: chartData.value.map(item => item.sosCount),
itemStyle: { color: '#F56C6C' }
},
{
name: '分析预警',
type: 'bar',
stack: 'type',
data: chartData.value.map(item => item.analysisCount),
itemStyle: { color: '#E6A23C' }
},
{
name: '已读',
type: 'bar',
stack: 'status',
data: chartData.value.map(item => item.sosReadCount + item.analysisReadCount),
itemStyle: { color: '#67C23A' }
},
{
name: '未读',
type: 'bar',
stack: 'status',
data: chartData.value.map(item => item.sosUnreadCount + item.analysisUnreadCount),
itemStyle: { color: '#909399' }
}
]
} else {
// 线
option.series = [
{
name: '总预警数',
type: chartType.value,
data: chartData.value.map(item => item.totalCount),
data: chartData.value.map(item => item.sosCount + item.analysisCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: chartType.value === 'line' ? '#409EFF' : '#67C23A',
color: '#409EFF',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
@ -97,22 +184,112 @@ const renderChart = () => {
color: '#409EFF'
},
areaStyle: chartType.value === 'line' ? { color: 'rgba(64,158,255,0.15)' } : undefined
},
{
name: 'SOS预警',
type: chartType.value,
data: chartData.value.map(item => item.sosCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#F56C6C',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#F56C6C'
}
},
{
name: '分析预警',
type: chartType.value,
data: chartData.value.map(item => item.analysisCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#E6A23C',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#E6A23C'
}
},
{
name: '已读',
type: chartType.value,
data: chartData.value.map(item => item.sosReadCount + item.analysisReadCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#67C23A',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#67C23A'
}
},
{
name: '未读',
type: chartType.value,
data: chartData.value.map(item => item.sosUnreadCount + item.analysisUnreadCount),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: '#909399',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#909399'
}
}
]
})
}
myChart.setOption(option)
}
//
const renderPieChart = () => {
const dom = document.getElementById('pieChart')
if (!dom) return
//
const readCount = chartData.value.reduce((sum, item) => sum + item.readCount, 0)
const unreadCount = chartData.value.reduce((sum, item) => sum + item.unreadCount, 0)
const pieChart = echarts.init(dom)
//
if (!chartData.value || chartData.value.length === 0) {
pieChart.setOption(
{
title: {
text: '当前时间段无数据',
left: 'center',
top: 'center',
textStyle: { color: '#909399', fontSize: 16 }
},
series: []
},
true
)
return
}
//
const totalSos = chartData.value.reduce((sum, item) => sum + item.sosCount, 0)
const totalAnalysis = chartData.value.reduce((sum, item) => sum + item.analysisCount, 0)
const totalRead = chartData.value.reduce((sum, item) => sum + item.sosReadCount + item.analysisReadCount, 0)
const totalUnread = chartData.value.reduce((sum, item) => sum + item.sosUnreadCount + item.analysisUnreadCount, 0)
pieChart.setOption({
title: {
text: '预警状态分布',
left: 'center',
top: 0,
textStyle: {
fontSize: 16,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
@ -124,10 +301,10 @@ const renderPieChart = () => {
},
series: [
{
name: '预警信息',
name: '预警状态',
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '60%'],
center: ['65%', '50%'],
avoidLabelOverlap: false,
label: {
show: false,
@ -144,8 +321,8 @@ const renderPieChart = () => {
show: false
},
data: [
{ value: readCount, name: '已读', itemStyle: { color: '#67C23A' } },
{ value: unreadCount, name: '未读', itemStyle: { color: '#E6A23C' } }
{ value: totalRead, name: '已读', itemStyle: { color: '#67C23A' } },
{ value: totalUnread, name: '未读', itemStyle: { color: '#909399' } }
]
}
]
@ -160,10 +337,12 @@ const params = ref({
//
const fetchData = async () => {
try {
const userProfile = await getUserProfile()
params.value.orgid = userProfile.dept.id
try {
const res = await AlertMessageApi.getAlertMessageStatistics(params.value)
console.log(res)
//
if (res && res.dailyData && Array.isArray(res.dailyData)) {
chartData.value = res.dailyData.map((item: any) => {
@ -178,14 +357,19 @@ const fetchData = async () => {
return {
date: dateStr,
totalCount: item.totalCount || 0,
readCount: item.readCount || 0,
unreadCount: item.unreadCount || 0
sosCount: item.sosCount || 0,
analysisCount: item.analysisCount || 0,
sosReadCount: item.sosReadCount || 0,
analysisReadCount: item.analysisReadCount || 0,
sosUnreadCount: item.sosUnreadCount || 0,
analysisUnreadCount: item.analysisUnreadCount || 0
}
})
} else {
chartData.value = []
}
console.log('处理后的图表数据:', chartData.value)
} catch (error) {
console.error('获取预警统计数据失败:', error)
chartData.value = []
@ -193,9 +377,17 @@ const fetchData = async () => {
}
//
const onDateChange = (range: [string, string]) => {
dateRange.value = range
fetchData()
const onDateChange = (range: [string, string] | null) => {
// range
if (range && range.length === 2) {
dateRange.value = range
params.value.startTime = range[0]
params.value.endTime = range[1]
fetchData()
} else {
//
chartData.value = []
}
}
//