病例库-页面搭建

main
ysn 2 weeks ago
parent cbd249fec4
commit 9888b281a1
  1. 44
      src/api/cases/index.js
  2. 7
      src/assets/styles/element-ui.scss
  3. 33
      src/router/index.js
  4. 910
      src/views/cases/detail.vue
  5. 1079
      src/views/cases/index.vue

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询病例列表
export function listCase(query) {
return request({
url: '/business/case/list',
method: 'get',
params: query
})
}
// 查询病例详情
export function getCase(id) {
return request({
url: '/business/case/' + id,
method: 'get'
})
}
// 新增病例
export function addCase(data) {
return request({
url: '/business/case',
method: 'post',
data: data
})
}
// 修改病例
export function updateCase(data) {
return request({
url: '/business/case',
method: 'put',
data: data
})
}
// 删除病例
export function delCase(id) {
return request({
url: '/business/case/' + id,
method: 'delete'
})
}

@ -113,4 +113,11 @@
.el-menu--collapse>div>.el-submenu>.el-submenu__title .el-submenu__icon-arrow {
display: none;
}
.el-table {
th {
background: #d5f5f5 !important;
color: $base-menu-background !important;
}
}

@ -126,19 +126,26 @@ export const constantRoutes = [
}
]
},
// {
// path: '/cases',
// component: Layout,
// redirect: '/cases/index',
// children: [
// {
// path: 'index',
// component: () => import('@/views/cases/index'),
// name: 'Cases',
// meta: { title: '病例库', icon: 'example' }
// }
// ]
// },
{
path: '/cases',
component: Layout,
redirect: '/cases/index',
children: [
{
path: 'index',
component: () => import('@/views/cases/index'),
name: 'Cases',
meta: { title: '病例库', icon: 'example' }
},
{
path: 'detail/:id',
component: () => import('@/views/cases/detail'),
name: 'PatientDetail',
hidden: true,
meta: { title: '病例库详情' }
}
]
},
{
path: '/utility',
component: Layout,

@ -0,0 +1,910 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="card-row">
<!-- 左侧影像文件 -->
<el-col :span="3">
<el-card class="left-card">
<div slot="header" class="card-header">
<span>影像文件</span>
<el-button
type="text"
icon="el-icon-plus"
@click="handleOpenFile"
:disabled="caseStatus === 'submitted'"
>
新增
</el-button>
</div>
<el-table
:data="imgList"
:show-header="false"
height="calc(100vh - 235px)"
>
<el-table-column align="center">
<template slot-scope="scope">
<el-image
:src="scope.row.url"
fit="cover"
:preview-src-list="[scope.row.url]"
style="width: 100px; height: 80px"
/>
<div class="img-status">上传完成</div>
<el-button
type="text"
icon="el-icon-delete"
@click="handleDeleteImg(scope.row)"
:disabled="caseStatus === 'submitted'"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<input
ref="fileInput"
type="file"
multiple
accept="image/*"
style="display: none"
@change="handleFileChange"
/>
</el-card>
</el-col>
<!-- 中间报告表单 -->
<el-col :span="17">
<el-card class="center-card">
<div slot="header" class="card-header">
<span class="status-text"
>病例状态
{{ caseStatus === "submitted" ? "已提交" : "暂存" }}
</span>
<div class="btn-group">
<el-button
type="text"
icon="el-icon-check"
@click="handleSave"
v-if="caseStatus !== 'submitted'"
>
暂存
</el-button>
<el-button
type="text"
icon="el-icon-check"
@click="handleSubmit"
v-if="caseStatus !== 'submitted'"
>
提交
</el-button>
<el-button
type="text"
icon="el-icon-print"
@click="handlePrint"
v-if="caseStatus == 'submitted'"
>
打印
</el-button>
<el-button type="text" icon="el-icon-close" @click="handleClose">
关闭
</el-button>
<el-button type="text" icon="el-icon-share" @click="handleShare">
分享
</el-button>
</div>
</div>
<el-divider>远程超声检查报告</el-divider>
<el-form class="report-form" :model="form" label-width="70px">
<el-row :gutter="15">
<el-col :span="6">
<el-form-item label="患者姓名">
<el-input v-model="form.patientName" readonly />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="性别">
<el-select v-model="form.gender" readonly>
<el-option
v-for="item in genderList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label-width="0">
<el-input-number
v-model="form.age"
:min="0"
readonly
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="1">
<el-form-item label-width="0">
<el-select v-model="form.ageUnit" readonly>
<el-option
v-for="item in ageUnitList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请医师">
<el-input v-model="form.doctor" readonly />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="病历号">
<el-input v-model="form.medicalNo" readonly />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="门诊号">
<el-input v-model="form.outNo" readonly />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="住院号">
<el-input v-model="form.inNo" readonly />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="病区号">
<el-input v-model="form.wardNo" readonly />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="床位号">
<el-input v-model="form.bedNo" readonly />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="检查设备">
<el-input
type="textarea"
autosize
v-model="form.device"
readonly
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="检查部位">
<el-input
type="textarea"
autosize
v-model="form.part"
readonly
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="超声所见">
<el-input
v-model="form.findings"
type="textarea"
:rows="8"
:disabled="caseStatus === 'submitted'"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="检查结论">
<el-input
v-model="form.conclusion"
type="textarea"
:rows="6"
:disabled="caseStatus === 'submitted'"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="专家意见" />
</el-col>
<el-col
:span="24"
v-for="(item, idx) in expertList"
:key="idx"
class="expert-item"
>
<el-form-item :label="item.name">
<el-input
v-model="item.content"
type="textarea"
:rows="4"
:disabled="caseStatus === 'submitted'"
/>
<div class="expert-btns">
<el-button
type="text"
icon="el-icon-document-copy"
:disabled="caseStatus === 'submitted'"
>
全部复制
</el-button>
<el-button
type="text"
icon="el-icon-check"
:disabled="caseStatus === 'submitted'"
>
一键同意
</el-button>
<el-button
type="text"
icon="el-icon-warning"
:disabled="caseStatus === 'submitted'"
>
待确认
</el-button>
</div>
</el-form-item>
</el-col>
<el-col :span="18">
<el-form-item label="报告人">
{{ $store.state.user.nickName || "未登录" }}
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="审核人">
<el-select
v-model="form.auditUser"
clearable
filterable
style="width: 100%"
:disabled="caseStatus === 'submitted'"
>
<el-option label="请选择" value="" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<el-checkbox
v-model="form.isPositive"
:disabled="caseStatus === 'submitted'"
>
阳性
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</el-col>
<!-- 右侧部位模板 + 片语 -->
<el-col :span="4">
<el-card class="right-card">
<el-tabs v-model="activeTab">
<el-tab-pane label="部位模板" name="template">
<div class="template-buttons">
<el-button
type="text"
icon="el-icon-plus"
@click="openTemplateDialog('add')"
:disabled="caseStatus === 'submitted'"
>
新增
</el-button>
<el-button
type="text"
icon="el-icon-delete"
@click="deleteTemplate"
:disabled="caseStatus === 'submitted'"
>
删除
</el-button>
<el-button
type="text"
icon="el-icon-edit"
@click="openTemplateDialog('edit')"
:disabled="caseStatus === 'submitted'"
>
编辑
</el-button>
</div>
<el-form
label-position="top"
:model="templateForm"
class="template-form"
>
<el-form-item>
<el-select
v-model="templateForm.id"
style="width: 100%"
@change="changeTemplate"
>
<el-option
v-for="item in templateList"
:key="item.id"
:label="item.title"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="超声所见">
<el-input
v-model="templateForm.findings"
type="textarea"
readonly
:rows="6"
/>
<el-button
:disabled="caseStatus === 'submitted'"
type="text"
icon="el-icon-check"
@click="applyFindings"
style="float: right; margin-top: 6px"
>
应用
</el-button>
</el-form-item>
<el-form-item label="结论" style="margin-top: 20px">
<el-input
v-model="templateForm.conclusion"
type="textarea"
readonly
:rows="6"
/>
<el-button
type="text"
icon="el-icon-check"
@click="applyConclusion"
style="float: right; margin-top: 6px"
:disabled="caseStatus === 'submitted'"
>
应用
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 片语功能改造 -->
<el-tab-pane label="片语" name="phrase">
<div class="phrase-buttons">
<el-button
type="text"
icon="el-icon-plus"
@click="openPhraseDialog('add')"
:disabled="caseStatus === 'submitted'"
>
新增
</el-button>
</div>
<el-table
:data="phraseList"
style="width: 100%"
:show-header="false"
height="calc(100vh - 265px)"
>
<el-table-column prop="content" label="内容" />
<el-table-column label="操作" width="80">
<template slot-scope="scope">
<el-button
type="text"
icon="el-icon-edit"
@click="openPhraseDialog('edit', scope.row)"
:disabled="caseStatus === 'submitted'"
>
</el-button>
<el-button
type="text"
icon="el-icon-delete"
@click="deletePhrase(scope.row)"
:disabled="caseStatus === 'submitted'"
>
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
<!-- 模板弹窗新增 + 编辑 共用 -->
<el-dialog
:title="dialogTitle"
:visible.sync="templateDialogVisible"
width="33%"
>
<el-form :model="newTemplateForm" label-width="60px">
<el-form-item label="部位">
<el-select v-model="newTemplateForm.type" style="width: 100%">
<el-option label="肝脏-腹部" value="abdomen-liver" />
<el-option label="心脏" value="heart" />
<el-option label="肾脏" value="kidney" />
<el-option label="妇科" value="gynecology" />
</el-select>
</el-form-item>
<el-form-item label="标题">
<el-input
v-model="newTemplateForm.title"
placeholder="请输入模板标题"
/>
</el-form-item>
<el-form-item label="所见">
<el-input
type="textarea"
:rows="6"
v-model="newTemplateForm.findings"
placeholder="请输入超声所见内容"
/>
</el-form-item>
<el-form-item label="结论">
<el-input
type="textarea"
:rows="6"
v-model="newTemplateForm.conclusion"
placeholder="请输入检查结论内容"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="templateDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveTemplate">确认保存</el-button>
</div>
</el-dialog>
<!-- 片语弹窗创建 + 更新 共用 -->
<el-dialog
:title="phraseDialogTitle"
:visible.sync="phraseDialogVisible"
width="33%"
>
<el-form :model="newPhraseForm" label-width="60px">
<el-form-item label="内容">
<el-input
type="textarea"
:rows="10"
v-model="newPhraseForm.content"
placeholder="请输入片语内容"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="phraseDialogVisible = false">取消</el-button>
<el-button type="primary" @click="savePhrase">
{{ phraseType === "add" ? "创建" : "更新" }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "UltrasoundReport",
data() {
return {
activeTab: "template",
// : pending-, submitted-
caseStatus: "submitted",
//
imgList: [
{
id: Date.now(),
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
},
],
//
form: {
patientName: "",
gender: "",
age: 0,
ageUnit: "1",
doctor: "",
medicalNo: "",
outNo: "",
inNo: "",
wardNo: "",
bedNo: "",
device: "",
part: "",
findings: "",
conclusion: "",
auditUser: "",
isPositive: false,
},
//
genderList: [
{ label: "未知", value: "0" },
{ label: "男", value: "1" },
{ label: "女", value: "2" },
],
ageUnitList: [
{ label: "岁", value: "1" },
{ label: "月", value: "2" },
{ label: "天", value: "3" },
],
// Mock
caseMockList: [
{
id: 1,
patientName: "测试患者",
gender: "1",
age: 30,
ageUnit: "1",
checkType: "1",
outpatientNumber: "123456",
inpatientNumber: "IN789012",
wardNumber: "W01",
bedNumber: "05",
applyDoctor: "张医生",
expertDoctor: "李专家",
checkDevice: "GE-E8",
checkRoom: "1",
customPart: "肝脏",
reserveDate: "2023-01-01",
reserveTime: "13:30-14:00",
diagnosisDoctor: "",
auditDoctor: "",
reportStatus: "",
createTime: "2023-01-01 10:00:00",
updateTime: "2023-01-01 10:00:00",
},
{
id: 2,
patientName: "王小明",
gender: "1",
age: 45,
ageUnit: "1",
checkType: "2",
outpatientNumber: "654321",
inpatientNumber: "",
wardNumber: "",
bedNumber: "",
applyDoctor: "刘医生",
expertDoctor: "王专家",
checkDevice: "Philips-iU22",
checkRoom: "2",
customPart: "心脏",
reserveDate: "2023-01-02",
reserveTime: "09:00-09:30",
diagnosisDoctor: "",
auditDoctor: "",
reportStatus: "",
createTime: "2023-01-02 09:00:00",
updateTime: "2023-01-02 09:00:00",
},
{
id: 3,
patientName: "李小红",
gender: "2",
age: 28,
ageUnit: "1",
checkType: "3",
outpatientNumber: "",
inpatientNumber: "IN345678",
wardNumber: "W03",
bedNumber: "12",
applyDoctor: "赵医生",
expertDoctor: "孙专家",
checkDevice: "GE-E8",
checkRoom: "1",
customPart: "肾脏",
reserveDate: "2023-01-03",
reserveTime: "14:00-14:30",
diagnosisDoctor: "",
auditDoctor: "",
reportStatus: "",
createTime: "2023-01-03 14:00:00",
updateTime: "2023-01-03 14:00:00",
},
],
//
expertList: [
{ name: "韩主任", content: "" },
{ name: "景静静", content: "" },
{ name: "郑曙光", content: "" },
],
// ====================== ======================
templateType: "add",
dialogTitle: "模板创建",
templateDialogVisible: false,
templateList: [
{
id: 1,
type: "abdomen-liver",
title: "腹部(肝、胆、胰、脾)",
findings: `肝脏剑下cm,肋下cm,右肝斜径cm,包膜光滑,实质回声均匀,未见确切占位。肝静脉走行正常,门静脉不扩张。
胆囊前后径cm壁薄光滑囊内未见异常回声
胰腺形态规则大小正常边界清晰胰腺实质回声未见异常主胰管不扩张
脾脏肋间厚cm轮廓清楚脾门切迹清晰可见实质回声未见异常脾静脉不扩张`,
conclusion: "肝脏、胆囊、胰腺、脾脏未见明显异常",
},
],
templateForm: {
id: 1,
type: "abdomen-liver",
title: "腹部(肝、胆、胰、脾)",
findings: "",
conclusion: "",
},
newTemplateForm: {
id: null,
type: "abdomen-liver",
title: "",
findings: "",
conclusion: "",
},
// ====================== ======================
phraseType: "add", // add / edit
phraseDialogTitle: "片语创建",
phraseDialogVisible: false,
phraseList: [{ id: 1, content: "片语示例:未见明显异常" }],
newPhraseForm: {
id: null,
content: "",
},
};
},
created() {
this.getCaseData();
},
methods: {
//
getCaseData() {
const caseId = parseInt(this.$route.params.id);
if (caseId) {
const caseData = this.caseMockList.find((item) => item.id === caseId);
if (caseData) {
this.form = {
patientName: caseData.patientName,
gender: caseData.gender,
age: caseData.age,
ageUnit: caseData.ageUnit,
doctor: caseData.applyDoctor,
medicalNo: caseData.patientRecordNumber || "",
outNo: caseData.outpatientNumber || "",
inNo: caseData.inpatientNumber || "",
wardNo: caseData.wardNumber || "",
bedNo: caseData.bedNumber || "",
device: caseData.checkDevice || "",
part: caseData.customPart || "",
findings: "",
conclusion: "",
auditUser: "",
isPositive: false,
};
//
this.$store.dispatch("settings/setTitle", caseData.patientName);
}
}
},
//
handlePrint() {
window.print();
},
//
handleSave() {
this.$confirm("您确定暂存病例吗?").then(() => {
this.$message.success("暂存成功");
});
},
handleSubmit() {
this.$confirm("提交后不可修改,确定提交?").then(() => {
this.$message.success("提交成功");
this.caseStatus = "submitted";
});
},
handleClose() {
this.$confirm("未保存内容将丢失,确定退出?").then(() => {
this.$router.back();
});
},
handleShare() {
this.$message.success("分享成功");
},
//
handleOpenFile() {
this.$refs.fileInput.click();
},
handleFileChange(e) {
const files = e.target.files || [];
for (let file of files) {
if (!file.type.startsWith("image/")) continue;
const url = URL.createObjectURL(file);
this.imgList.push({ id: Date.now(), url });
}
e.target.value = "";
},
handleDeleteImg(row) {
this.$confirm("确定删除该影像?").then(() => {
this.imgList = this.imgList.filter((i) => i.id !== row.id);
this.$message.success("删除成功");
});
},
// ====================== ======================
changeTemplate() {
const target = this.templateList.find(
(t) => t.id === this.templateForm.id
);
if (target) {
this.templateForm = { ...target };
}
},
openTemplateDialog(type) {
this.templateType = type;
this.dialogTitle = type === "add" ? "模板创建" : "模板编辑";
if (type === "add") {
this.newTemplateForm = {
id: Date.now(),
type: "abdomen-liver",
title: "",
findings: "",
conclusion: "",
};
} else {
this.newTemplateForm = { ...this.templateForm };
}
this.templateDialogVisible = true;
},
saveTemplate() {
if (!this.newTemplateForm.title) {
this.$message.warning("请输入模板标题");
return;
}
if (this.templateType === "add") {
this.templateList.push({ ...this.newTemplateForm });
this.templateForm = { ...this.newTemplateForm };
} else {
const index = this.templateList.findIndex(
(t) => t.id === this.newTemplateForm.id
);
if (index > -1) {
this.templateList.splice(index, 1, { ...this.newTemplateForm });
this.templateForm = { ...this.newTemplateForm };
}
}
this.$message.success("保存成功");
this.templateDialogVisible = false;
},
deleteTemplate() {
if (this.templateList.length <= 1) {
this.$message.warning("至少保留一个模板");
return;
}
this.$confirm("确定删除当前模板?").then(() => {
const index = this.templateList.findIndex(
(t) => t.id === this.templateForm.id
);
this.templateList.splice(index, 1);
this.templateForm = { ...this.templateList[0] };
this.$message.success("删除成功");
});
},
applyFindings() {
this.form.findings = this.templateForm.findings;
this.$message.success("已应用超声所见");
},
applyConclusion() {
this.form.conclusion = this.templateForm.conclusion;
this.$message.success("已应用检查结论");
},
// ====================== ======================
// /
openPhraseDialog(type, row) {
this.phraseType = type;
this.phraseDialogTitle = type === "add" ? "片语创建" : "片语更新";
if (type === "add") {
this.newPhraseForm = { id: Date.now(), content: "" };
} else {
this.newPhraseForm = { ...row };
}
this.phraseDialogVisible = true;
},
//
savePhrase() {
if (!this.newPhraseForm.content) {
this.$message.warning("请输入片语内容");
return;
}
if (this.phraseType === "add") {
this.phraseList.push({ ...this.newPhraseForm });
} else {
const index = this.phraseList.findIndex(
(p) => p.id === this.newPhraseForm.id
);
if (index > -1) {
this.phraseList.splice(index, 1, { ...this.newPhraseForm });
}
}
this.$message.success(
this.phraseType === "add" ? "创建成功" : "更新成功"
);
this.phraseDialogVisible = false;
},
//
deletePhrase(row) {
this.$confirm("确定删除该片语?").then(() => {
this.phraseList = this.phraseList.filter((p) => p.id !== row.id);
this.$message.success("删除成功");
});
},
},
};
</script>
<style lang="scss" scoped>
/* 内部滚动区域高度自适应 */
.report-form {
height: calc(100vh - 285px);
overflow-y: auto;
overflow-x: hidden;
width: 100%;
box-sizing: border-box;
}
.template-form {
height: calc(100vh - 265px);
overflow-y: auto;
overflow-x: hidden;
}
/* 原有样式 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.status-text {
font-weight: 500;
color: #333;
}
.btn-group {
display: flex;
gap: 4px;
}
.expert-item {
margin-bottom: 10px;
}
.expert-btns {
text-align: right;
margin-top: 6px;
}
.img-status {
font-size: 12px;
color: #666;
margin: 4px 0;
}
.template-buttons,
.phrase-buttons {
display: flex;
gap: 6px;
margin-bottom: 10px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save