<template>
|
<div class="continued-care">
|
<div class="your-table-container">
|
<el-table
|
v-loading="loading"
|
:data="tableData"
|
:border="true"
|
show-summary
|
:summary-method="getSummaries"
|
>
|
<!-- 表格列定义 -->
|
<el-table-column
|
label="序号"
|
type="index"
|
align="center"
|
width="60"
|
/>
|
|
<el-table-column
|
label="病区名称"
|
align="center"
|
prop="wardName"
|
width="200"
|
:show-overflow-tooltip="true"
|
/>
|
|
<el-table-column
|
label="已延续数量"
|
align="center"
|
prop="continuedCount"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="handleViewDetails(scope.row, 'continued', '已延续列表')"
|
>
|
<span class="button-zx">{{ scope.row.continuedCount }}</span>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="未延续数量"
|
align="center"
|
prop="unContinuedCount"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="medium"
|
type="text"
|
@click="handleViewDetails(scope.row, 'uncontinued', '未延续列表')"
|
>
|
<span class="button-zx">{{ scope.row.unContinuedCount }}</span>
|
</el-button>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="延续率"
|
align="center"
|
prop="continuedRate"
|
width="120"
|
/>
|
|
<!-- <el-table-column
|
label="操作"
|
align="center"
|
width="150"
|
>
|
<template slot-scope="scope">
|
<el-button
|
size="small"
|
type="primary"
|
@click="handleRowClick(scope.row)"
|
>
|
查看护士详情
|
</el-button>
|
</template>
|
</el-table-column> -->
|
</el-table>
|
</div>
|
|
<!-- 护士详情弹窗 -->
|
<el-dialog
|
title="护士延续护理详情"
|
:visible.sync="nurseDialogVisible"
|
width="80%"
|
:close-on-click-modal="false"
|
>
|
<div v-if="currentWardData">
|
<el-table
|
v-loading="nurseLoading"
|
:data="nurseData"
|
:border="true"
|
>
|
<el-table-column
|
label="护士姓名"
|
prop="nurseName"
|
align="center"
|
width="120"
|
/>
|
<el-table-column
|
label="科室"
|
prop="deptName"
|
align="center"
|
width="150"
|
/>
|
<el-table-column
|
label="已延续数量"
|
prop="continuedCount"
|
align="center"
|
/>
|
<el-table-column
|
label="未延续数量"
|
prop="unContinuedCount"
|
align="center"
|
/>
|
<el-table-column
|
label="延续率"
|
prop="continuedRate"
|
align="center"
|
width="120"
|
/>
|
</el-table>
|
</div>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { getContinueNerseCount, getNurseContinuedDetail } from "@/api/system/user";
|
import ExcelJS from "exceljs";
|
import { saveAs } from "file-saver";
|
|
export default {
|
name: "ContinuedCare",
|
props: {
|
queryParams: {
|
type: Object,
|
required: true,
|
},
|
flatArrayhospit: {
|
type: Array,
|
default: () => [],
|
},
|
flatArraydept: {
|
type: Array,
|
default: () => [],
|
},
|
options: {
|
type: Array,
|
default: () => [],
|
},
|
orgname: {
|
type: String,
|
default: "",
|
},
|
},
|
data() {
|
return {
|
tableData: [],
|
loading: false,
|
nurseDialogVisible: false,
|
nurseLoading: false,
|
currentWardData: null,
|
nurseData: [],
|
originalData: {},
|
totalData: {
|
continued: 0,
|
uncontinued: 0
|
}
|
};
|
},
|
methods: {
|
loadData() {
|
this.loading = true;
|
|
const params = {
|
leavehospitaldistrictcodes:
|
this.queryParams.leavehospitaldistrictcodes.includes("all")
|
? this.getAllWardCodes()
|
: this.queryParams.leavehospitaldistrictcodes,
|
deptcodes: this.queryParams.deptcodes.includes("all")
|
? this.getAllDeptCodes()
|
: this.queryParams.deptcodes,
|
|
};
|
|
delete params.leavehospitaldistrictcodes.all;
|
delete params.deptcodes.all;
|
|
getContinueNerseCount(params)
|
.then((response) => {
|
this.originalData = response.data;
|
this.processData(response.data);
|
})
|
.catch((error) => {
|
console.error("获取延续护理数据失败:", error);
|
this.$message.error("获取延续护理数据失败");
|
})
|
.finally(() => {
|
this.loading = false;
|
});
|
},
|
|
processData(data) {
|
this.totalData = {
|
continued: data.已延续总数量 || 0,
|
uncontinued: data.未延续总数量 || 0
|
};
|
|
const processedData = [];
|
|
if (data.详情 && Array.isArray(data.详情)) {
|
data.详情.forEach((item) => {
|
// 提取病区名称和数据
|
Object.keys(item).forEach(key => {
|
if (key.startsWith('已延续_')) {
|
const wardName = key.replace('已延续_', '');
|
const continuedCount = item[key];
|
const unContinuedCount = item[`未延续_${wardName}`] || 0;
|
const total = continuedCount + unContinuedCount;
|
const continuedRate = total > 0 ? ((continuedCount / total) * 100).toFixed(2) + '%' : '0.00%';
|
|
processedData.push({
|
wardName,
|
continuedCount,
|
unContinuedCount,
|
continuedRate,
|
originalData: item
|
});
|
}
|
});
|
});
|
}
|
|
// 排序
|
this.tableData = this.customSort(processedData);
|
},
|
|
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);
|
},
|
|
customSort(data) {
|
const order = [
|
"一", "二", "三", "四", "五", "六", "七", "八", "九", "十",
|
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
|
"二十一", "二十二", "二十三", "二十四", "二十五", "二十六", "二十七", "二十八", "二十九", "三十",
|
"三十一", "三十二", "三十三", "三十四", "三十五", "三十六", "三十七", "三十八", "三十九", "四十",
|
"四十一", "四十二", "四十三", "四十四", "四十五"
|
];
|
|
return data.sort((a, b) => {
|
const getIndex = (name) => {
|
if (!name || typeof name !== "string") return -1;
|
const chineseMatch = name.match(/^(\d+)-/);
|
if (chineseMatch && chineseMatch[1]) {
|
const num = parseInt(chineseMatch[1], 10);
|
if (num >= 1 && num <= 45) {
|
return num - 1;
|
}
|
}
|
|
// 尝试匹配中文数字
|
for (let i = 0; i < order.length; i++) {
|
if (name.includes(order[i])) {
|
return i;
|
}
|
}
|
return -1;
|
};
|
|
const indexA = getIndex(a.wardName);
|
const indexB = getIndex(b.wardName);
|
|
if (indexA === -1 && indexB === -1) {
|
return (a.wardName || "").localeCompare(b.wardName || "");
|
}
|
if (indexA === -1) return 1;
|
if (indexB === -1) return -1;
|
return indexA - indexB;
|
});
|
},
|
|
getSummaries(param) {
|
const { columns, data } = param;
|
const sums = [];
|
|
columns.forEach((column, index) => {
|
if (index === 0) {
|
sums[index] = "合计";
|
return;
|
}
|
|
if (index === 1) { // 病区名称列
|
sums[index] = "/";
|
return;
|
}
|
|
if (index === 2) { // 已延续数量合计
|
const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0);
|
sums[index] = this.formatNumber(totalContinued);
|
} else if (index === 3) { // 未延续数量合计
|
const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0);
|
sums[index] = this.formatNumber(totalUnContinued);
|
} else if (index === 4) { // 延续率平均值
|
const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0);
|
const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0);
|
const total = totalContinued + totalUnContinued;
|
const avgRate = total > 0 ? ((totalContinued / total) * 100).toFixed(2) + '%' : '0.00%';
|
sums[index] = avgRate;
|
} else {
|
sums[index] = "";
|
}
|
});
|
|
return sums;
|
},
|
|
formatNumber(num) {
|
if (isNaN(num)) return "-";
|
return Number.isInteger(num) ? num.toString() : num.toFixed(0);
|
},
|
|
handleViewDetails(row, type, titleSuffix) {
|
const title = `${row.wardName}${titleSuffix}`;
|
// 这里需要根据你的实际情况获取详情数据
|
// 你可以从row.originalData中获取,或者调用新的API
|
const detailData = this.getMockDetailData(row, type);
|
this.$emit("view-details", detailData, title);
|
},
|
|
handleRowClick(row) {
|
this.currentWardData = row;
|
this.nurseDialogVisible = true;
|
this.loadNurseData(row.wardName);
|
},
|
|
async loadNurseData(wardName) {
|
this.nurseLoading = true;
|
try {
|
const params = {
|
wardName: wardName,
|
startTime: this.queryParams.startTime,
|
endTime: this.queryParams.endTime
|
};
|
|
const response = await getNurseContinuedDetail(params);
|
this.nurseData = this.processNurseData(response.data);
|
} catch (error) {
|
console.error("获取护士详情失败:", error);
|
this.$message.error("获取护士详情失败");
|
} finally {
|
this.nurseLoading = false;
|
}
|
},
|
|
processNurseData(data) {
|
// 根据你的实际API响应结构处理数据
|
// 这里是一个示例
|
return [
|
{
|
nurseName: "护士A",
|
deptName: this.currentWardData.wardName,
|
continuedCount: Math.floor(this.currentWardData.continuedCount * 0.3),
|
unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.3),
|
continuedRate: "75.00%"
|
},
|
{
|
nurseName: "护士B",
|
deptName: this.currentWardData.wardName,
|
continuedCount: Math.floor(this.currentWardData.continuedCount * 0.4),
|
unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.4),
|
continuedRate: "80.00%"
|
},
|
{
|
nurseName: "护士C",
|
deptName: this.currentWardData.wardName,
|
continuedCount: Math.floor(this.currentWardData.continuedCount * 0.3),
|
unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.3),
|
continuedRate: "70.00%"
|
}
|
];
|
},
|
|
getMockDetailData(row, type) {
|
// 模拟详情数据,实际应该调用API
|
const detailData = [];
|
const count = type === 'continued' ? row.continuedCount : row.unContinuedCount;
|
|
for (let i = 1; i <= Math.min(count, 10); i++) {
|
detailData.push({
|
sendname: `患者${i}`,
|
taskName: `${row.wardName}延续护理`,
|
sendstate: type === 'continued' ? 6 : 2,
|
preachform: ["人工随访", "电话随访"],
|
visitTime: "2024-01-15 10:00:00",
|
finishtime: type === 'continued' ? "2024-01-15 11:00:00" : "",
|
endtime: "2024-01-10 09:00:00",
|
nurseName: "护士A",
|
drname: "医生A",
|
excep: 1,
|
suggest: 2,
|
templatename: "延续护理服务模板",
|
remark: type === 'continued' ? "已完成延续护理" : "未开始延续护理",
|
bankcardno: "已完成"
|
});
|
}
|
|
return detailData;
|
},
|
|
async exportTable() {
|
try {
|
let dateRangeString = "";
|
let sheetNameSuffix = "";
|
|
if (
|
this.queryParams.dateRange &&
|
this.queryParams.dateRange.length === 2
|
) {
|
const startDateStr = this.queryParams.dateRange[0];
|
const endDateStr = this.queryParams.dateRange[1];
|
const formatDateForDisplay = (dateTimeStr) => {
|
return dateTimeStr.split(" ")[0];
|
};
|
const startDateFormatted = formatDateForDisplay(startDateStr);
|
const endDateFormatted = formatDateForDisplay(endDateStr);
|
dateRangeString = `${startDateFormatted}至${endDateFormatted}`;
|
sheetNameSuffix = `${startDateFormatted}至${endDateFormatted}`;
|
} else {
|
const now = new Date();
|
const currentMonth = now.getMonth() + 1;
|
dateRangeString = `${currentMonth}月`;
|
sheetNameSuffix = `${currentMonth}月`;
|
}
|
|
const excelName = `延续护理统计表_${dateRangeString}.xlsx`;
|
const worksheetName = `延续护理统计_${sheetNameSuffix}`;
|
|
if (!this.tableData || this.tableData.length === 0) {
|
this.$message.warning("暂无延续护理数据可导出");
|
return false;
|
}
|
|
const workbook = new ExcelJS.Workbook();
|
const worksheet = workbook.addWorksheet(worksheetName);
|
|
this.buildExportSheet(worksheet, sheetNameSuffix);
|
|
const buffer = await workbook.xlsx.writeBuffer();
|
const blob = new Blob([buffer], {
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
});
|
saveAs(blob, excelName);
|
|
this.$message.success("导出成功");
|
return true;
|
} catch (error) {
|
console.error("导出失败:", error);
|
this.$message.error(`导出失败: ${error.message}`);
|
return false;
|
}
|
},
|
|
buildExportSheet(worksheet, sheetNameSuffix) {
|
const titleStyle = {
|
font: {
|
name: "微软雅黑",
|
size: 16,
|
bold: true,
|
color: { argb: "FF000000" },
|
},
|
fill: {
|
type: "pattern",
|
pattern: "solid",
|
fgColor: { argb: "FFE6F3FF" },
|
},
|
alignment: { vertical: "middle", horizontal: "center", wrapText: true },
|
border: {
|
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
},
|
};
|
|
const headerStyle = {
|
font: {
|
name: "微软雅黑",
|
size: 11,
|
bold: true,
|
color: { argb: "FF000000" },
|
},
|
fill: {
|
type: "pattern",
|
pattern: "solid",
|
fgColor: { argb: "FFF5F7FA" },
|
},
|
alignment: { vertical: "middle", horizontal: "center", wrapText: true },
|
border: {
|
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
},
|
};
|
|
const cellStyle = {
|
font: { name: "宋体", size: 10, color: { argb: "FF000000" } },
|
alignment: { vertical: "middle", horizontal: "center" },
|
border: {
|
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
},
|
};
|
|
const summaryStyle = {
|
font: {
|
name: "宋体",
|
size: 10,
|
bold: true,
|
color: { argb: "FF409EFF" },
|
},
|
fill: {
|
type: "pattern",
|
pattern: "solid",
|
fgColor: { argb: "FFF5F7FA" },
|
},
|
alignment: { vertical: "middle", horizontal: "center" },
|
border: {
|
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
},
|
};
|
|
// 添加标题行
|
worksheet.mergeCells(1, 1, 1, 6);
|
const titleCell = worksheet.getCell(1, 1);
|
titleCell.value = `延续护理统计表_${sheetNameSuffix}`;
|
titleCell.style = titleStyle;
|
worksheet.getRow(1).height = 35;
|
|
// 表头
|
const headers = ["序号", "病区名称", "已延续数量", "未延续数量", "延续率", "操作"];
|
|
headers.forEach((header, index) => {
|
const cell = worksheet.getCell(2, index + 1);
|
cell.value = header;
|
cell.style = headerStyle;
|
});
|
|
worksheet.getRow(2).height = 25;
|
|
// 数据行
|
this.tableData.forEach((item, rowIndex) => {
|
const dataRow = worksheet.addRow(
|
[
|
rowIndex + 1,
|
item.wardName,
|
item.continuedCount,
|
item.unContinuedCount,
|
item.continuedRate,
|
"查看护士详情"
|
],
|
rowIndex + 3
|
);
|
|
dataRow.eachCell((cell) => {
|
cell.style = cellStyle;
|
});
|
dataRow.height = 24;
|
});
|
|
// 合计行
|
const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0);
|
const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0);
|
const total = totalContinued + totalUnContinued;
|
const totalRate = total > 0 ? ((totalContinued / total) * 100).toFixed(2) + '%' : '0.00%';
|
|
const summaryRow = worksheet.addRow([
|
"合计",
|
"/",
|
totalContinued,
|
totalUnContinued,
|
totalRate,
|
"/"
|
]);
|
|
summaryRow.eachCell((cell, colNumber) => {
|
cell.style = summaryStyle;
|
});
|
summaryRow.height = 28;
|
|
// 列宽
|
worksheet.columns = [
|
{ width: 8 },
|
{ width: 30 },
|
{ width: 15 },
|
{ width: 15 },
|
{ width: 12 },
|
{ width: 15 }
|
];
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.continued-care {
|
.your-table-container {
|
margin-top: 10px;
|
}
|
|
.button-zx {
|
color: rgb(70, 204, 238);
|
}
|
}
|
</style>
|