index.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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="85">
  7. <el-form-item label="开方时间" prop="orderTime">
  8. <el-date-picker v-model="queryParams.dateRange" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
  9. </el-form-item>
  10. <el-form-item label="看诊类型:">
  11. <el-select v-model="queryParams.visitType">
  12. <el-option v-for="dict in treatment_user_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  13. </el-select>
  14. </el-form-item>
  15. <el-form-item label="科室" prop="doorId">
  16. <el-tree-select v-model="queryParams.doorId" :data="treeData" :props="treeProps" placeholder="请选择" check-strictly node-key="id" @keyup.enter="handleQuery" />
  17. </el-form-item>
  18. <el-form-item label="审核状态" prop="checkStatus">
  19. <el-select v-model="queryParams.checkStatus">
  20. <el-option v-for="dict in check_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="处方类型" prop="recipeType">
  24. <el-select v-model="queryParams.chargeType">
  25. <el-option v-for="dict in fee_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  26. </el-select>
  27. </el-form-item>
  28. <el-form-item>
  29. <el-input v-model="queryParams.searchValue" placeholder="医生姓名/门诊号/住院号" style="width: 240px; " clearable />
  30. </el-form-item>
  31. <el-form-item>
  32. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  33. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  34. </el-form-item>
  35. </el-form>
  36. </el-card>
  37. </div>
  38. </transition>
  39. <el-card shadow="never">
  40. <span>待审核数量:<span style="color:red">{{total}}</span></span>
  41. <el-table v-loading="loading" border :data="settlementList" @selection-change="handleSelectionChange">
  42. <el-table-column type="selection" width="55" align="center" />
  43. <el-table-column label="开方时间" align="center" prop="orderTime" width="180">
  44. <template #default="scope">
  45. <span>{{ parseTime(scope.row.orderTime, '{y}-{m}-{d}') }}</span>
  46. </template>
  47. </el-table-column>
  48. <el-table-column label="开方医生" align="center" prop="createByUser" />
  49. <el-table-column label="处方ID" align="center" prop="id" />
  50. <el-table-column label="处方类型" align="center" prop="chargeType">
  51. <template #default="scope">
  52. <span>{{ getDictLabel(fee_type, scope.row.chargeType) || '--' }}</span>
  53. </template>
  54. </el-table-column>
  55. <!-- <el-table-column label="处方类型" align="center" prop="recipeType">
  56. <template #default="scope">
  57. <span>{{ getDictLabel(recipe_type, scope.row.recipeType) || '--' }}</span>
  58. </template>
  59. </el-table-column> -->
  60. <el-table-column label="处方明细" align="center" prop="recipeDetail" />
  61. <el-table-column label="患者科室" align="center" prop="patientDepartment" />
  62. <el-table-column label="患者姓名" align="center" prop="patientName" />
  63. <el-table-column label="看诊类型" align="center" prop="visitType">
  64. <template #default="scope">
  65. <span>{{ getDictLabel(treatment_user_type, scope.row.visitType) || '--' }}</span>
  66. </template>
  67. </el-table-column>
  68. <el-table-column label="身份证号" align="center" prop="idCard" />
  69. <el-table-column label="诊疗卡号" align="center" prop="treatNum" />
  70. <el-table-column label="住院/门诊号" align="center" prop="patientNo" />
  71. <el-table-column label="病区" align="center" prop="wardName">
  72. <template #default="scope">
  73. <span>{{ scope.row.wardName || '--' }}</span>
  74. </template>
  75. </el-table-column>
  76. <el-table-column label="床号" align="center" prop="bedNo">
  77. <template #default="scope">
  78. <span>{{ scope.row.bedNo || '--' }}</span>
  79. </template>
  80. </el-table-column>
  81. <el-table-column label="审核状态" align="center" prop="checkStatus">
  82. <template #default="scope">
  83. <span>{{ getDictLabel(check_status, scope.row.checkStatus) || '--' }}</span>
  84. </template>
  85. </el-table-column>
  86. <el-table-column label="审核医生" align="center" prop="updateByUser">
  87. <template #default="scope">
  88. <span>{{ scope.row.updateByUser || '--' }}</span>
  89. </template>
  90. </el-table-column>
  91. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  92. <template #default="scope">
  93. <div v-if="scope.row.checkStatus=='1'||scope.row.checkStatus=='3'">
  94. <el-tooltip content="审核" placement="top">
  95. <el-button link type="primary" @click="handleCheck(scope.row)" v-hasPermi="['system:settlement:edit']">审核</el-button>
  96. </el-tooltip>
  97. </div>
  98. <div v-else>
  99. <el-tooltip content="详情" placement="top">
  100. <el-button link type="primary" @click="handleDetail(scope.row)" v-hasPermi="['system:settlement:edit']">详情</el-button>
  101. </el-tooltip>
  102. </div>
  103. </template>
  104. </el-table-column>
  105. </el-table>
  106. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
  107. </el-card>
  108. <!-- 审核处方对话框 -->
  109. <el-dialog :title="dialog.title" v-model="dialog.visible" width="65%" append-to-body @close="cancel">
  110. <!-- 患者信息 -->
  111. <div class="patient-info">
  112. <el-row :gutter="20">
  113. <el-col :span="5">
  114. <div class="info-item">
  115. <span class="label">开方日期:</span>
  116. <span class="value">{{ parseTime(form.orderTime, '{y}-{m}-{d}') || '' }}</span>
  117. </div>
  118. </el-col>
  119. <el-col :span="5">
  120. <div class="info-item">
  121. <span class="label">患者姓名:</span>
  122. <span class="value">{{ form.patientName || '' }}</span>
  123. </div>
  124. </el-col>
  125. <el-col :span="5">
  126. <div class="info-item">
  127. <span class="label">身份证号:</span>
  128. <span class="value">{{ form.idCard || '469026200211076747' }}</span>
  129. </div>
  130. </el-col>
  131. <el-col :span="4">
  132. <div class="info-item">
  133. <span class="label">看诊类型:</span>
  134. <span class="value">{{ getDictLabel(treatment_user_type, form.visitType) || '住院' }}</span>
  135. </div>
  136. </el-col>
  137. <el-col :span="5">
  138. <div class="info-item">
  139. <span class="label">诊疗卡号:</span>
  140. <span class="value">{{ form.treatNum || '' }}</span>
  141. </div>
  142. </el-col>
  143. </el-row>
  144. <el-row :gutter="20">
  145. <el-col :span="5">
  146. <div class="info-item">
  147. <span class="label">门诊/住院号:</span>
  148. <span class="value">{{ form.patientNo || '' }}</span>
  149. </div>
  150. </el-col>
  151. <el-col :span="5">
  152. <div class="info-item">
  153. <span class="label">患者科室:</span>
  154. <span class="value">{{ form.patientDepartment || '' }}</span>
  155. </div>
  156. </el-col>
  157. <el-col :span="5">
  158. <div class="info-item">
  159. <span class="label">处方类型:</span>
  160. <span class="value">{{ getDictLabel(fee_type, form.chargeType) || '' }}</span>
  161. </div>
  162. </el-col>
  163. <el-col :span="5">
  164. <div class="info-item">
  165. <span class="label">病区:</span>
  166. <span class="value">{{ form.wardName || '--' }}</span>
  167. </div>
  168. </el-col>
  169. </el-row>
  170. </div>
  171. <!-- 处方明细表格 -->
  172. <!-- 处方表格 -->
  173. <div class="prescription-table-wrapper">
  174. <table class="prescription-table">
  175. <thead>
  176. <tr>
  177. <th>组号</th>
  178. <th>产品名称</th>
  179. <th>规格</th>
  180. <th>用量/次</th>
  181. <th>流量/次(ml)</th>
  182. <th>浓度/次(%)</th>
  183. <th>能量密度/次(kcal/ml)</th>
  184. <th>餐次时间</th>
  185. <th>频次</th>
  186. <th>首日</th>
  187. <th>使用天数</th>
  188. <th>用法</th>
  189. <th>处方备注</th>
  190. </tr>
  191. </thead>
  192. <tbody>
  193. <tr v-for="(product, index) in recipeDetailData" :key="index">
  194. <td>{{ Number(product.groupNo) + 1 || '--' }}</td>
  195. <td>{{ product.nutritionProduct || '--' }}</td>
  196. <td>{{ product.specification || '--' }}</td>
  197. <td>{{ product.dosagePerTime || '--' }}</td>
  198. <td>{{ product.preparationVolumePerTime || '--' }}</td>
  199. <td>{{ product.preparationConcentrationPerTime || '--' }}</td>
  200. <td>{{ product.energyDensityPerTime || '--' }}</td>
  201. <td>{{ product.mealTime || '--' }}</td>
  202. <td>{{ product.frequency || '--' }}</td>
  203. <td>{{ product.firstDay || '--' }}</td>
  204. <td>{{ product.usageDays || '--' }}</td>
  205. <td>{{ product.usage || '--' }}</td>
  206. <td>{{ product.prescriptionRemark || '--' }}</td>
  207. </tr>
  208. <!-- 无数据提示 -->
  209. <tr v-if="recipeDetailData.length === 0">
  210. <td colspan="14" style="text-align: center; color: #909399">暂无处方数据</td>
  211. </tr>
  212. </tbody>
  213. </table>
  214. </div>
  215. <template #footer v-if="buttonShow.visible">
  216. <div class="dialog-footer">
  217. <el-button :loading="buttonLoading" @click="handleRecalculate">处方重申</el-button>
  218. <el-button :loading="buttonLoading" type="primary" @click="handleApprove">审核通过</el-button>
  219. </div>
  220. </template>
  221. </el-dialog>
  222. <!-- 处方重审dialog框 -->
  223. <el-dialog :title="reCheckDialog.title" v-model="reCheckDialog.visible" width="20%" append-to-body >
  224. <el-input v-model="form.remark" type="textarea" placeholder="请输入重审原因" :rows="5"></el-input>
  225. <template #footer>
  226. <div class="dialog-footer">
  227. <el-button :loading="buttonLoading" @click="handleRecheck">提交</el-button>
  228. </div>
  229. </template>
  230. </el-dialog>
  231. </div>
  232. </template>
  233. <script setup name="Settlement" lang="ts">
  234. import { listRecipeCheck, getSettlement, delSettlement, addSettlement, updateSettlement } from '@/api/patients/settlement';
  235. import { SettlementVO, SettlementQuery, SettlementForm } from '@/api/patients/settlement/types';
  236. import { listDept } from '@/api/system/dept'; // 部门列表
  237. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  238. const { treatment_user_type, check_status, recipe_type, fee_type } = toRefs < any > (proxy ?.useDict('treatment_user_type', 'check_status', 'recipe_type', 'fee_type'));
  239. const settlementList = ref < SettlementVO[] > ([]);
  240. const buttonLoading = ref(false);
  241. const loading = ref(true);
  242. const showSearch = ref(true);
  243. const ids = ref < Array < string | number >> ([]);
  244. const single = ref(true);
  245. const multiple = ref(true);
  246. const total = ref(0);
  247. const treeData = ref([]); // 定义 treeData
  248. const queryFormRef = ref < ElFormInstance > ();
  249. const settlementFormRef = ref < ElFormInstance > ();
  250. // 处方明细数据
  251. const recipeDetailData = ref([]);
  252. const dialog = reactive < DialogOption > ({
  253. visible: false,
  254. title: ''
  255. });
  256. // 处方重审
  257. const reCheckDialog = reactive < DialogOption > ({
  258. visible: false,
  259. title: ''
  260. });
  261. const buttonShow = reactive < DialogOption > ({
  262. visible: true,
  263. title: ''
  264. });
  265. const initFormData: SettlementForm = {
  266. id: undefined,
  267. orderTime: undefined,
  268. visitType: undefined,
  269. chargeType: undefined,
  270. patientId: undefined,
  271. patientName: undefined,
  272. patientDepartment: undefined,
  273. patientNo: undefined,
  274. phone: undefined,
  275. idCard: undefined,
  276. receivableAmount: undefined,
  277. paymentStatus: undefined,
  278. paymentMethod: undefined,
  279. status: undefined,
  280. wardName: undefined,
  281. checkStatus: undefined,
  282. treatNum: undefined
  283. }
  284. const data = reactive < PageData < SettlementForm,
  285. SettlementQuery >> ({
  286. form: { ...initFormData },
  287. queryParams: {
  288. pageNum: 1,
  289. pageSize: 10,
  290. orderTime: undefined,
  291. visitType: undefined,
  292. chargeType: undefined,
  293. doorId: undefined,
  294. checkStatus: '1',
  295. searchValue: undefined,
  296. dateRange: [],
  297. params: {}
  298. },
  299. rules: {
  300. id: [
  301. { required: true, message: "主键ID不能为空", trigger: "blur" }
  302. ],
  303. }
  304. });
  305. const { queryParams, form, rules } = toRefs(data);
  306. const treeProps = ref({
  307. value: 'deptId', // 对应部门的 deptId
  308. label: 'deptName', // 对应部门的 deptName
  309. children: 'children' // 保持原有的父子结构
  310. });
  311. /** 查询结算管理列表 */
  312. const getList = async () => {
  313. loading.value = true;
  314. const res = await listRecipeCheck(queryParams.value);
  315. settlementList.value = res.rows;
  316. // 获取部门数据
  317. const deptMap = new Map();
  318. treeData.value.forEach(dept => {
  319. deptMap.set(dept.deptId, dept.deptName);
  320. if (dept.children) {
  321. dept.children.forEach(child => {
  322. deptMap.set(child.deptId, child.deptName);
  323. });
  324. }
  325. });
  326. total.value = res.total;
  327. loading.value = false;
  328. }
  329. /** 取消按钮 */
  330. const cancel = () => {
  331. reset();
  332. dialog.visible = false;
  333. buttonShow.visible = true;
  334. }
  335. const getDeptList = async () => {
  336. loading.value = true;
  337. try {
  338. const res = await listDept({ pageNum: 1, pageSize: 999 });
  339. if (!res.data) {
  340. console.warn("部门数据为空");
  341. treeData.value = [];
  342. return;
  343. }
  344. // 处理树形数据
  345. const processedData = proxy ?.handleTree(res.data, 'deptId');
  346. if (!processedData) {
  347. console.warn("树形数据处理失败");
  348. treeData.value = [];
  349. return;
  350. }
  351. treeData.value = processedData;
  352. } catch (error) {
  353. console.error('获取部门列表失败:', error);
  354. treeData.value = [];
  355. } finally {
  356. loading.value = false;
  357. }
  358. };
  359. // 字典label工具
  360. function getDictLabel(dictList: any[], value: string) {
  361. if (!dictList || !Array.isArray(dictList)) return value || '--';
  362. const found = dictList.find((item) => item.value === value);
  363. return found ? found.label : value || '--';
  364. }
  365. /** 表单重置 */
  366. const reset = () => {
  367. form.value = { ...initFormData };
  368. settlementFormRef.value ?.resetFields();
  369. }
  370. /** 搜索按钮操作 */
  371. const handleQuery = () => {
  372. queryParams.value.pageNum = 1;
  373. getList();
  374. }
  375. /** 重置按钮操作 */
  376. const resetQuery = () => {
  377. queryFormRef.value ?.resetFields();
  378. queryParams.value.dateRange = undefined;
  379. queryParams.value.searchValue = undefined;
  380. queryParams.value.visitType = undefined;
  381. queryParams.value.chargeType = undefined;
  382. queryParams.value.doorId = undefined;
  383. queryParams.value.pageNum = 1;
  384. queryParams.value.pageSize = 10;
  385. handleQuery();
  386. }
  387. /** 多选框选中数据 */
  388. const handleSelectionChange = (selection: SettlementVO[]) => {
  389. ids.value = selection.map(item => item.id);
  390. single.value = selection.length != 1;
  391. multiple.value = !selection.length;
  392. }
  393. /** 新增按钮操作 */
  394. const handleAdd = () => {
  395. reset();
  396. dialog.visible = true;
  397. dialog.title = "添加结算管理";
  398. }
  399. /** 审核操作 */
  400. const handleCheck = async (row ? : SettlementVO) => {
  401. reset();
  402. const _id = row ?.id || ids.value[0]
  403. const res = await getSettlement(_id);
  404. recipeDetailData.value = res.data.enteralNutritionList
  405. Object.assign(form.value, res.data);
  406. dialog.visible = true;
  407. dialog.title = "处方审核";
  408. }
  409. /** 详情 */
  410. const handleDetail = async (row ? : SettlementVO) => {
  411. reset();
  412. const _id = row ?.id || ids.value[0]
  413. const res = await getSettlement(_id);
  414. recipeDetailData.value = res.data.enteralNutritionList
  415. Object.assign(form.value, res.data);
  416. buttonShow.visible = false
  417. dialog.visible = true;
  418. dialog.title = "处方详情";
  419. }
  420. /** 提交按钮 */
  421. const submitForm = () => {
  422. settlementFormRef.value ?.validate(async (valid: boolean) => {
  423. if (valid) {
  424. buttonLoading.value = true;
  425. if (form.value.id) {
  426. await updateSettlement(form.value).finally(() => buttonLoading.value = false);
  427. } else {
  428. await addSettlement(form.value).finally(() => buttonLoading.value = false);
  429. }
  430. proxy ?.$modal.msgSuccess("操作成功");
  431. dialog.visible = false;
  432. await getList();
  433. }
  434. });
  435. }
  436. /** 删除按钮操作 */
  437. const handleDelete = async (row ? : SettlementVO) => {
  438. const _ids = row ?.id || ids.value;
  439. await proxy ?.$modal.confirm('是否确认删除结算管理编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
  440. await delSettlement(_ids);
  441. proxy ?.$modal.msgSuccess("删除成功");
  442. await getList();
  443. }
  444. /** 处方重审 */
  445. const handleRecalculate = async () => {
  446. reCheckDialog.visible = true;
  447. reCheckDialog.title = "重审原因";
  448. }
  449. /** 新增按钮操作 */
  450. const handleRecheck = async () => {
  451. form.value.checkStatus = "3" //处方重审
  452. await updateSettlement(form.value).finally(() => buttonLoading.value = false);
  453. reCheckDialog.visible=false
  454. dialog.visible=false
  455. proxy ?.$modal.msgSuccess("操作成功");
  456. await getList();
  457. }
  458. /** 审核通过 */
  459. const handleApprove = async () => {
  460. form.value.checkStatus = "2" //审核通过
  461. await proxy ?.$modal.confirm('是否确定审核通过该条处方?').finally(() => loading.value = false);
  462. await updateSettlement(form.value).finally(() => dialog.visible = false);
  463. proxy ?.$modal.msgSuccess("操作成功");
  464. await getList();
  465. }
  466. onMounted(() => {
  467. getList();
  468. getDeptList(); // 初始化时加载部门数据
  469. });
  470. </script>
  471. <style lang="scss" scoped>
  472. .patient-info {
  473. background-color: #f5f7fa;
  474. padding: 16px;
  475. border-radius: 4px;
  476. margin-bottom: 20px;
  477. }
  478. .info-item {
  479. display: flex;
  480. margin-bottom: 12px;
  481. }
  482. .info-item .label {
  483. font-weight: bold;
  484. color: #606266;
  485. min-width: 100px;
  486. flex-shrink: 0;
  487. }
  488. .info-item .value {
  489. color: #303133;
  490. flex: 1;
  491. }
  492. .recipe-detail {
  493. margin-top: 20px;
  494. }
  495. .dialog-footer {
  496. text-align: center;
  497. }
  498. .dialog-footer .el-button {
  499. margin: 0 10px;
  500. }
  501. .prescription-table-wrapper {
  502. overflow-x: auto;
  503. .prescription-table {
  504. width: 100%;
  505. border-collapse: collapse;
  506. border: 1px solid #ddd;
  507. background: white;
  508. font-size: 14px;
  509. th,
  510. td {
  511. border: 1px solid #ddd;
  512. padding: 10px 12px;
  513. text-align: center;
  514. vertical-align: middle;
  515. min-width: 90px;
  516. white-space: nowrap;
  517. overflow: hidden;
  518. text-overflow: ellipsis;
  519. }
  520. th {
  521. background: #e8f4fd !important;
  522. color: #303133 !important;
  523. font-weight: 600 !important;
  524. font-size: 14px !important;
  525. height: 40px !important;
  526. }
  527. td {
  528. color: #303133;
  529. font-size: 14px;
  530. height: 36px;
  531. &:first-child {
  532. font-weight: 500;
  533. }
  534. }
  535. // 斑马纹
  536. tbody tr:nth-child(even) {
  537. background: #fafbfc;
  538. }
  539. // 悬停效果
  540. tbody tr:hover {
  541. background: #f0f9ff;
  542. }
  543. }
  544. }
  545. </style>