<template>
|
<div class="app-container">
|
<!-- 查询栏 -->
|
<el-form :inline="true" :model="queryParams" size="small" class="query-form">
|
<el-form-item label="患者姓名">
|
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter.native="handleQuery" />
|
</el-form-item>
|
<el-form-item label="主治医生">
|
<el-input v-model="queryParams.drname" placeholder="请输入医生姓名" clearable @keyup.enter.native="handleQuery" />
|
</el-form-item>
|
<el-form-item label="科室/病区">
|
<el-select v-model="queryParams.deptWard" placeholder="全部" clearable filterable>
|
<el-option-group label="科室">
|
<el-option v-for="d in deptOptions" :key="'dept:' + d" :label="d" :value="'dept:' + d" />
|
</el-option-group>
|
<el-option-group label="病区">
|
<el-option v-for="w in wardOptions" :key="'ward:' + w" :label="w" :value="'ward:' + w" />
|
</el-option-group>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="管饲方式">
|
<el-select v-model="queryParams.feedType" placeholder="全部" clearable>
|
<el-option v-for="t in TUBE_FEED_TYPES" :key="t" :label="t" :value="t" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="管饲状态">
|
<el-select v-model="queryParams.feedStatus" placeholder="全部" clearable>
|
<el-option v-for="s in TUBE_STATUS" :key="s" :label="s" :value="s" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
</el-form-item>
|
</el-form>
|
|
<!-- 操作栏 -->
|
<el-row :gutter="10" class="mb8">
|
<el-col :span="1.5">
|
<el-button type="primary" icon="el-icon-plus" size="mini" @click="openAddDialog">添加患者</el-button>
|
</el-col>
|
<el-col :span="1.5">
|
<el-button type="danger" icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">删除</el-button>
|
</el-col>
|
<el-col :span="1.5">
|
<el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
</el-col>
|
</el-row>
|
|
<!-- 列表 -->
|
<el-table :data="currentPageData" v-loading="loading" border @selection-change="handleSelectionChange">
|
<el-table-column type="selection" width="50" align="center" />
|
<el-table-column label="姓名" prop="name" min-width="90" align="center" fixed="left" />
|
<el-table-column label="性别" prop="sex" width="60" align="center" />
|
<el-table-column label="年龄" prop="age" width="70" align="center" />
|
<el-table-column label="联系电话" prop="telcode" width="120" align="center" />
|
<el-table-column label="科室" prop="deptname" min-width="110" align="center" show-overflow-tooltip />
|
<el-table-column label="病区" prop="wardname" min-width="90" align="center" />
|
<el-table-column label="主治医生" prop="drname" width="90" align="center" />
|
<el-table-column label="诊断" prop="diagname" min-width="120" align="center" show-overflow-tooltip />
|
<el-table-column label="管饲方式" prop="feedType" width="100" align="center" />
|
<el-table-column label="管饲开始日期" prop="feedStartDate" width="120" align="center" />
|
<el-table-column label="营养方案" prop="nutrition" min-width="140" align="center" show-overflow-tooltip />
|
<el-table-column label="每日能量(kcal)" prop="energy" width="110" align="center" />
|
<el-table-column label="管饲状态" prop="feedStatus" width="90" align="center">
|
<template slot-scope="scope">
|
<el-tag :type="scope.row.feedStatus === '已停用' ? 'info' : 'success'" size="small">{{ scope.row.feedStatus }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="来源" prop="source" width="90" align="center" />
|
<el-table-column label="加入时间" prop="addTime" width="150" align="center" />
|
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<template slot-scope="scope">
|
<el-button type="text" size="mini" icon="el-icon-view" @click="openDetail(scope.row)">详情</el-button>
|
<el-button type="text" size="mini" icon="el-icon-close" class="danger-text" @click="removeRow(scope.row)">移除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 分页 -->
|
<el-pagination
|
class="pagination-wrap"
|
background
|
layout="total, sizes, prev, pager, next, jumper"
|
:total="filteredList.length"
|
:current-page.sync="pageNum"
|
:page-size.sync="pageSize"
|
:page-sizes="[10, 20, 50]"
|
/>
|
|
<!-- 添加患者弹窗 -->
|
<el-dialog title="添加专病患者" :visible.sync="addVisible" width="80%" top="6vh">
|
<el-tabs v-model="activeSource" @tab-click="onSourceTabChange">
|
<el-tab-pane label="患者档案" name="archive" />
|
<el-tab-pane label="出院记录" name="hospital" />
|
<el-tab-pane label="门诊记录" name="outpatient" />
|
<el-tab-pane label="在院记录" name="inpatient" />
|
</el-tabs>
|
<el-form :inline="true" size="small">
|
<el-form-item label="姓名/诊断">
|
<el-input v-model="poolKeyword" placeholder="输入姓名或诊断关键字" clearable style="width: 220px" @input="handlePoolSearch" />
|
</el-form-item>
|
<span class="pool-tip">共 {{ filteredPool.length }} 条,已选 {{ addSelected.length }} 条</span>
|
</el-form>
|
<el-table ref="addTable" :data="filteredPool" border size="small" max-height="420" @selection-change="onAddSelectionChange">
|
<el-table-column type="selection" width="50" align="center" />
|
<el-table-column label="姓名" prop="name" width="90" align="center" />
|
<el-table-column label="性别" prop="sex" width="60" align="center" />
|
<el-table-column label="年龄" prop="age" width="70" align="center" />
|
<el-table-column label="联系电话" prop="telcode" width="120" align="center" />
|
<el-table-column label="科室" prop="deptname" min-width="110" align="center" show-overflow-tooltip />
|
<el-table-column label="病区" prop="wardname" min-width="90" align="center" />
|
<el-table-column label="诊断" prop="diagname" min-width="130" align="center" show-overflow-tooltip />
|
<el-table-column label="医生" prop="drname" width="90" align="center" />
|
<el-table-column label="日期" prop="sourceDate" width="110" align="center" />
|
</el-table>
|
<div slot="footer" class="dialog-footer">
|
<el-button @click="addVisible = false">取 消</el-button>
|
<el-button type="primary" @click="confirmAdd">确定添加</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 详情弹窗 -->
|
<el-dialog title="专病患者详情" :visible.sync="detailVisible" width="720px">
|
<el-descriptions :column="2" border size="medium" v-if="detailRow">
|
<el-descriptions-item label="姓名">{{ detailRow.name }}</el-descriptions-item>
|
<el-descriptions-item label="性别">{{ detailRow.sex }}</el-descriptions-item>
|
<el-descriptions-item label="年龄">{{ detailRow.age }}</el-descriptions-item>
|
<el-descriptions-item label="联系电话">{{ detailRow.telcode }}</el-descriptions-item>
|
<el-descriptions-item label="科室">{{ detailRow.deptname }}</el-descriptions-item>
|
<el-descriptions-item label="病区">{{ detailRow.wardname || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="主治医生">{{ detailRow.drname }}</el-descriptions-item>
|
<el-descriptions-item label="诊断">{{ detailRow.diagname }}</el-descriptions-item>
|
<el-descriptions-item label="管饲方式">{{ detailRow.feedType }}</el-descriptions-item>
|
<el-descriptions-item label="管饲开始日期">{{ detailRow.feedStartDate }}</el-descriptions-item>
|
<el-descriptions-item label="营养方案">{{ detailRow.nutrition }}</el-descriptions-item>
|
<el-descriptions-item label="每日能量">{{ detailRow.energy }} kcal</el-descriptions-item>
|
<el-descriptions-item label="管饲状态">{{ detailRow.feedStatus }}</el-descriptions-item>
|
<el-descriptions-item label="患者来源">{{ detailRow.source }}</el-descriptions-item>
|
<el-descriptions-item label="加入时间">{{ detailRow.addTime }}</el-descriptions-item>
|
</el-descriptions>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
// 管饲专病患者 —— 模拟数据,接口就绪后替换 getList / confirmAdd 中的本地数据逻辑
|
const PERSONS = [
|
{ name: '张伟', sex: '男', age: 68 },
|
{ name: '李娜', sex: '女', age: 55 },
|
{ name: '王强', sex: '男', age: 72 },
|
{ name: '刘敏', sex: '女', age: 61 },
|
{ name: '陈静', sex: '女', age: 66 },
|
{ name: '杨军', sex: '男', age: 59 },
|
{ name: '赵丽', sex: '女', age: 70 },
|
{ name: '黄勇', sex: '男', age: 64 },
|
{ name: '周艳', sex: '女', age: 58 },
|
{ name: '吴刚', sex: '男', age: 75 },
|
];
|
const TUBE_DIAGNOSES = ['脑卒中', '吞咽困难', '食管癌术后', '昏迷', '重症肺炎', '运动神经元病', '颅脑损伤', '帕金森病', '脑出血恢复期', '肌萎缩侧索硬化'];
|
const DEPTS = ['神经内科', '康复医学科', '老年科', '呼吸内科', '普外科', '重症医学科', '内分泌科', '消化内科'];
|
const WARDS = ['一病区', '二病区', '三病区', '四病区', '五病区'];
|
const DOCTORS = ['王主任', '李医生', '张医生', '刘医生', '陈医生'];
|
const TUBE_FEED_TYPES = ['鼻胃管', '胃造瘘管', '空肠造瘘管', '鼻肠管'];
|
const TUBE_STATUS = ['管饲中', '间歇管饲', '已停用'];
|
const NUTRITIONS = ['肠内营养乳剂(TPF)', '肠内营养混悬液(TPF-D)', '自制匀浆膳', '短肽型营养液'];
|
const SOURCE_LABEL = { archive: '患者档案', hospital: '出院记录', outpatient: '门诊记录', inpatient: '在院记录' };
|
|
export default {
|
name: 'TubeFeedDedicated',
|
data() {
|
return {
|
loading: false,
|
// 专病患者列表(已加入)
|
patientList: [],
|
ids: [],
|
single: true,
|
multiple: true,
|
// 查询条件
|
queryParams: { name: '', drname: '', deptWard: '', feedType: '', feedStatus: '' },
|
pageNum: 1,
|
pageSize: 10,
|
deptOptions: DEPTS,
|
wardOptions: WARDS,
|
TUBE_FEED_TYPES,
|
TUBE_STATUS,
|
// 添加弹窗
|
addVisible: false,
|
activeSource: 'archive',
|
poolKeyword: '',
|
addSelected: [],
|
mockPool: { archive: [], hospital: [], outpatient: [], inpatient: [] },
|
// 详情弹窗
|
detailVisible: false,
|
detailRow: null,
|
};
|
},
|
computed: {
|
filteredList() {
|
const { name, drname, deptWard, feedType, feedStatus } = this.queryParams;
|
return this.patientList.filter((r) => {
|
if (name && !r.name.includes(name)) return false;
|
if (drname && !r.drname.includes(drname)) return false;
|
if (deptWard) {
|
const label = deptWard.split(':')[1];
|
if (label && r.deptname !== label && r.wardname !== label) return false;
|
}
|
if (feedType && r.feedType !== feedType) return false;
|
if (feedStatus && r.feedStatus !== feedStatus) return false;
|
return true;
|
});
|
},
|
currentPageData() {
|
const start = (this.pageNum - 1) * this.pageSize;
|
return this.filteredList.slice(start, start + this.pageSize);
|
},
|
filteredPool() {
|
const kw = this.poolKeyword.trim();
|
const list = this.mockPool[this.activeSource] || [];
|
if (!kw) return list;
|
return list.filter((r) => r.name.includes(kw) || r.diagname.includes(kw));
|
},
|
},
|
created() {
|
this.buildPool();
|
this.buildInitialList();
|
},
|
methods: {
|
// 生成各来源的模拟患者池
|
buildPool() {
|
const mk = (idx, source, noPrefix) => {
|
const p = PERSONS[idx % PERSONS.length];
|
const isWard = source === 'hospital' || source === 'inpatient';
|
return {
|
id: noPrefix + (idx + 1),
|
name: p.name,
|
sex: p.sex,
|
age: p.age,
|
telcode: '138' + (10000000 + idx * 1111111),
|
deptname: DEPTS[idx % DEPTS.length],
|
wardname: isWard ? WARDS[idx % WARDS.length] : '',
|
drname: DOCTORS[idx % DOCTORS.length],
|
diagname: TUBE_DIAGNOSES[idx % TUBE_DIAGNOSES.length],
|
sourceDate: this.recentDate(idx * 3 + (source === 'outpatient' ? 2 : 5)),
|
};
|
};
|
this.mockPool = {
|
archive: PERSONS.map((_, i) => mk(i, 'archive', 'da')),
|
hospital: PERSONS.map((_, i) => mk(i, 'hospital', 'zy')),
|
outpatient: PERSONS.map((_, i) => mk(i, 'outpatient', 'mz')),
|
inpatient: PERSONS.map((_, i) => mk(i, 'inpatient', 'zy')),
|
};
|
},
|
// 预置若干已加入的患者,保证首屏有数据
|
buildInitialList() {
|
const take = (source, n) =>
|
this.mockPool[source].slice(0, n).map((row) => this.toDedicated(row, source));
|
this.patientList = [
|
...take('hospital', 3),
|
...take('inpatient', 2),
|
...take('archive', 2),
|
...take('outpatient', 1),
|
];
|
},
|
// 池患者 => 专病患者(补充管饲相关字段)
|
toDedicated(row, source) {
|
const n = Number(String(row.id).replace(/\D/g, '')) || 0;
|
return {
|
id: row.id,
|
name: row.name,
|
sex: row.sex,
|
age: row.age,
|
telcode: row.telcode,
|
deptname: row.deptname,
|
wardname: row.wardname,
|
drname: row.drname,
|
diagname: row.diagname,
|
feedType: TUBE_FEED_TYPES[n % TUBE_FEED_TYPES.length],
|
feedStartDate: this.recentDate(n % 10),
|
nutrition: NUTRITIONS[n % NUTRITIONS.length],
|
energy: 1500 + (n % 5) * 100,
|
feedStatus: TUBE_STATUS[n % 2],
|
source: SOURCE_LABEL[source] || source,
|
addTime: this.now(),
|
};
|
},
|
handleQuery() {
|
this.pageNum = 1;
|
},
|
resetQuery() {
|
this.queryParams = { name: '', drname: '', deptWard: '', feedType: '', feedStatus: '' };
|
this.pageNum = 1;
|
},
|
handleSelectionChange(selection) {
|
this.ids = selection.map((item) => item.id);
|
this.single = selection.length !== 1;
|
this.multiple = !selection.length;
|
},
|
openAddDialog() {
|
this.addSelected = [];
|
this.poolKeyword = '';
|
this.activeSource = 'archive';
|
this.addVisible = true;
|
this.$nextTick(() => this.$refs.addTable && this.$refs.addTable.clearSelection());
|
},
|
onSourceTabChange() {
|
this.addSelected = [];
|
this.$nextTick(() => this.$refs.addTable && this.$refs.addTable.clearSelection());
|
},
|
handlePoolSearch() {
|
// 本地过滤,由 filteredPool 计算属性完成
|
},
|
onAddSelectionChange(selection) {
|
this.addSelected = selection;
|
},
|
confirmAdd() {
|
if (!this.addSelected.length) {
|
this.$modal.msgWarning('请先选择要添加的患者');
|
return;
|
}
|
const existing = new Set(this.patientList.map((r) => r.id));
|
const toAdd = this.addSelected
|
.filter((row) => !existing.has(row.id))
|
.map((row) => this.toDedicated(row, this.activeSource));
|
this.patientList = [...toAdd, ...this.patientList];
|
this.addVisible = false;
|
this.$modal.msgSuccess(`成功添加 ${toAdd.length} 位患者`);
|
},
|
removeRow(row) {
|
this.$modal
|
.confirm(`是否确认将患者"${row.name}"移出管饲专病列表?`)
|
.then(() => {
|
this.patientList = this.patientList.filter((r) => r.id !== row.id);
|
this.$modal.msgSuccess('移除成功');
|
})
|
.catch(() => {});
|
},
|
handleDelete() {
|
this.$modal
|
.confirm(`是否确认删除选中的 ${this.ids.length} 位患者?`)
|
.then(() => {
|
this.patientList = this.patientList.filter((r) => !this.ids.includes(r.id));
|
this.$modal.msgSuccess('删除成功');
|
})
|
.catch(() => {});
|
},
|
openDetail(row) {
|
this.detailRow = row;
|
this.detailVisible = true;
|
},
|
handleExport() {
|
const rows = this.filteredList;
|
if (!rows.length) {
|
this.$modal.msgWarning('暂无数据可导出');
|
return;
|
}
|
const head = ['姓名', '性别', '年龄', '联系电话', '科室', '病区', '主治医生', '诊断', '管饲方式', '管饲开始日期', '营养方案', '每日能量(kcal)', '管饲状态', '来源', '加入时间'];
|
const keys = ['name', 'sex', 'age', 'telcode', 'deptname', 'wardname', 'drname', 'diagname', 'feedType', 'feedStartDate', 'nutrition', 'energy', 'feedStatus', 'source', 'addTime'];
|
const csv = [head.join(',')]
|
.concat(rows.map((r) => keys.map((k) => `"${(r[k] ?? '').toString().replace(/"/g, '""')}"`).join(',')))
|
.join('\n');
|
const blob = new Blob(['' + csv], { type: 'text/csv;charset=utf-8;' });
|
const link = document.createElement('a');
|
link.href = URL.createObjectURL(blob);
|
link.download = `管饲专病患者_${Date.now()}.csv`;
|
link.click();
|
URL.revokeObjectURL(link.href);
|
},
|
recentDate(daysAgo) {
|
const d = new Date();
|
d.setDate(d.getDate() - daysAgo);
|
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
const dd = String(d.getDate()).padStart(2, '0');
|
return `${d.getFullYear()}-${mm}-${dd}`;
|
},
|
now() {
|
const d = new Date();
|
const pad = (x) => String(x).padStart(2, '0');
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.query-form { margin-bottom: 4px; }
|
.mb8 { margin-bottom: 8px; }
|
.pagination-wrap { margin-top: 16px; text-align: right; }
|
.pool-tip { font-size: 12px; color: #909399; margin-left: 8px; }
|
.danger-text { color: #f56c6c; }
|
</style>
|