index.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
  4. <div v-show="showSearch" class="mb-[10px]">
  5. <el-card shadow="hover">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="150px">
  7. <el-form-item label="患者类型" prop="type">
  8. <el-select v-model="queryParams.type" clearable>
  9. <el-option v-for="dict in treatment_user_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  10. </el-select>
  11. </el-form-item>
  12. <el-form-item label="科室" prop="doorId">
  13. <el-tree-select v-model="queryParams.doorId" :data="treeData" :props="treeProps" placeholder="请选择" check-strictly node-key="id" @keyup.enter="handleQuery" />
  14. </el-form-item>
  15. <el-form-item label="患者状态" prop="treatmentUserStatus" v-if="showStatusSearch">
  16. <el-select v-model="queryParams.treatmentUserStatus" clearable>
  17. <el-option v-for="dict in treatment_user_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  18. </el-select>
  19. </el-form-item>
  20. <el-form-item label="评估状态" prop="evaluationStatus">
  21. <el-select v-model="queryParams.evaluationStatus" clearable>
  22. <el-option v-for="dict in evaluation_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  23. </el-select>
  24. </el-form-item>
  25. <el-form-item prop="searchFlag">
  26. <el-input v-model="queryParams.searchFlag" placeholder="姓名/门诊号/身份证号/医生" clearable @keyup.enter="handleQuery" />
  27. </el-form-item>
  28. <el-form-item>
  29. <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
  30. <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workbench:treatmentUser:add']">添加患者</el-button>
  31. </el-form-item>
  32. </el-form>
  33. </el-card>
  34. </div>
  35. </transition>
  36. <el-card shadow="never">
  37. <div class="status-tabs">
  38. <div class="tab-item" :class="{ 'active': activeTab === 'waiting' }" @click="handleTabClick('waiting')">
  39. 待诊
  40. <el-badge :value="waitingCount" class="tab-badge" />
  41. </div>
  42. <div class="tab-item" :class="{ 'active': activeTab === 'treating' }" @click="handleTabClick('treating')">
  43. 诊中
  44. <el-badge :value="treatingCount" class="tab-badge" />
  45. </div>
  46. <div class="tab-item" :class="{ 'active': activeTab === 'treated' }" @click="handleTabClick('treated')">
  47. 已诊
  48. <el-badge :value="treatedCount" class="tab-badge" />
  49. </div>
  50. <div class="tab-item" :class="{ 'active': activeTab === 'reapply' }" @click="handleTabClick('reapply')">
  51. 处方重申
  52. <el-badge :value="reapplyCount" class="tab-badge" />
  53. </div>
  54. </div>
  55. <div class="patient-cards-container" v-if="userList.length > 0">
  56. <el-row :gutter="20">
  57. <el-col v-for="(patient, index) in userList" :key="index" :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
  58. <el-card class="patient-card" shadow="hover" @click="toPatients(patient)">
  59. <div class="patient-status-bar" :class="getStatusBarClass(patient)">
  60. {{ getStatusText(patient) }}
  61. </div>
  62. <div class="patient-info">
  63. <div class="patient-header">
  64. <div class="patient-name-age">
  65. <span class="patient-name">{{ patient.treatName }}</span>
  66. <span class="patient-age">{{ patient.age }}</span>
  67. </div>
  68. <span class="gender-icon" :class="patient.sex === '1' ? 'male' : 'female'">
  69. {{ patient.sex === '1' ? '♂' : '♀' }}
  70. </span>
  71. </div>
  72. <div class="patient-detail">
  73. <span class="detail-label">门诊号:</span>
  74. <span>{{ patient.outpatientNo }}</span>
  75. </div>
  76. <div class="patient-detail">
  77. <span class="detail-label">科室:</span>
  78. <span>{{ patient.deptName }}</span>
  79. </div>
  80. <div class="evaluation-info">
  81. <span class="evaluation-score" v-if="patient.treatmentUserStatus==='4'">营养筛查得分:{{ patient.score }}</span>
  82. <span class="evaluation-status" :class="patient.evaluationStatus === '1' ? 'evaluated' : 'not-evaluated'">
  83. {{ patient.evaluationStatus === '1' ? '已评估' : '未评估' }}
  84. </span>
  85. </div>
  86. </div>
  87. </el-card>
  88. </el-col>
  89. </el-row>
  90. </div>
  91. <div v-else class="empty-state">
  92. <img src="@/assets/images/empty.png" alt="暂无数据" />
  93. <p>暂无数据</p>
  94. </div>
  95. </el-card>
  96. <!-- 添加或修改【待诊患者】对话框 -->
  97. <el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
  98. <el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
  99. <el-form-item label="看诊类型" prop="type">
  100. <el-radio-group v-model="form.type">
  101. <el-radio label="1">住院</el-radio>
  102. <el-radio label="0">门诊</el-radio>
  103. </el-radio-group>
  104. </el-form-item>
  105. <el-row :gutter="10">
  106. <el-col :span="12">
  107. <el-form-item label="诊疗卡号" prop="treatNum">
  108. <el-input v-model="form.treatNum" placeholder="系统生成" disabled />
  109. </el-form-item>
  110. </el-col>
  111. <el-col :span="12">
  112. <el-form-item label="门诊号" prop="outpatientNo">
  113. <el-input v-model="form.outpatientNo" placeholder="系统生成" disabled />
  114. </el-form-item>
  115. </el-col>
  116. </el-row>
  117. <el-row :gutter="10">
  118. <el-col :span="12">
  119. <el-form-item label="科室" prop="doorId">
  120. <el-tree-select v-model="form.doorId" :data="treeData" :props="treeProps" placeholder="请选择" check-strictly node-key="id" />
  121. </el-form-item>
  122. </el-col>
  123. </el-row>
  124. <div v-show="form.type == '1'">
  125. <el-row :gutter="10">
  126. <el-col :span="12">
  127. <el-form-item label="床号" prop="bedNum">
  128. <el-input v-model="form.bedNum" placeholder="请输入" />
  129. </el-form-item>
  130. </el-col>
  131. <el-col :span="12">
  132. <el-form-item label="病区" prop="inpatientWard">
  133. <el-select v-model="form.inpatientWard" placeholder="请选择">
  134. <el-option v-for="item in inpatientWardList" :key="item.value" :label="item.label" :value="item.value" />
  135. </el-select>
  136. </el-form-item>
  137. </el-col>
  138. </el-row>
  139. <el-row :gutter="10">
  140. <el-col :span="12">
  141. <el-form-item v-if="data.form.type === '1'" label="入院日期" prop="admissionDate" :required="data.form.type === '1'">
  142. <el-date-picker v-model="data.form.admissionDate" type="date" placeholder="请选择" value-format="YYYY-MM-DD" style="width: 100%" />
  143. </el-form-item>
  144. </el-col>
  145. </el-row>
  146. </div>
  147. <el-row :gutter="10">
  148. <el-col :span="12">
  149. <el-form-item label="姓名" prop="treatName">
  150. <el-input v-model="form.treatName" placeholder="请输入" />
  151. </el-form-item>
  152. <el-form-item label="身份证" prop="idCard">
  153. <el-input v-model="form.idCard" placeholder="请输入" @input="handleIdCardChange" />
  154. </el-form-item>
  155. <el-form-item label="年龄" prop="age">
  156. <el-input v-model="form.age" placeholder="请输入" disabled />
  157. </el-form-item>
  158. <el-form-item label="身高" prop="height">
  159. <el-input v-model="form.height" placeholder="请输入" min="0" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: calc(100% - 40px)">
  160. <template #append>cm</template>
  161. </el-input>
  162. </el-form-item>
  163. </el-col>
  164. <el-col :span="12">
  165. <el-form-item label="性别" prop="sex">
  166. <el-select v-model="form.sex" placeholder="请选择" clearable>
  167. <el-option v-for="dict in user_sex" :key="dict.value" :label="dict.label" :value="dict.value" />
  168. </el-select>
  169. </el-form-item>
  170. <el-form-item label="出生日期" prop="birthday">
  171. <el-input v-model="form.birthday" placeholder="请输入" disabled />
  172. </el-form-item>
  173. <el-form-item label="联系电话" prop="phoneNum">
  174. <el-input v-model="form.phoneNum" placeholder="请输入" />
  175. </el-form-item>
  176. <el-form-item label="体重" prop="weight">
  177. <el-input v-model="form.weight" placeholder="请输入" style="width: calc(100% - 40px)">
  178. <template #append>kg</template>
  179. </el-input>
  180. </el-form-item>
  181. </el-col>
  182. </el-row>
  183. <el-form-item label="过敏食物" prop="allergyFoot">
  184. <el-input type="textarea" v-model="form.allergyFoot" placeholder="请输入" maxlength="120" show-word-limit @input="handleTextareaInput" />
  185. </el-form-item>
  186. <el-form-item label="过敏药物" prop="allergyDrug">
  187. <el-input type="textarea" v-model="form.allergyDrug" placeholder="请输入" maxlength="120" show-word-limit @input="handleTextareaInput" />
  188. </el-form-item>
  189. <el-row :gutter="10">
  190. <el-col :span="12">
  191. <el-form-item label="体力活动" prop="activity">
  192. <el-select v-model="form.activity" placeholder="请选择" clearable>
  193. <el-option v-for="dict in physical_activity" :key="dict.value" :label="dict.label" :value="dict.value" />
  194. </el-select>
  195. </el-form-item>
  196. </el-col>
  197. </el-row>
  198. </el-form>
  199. <template #footer>
  200. <div class="dialog-footer">
  201. <el-button :loading="buttonLoading" type="primary" @click="submitForm">保存</el-button>
  202. <el-button @click="cancel">关闭</el-button>
  203. </div>
  204. </template>
  205. </el-dialog>
  206. </div>
  207. </template>
  208. <script setup name="TreatmentUser" lang="ts">
  209. import { listTreatmentUser, getTreatmentUser, delTreatmentUser, addTreatmentUser, updateTreatmentUser } from '@/api/workbench/treatmentUser';
  210. import { listDept } from '@/api/system/dept';
  211. import { listWard } from '@/api/system/ward';
  212. import { TreatmentUserVo, TreatmentUserForm, TreatmentUserQuery } from '@/api/workbench/treatmentUser/types';
  213. import { log } from 'console';
  214. import { get } from 'http';
  215. import { useRouter } from 'vue-router';
  216. const router = useRouter();
  217. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  218. const { treatment_user_type, treatment_user_status, evaluation_status, physical_activity, user_sex } = toRefs < any > (proxy ?.useDict('treatment_user_type', 'treatment_user_status', 'evaluation_status', 'physical_activity', 'user_sex'));
  219. const userList = ref < TreatmentUserVo[] > ([]);
  220. const buttonLoading = ref(false);
  221. const loading = ref(true);
  222. const showSearch = ref(true);
  223. const ids = ref < Array < string | number >> ([]);
  224. const single = ref(true);
  225. const multiple = ref(true);
  226. const total = ref(0);
  227. const treeData = ref([]); // 定义 treeData
  228. const inpatientWardList = ref([]);
  229. // 获取病区列表数据
  230. const getWardList = async () => {
  231. try {
  232. const res = await listWard({
  233. pageNum: 1,
  234. pageSize: 999
  235. });
  236. if (res.rows && Array.isArray(res.rows)) {
  237. inpatientWardList.value = res.rows.map(item => ({
  238. value: item.wardId,
  239. label: item.wardName
  240. }));
  241. }
  242. } catch (error) {
  243. console.error('获取病区列表失败:', error);
  244. inpatientWardList.value = [];
  245. }
  246. };
  247. const waitingCount = ref(0);
  248. const treatingCount = ref(0);
  249. const treatedCount = ref(0);
  250. const reapplyCount = ref(0);
  251. const showStatusSearch = computed(() => activeTab.value !== 'reapply');
  252. // 当前选中的标签
  253. const activeTab = ref('waiting');
  254. // 点击标签切换
  255. const handleTabClick = (tabType: string) => {
  256. activeTab.value = tabType;
  257. if (tabType === 'waiting') {
  258. getList(); // 重新加载数据
  259. } else {
  260. userList.value = [];
  261. }
  262. };
  263. const queryFormRef = ref < ElFormInstance > ();
  264. const userFormRef = ref < ElFormInstance > ();
  265. const dialog = reactive < DialogOption > ({
  266. visible: false,
  267. title: ''
  268. });
  269. const treeProps = ref({
  270. value: 'deptId', // 对应部门的 deptId
  271. label: 'deptName', // 对应部门的 deptName
  272. children: 'children' // 保持原有的父子结构
  273. });
  274. const initFormData: TreatmentUserForm = {
  275. id: undefined,
  276. type: '0',
  277. treatNum: undefined,
  278. outpatientNo: undefined,
  279. doorId: undefined,
  280. treatName: undefined,
  281. sex: undefined,
  282. idCard: undefined,
  283. phoneNum: undefined,
  284. birthday: undefined,
  285. age: undefined,
  286. height: undefined,
  287. weight: undefined,
  288. allergyFoot: undefined,
  289. allergyDrug: undefined,
  290. activity: undefined,
  291. }
  292. const data = reactive < PageData < TreatmentUserForm,
  293. TreatmentUserQuery >> ({
  294. form: { ...initFormData },
  295. queryParams: {
  296. pageNum: 1,
  297. pageSize: 10,
  298. type: '0',
  299. doorId: undefined,
  300. treatNum: undefined,
  301. searchFlag: undefined,
  302. outpatientNo: undefined,
  303. evaluationStatus: undefined,
  304. treatmentUserStatus: undefined,
  305. params: {}
  306. },
  307. // 状态计数(独立 ref)
  308. rules: {
  309. type: [{ required: true, message: '看诊类型为空', trigger: 'blur' }],
  310. doorId: [{ required: true, message: '科室不能为空', trigger: 'blur' }],
  311. treatName: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
  312. sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
  313. idCard: [{ required: true, message: '身份证不能为空', trigger: 'blur' }],
  314. admissionDate: [{
  315. validator: (rule, value, callback) => {
  316. // 只有当 type 为 '1' 时才验证
  317. if (data.form.type === '1' && !value) {
  318. callback(new Error('入院时间不能为空'));
  319. } else {
  320. callback();
  321. }
  322. },
  323. trigger: 'blur'
  324. }]
  325. }
  326. });
  327. const { queryParams, form, rules } = toRefs(data);
  328. /** 查询【待诊患者】列表 */
  329. const getList = async () => {
  330. loading.value = true;
  331. try {
  332. const res = await listTreatmentUser(queryParams.value);
  333. // 获取部门数据
  334. const deptMap = new Map();
  335. treeData.value.forEach(dept => {
  336. deptMap.set(dept.deptId, dept.deptName);
  337. if (dept.children) {
  338. dept.children.forEach(child => {
  339. deptMap.set(child.deptId, child.deptName);
  340. });
  341. }
  342. });
  343. // 为每个患者添加科室名称
  344. userList.value = res.rows.map(patient => ({
  345. ...patient,
  346. deptName: deptMap.get(patient.doorId) || '未知科室'
  347. }));
  348. total.value = res.total;
  349. loading.value = false;
  350. waitingCount.value = res.total;
  351. } catch (error) {
  352. console.error('获取列表失败:', error);
  353. loading.value = false;
  354. }
  355. }
  356. const getDeptList = async () => {
  357. loading.value = true;
  358. try {
  359. const res = await listDept({});
  360. if (!res.data) {
  361. console.warn("部门数据为空");
  362. treeData.value = [];
  363. return;
  364. }
  365. // 处理树形数据
  366. const processedData = proxy ?.handleTree(res.data, 'deptId');
  367. if (!processedData) {
  368. console.warn("树形数据处理失败");
  369. treeData.value = [];
  370. return;
  371. }
  372. treeData.value = processedData;
  373. } catch (error) {
  374. console.error('获取部门列表失败:', error);
  375. treeData.value = [];
  376. } finally {
  377. loading.value = false;
  378. }
  379. };
  380. const getDictLabel = (dictList, value) => {
  381. if (!Array.isArray(dictList)) return value || '--';
  382. const item = dictList.find(item => item.value === value);
  383. return item ? item.label : (value || '--');
  384. };
  385. const handleTextareaInput = () => {
  386. if (form.allergyFoot.length > 120) {
  387. form.allergyFoot = form.allergyFoot.slice(0, 120); // 强制截断
  388. }
  389. if (form.allergyDrug.length > 120) {
  390. form.allergyDrug = form.allergyDrug.slice(0, 120); // 强制截断
  391. }
  392. };
  393. const handleIdCardChange = (idCard: string) => {
  394. if (!validateIdCard(idCard)) {
  395. return;
  396. }
  397. // 当身份证号长度足够时(18位或15位)
  398. if (idCard.length === 18 || idCard.length === 15) {
  399. // 提取出生日期
  400. let birthdayStr = '';
  401. if (idCard.length === 18) {
  402. birthdayStr = idCard.substring(6, 14);
  403. form.value.birthday = `${birthdayStr.substring(0, 4)}-${birthdayStr.substring(4, 6)}-${birthdayStr.substring(6, 8)}`;
  404. } else if (idCard.length === 15) {
  405. birthdayStr = '19' + idCard.substring(6, 12);
  406. form.value.birthday = `${birthdayStr.substring(0, 4)}-${birthdayStr.substring(4, 6)}-${birthdayStr.substring(6, 8)}`;
  407. }
  408. // 计算年龄
  409. if (birthdayStr) {
  410. const birthYear = parseInt(birthdayStr.substring(0, 4));
  411. const birthMonth = parseInt(birthdayStr.substring(4, 6)) - 1;
  412. const birthDay = parseInt(birthdayStr.substring(6, 8));
  413. const today = new Date();
  414. const birthDate = new Date(birthYear, birthMonth, birthDay);
  415. let age = today.getFullYear() - birthDate.getFullYear();
  416. if (today.getMonth() < birthDate.getMonth() || (today.getMonth() === birthDate.getMonth() && today.getDate() < birthDate.getDate())) {
  417. age--;
  418. }
  419. // 设置为"xx岁x月"的格式
  420. form.value.age = `${age}岁${today.getMonth() - birthDate.getMonth()}月`;
  421. }
  422. // 根据身份证倒数第二位判断性别(奇数为男,偶数为女)
  423. const genderNum = parseInt(idCard.length === 18 ? idCard.charAt(16) : idCard.charAt(14));
  424. form.value.sex = (genderNum % 2 === 1 ? '1' : '2').toString();
  425. }
  426. }
  427. const validateIdCard = (idCard: string) => {
  428. // 18位身份证正则
  429. const reg18 = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
  430. // 15位身份证正则
  431. const reg15 = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/;
  432. return reg18.test(idCard) || reg15.test(idCard);
  433. }
  434. /** 取消按钮 */
  435. const cancel = () => {
  436. reset();
  437. dialog.visible = false;
  438. }
  439. /** 表单重置 */
  440. const reset = () => {
  441. form.value = { ...initFormData };
  442. userFormRef.value ?.resetFields();
  443. }
  444. /** 搜索按钮操作 */
  445. const handleQuery = () => {
  446. queryParams.value.pageNum = 1;
  447. getList();
  448. }
  449. /** 重置按钮操作 */
  450. const resetQuery = () => {
  451. queryFormRef.value ?.resetFields();
  452. handleQuery();
  453. }
  454. /** 多选框选中数据 */
  455. const handleSelectionChange = (selection: TreatmentUserVo[]) => {
  456. ids.value = selection.map(item => item.id);
  457. single.value = selection.length != 1;
  458. multiple.value = !selection.length;
  459. }
  460. /** 新增按钮操作 */
  461. const handleAdd = () => {
  462. reset();
  463. dialog.visible = true;
  464. dialog.title = "添加门诊患者";
  465. }
  466. /** 修改按钮操作 */
  467. const handleUpdate = async (row ? : TreatmentUserVo) => {
  468. reset();
  469. const _id = row ?.id || ids.value[0]
  470. const res = await getTreatmentUser(_id);
  471. Object.assign(form.value, res.data);
  472. dialog.visible = true;
  473. dialog.title = "修改门诊患者";
  474. }
  475. /** 提交按钮 */
  476. const submitForm = () => {
  477. userFormRef.value ?.validate(async (valid: boolean) => {
  478. if (valid) {
  479. buttonLoading.value = true;
  480. if (form.value.id) {
  481. await updateTreatmentUser(form.value).finally(() => buttonLoading.value = false);
  482. } else {
  483. await addTreatmentUser(form.value).finally(() => buttonLoading.value = false);
  484. }
  485. proxy ?.$modal.msgSuccess("操作成功");
  486. dialog.visible = false;
  487. await getList();
  488. }
  489. });
  490. }
  491. /** 删除按钮操作 */
  492. const handleDelete = async (row ? : TreatmentUserVo) => {
  493. const _ids = row ?.id || ids.value;
  494. await proxy ?.$modal.confirm('是否确认删除门诊患者编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
  495. await delTreatmentUser(_ids);
  496. proxy ?.$modal.msgSuccess("删除成功");
  497. await getList();
  498. }
  499. /** 导出按钮操作 */
  500. const handleExport = () => {
  501. proxy ?.download('workbench/treatmentUser/export', {
  502. ...queryParams.value
  503. }, `user_${new Date().getTime()}.xlsx`)
  504. }
  505. const getStatusBarClass = (patient) => {
  506. if (patient.treatmentUserStatus === '0') {
  507. return 'need-intervention'
  508. }
  509. return 'not-checked';
  510. };
  511. const getStatusText = (patient) => {
  512. if (patient.treatmentUserStatus === '0') {
  513. return '需进行营养干预'
  514. }
  515. if (patient.treatmentUserStatus === '1') {
  516. return '已过复筛日'
  517. }
  518. if (patient.treatmentUserStatus === '2') {
  519. return '待筛查'
  520. }
  521. if (patient.treatmentUserStatus === '3') {
  522. return '未筛查'
  523. }
  524. if (patient.treatmentUserStatus === '4') {
  525. return '已筛查'
  526. }
  527. if (patient.treatmentUserStatus === '5') {
  528. return '复筛提醒'
  529. }
  530. if (patient.treatmentUserStatus === '6') {
  531. return '今日复筛'
  532. }
  533. return '';
  534. };
  535. const getAge = (birthday) => {
  536. if (!birthday) return 0;
  537. const birthDate = new Date(birthday);
  538. const today = new Date();
  539. let age = today.getFullYear() - birthDate.getFullYear();
  540. if (today.getMonth() < birthDate.getMonth() || (today.getMonth() === birthDate.getMonth() && today.getDate() < birthDate.getDate())) {
  541. age--;
  542. }
  543. return age;
  544. };
  545. const getMonths = (birthday) => {
  546. if (!birthday) return 0;
  547. const birthDate = new Date(birthday);
  548. const today = new Date();
  549. const months = (today.getFullYear() - birthDate.getFullYear()) * 12 +
  550. (today.getMonth() - birthDate.getMonth());
  551. return months % 12;
  552. };
  553. onMounted(() => {
  554. getList();
  555. getDeptList(); // 初始化时加载部门数据
  556. getWardList(); // 初始化时加载病区数据
  557. });
  558. const detailData = ref({});
  559. const toPatients = (patient: TreatmentUserVo) => {
  560. router.push({
  561. path: '/patients/medicalRecord',
  562. query: {
  563. id: patient.id,
  564. type: patient.type,
  565. treatName: patient.treatName,
  566. outpatientNo: patient.outpatientNo
  567. }
  568. });
  569. };
  570. </script>
  571. <style scoped>
  572. .item {
  573. cursor: pointer;
  574. padding: 8px 12px;
  575. border-radius: 4px;
  576. transition: all 0.3s;
  577. }
  578. .item:hover {
  579. background-color: #f5f5f5;
  580. }
  581. .active-tab {
  582. font-weight: 300;
  583. /* 激活标签文字加粗 */
  584. border-bottom: 6px solid var(--el-color-primary);
  585. padding-bottom: 6px;
  586. /* 调整下划线间距 */
  587. }
  588. .active-tab .el-badge__content {
  589. background-color: white;
  590. color: var(--el-color-primary);
  591. }
  592. .fade-enter-active,
  593. .fade-leave-active {
  594. transition: opacity 0.3s;
  595. }
  596. .fade-enter-from,
  597. .fade-leave-to {
  598. opacity: 0;
  599. }
  600. .word-count {
  601. text-align: right;
  602. font-size: 12px;
  603. color: #999;
  604. margin-top: 4px;
  605. }
  606. .patient-cards-container {
  607. margin-top: 20px;
  608. }
  609. .patient-card {
  610. margin-bottom: 20px;
  611. border: 1px solid #e4e7ed;
  612. transition: all 0.3s;
  613. position: relative;
  614. overflow: hidden;
  615. border-radius: 4px;
  616. height: 100%;
  617. }
  618. .patient-card:hover {
  619. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  620. }
  621. .patient-status-bar {
  622. position: absolute;
  623. left: 0;
  624. top: 0;
  625. bottom: 0;
  626. width: 32px;
  627. display: flex;
  628. align-items: center;
  629. justify-content: center;
  630. color: white;
  631. font-size: 16px;
  632. writing-mode: vertical-lr;
  633. text-orientation: upright;
  634. letter-spacing: 4px;
  635. text-align: center;
  636. padding: 10px 0;
  637. }
  638. .patient-status-bar.need-intervention {
  639. background-color: #ff4949;
  640. }
  641. .patient-status-bar.not-checked {
  642. background-color: #409eff;
  643. }
  644. .patient-info {
  645. padding: 15px 15px 15px 42px;
  646. margin-left: 32px;
  647. }
  648. .patient-header {
  649. display: flex;
  650. justify-content: space-between;
  651. align-items: center;
  652. margin-bottom: 10px;
  653. }
  654. .patient-name-age {
  655. display: flex;
  656. align-items: center;
  657. gap: 60px;
  658. }
  659. .patient-name {
  660. font-size: 18px;
  661. font-weight: bold;
  662. }
  663. .patient-age {
  664. font-size: 18px;
  665. color: #606266;
  666. white-space: nowrap;
  667. }
  668. .gender-icon {
  669. font-size: 25px;
  670. font-weight: bold;
  671. }
  672. .gender-icon.female {
  673. color: #ff4949;
  674. }
  675. .gender-icon.male {
  676. color: #409eff;
  677. }
  678. .patient-detail {
  679. margin: 8px 0;
  680. font-size: 14px;
  681. color: #606266;
  682. }
  683. .detail-label {
  684. color: #909399;
  685. margin-right: 4px;
  686. font-size: 16px;
  687. }
  688. .evaluation-info {
  689. display: flex;
  690. justify-content: space-between;
  691. align-items: center;
  692. margin-top: 12px;
  693. font-size: 16px;
  694. position: relative;
  695. min-height: 24px;
  696. }
  697. .evaluation-score {
  698. flex: 1;
  699. font-size: 16px;
  700. }
  701. .evaluation-status {
  702. padding: 2px 8px;
  703. border-radius: 4px;
  704. position: absolute;
  705. right: 0;
  706. font-size: 16px;
  707. }
  708. .evaluation-status.evaluated {
  709. color: #67c23a;
  710. background-color: #f0f9eb;
  711. }
  712. .evaluation-status.not-evaluated {
  713. color: #909399;
  714. background-color: #f4f4f5;
  715. }
  716. .status-text {
  717. writing-mode: vertical-lr;
  718. text-orientation: mixed;
  719. white-space: nowrap;
  720. color: #fff;
  721. font-size: 14px;
  722. letter-spacing: 1px;
  723. }
  724. .search-form {
  725. margin-bottom: 20px;
  726. }
  727. .search-form .el-form-item {
  728. margin-bottom: 18px;
  729. margin-right: 18px;
  730. }
  731. .search-form .el-input,
  732. .search-form .el-select,
  733. .search-form .el-tree-select {
  734. width: 200px;
  735. }
  736. .search-form .el-button {
  737. margin-left: 10px;
  738. }
  739. .status-tabs {
  740. display: flex;
  741. border-bottom: 1px solid #e4e7ed;
  742. margin-bottom: 20px;
  743. padding: 0 25px;
  744. }
  745. .tab-item {
  746. position: relative;
  747. padding: 0 20px 16px;
  748. font-size: 22px;
  749. cursor: pointer;
  750. color: #606266;
  751. margin-right: 32px;
  752. }
  753. .tab-item.active {
  754. color: var(--el-color-primary);
  755. font-weight: 500;
  756. }
  757. .tab-item.active::after {
  758. content: '';
  759. position: absolute;
  760. bottom: -1px;
  761. left: 0;
  762. width: 100%;
  763. height: 6px;
  764. background-color: var(--el-color-primary);
  765. border-radius: 3px 3px 0 0;
  766. }
  767. .tab-badge {
  768. position: absolute;
  769. top: -8px;
  770. right: 0;
  771. transform: translateX(50%);
  772. }
  773. .tab-badge :deep(.el-badge__content) {
  774. background-color: #ff4949;
  775. }
  776. .empty-state {
  777. display: flex;
  778. flex-direction: column;
  779. align-items: center;
  780. justify-content: center;
  781. padding: 40px 0;
  782. color: #909399;
  783. }
  784. .empty-state img {
  785. width: 160px;
  786. height: 160px;
  787. margin-bottom: 16px;
  788. }
  789. </style>