<template>
|
<div class="education-statistics">
|
<!-- 搜索区域 -->
|
<div class="search-container">
|
<el-form
|
:model="queryParams"
|
ref="queryForm"
|
size="small"
|
:inline="true"
|
label-width="100px"
|
>
|
<el-form-item label="统计维度" prop="groupType">
|
<el-select
|
v-model="queryParams.groupType"
|
placeholder="请选择统计维度"
|
@change="handleGroupTypeChange"
|
style="width: 180px"
|
>
|
<el-option label="按科室统计" value="1"></el-option>
|
<el-option label="按病区统计" value="2"></el-option>
|
</el-select>
|
<el-select
|
style="margin-left: 10px"
|
v-if="queryParams.groupType == '2'"
|
v-model="queryParams.hospitaldistrictcodes"
|
size="medium"
|
multiple
|
filterable
|
placeholder="请选择病区"
|
>
|
<el-option
|
v-for="item in flatArrayhospit"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
<el-select
|
v-else-if="queryParams.groupType == '1'"
|
v-model="queryParams.deptcodes"
|
size="medium"
|
multiple
|
filterable
|
placeholder="请选择科室"
|
>
|
<el-option
|
v-for="item in flatArraydept"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="就诊类型" prop="hospType">
|
<el-select
|
v-model="queryParams.hospType"
|
placeholder="请选择就诊类型"
|
clearable
|
style="width: 150px"
|
>
|
<el-option label="门诊" value="1"></el-option>
|
<el-option label="出院" value="2"></el-option>
|
<el-option label="专病" value="3"></el-option>
|
<el-option label="入院/外部导入" value="4"></el-option>
|
<el-option label="体检" value="5"></el-option>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="入院时间" prop="dateRange">
|
<el-date-picker
|
v-model="queryParams.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="yyyy-MM-dd"
|
style="width: 280px"
|
:picker-options="datePickerOptions"
|
>
|
</el-date-picker>
|
</el-form-item>
|
|
<el-form-item label="宣教发送时间" prop="visittime">
|
<el-date-picker
|
v-model="queryParams.visittime"
|
type="date"
|
placeholder="选择日期"
|
value-format="yyyy-MM-dd"
|
style="width: 180px"
|
>
|
</el-date-picker>
|
</el-form-item>
|
|
<el-form-item>
|
<el-button
|
type="primary"
|
icon="el-icon-search"
|
size="medium"
|
@click="handleQuery"
|
:loading="loading"
|
>搜索</el-button
|
>
|
<el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
|
>重置</el-button
|
>
|
<el-button
|
type="warning"
|
plain
|
icon="el-icon-download"
|
size="medium"
|
@click="handleExport"
|
v-hasPermi="['system:statistics:export']"
|
>导出</el-button
|
>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<!-- 统计数据概览 -->
|
<div class="summary-cards" v-if="statisticsData.total > 0">
|
<el-row :gutter="20">
|
<el-col :span="6">
|
<div class="summary-card">
|
<div class="card-title">发送总量</div>
|
<div class="card-value">{{ totalCount }}</div>
|
<div class="card-desc">总宣教发送次数</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="summary-card">
|
<div class="card-title">发送成功量</div>
|
<div class="card-value">{{ sendSuccessCount }}</div>
|
<div class="card-desc">已成功发送的宣教</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="summary-card">
|
<div class="card-title">已读量</div>
|
<div class="card-value">{{ readCount }}</div>
|
<div class="card-desc">患者已阅读的宣教</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="summary-card">
|
<div class="card-title">平均发送成功率</div>
|
<div class="card-value">{{ avgSendSuccessRate }}%</div>
|
<div class="card-desc">整体发送成功比例</div>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
|
<!-- 数据表格 -->
|
<div class="table-container">
|
<el-table
|
v-loading="loading"
|
:data="statisticsData.list"
|
:border="true"
|
style="width: 100%"
|
:default-sort="{ prop: 'totalCount', order: 'descending' }"
|
@sort-change="handleSortChange"
|
>
|
<el-table-column prop="groupName" label="分组名称" align="center" fixed>
|
<!-- <template slot-scope="scope">
|
<span class="group-name" @click="handleGroupDetail(scope.row)">
|
{{ scope.row.groupName }}
|
</span>
|
</template> -->
|
</el-table-column>
|
|
<el-table-column
|
prop="groupCode"
|
label="分组编码"
|
align="center"
|
></el-table-column>
|
|
<el-table-column
|
prop="totalCount"
|
label="宣教发送总量"
|
align="center"
|
sortable="custom"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="
|
handleViewDetails(
|
scope.row,
|
'totalCountInfo',
|
'宣教发送总量明细'
|
)
|
"
|
>
|
<span class="count-highlight">{{ scope.row.totalCount }}</span>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
prop="sendSuccessCount"
|
label="发送成功量"
|
align="center"
|
sortable="custom"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="
|
handleViewDetails(
|
scope.row,
|
'sendSuccessCountInfo',
|
'发送成功量明细'
|
)
|
"
|
>
|
<span class="success-count">{{ scope.row.sendSuccessCount }}</span>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
prop="readCount"
|
label="已读量"
|
align="center"
|
sortable="custom"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="
|
handleViewDetails(scope.row, 'readCountInfo', '已读量明细')
|
"
|
>
|
<span class="read-count">{{ scope.row.readCount }}</span>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
prop="sendSuccessRate"
|
label="发送成功率"
|
align="center"
|
width="200"
|
sortable="custom"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="
|
handleViewDetails(
|
scope.row,
|
'sendSuccessRate',
|
'发送成功率明细'
|
)
|
"
|
>
|
<el-progress
|
:percentage="scope.row.sendSuccessRate * 100"
|
:stroke-width="8"
|
:show-text="false"
|
style="width: 80px; margin: 0 auto"
|
:color="getRateColor(scope.row.sendSuccessRate)"
|
/>
|
<span class="rate-text"
|
>{{ (scope.row.sendSuccessRate * 100).toFixed(1) }}%</span
|
>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
prop="readRate"
|
label="已读率"
|
align="center"
|
width="200"
|
sortable="custom"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="
|
handleViewDetails(scope.row, 'readRate', '已读率明细')
|
"
|
>
|
<el-progress
|
:percentage="scope.row.readRate * 100"
|
:stroke-width="8"
|
:show-text="false"
|
style="width: 80px; margin: 0 auto"
|
:color="getRateColor(scope.row.readRate)"
|
/>
|
<span class="rate-text"
|
>{{ (scope.row.readRate * 100).toFixed(1) }}%</span
|
>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<!-- <el-table-column label="操作" align="center" width="200" fixed="right">
|
<template slot-scope="scope">
|
<el-button
|
type="text"
|
size="small"
|
@click="handleDetail(scope.row)"
|
icon="el-icon-document"
|
v-hasPermi="['system:statistics:detail']"
|
>详细数据</el-button
|
>
|
<el-button
|
type="text"
|
size="small"
|
@click="handleExportGroup(scope.row)"
|
icon="el-icon-download"
|
v-hasPermi="['system:statistics:export']"
|
>导出</el-button
|
>
|
</template>
|
</el-table-column> -->
|
</el-table>
|
|
<!-- 分页 -->
|
<pagination
|
v-show="statisticsData.total > 0"
|
:total="statisticsData.total"
|
:page.sync="queryParams.pageNum"
|
:limit.sync="queryParams.pageSize"
|
@pagination="getList"
|
/>
|
</div>
|
|
<!-- 明细数据弹窗 -->
|
<PropagandaDetailDialog
|
:visible="detailVisible"
|
:title="detailTitle"
|
:data="detailList"
|
:loading="detailLoading"
|
@close="detailVisible = false"
|
/>
|
</div>
|
</template>
|
|
<script>
|
import { gethelibraryCount, gethelibraryCountHyperlink } from "@/api/AiCentre/index";
|
import PropagandaDetailDialog from "./components/PropagandaDetailDialog.vue";
|
|
export default {
|
name: "EducationStatistics",
|
components: { PropagandaDetailDialog },
|
data() {
|
return {
|
// 查询参数
|
queryParams: {
|
pageNum: 1,
|
pageSize: 1000,
|
groupType: "1", // 1-科室,2-病区
|
deptcodes: ['all'], // 科室code数组
|
hospitaldistrictcodes: [], // 病区code数组
|
hospType: undefined, // 就诊类型
|
starttime: undefined, // 入院开始时间
|
endtime: undefined, // 入院结束时间
|
visittime: undefined, // 宣教发送时间
|
dateRange: [], // 入院时间范围
|
orderBy: "totalCount", // 排序字段
|
order: "descending", // 排序方式
|
},
|
|
// 统计数据
|
statisticsData: {
|
total: 0,
|
list: [],
|
},
|
|
// 下拉选项
|
flatArraydept: [],
|
|
flatArrayhospit: [],
|
|
// 加载状态
|
loading: false,
|
detailLoading: false,
|
|
// 详情弹窗
|
detailVisible: false,
|
detailTitle: "",
|
detailList: [],
|
|
// 日期选择器配置
|
datePickerOptions: {
|
disabledDate(time) {
|
return time.getTime() > Date.now();
|
},
|
shortcuts: [
|
{
|
text: "最近一周",
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
picker.$emit("pick", [start, end]);
|
},
|
},
|
{
|
text: "最近一个月",
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
picker.$emit("pick", [start, end]);
|
},
|
},
|
{
|
text: "最近三个月",
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
picker.$emit("pick", [start, end]);
|
},
|
},
|
],
|
},
|
};
|
},
|
|
computed: {
|
// 计算总发送量
|
totalCount() {
|
return this.statisticsData.list.reduce(
|
(sum, item) => sum + item.totalCount,
|
0
|
);
|
},
|
|
// 计算总发送成功量
|
sendSuccessCount() {
|
return this.statisticsData.list.reduce(
|
(sum, item) => sum + item.sendSuccessCount,
|
0
|
);
|
},
|
|
// 计算总已读量
|
readCount() {
|
return this.statisticsData.list.reduce(
|
(sum, item) => sum + item.readCount,
|
0
|
);
|
},
|
|
// 计算平均发送成功率
|
avgSendSuccessRate() {
|
if (this.statisticsData.list.length === 0) return 0;
|
const totalRate = this.statisticsData.list.reduce(
|
(sum, item) => sum + item.sendSuccessRate,
|
0
|
);
|
return ((totalRate / this.statisticsData.list.length) * 100).toFixed(1);
|
},
|
},
|
|
created() {
|
this.flatArrayhospit = this.$store.getters.belongWards.map((ward) => {
|
return {
|
label: ward.districtName,
|
value: ward.districtCode,
|
};
|
});
|
this.flatArraydept = this.$store.getters.belongDepts.map((dept) => {
|
return {
|
label: dept.deptName,
|
value: dept.deptCode,
|
};
|
});
|
console.log(this.flatArrayhospit,'this.flatArrayhospit');
|
|
this.flatArraydept.push({ label: "全部", value: "all" });
|
this.flatArrayhospit.push({ label: "全部", value: "all" });
|
this.getList();
|
},
|
|
methods: {
|
// 获取统计数据列表
|
async getList() {
|
this.loading = true;
|
try {
|
console.log(this.queryParams.hospitaldistrictcodes);
|
|
// 构建请求参数
|
const params = {
|
pageNum: this.queryParams.pageNum,
|
pageSize: this.queryParams.pageSize,
|
hospitaldistrictcodes:
|
this.queryParams.hospitaldistrictcodes.includes("all")
|
? this.getAllWardCodes()
|
: this.queryParams.hospitaldistrictcodes,
|
deptcodes: this.queryParams.deptcodes.includes("all")
|
? this.getAllDeptCodes()
|
: this.queryParams.deptcodes,
|
};
|
|
// 根据统计维度设置参数
|
if (this.queryParams.groupType == "1") {
|
params.hospitaldistrictcodes = [];
|
} else if (this.queryParams.groupType == "2") {
|
params.deptcodes = [];
|
}
|
|
// 设置时间参数
|
if (
|
this.queryParams.dateRange &&
|
this.queryParams.dateRange.length === 2
|
) {
|
params.starttime = this.queryParams.dateRange[0];
|
params.endtime = this.queryParams.dateRange[1];
|
}
|
|
// 设置其他参数
|
if (this.queryParams.hospType) {
|
params.hospType = this.queryParams.hospType;
|
}
|
if (this.queryParams.visittime) {
|
params.visittime = this.queryParams.visittime;
|
}
|
|
// 调用接口
|
const response = await gethelibraryCount(params);
|
this.statisticsData = {
|
total: response.total || 0,
|
list: response.list || [],
|
};
|
} catch (error) {
|
console.error("获取统计数据失败:", error);
|
this.$message.error("获取统计数据失败");
|
this.statisticsData = { total: 0, list: [] };
|
} finally {
|
this.loading = false;
|
}
|
},
|
getAllWardCodes() {
|
return this.flatArrayhospit
|
.filter((item) => item.value !== "all")
|
.map((item) => item.value);
|
},
|
|
getAllDeptCodes() {
|
return this.flatArraydept
|
.filter((item) => item.value !== "all")
|
.map((item) => item.value);
|
},
|
// 统计维度变更
|
handleGroupTypeChange(value) {
|
// 切换维度时清空对应的选择
|
if (value === "dept") {
|
this.queryParams.hospitaldistrictcodes = [];
|
} else if (value === "ward") {
|
this.queryParams.deptcodes = [];
|
}
|
},
|
|
// 处理搜索
|
handleQuery() {
|
this.queryParams.pageNum = 1;
|
this.getList();
|
},
|
|
// 重置搜索条件
|
resetQuery() {
|
this.queryParams = {
|
pageNum: 1,
|
pageSize: 10,
|
groupType: "dept",
|
deptcodes: [],
|
hospitaldistrictcodes: [],
|
hospType: undefined,
|
starttime: undefined,
|
endtime: undefined,
|
visittime: undefined,
|
dateRange: [],
|
orderBy: "totalCount",
|
order: "descending",
|
};
|
this.getList();
|
},
|
|
// 表格排序
|
handleSortChange({ column, prop, order }) {
|
if (prop) {
|
this.queryParams.orderBy = prop;
|
this.queryParams.order = order;
|
this.getList();
|
}
|
},
|
|
// 根据成功率获取进度条颜色
|
getRateColor(rate) {
|
const percentage = rate * 100;
|
if (percentage >= 90) return "#67C23A";
|
if (percentage >= 80) return "#E6A23C";
|
if (percentage >= 60) return "#409EFF";
|
return "#F56C6C";
|
},
|
|
// 点击统计数字查看明细(仿随访统计模式)
|
handleViewDetails(row, infoKey, titleSuffix) {
|
const groupName = row.groupName || "";
|
this.detailTitle = `${groupName} - ${titleSuffix}`;
|
this.detailVisible = true;
|
this.detailLoading = true;
|
this.detailList = [];
|
|
const params = {
|
hyperLinkInfoType: infoKey,
|
hospType: this.queryParams.hospType,
|
};
|
|
// 根据统计维度传入对应的过滤参数
|
if (this.queryParams.groupType == "1") {
|
// 按科室统计
|
params.deptcodes = [row.groupCode];
|
} else if (this.queryParams.groupType == "2") {
|
// 按病区统计
|
params.hospitaldistrictcodes = [row.groupCode];
|
}
|
|
// 传入时间范围
|
if (
|
this.queryParams.dateRange &&
|
this.queryParams.dateRange.length === 2
|
) {
|
params.starttime = this.queryParams.dateRange[0];
|
params.endtime = this.queryParams.dateRange[1];
|
}
|
if (this.queryParams.visittime) {
|
params.visittime = this.queryParams.visittime;
|
}
|
|
gethelibraryCountHyperlink(params)
|
.then((response) => {
|
this.detailList = response.data || [];
|
this.detailLoading = false;
|
})
|
.catch(() => {
|
this.detailLoading = false;
|
});
|
},
|
|
// 导出全部数据
|
handleExport() {
|
this.$confirm("确定要导出全部统计数据吗?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
const exportParams = { ...this.queryParams };
|
delete exportParams.pageNum;
|
delete exportParams.pageSize;
|
this.$message.success("导出任务已开始,请稍后在下载中心查看");
|
})
|
.catch(() => {});
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.education-statistics {
|
padding: 20px;
|
background: #fff;
|
min-height: calc(100vh - 84px);
|
|
.search-container {
|
background: #f8f9fa;
|
padding: 20px;
|
border-radius: 8px;
|
margin-bottom: 20px;
|
border: 1px solid #ebeef5;
|
}
|
|
.summary-cards {
|
margin-bottom: 20px;
|
|
.summary-card {
|
background: #fff;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
text-align: center;
|
border: 1px solid #ebeef5;
|
transition: all 0.3s ease;
|
|
&:hover {
|
transform: translateY(-5px);
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15);
|
}
|
|
.card-title {
|
font-size: 14px;
|
color: #909399;
|
margin-bottom: 10px;
|
}
|
|
.card-value {
|
font-size: 28px;
|
font-weight: 600;
|
color: #409eff;
|
margin-bottom: 5px;
|
}
|
|
.card-desc {
|
font-size: 12px;
|
color: #c0c4cc;
|
}
|
}
|
}
|
|
.table-container {
|
background: #fff;
|
border-radius: 8px;
|
padding: 20px;
|
border: 1px solid #ebeef5;
|
|
.group-name {
|
color: #409eff;
|
cursor: pointer;
|
text-decoration: underline;
|
transition: color 0.3s;
|
|
&:hover {
|
color: #66b1ff;
|
}
|
}
|
|
.count-highlight {
|
font-weight: 600;
|
color: #606266;
|
}
|
|
.success-count {
|
color: #67c23a;
|
font-weight: 600;
|
}
|
|
.read-count {
|
color: #e6a23c;
|
font-weight: 600;
|
}
|
|
.rate-text {
|
display: block;
|
margin-top: 5px;
|
font-size: 12px;
|
color: #606266;
|
}
|
}
|
|
}
|
|
// 响应式调整
|
@media (max-width: 1200px) {
|
.education-statistics {
|
padding: 10px;
|
}
|
|
.summary-cards {
|
.el-col {
|
margin-bottom: 10px;
|
}
|
}
|
}
|
</style>
|