chenying2100 4 هفته پیش
والد
کامیت
bee0bd54c6
31فایلهای تغییر یافته به همراه6035 افزوده شده و 2167 حذف شده
  1. 200 0
      .specstory/history/2025-07-14_04-02Z-修改-index-vue-以实现宫格按钮样式.md
  2. 71 0
      prompt/info.txt
  3. 22 6
      src/api/patients/nutrition/index.ts
  4. 43 11
      src/api/patients/nutrition/types.ts
  5. 8 0
      src/api/system/screeningAssessmentConfig/index.ts
  6. 33 1
      src/api/system/screeningAssessmentConfig/types.ts
  7. BIN
      src/assets/images/shiXiao.png
  8. BIN
      src/assets/images/zuoFei.png
  9. 26 0
      src/store/modules/history.ts
  10. 8 8
      src/store/modules/permission.ts
  11. 19 0
      src/store/modules/productCategory.ts
  12. 196 31
      src/views/patients/enteralNutrition/components/LeftCard.vue
  13. 70 47
      src/views/patients/enteralNutrition/components/history/LeftCard.vue
  14. 563 157
      src/views/patients/enteralNutrition/components/history/RightCard.vue
  15. 227 130
      src/views/patients/enteralNutrition/components/table/ConfigureTable.vue
  16. 498 83
      src/views/patients/enteralNutrition/components/table/MaterialTable.vue
  17. 837 0
      src/views/patients/enteralNutrition/components/table/MaterialTable2.vue
  18. 395 110
      src/views/patients/enteralNutrition/components/table/NutritionTable.vue
  19. 477 78
      src/views/patients/enteralNutrition/components/table/PackageTable.vue
  20. 4 6
      src/views/patients/enteralNutrition/history.vue
  21. 3 3
      src/views/patients/enteralNutrition/index.vue
  22. 72 8
      src/views/patients/evaluation/add.vue
  23. 5 0
      src/views/patients/evaluation/index.vue
  24. 575 0
      src/views/patients/hospitalRecord/index.vue
  25. 279 300
      src/views/patients/medicalRecord/index.vue
  26. 243 249
      src/views/patients/nutritionSetting/addForm.vue
  27. 93 92
      src/views/patients/nutritionSetting/index.vue
  28. 617 628
      src/views/patients/screening/add.vue
  29. 95 97
      src/views/patients/screening/index.vue
  30. 147 108
      src/views/system/screeningAssessmentConfig/add.vue
  31. 209 14
      src/views/system/screeningAssessmentConfig/index.vue

+ 200 - 0
.specstory/history/2025-07-14_04-02Z-修改-index-vue-以实现宫格按钮样式.md

@@ -125,7 +125,15 @@ _**Assistant**_
 
 ---
 
+<details><summary>Edit file: src/views/patients/screening/index.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 12, lines removed: 3
+
 ```diff
+@@ -17,7 +17,16 @@
               </el-form-item>
               <el-form-item label="筛查类型" prop="configId">
 -               <el-select v-model="queryParams.configId" placeholder="请选择" clearable>
@@ -147,7 +155,11 @@ _**Assistant**_
               <el-form-item prop="patientNo">
 ```
 
+**Chunk 2**
+Lines added: 12, lines removed: 1
+
 ```diff
+@@ -92,5 +101,16 @@
           </el-form-item>
           <el-form-item label="营养筛查类型" prop="configId">
 -           <el-input v-model="form.configId" placeholder="请输入营养筛查类型" />
@@ -167,7 +179,11 @@ _**Assistant**_
           <el-form-item label="筛查时间" prop="screeningTime">
 ```
 
+**Chunk 3**
+Lines added: 14, lines removed: undefined
+
 ```diff
+@@ -303,3 +323,17 @@
   });
   </script>
 + 
@@ -187,6 +203,8 @@ _**Assistant**_
   
 ```
 
+</details>
+
 ---
 
 已将“营养筛查类型”表单项改为宫格按钮选择,样式与图片类似。你现在可以在弹窗表单中直接点击按钮选择筛查类型,选中项会高亮显示,体验与图片一致。
@@ -506,7 +524,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 14, lines removed: 9
+
 ```diff
+@@ -17,16 +17,21 @@
                   <el-row>
                       <el-col :span="24">
 -                         * 年龄
@@ -539,7 +565,11 @@ _**Assistant**_
                   </el-row>
 ```
 
+**Chunk 2**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -58,4 +63,5 @@
     import { ScreeningVO, ScreeningQuery, ScreeningForm } from '@/api/patients/screening/types';
     import {  getScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig'; 
 +   import { ref } from 'vue'
@@ -547,7 +577,11 @@ _**Assistant**_
     const { proxy } = getCurrentInstance() as ComponentInternalInstance; 
 ```
 
+**Chunk 3**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -66,4 +72,5 @@
      
     let form=ref({});   
 +   const age = ref('<50')
@@ -555,7 +589,11 @@ _**Assistant**_
     
 ```
 
+**Chunk 4**
+Lines added: 13, lines removed: undefined
+
 ```diff
+@@ -108,4 +115,17 @@
       margin-left: ;
     }
 +   .el-radio-group {
@@ -575,9 +613,19 @@ _**Assistant**_
     
 ```
 
+</details>
+
 ---
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: undefined, lines removed: 1
+
 ```diff
+@@ -113,5 +113,4 @@
       height: 48px;
       border-radius: 8px;
 -     margin-left: ;
@@ -585,6 +633,8 @@ _**Assistant**_
     .el-radio-group {
 ```
 
+</details>
+
 ---
 
 已将“年龄”部分修改为与图片一致的竖直单选框样式,左侧有红色星号,选项为 `<50`、`50~74`、`≥75`,选中项为蓝色下划线,并修复了样式错误。
@@ -601,7 +651,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -23,4 +23,5 @@
                   <el-row>
                       <el-col :span="24">
 +                         <div class="radio-card">
@@ -609,7 +667,11 @@ _**Assistant**_
                                 <el-radio :label="'<50'">
 ```
 
+**Chunk 2**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -34,4 +35,5 @@
                                 </el-radio>
                             </el-radio-group>
 +                         </div>
@@ -617,7 +679,11 @@ _**Assistant**_
                   </el-row>
 ```
 
+**Chunk 3**
+Lines added: 8, lines removed: undefined
+
 ```diff
+@@ -114,4 +116,12 @@
       border-radius: 8px;
     }
 +   .radio-card {
@@ -632,7 +698,11 @@ _**Assistant**_
       display: flex;
 ```
 
+**Chunk 4**
+Lines added: 5, lines removed: 1
+
 ```diff
+@@ -120,5 +130,9 @@
     }
     .el-radio {
 -     margin-bottom: 12px;
@@ -645,6 +715,8 @@ _**Assistant**_
     .blue-underline {
 ```
 
+</details>
+
 ---
 
 已将年龄单选框部分的样式调整如下:
@@ -665,7 +737,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 9, lines removed: 1
+
 ```diff
+@@ -42,5 +42,13 @@
                   <el-row>
                       <el-col :span="24">
 - AAA
@@ -682,7 +762,11 @@ _**Assistant**_
                   </el-row>
 ```
 
+**Chunk 2**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -75,4 +83,5 @@
     let form=ref({});   
     const age = ref('<50')
 +   const exerciseFreq = ref('无')
@@ -690,7 +774,11 @@ _**Assistant**_
     
 ```
 
+**Chunk 3**
+Lines added: 11, lines removed: undefined
+
 ```diff
+@@ -131,4 +140,15 @@
       margin-bottom: 0;
     }
 +   .horizontal-radio-group {
@@ -708,6 +796,8 @@ _**Assistant**_
     </style>
 ```
 
+</details>
+
 ---
 
 已将 AAA、BBB、CCC 替换为“1.平均运动次数是”横向单选题,选项为“无、偶尔、1-2次/周、3-4次/周、≥5次/周”,并加上红色星号和题号,样式与图片一致(横向排列,间距合适)。
@@ -796,7 +886,15 @@ const malnutritionSigns = ref<string[]>([])
 
 ---
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 16, lines removed: 11
+
 ```diff
+@@ -49,16 +49,21 @@
                           </el-row>
   
 -                         <el-row style="margin-bottom: 10px; border-top: 1px dashed  #e5e7eb; padding-top: 20px;">
@@ -831,7 +929,11 @@ const malnutritionSigns = ref<string[]>([])
   </el-row>
 ```
 
+**Chunk 2**
+Lines added: 2, lines removed: undefined
+
 ```diff
+@@ -122,4 +127,6 @@
     let form=ref<ScreeningAssessmentConfigForm>({});   
    
 +   const malnutritionSigns = ref<string[]>([])
@@ -840,7 +942,11 @@ const malnutritionSigns = ref<string[]>([])
     
 ```
 
+**Chunk 3**
+Lines added: 10, lines removed: undefined
+
 ```diff
+@@ -189,4 +196,14 @@
       margin-left: 0;
     }
 +   .horizontal-checkbox-group {
@@ -857,6 +963,8 @@ const malnutritionSigns = ref<string[]>([])
     </style>
 ```
 
+</details>
+
 ---
 
 1.2题已改为横向排列的多选框,题号加粗,选项与图片一致,间距适中,自动换行,整体左对齐。  
@@ -872,7 +980,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 2, lines removed: undefined
+
 ```diff
+@@ -18,4 +18,6 @@
                           <span style="color: #f56c6c; margin-right: 4px;">*</span>
                           <span style="font-weight: bold;">年龄</span>
 +                     </el-col>
@@ -881,7 +997,11 @@ _**Assistant**_
                               <el-radio-group v-model="age" style="margin-top: 8px;">
 ```
 
+**Chunk 2**
+Lines added: 1, lines removed: undefined
+
 ```diff
+@@ -153,4 +155,5 @@
    
     const malnutritionSigns = ref<string[]>([])
 +   const age = ref('<50')
@@ -889,6 +1009,8 @@ _**Assistant**_
    
 ```
 
+</details>
+
 ---
 
 已将“年龄”标题和单选框分为两行,单选框单独一行并左对齐,样式更美观,符合你的需求。
@@ -905,7 +1027,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/screening/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 1, lines removed: 1
+
 ```diff
+@@ -64,5 +64,5 @@
                   <el-row style="margin-bottom: 20px;">
                       <el-col :span="24">
 -                         <div class="radio-card">
@@ -914,7 +1044,11 @@ _**Assistant**_
                                   <el-checkbox label="饮食行为问题">饮食行为问题</el-checkbox>
 ```
 
+**Chunk 2**
+Lines added: 12, lines removed: undefined
+
 ```diff
+@@ -243,4 +243,16 @@
       margin-bottom: 8px;
     }
 +   .checkbox-vertical-card .el-checkbox-group {
@@ -933,6 +1067,8 @@ _**Assistant**_
     </style>
 ```
 
+</details>
+
 ---
 
 已将该段 checkbox 样式调整为每个选项一行,左对齐,间距适中,视觉更清晰整齐。如需进一步美化或有其他表单项需要类似处理,请随时告知!
@@ -1148,7 +1284,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: src/views/patients/evaluation/add.vue</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 4, lines removed: 2
+
 ```diff
+@@ -53,7 +53,9 @@
                                         <template v-for="ctl,i in item.contentList">
                                             <el-radio :label="ctl.label" :value="i" :disabled="isView">
 -                                               <span>{{ctl.label }}</span>
@@ -1162,7 +1306,11 @@ _**Assistant**_
                                             </el-radio>
 ```
 
+**Chunk 2**
+Lines added: 5, lines removed: undefined
+
 ```diff
+@@ -81,5 +83,10 @@
                                             <template v-for="ctl,i in item.contentList">
                                                 <el-checkbox :label="ctl.label" :value="i" :disabled="isView">
 +                                                   <span class="option-content">
@@ -1175,7 +1323,11 @@ _**Assistant**_
                                             </template>
 ```
 
+**Chunk 3**
+Lines added: 5, lines removed: undefined
+
 ```diff
+@@ -101,5 +108,10 @@
                                         <template v-for="ctl,i in item.contentList">
                                             <el-checkbox :label="ctl.label" :value="i" :disabled="isView">
 +                                               <span class="option-content">
@@ -1188,7 +1340,11 @@ _**Assistant**_
                                         </template>
 ```
 
+**Chunk 4**
+Lines added: 10, lines removed: undefined
+
 ```diff
+@@ -458,4 +470,14 @@
     border-left: none;
   }
 +  
@@ -1205,6 +1361,8 @@ _**Assistant**_
   
 ```
 
+</details>
+
 ---
 
 已优化图片和文字的样式:  
@@ -1599,7 +1757,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: prompt/info.txt</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 19, lines removed: 19
+
 ```diff
+@@ -12,28 +12,28 @@
     支付方式
   
 - CREATE TABLE settlement (
@@ -1649,6 +1815,8 @@ _**Assistant**_
   
 ```
 
+</details>
+
 ---
 
 已将 settlement 表的所有字段名都加上了反引号 `,包括主键和系统字段,完全符合你的要求。如需其它表也这样处理,请随时告知!
@@ -1663,7 +1831,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: prompt/info.txt</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 31, lines removed: 31
+
 ```diff
+@@ -63,38 +63,38 @@
     金额
    
 - CREATE TABLE enteral_nutrition (
@@ -1735,6 +1911,8 @@ _**Assistant**_
   
 ```
 
+</details>
+
 ---
 
 已将 enteral_nutrition 表的所有字段名都加上了反引号 `,包括主键和系统字段,完全符合你的要求。如需其它表也这样处理,请随时告知!
@@ -1749,7 +1927,15 @@ _**User**_
 
 _**Assistant**_
 
+<details><summary>Edit file: prompt/info.txt</summary>
+
+undefined
+
+**Chunk 1**
+Lines added: 20, lines removed: 20
+
 ```diff
+@@ -113,27 +113,27 @@
   
    
 -  CREATE TABLE enteral_nutrition_consumable (
@@ -1799,6 +1985,8 @@ _**Assistant**_
   
 ```
 
+</details>
+
 ---
 
 已将 enteral_nutrition_consumable 表的所有字段名都加上了反引号 `,包括主键和系统字段,完全符合你的要求。如需其它表也这样处理,请随时告知!
@@ -1817,7 +2005,19 @@ Cancelled
 
 ---
 
+已将选项图片显示样式调整为图片在文字右侧,且图片和文字分开,间距美观。如需进一步调整样式或有其他需求,请随时告知!
+
+---
+
+_**User**_
+
+帮我使用js代码计算日期之差:2025-07-28、2025-07-30
+
+---
+
+_**Assistant**_
 
+*You've hit your free requests limit. [Upgrade to Pro](https://www.cursor.com/api/auth/checkoutDeepControl?tier=pro) for more usage, frontier models, Background Agents, and more. Your usage limits will reset when your monthly cycle ends on 8/9/2025.*
 
 ---
 

+ 71 - 0
prompt/info.txt

@@ -1,3 +1,74 @@
+
+
+添加配置处方
+    组号
+    营养产品
+    用量/次
+    餐次时间
+    频次
+    首日
+    用量/日
+    使用天数
+    用量/总
+    规格
+    用法
+    制剂液量/次
+    制剂浓度/次
+    能量密度/次
+    处方备注
+    每日热量
+    金额
+    操作    
+
+
+
+
+
+添加预包装处方
+    营养产品
+    数量
+    用量/次
+    餐次时间
+    频次
+    首日
+    用量/日
+    使用天数
+    规格
+    用法
+    处方备注
+    每日热量
+    金额
+    操作
+
+
+添加长期医嘱处方 
+    组号
+    营养产品
+    用量/次
+    餐次时间
+    频次
+    首日
+    用量/日
+    规格
+    用法
+    制剂液量/次
+    制剂浓度/次
+    能量密度/次
+    处方备注
+    每日热量
+    金额
+    操作
+
+
+
+
+
+
+
+
+
+
+
 营养筛查
 营养评估
 预包装处方

+ 22 - 6
src/api/patients/nutrition/index.ts

@@ -1,14 +1,15 @@
 import request from '@/utils/request';
-import { AxiosPromise } from 'axios';
-import { NutritionVO, NutritionForm, NutritionQuery } from '@/api/system/nutrition/types';
+import {AxiosPromise} from 'axios';
+import {EnteralNutritionVO, EnteralNutritionForm, EnteralNutritionQuery, EnteralNutritionSaveForm} from '@/api/patients/nutrition/types';
 
 /**
  * 查询肠内营养列表
  * @param query
  * @returns {*}
+ *
  */
 
-export const listEnteralNutrition = (query?: NutritionQuery): AxiosPromise<NutritionVO[]> => {
+export const listEnteralNutrition = (query?: EnteralNutritionQuery): AxiosPromise<EnteralNutritionVO[]> => {
   return request({
     url: '/system/nutrition/list',
     method: 'get',
@@ -16,11 +17,26 @@ export const listEnteralNutrition = (query?: NutritionQuery): AxiosPromise<Nutri
   });
 };
 
+export const listEnteralNutritionHistory = (query?: EnteralNutritionQuery): AxiosPromise<EnteralNutritionVO[]> => {
+  return request({
+    url: '/system/nutrition/listHistory',
+    method: 'get',
+    params: query
+  });
+};
+
+export const listEnteralNutritionDetail = (id?: number): AxiosPromise<EnteralNutritionVO[]> => {
+  return request({
+    url: '/system/nutrition/detail?id=' + id,
+    method: 'get'
+  });
+};
+
 /**
  * 查询肠内营养详细
  * @param id
  */
-export const getEnteralNutrition = (id: string | number): AxiosPromise<NutritionVO> => {
+export const getEnteralNutrition = (id: string | number): AxiosPromise<EnteralNutritionVO> => {
   return request({
     url: '/system/nutrition/' + id,
     method: 'get'
@@ -31,7 +47,7 @@ export const getEnteralNutrition = (id: string | number): AxiosPromise<Nutrition
  * 新增肠内营养
  * @param data
  */
-export const addEnteralNutrition = (data: NutritionForm) => {
+export const addEnteralNutrition = (data: EnteralNutritionSaveForm) => {
   return request({
     url: '/system/nutrition',
     method: 'post',
@@ -43,7 +59,7 @@ export const addEnteralNutrition = (data: NutritionForm) => {
  * 修改肠内营养
  * @param data
  */
-export const updateEnteralNutrition = (data: NutritionForm) => {
+export const updateEnteralNutrition = (data: EnteralNutritionForm) => {
   return request({
     url: '/system/nutrition',
     method: 'put',

+ 43 - 11
src/api/patients/nutrition/types.ts

@@ -118,7 +118,42 @@ export interface EnteralNutritionVO {
    * 状态(0正常 1停用)
    */
   status: string;
+}
+
+export interface EnteralNutritionSaveForm extends BaseEntity {
+  /**
+   * 结算管理id
+   */
+  settlementId?: string | number;
+
+  /**
+   * 患者ID
+   */
+  patientId?: string | number;
+
+  /**
+   * 看诊类型
+   */
+  visitType?: string | number;
 
+  /**
+   * 收费类型
+   */
+  chargeType?: string | number;
+
+  /**
+   * 门诊/住院号
+   */
+  patientNo?: string | number;
+
+  /**
+   * 状态(0正常 1停用 2草稿)
+   */
+  status?: string | number;
+
+  nutritionList?: any[];
+
+  consumableList?: any[];
 }
 
 export interface EnteralNutritionForm extends BaseEntity {
@@ -132,6 +167,8 @@ export interface EnteralNutritionForm extends BaseEntity {
    */
   settlementId?: string | number;
 
+  productSpecUnit?: string;
+
   /**
    * 开方日期
    */
@@ -166,7 +203,7 @@ export interface EnteralNutritionForm extends BaseEntity {
   /**
    * 用量/次
    */
-  dosePerTime?: string|;
+  dosePerTime?: string;
 
   /**
    * 餐次时间
@@ -181,7 +218,7 @@ export interface EnteralNutritionForm extends BaseEntity {
   /**
    * 首日
    */
-  firstDay?: number;
+  firstDay?: number | string;
 
   /**
    * 用量/日
@@ -242,11 +279,9 @@ export interface EnteralNutritionForm extends BaseEntity {
    * 状态(0正常 1停用)
    */
   status?: string;
-
 }
 
 export interface EnteralNutritionQuery extends PageQuery {
-
   /**
    * 结算管理id
    */
@@ -362,11 +397,8 @@ export interface EnteralNutritionQuery extends PageQuery {
    */
   status?: string;
 
-    /**
-     * 日期范围参数
-     */
-    params?: any;
+  /**
+   * 日期范围参数
+   */
+  params?: any;
 }
-
-
-

+ 8 - 0
src/api/system/screeningAssessmentConfig/index.ts

@@ -51,6 +51,14 @@ export const updateScreeningAssessmentConfig = (data: ScreeningAssessmentConfigF
   });
 };
 
+export const updateConclusion = (data: ScreeningAssessmentConfigForm) => {
+  return request({
+    url: '/system/screeningAssessmentConfig/editConclusion',
+    method: 'post',
+    data: data
+  });
+};
+
 export const changeConfigStatus = (configId: string | number, status: string) => {
   const data = {
     configId,

+ 33 - 1
src/api/system/screeningAssessmentConfig/types.ts

@@ -28,11 +28,28 @@ export interface ScreeningAssessmentConfigVO {
    * 量表说明
    */
   description: string;
+  
 
   /**
    * 备注
    */
   remark: string;
+  
+  /**
+   * 结论
+   */
+  conclusion: string;
+ 
+
+    /**
+     * 来源
+     */
+    source : string;
+  
+    /**
+     * 目的
+     */
+    purpose : string;
 
   /**
    * 状态(0正常 1停用)
@@ -56,6 +73,7 @@ export interface QuestionAttrVO {
 }
 
 export interface QuestionVO {
+  qid?: string;
   /**
    * 主键ID
    */
@@ -148,6 +166,20 @@ export interface ScreeningAssessmentConfigForm extends BaseEntity {
    * 备注
    */
   remark?: string;
+  /**
+   * 结论
+   */
+  conclusion?: string;
+
+  /**
+   * 来源
+   */
+  source?: string;
+
+  /**
+   * 目的
+   */
+  purpose?: string;
 
   /**
    * 状态(0正常 1停用)
@@ -156,7 +188,7 @@ export interface ScreeningAssessmentConfigForm extends BaseEntity {
 
   baseInfo?: QuestionVO;
 
-  otherInfo?: QuestionVO[];
+  otherInfo?: QuestionVO[];  
 }
 
 export interface ScreeningAssessmentConfigQuery extends PageQuery {

BIN
src/assets/images/shiXiao.png


BIN
src/assets/images/zuoFei.png


+ 26 - 0
src/store/modules/history.ts

@@ -0,0 +1,26 @@
+import {defineStore} from 'pinia';
+import {reactive} from 'vue';
+
+interface NoticeItem {
+  title?: string;
+  read: boolean;
+  message: any;
+  time: string;
+}
+
+export const useHistoryStore = defineStore('history', () => {
+  const historyList = ref([]);
+
+  const setHistoryList = (list: any) => {
+    historyList.value = list;
+  };
+
+  const getHistoryList = () => {
+    return historyList.value;
+  };
+  return {
+    historyList,
+    setHistoryList,
+    getHistoryList
+  };
+});

+ 8 - 8
src/store/modules/permission.ts

@@ -1,14 +1,14 @@
-import { defineStore } from 'pinia';
-import router, { constantRoutes, dynamicRoutes } from '@/router';
+import {defineStore} from 'pinia';
+import router, {constantRoutes, dynamicRoutes} from '@/router';
 import store from '@/store';
-import { getRouters } from '@/api/menu';
+import {getRouters} from '@/api/menu';
 import auth from '@/plugins/auth';
-import { RouteRecordRaw } from 'vue-router';
+import {RouteRecordRaw} from 'vue-router';
 import Layout from '@/layout/index.vue';
 import ParentView from '@/components/ParentView/index.vue';
 import InnerLink from '@/layout/components/InnerLink/index.vue';
-import { ref } from 'vue';
-import { createCustomNameComponent } from '@/utils/createCustomNameComponent';
+import {ref} from 'vue';
+import {createCustomNameComponent} from '@/utils/createCustomNameComponent';
 
 // 匹配views里面所有的.vue文件
 const modules = import.meta.glob('./../../views/**/*.vue');
@@ -47,7 +47,7 @@ export const usePermissionStore = defineStore('permission', () => {
   };
   const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
     const res = await getRouters();
-    const { data } = res;
+    const {data} = res;
     const sdata = JSON.parse(JSON.stringify(data));
     const rdata = JSON.parse(JSON.stringify(data));
     const defaultData = JSON.parse(JSON.stringify(data));
@@ -150,7 +150,7 @@ export const loadView = (view: any, name: string) => {
     let dir = path.substring(viewsIndex + 7);
     dir = dir.substring(0, dir.lastIndexOf('.vue'));
     if (dir === view) {
-      res = createCustomNameComponent(modules[path], { name });
+      res = createCustomNameComponent(modules[path], {name});
       return res;
     }
   }

+ 19 - 0
src/store/modules/productCategory.ts

@@ -0,0 +1,19 @@
+import {defineStore} from 'pinia';
+
+export const useProductCategoryStore = defineStore('productCategory', () => {
+  const productCategoryList = ref([]);
+
+  const setProductCategoryList = (list: any) => {
+    productCategoryList.value = list;
+  };
+
+  const getProductCategoryList = () => {
+    return productCategoryList.value;
+  };
+
+  return {
+    productCategoryList,
+    setProductCategoryList,
+    getProductCategoryList
+  };
+});

+ 196 - 31
src/views/patients/enteralNutrition/components/LeftCard.vue

@@ -8,52 +8,60 @@
           <div class="operation-bar">
             <!-- 日期选择 -->
             <el-row>
-              <el-col :span="10">
+              <el-col :span="8">
                 <span class="label">日期时间:</span>
-                <el-date-picker v-model="dateRange" type="date"  placeholder="请选择" />
+                <el-date-picker
+                  v-model="prescriptionDate"
+                  :clearable="false"
+                  type="date"
+                  :disabled-date="disabledBeforeToday"
+                  placeholder="请选择"
+                  value-format="YYYY-MM-DD"
+                />
               </el-col>
-              <el-col class="right_btn" :span="7">
-                <!-- <el-button type="primary" plain>打印知情同意书</el-button> -->
+              <el-col :span="4"> </el-col>
+              <el-col class="right_btn" :span="12">
+                <el-button type="primary" plain @click="submitForm('2')">存为草稿</el-button>
+                <el-button type="primary" @click="submitForm('0')" :loading="buttonLoading">提交</el-button>
+                <el-button type="danger" @click="deleteFrom">删除</el-button>
+                <el-button type="primary" plain>打印知情同意书</el-button>
                 <el-button type="success" @click="goHistoryPrescription">历史处方</el-button>
               </el-col>
             </el-row>
 
             <!-- 添加按钮组 -->
             <div class="add-buttons">
-              <el-button :class="['add-btn', {'is-active': prescriptionType === 'normal'}]"
-                @click="addPrescription">添加配置处方</el-button>
-              <el-button :class="['add-btn', {'is-active': prescriptionType === 'package'}]"
-                @click="addTemplate">添加预包装处方</el-button>
-              <el-button :class="['add-btn', {'is-active': prescriptionType === 'long-term'}]"
-                @click="addLongTerm">添加长期医嘱处方</el-button>
+              <el-button :class="['add-btn', {'is-active': prescriptionType === 'normal'}]" @click="addPrescription">添加配置处方</el-button>
+              <el-button :class="['add-btn', {'is-active': prescriptionType === 'package'}]" @click="addTemplate">添加预包装处方</el-button>
+              <el-button :class="['add-btn', {'is-active': prescriptionType === 'long-term'}]" @click="addLongTerm">添加长期医嘱处方</el-button>
             </div>
 
             <!-- 开方原因 -->
-            <div class="prescription-reason">
+            <!-- <div class="prescription-reason">
               <span class="reason-label">开方原因:</span>
               <span class="reason-value">1111</span>
-            </div>
+            </div> -->
           </div>
 
           <!-- 处方提示信息 -->
-          <div class="prescription-info">
+          <!-- <div class="prescription-info">
             <div class="warning-box">
               当前处方中的脂肪、维生素A、维生素D、维生素E、维生素B1、维生素B2、维生素B6、维生素B12、硒(尼克黄)、维生素C、生物素、泛酸元素可能过量
             </div>
-          </div>
+          </div> -->
 
           <!-- 处方表格 -->
           <div class="prescription-tables">
             <template v-if="tableType === 'normal'">
-              <ConfigureTable @change="updateConfigureData"/>
-              <MaterialTable v-model="configData"/>
+              <ConfigureTable @change="updateConfigureData" v-model="configData" />
+              <MaterialTable v-model="configData" @change="updateMaterialData" />
             </template>
             <template v-else-if="tableType === 'package'">
-              <PackageTable/>
+              <PackageTable @change="updatePackageData" />
             </template>
             <template v-else>
-              <NutritionTable/>
-              <MaterialTable/>
+              <NutritionTable @change="updateLongTermData" v-model="longTermData" :prescriptionDate="prescriptionDate" />
+              <MaterialTable2 v-model="longTermData" @change="updateMaterial2Data" />
             </template>
           </div>
         </div>
@@ -69,33 +77,172 @@
 import {ref} from 'vue';
 import NutritionTable from './table/NutritionTable.vue';
 import MaterialTable from './table/MaterialTable.vue';
+import MaterialTable2 from './table/MaterialTable2.vue';
 import PackageTable from './table/PackageTable.vue';
-import ConfigureTable from './table/ConfigureTable.vue'; 
- 
-import { listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition } from '@/api/patients/nutrition';
-import { EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm } from '@/api/patients/nutrition/types';
+import ConfigureTable from './table/ConfigureTable.vue';
+
+import {listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition} from '@/api/patients/nutrition';
+import {EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm, EnteralNutritionSaveForm} from '@/api/patients/nutrition/types';
+
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
- 
 const emit = defineEmits(['gotoTop']);
+const buttonLoading = ref(false);
+const {patientInfo} = defineProps<{patientInfo: any}>();
 
 // 顶部tab切换
 const activeTab = ref('nutrition');
-const configData= ref('');
+const configData = ref('');
+const packageData = ref('');
+const longTermData = ref('');
+const materialData = ref('');
+const materialData2 = ref('');
 
 // 表格类型
 const tableType = ref<'normal' | 'package' | 'long-term'>('normal');
 
 // 日期范围
-const dateRange = ref([]);
+const prescriptionDate = ref('');
 
 // 处方类型
 const prescriptionType = ref<'normal' | 'package' | 'long-term'>('normal');
 
-const updateConfigureData = (val:string) => {  
-  configData.value=val;  
+const updateConfigureData = (val: string) => {
+  configData.value = val;
 };
 
+const updateLongTermData = (val: string) => {
+  longTermData.value = val;
+};
+
+const updatePackageData = (val: string) => {
+  packageData.value = val;
+};
+
+const updateMaterialData = (val: string) => {
+  materialData.value = val;
+};
+
+const updateMaterial2Data = (val: string) => {
+  materialData2.value = val;
+};
+
+const deleteFrom = async () => {
+  configData.value = JSON.stringify([]);
+};
+
+const submitForm = async (status: string) => {
+  let param = ref<EnteralNutritionSaveForm>({});
+  param.value.patientId = patientInfo.id;
+  param.value.visitType = patientInfo.type;
+
+  param.value.patientNo = patientInfo.outpatientNo;
+  param.value.status = status;
+
+  if (prescriptionType.value == 'normal') {
+    param.value.chargeType = '3';
+    if (!configData.value || configData.value.length == 0) {
+      proxy?.$modal.msgError('营养产品不为空');
+      return;
+    }
+    param.value.nutritionList = JSON.parse(configData.value);
+
+    if (!materialData.value || materialData.value.length == 0) {
+      proxy?.$modal.msgError('耗材不为空');
+      return;
+    }
+    param.value.consumableList = JSON.parse(materialData.value);
+  } else if (prescriptionType.value == 'package') {
+    param.value.chargeType = '2';
+    if (!packageData.value || packageData.value.length == 0) {
+      proxy?.$modal.msgError('营养产品不为空');
+      return;
+    }
+    param.value.nutritionList = JSON.parse(packageData.value);
+  } else {
+    param.value.chargeType = '4';
+    if (!longTermData.value || longTermData.value.length == 0) {
+      proxy?.$modal.msgError('营养产品不为空');
+      return;
+    }
+    param.value.nutritionList = JSON.parse(longTermData.value);
+
+    if (!materialData2.value || materialData2.value.length == 0) {
+      proxy?.$modal.msgError('耗材不为空');
+      return;
+    }
+    param.value.consumableList = JSON.parse(materialData2.value);
+  }
+
+  if (param.value.chargeType != '2') {
+    for (let item of param.value.consumableList) {
+      if (!item.consumableId) {
+        proxy?.$modal.msgError('耗材没有选择');
+        return;
+      }
+
+      item.groupNo = item.sn;
+      let arr = item.consumableId.split('_');
+      item.consumableId = arr[0];
+    }
+
+    for (let item of param.value.nutritionList) {
+      if (!item.products || item.products.length == 0) {
+        proxy?.$modal.msgError('营养产品没有记录');
+        return;
+      }
+
+      let tsList = [];
+      item.timeSlots.forEach((ts) => {
+        tsList.push(ts.checked ? ts.time : '');
+      });
+      item.mealTime = tsList.join(',');
+      item.prescriptionDate = prescriptionDate.value;
+      item.prescriptionType = param.value.chargeType;
+
+      for (let pd of item.products) {
+        if (!pd.nutritionProductId) {
+          proxy?.$modal.msgError('营养产品没有选择');
+          return;
+        }
+
+        let arr = pd.nutritionProductId.split('_');
+        pd.nutritionProductId = arr[0];
+        pd.usage = item.usage ;
+        pd.preparationVolumePerTime = item.preparationVolumePerTime ;
+        pd.preparationConcentrationPerTime = item.preparationConcentrationPerTime ;
+        pd.energyDensityPerTime = item.energyDensityPerTime ;
+
+        pd.prescriptionDate = prescriptionDate.value;
+        pd.groupNo = item.sn;
+      }
+    }
+  } else {
+    for (let item of param.value.nutritionList) {
+      let tsList = [];
+      item.timeSlots.forEach((ts) => {
+        tsList.push(ts.checked ? ts.time : '');
+      });
+      item.mealTime = tsList.join(',');
+      item.prescriptionDate = prescriptionDate.value;
+      item.prescriptionType = param.value.chargeType;
+
+      if (!item.nutritionProductId) {
+        proxy?.$modal.msgError('营养产品没有选择');
+        return;
+      }
+
+      let arr = item.nutritionProductId.split('_');
+      item.nutritionProductId = arr[0];
+
+      item.prescriptionDate = prescriptionDate.value;
+      item.groupNo = item.sn;
+    }
+  }
+
+  await addEnteralNutrition(param.value).finally(() => (buttonLoading.value = false));
+  proxy?.$modal.msgSuccess('操作成功');
+};
 
 // 添加配置处方
 const addPrescription = () => {
@@ -117,9 +264,27 @@ const addLongTerm = () => {
 
 // 跳转历史处方页面
 const goHistoryPrescription = () => {
-  emit('gotoTop')
+  emit('gotoTop');
+};
+
+function formatDate(date) {
+  const pad = (n) => (n < 10 ? '0' + n : n);
+  return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate());
+}
+
+// 禁用今天之前的日期
+const disabledBeforeToday = (date) => {
+  const today = new Date();
+  // 设置时间为今天凌晨,确保比较的是日期而非具体时间
+  today.setHours(0, 0, 0, 0);
+  return date < today;
 };
 
+onMounted(() => {
+  let now = new Date();
+  let start = formatDate(now);
+  prescriptionDate.value = start;
+});
 </script>
 
 <style lang="scss" scoped>
@@ -142,7 +307,7 @@ const goHistoryPrescription = () => {
   .operation-bar {
     margin-bottom: 16px;
     .right_btn {
-      margin-left: 268px;
+      margin-left: 0px;
     }
 
     .add-buttons {

+ 70 - 47
src/views/patients/enteralNutrition/components/history/LeftCard.vue

@@ -13,16 +13,16 @@
     <div class="prescription-list">
       <div
         v-for="item in prescriptionList"
-        :key="item.id"
-        :class="['prescription-item', {active: item.id === activeId}]"
+        :key="item.settlementId"
+        :class="['prescription-item', {active: item.settlementId === activeId}]"
         @click="handleItemClick(item)"
       >
         <!-- 时间 -->
-        <div class="item-time">{{ item.time }}</div>
+        <div class="item-time">{{ item.orderTime }}</div>
         <!-- 处方类型 -->
-        <div class="item-type">{{ item.type }}</div>
+        <div class="item-type">{{ item.chargeTypeMame }}</div>
         <!-- 处方ID -->
-        <div class="item-id">处方ID:{{ item.id }}</div>
+        <div class="item-id">处方ID:{{ item.settlementId }}</div>
         <!-- 已编辑角标 -->
         <div v-if="item.isEdited" class="edit-badge">
           <span>已编辑</span>
@@ -33,58 +33,81 @@
 </template>
 
 <script setup lang="ts">
-import {ref} from 'vue';
+import {ref, onMounted} from 'vue';
 import {Search} from '@element-plus/icons-vue';
 
+import { listEnteralNutritionHistory,listEnteralNutritionDetail} from '@/api/patients/nutrition';
+import {useHistoryStore} from '@/store/modules/history';
+ 
+
+const historyStore = useHistoryStore();
+
 // 搜索值
 const searchValue = ref('');
+const {patientInfo} = defineProps<{patientInfo: any}>();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  patientId: patientInfo.id
+});
+
+const prescriptionList = ref([]);
+const getListEnteralNutritionFn = () => {
+  listEnteralNutritionHistory(queryParams.value).then((res) => {
+    prescriptionList.value = res.rows;    
+    // 如果有数据,自动选中第一个处方
+    if (prescriptionList.value && prescriptionList.value.length > 0) {
+      const firstPrescription = prescriptionList.value[0];
+      activeId.value = firstPrescription.settlementId;
+
+      listEnteralNutritionDetail(firstPrescription.settlementId).then(detailRes => {
+        let data = detailRes.data;
+        data.nutritionList.forEach(lt=>{
+          lt.paymentStatus=firstPrescription.paymentStatus;
+          lt.delFlag=firstPrescription.delFlag;
+          lt.stopFlag=firstPrescription.stopFlag;
+          lt.settlementId=firstPrescription.settlementId;          
+          lt.prescriptionDate=firstPrescription.chargeType=='4'?lt.prescriptionDate.substring(0,10):null;
+        });
+
+        historyStore.setHistoryList({
+          nutritionList: data.nutritionList || [],
+          consumableList: data.consumableList || []
+        });
+      });      
+    }
+  });
+  // console.log(patientInfo, 'patientInfo');
+};
+
+onMounted(() => {
+  getListEnteralNutritionFn();
+});
 
 // 当前选中的处方ID
 const activeId = ref('');
 
-// 处方列表数据
-const prescriptionList = ref([
-  {
-    id: '194705349732148019',
-    time: '2025-07-23 10:57:18',
-    type: '长期处方',
-    isEdited: false
-  },
-  {
-    id: '194754209113291980',
-    time: '2025-07-22 14:19:53',
-    type: '配置处方',
-    isEdited: true
-  },
-  {
-    id: '194753542425735168',
-    time: '2025-07-22 13:53:23',
-    type: '长期处方',
-    isEdited: true
-  },
-  {
-    id: '194746976848480665',
-    time: '2025-07-22 09:32:30',
-    type: '长期处方',
-    isEdited: false
-  },
-  {
-    id: '194746840280684134',
-    time: '2025-07-22 09:27:04',
-    type: '长期处方',
-    isEdited: false
-  },
-  {
-    id: '194746840280681234',
-    time: '2025-07-21 16:28:56',
-    type: '长期处方',
-    isEdited: true
-  }
-]);
-
 // 点击处方项
 const handleItemClick = (item) => {
-  activeId.value = item.id;
+  activeId.value = item.settlementId;
+
+  listEnteralNutritionDetail(item.settlementId).then(detailRes => {
+    let data = detailRes.data;
+    data.nutritionList.forEach(lt=>{
+          lt.paymentStatus=item.paymentStatus;
+          lt.delFlag=item.delFlag;
+          lt.stopFlag=item.stopFlag;
+          lt.settlementId=item.settlementId;
+          lt.prescriptionDate=item.chargeType=='4'?lt.prescriptionDate.substring(0,10):null;
+        });
+
+    historyStore.setHistoryList({
+      nutritionList: data.nutritionList || [],
+      consumableList: data.consumableList || []
+    });
+  }); 
+ 
 };
 </script>
 

+ 563 - 157
src/views/patients/enteralNutrition/components/history/RightCard.vue

@@ -4,121 +4,120 @@
     <div class="operation-buttons">
       <div class="left-buttons">
         <el-button plain @click="goBack">返回</el-button>
-        <el-button plain>打印</el-button>
+        <!-- <el-button plain>打印</el-button> -->
         <el-button type="success" plain>引用处方</el-button>
-        <el-button type="danger" plain>处方作废</el-button>
+        <el-button type="danger" plain @click="deleteNutrition" v-if="!status.delFlag">处方作废</el-button>
       </div>
       <div class="pay-status">
-        <img src="@/assets/images/pay.png" alt="待支付" />
+        <template v-if="status.delFlag">
+          <img src="@/assets/images/zuoFei.png" alt="已作废" />
+        </template>
+        <template v-else-if="status.paymentStatus=='0'">
+          <img src="@/assets/images/pay.png" alt="待支付" />
+        </template>
+        <template v-else-if="status.paymentStatus=='2'">
+          <img src="@/assets/images/pay.png" alt="已退款" />
+        </template>
+        <template v-else-if="status.paymentStatus=='3'">
+          <img src="@/assets/images/shiXiao.png" alt="已失效" />
+        </template>
+        <template v-else">
+          <img src="@/assets/images/pay.png" alt="已支付" />
+        </template>
       </div>
     </div>
 
     <!-- 停嘱日期 -->
-    <div class="stop-date">
+    <div class="stop-date" v-if="status.prescriptionDate!=null">
       <span class="label">停嘱日期:</span>
-      <span class="value">2025-07-24</span>
+      <span class="value">{{ status.prescriptionDate }}</span>
     </div>
 
     <!-- 处方表格 -->
     <div class="prescription-tables">
       <!-- 营养产品表格 -->
-      <el-table :data="productData" border>
-        <el-table-column type="index" label="组号" width="60" align="center" />
-        <el-table-column label="营养产品" min-width="180" prop="product" />
-        <el-table-column label="用量/次" width="100" align="center">
-          <template #default="{row}">
-            <span>{{ row.dosage }}g</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="餐次时间" width="180" align="center" prop="mealTime" />
-        <el-table-column label="频次" width="100" align="center">
-          <template #default>一天3次</template>
-        </el-table-column>
-        <el-table-column label="首日" width="80" align="center">
-          <template #default="{row}">
-            <span>{{ row.firstDay }}次</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="用量/日" width="100" align="center">
-          <template #default="{row}">
-            <span>{{ row.dailyDosage }}g</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="规格" width="140" align="center">
-          <template #default="{row}">
-            <span>{{ row.specification }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="用法" width="80" align="center" prop="usage" />
-        <el-table-column label="制剂液量/次" width="120" align="center">
-          <template #default="{row}">
-            <span>{{ row.liquidVolume }}ml</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="制剂浓度/次" width="120" align="center">
-          <template #default="{row}">
-            <span>{{ row.concentration }}%</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="能量密度/次" width="140" align="center">
-          <template #default="{row}">
-            <span>{{ row.energyDensity }}kcal/ml</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="处方备注" min-width="150" prop="note" />
-        <el-table-column label="每日热量" width="100" align="center">
-          <template #default="{row}">
-            <span>{{ row.dailyCalories }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="金额" width="100" align="right">
-          <template #default="{row}">
-            <span>{{ row.amount }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
+      <div class="table-wrapper">
+        <table class="custom-table">
+          <thead>
+            <tr>
+              <th width="60" class="fixed-width">组号</th>
+              <th width="160" class="fixed-width">营养产品</th>
+              <th width="100" class="fixed-width">用量/次</th>
+              <th width="180" class="fixed-width">餐次时间</th>
+              <th width="100" class="fixed-width">频次</th>
+              <th width="80" class="fixed-width">首日</th>
+              <th width="100" class="fixed-width">用量/日</th>
+              <th width="140" class="fixed-width">规格</th>
+              <th width="80" class="fixed-width">用法</th>
+              <th width="120" class="fixed-width">制剂液量/次</th>
+              <th width="120" class="fixed-width">制剂浓度/次</th>
+              <th width="140" class="fixed-width">能量密度/次</th>
+              <th width="150" class="fixed-width">处方备注</th>
+              <th width="100" class="fixed-width">每日热量</th>
+              <th width="100" class="fixed-width">金额</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(item, index) in tableNutritionData" :key="item.id">
+              <td>{{ index + 1 }}</td>
+              <td>{{ item.nutritionProduct || '--' }}</td>
+              <td>{{ item.dosePerTime || '--' }}g</td>
+              <td>{{ item.mealTime || '--' }}</td>
+              <td>一天{{ item.frequency || '--' }}次</td>
+              <td>{{ item.firstDay || '--' }}次</td>
+              <td>{{ item.dosePerDay || '--' }}g</td>
+              <td>{{ item.specification || '--' }}</td>
+              <td>{{ item.usage || '--' }}</td>
+              <td>{{ item.preparationVolumePerTime || '--' }}ml</td>
+              <td>{{ item.preparationConcentrationPerTime || '--' }}%</td>
+              <td>{{ item.energyDensityPerTime || '--' }}kcal/ml</td>
+              <td>{{ item.prescriptionRemark || '--' }}</td>
+              <td>{{ item.dailyCalories || '--' }}</td>
+              <td class="text-right">{{ item.amount || '--' }}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
 
       <!-- 耗材表格 -->
-      <el-table :data="materialData" border>
-        <el-table-column type="index" label="组号" width="60" align="center" />
-        <el-table-column label="耗材" min-width="200" />
-        <el-table-column label="规格" width="120" align="center" />
-        <el-table-column label="用量/次" width="100" align="center">
-          <template #default="{row}">
-            <span>{{ row.dosage }}袋</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="频次" width="100" align="center">
-          <template #default>一天3次</template>
-        </el-table-column>
-        <el-table-column label="用量/日" width="100" align="center">
-          <template #default="{row}">
-            <span>{{ row.dailyDosage }}袋</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="首日" width="80" align="center">
-          <template #default="{row}">
-            <span>{{ row.firstDay }}次</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="数量" width="80" align="center">
-          <template #default="{row}">
-            <span>{{ row.quantity }}袋</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="处方备注" min-width="150" />
-        <el-table-column label="金额" width="100" align="right">
-          <template #default="{row}">
-            <span>{{ row.amount }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
+      <div class="table-wrapper">
+        <table class="custom-table">
+          <thead>
+            <tr>
+              <th width="60" class="fixed-width">组号</th>
+              <th width="200" class="fixed-width">耗材</th>
+              <th width="120" class="fixed-width">规格</th>
+              <th width="100" class="fixed-width">用量/次</th>
+              <th width="100" class="fixed-width">频次</th>
+              <th width="100" class="fixed-width">用量/日</th>
+              <th width="80" class="fixed-width">首日</th>
+              <th width="80" class="fixed-width">数量</th>
+              <th width="150" class="fixed-width">处方备注</th>
+              <th width="100" class="fixed-width">金额</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(item, index) in consumableListData" :key="item.id">
+              <td>{{ index + 1 }}</td>
+              <td>{{ item.consumable || '--' }}</td>
+              <td>{{ item.specification || '--' }}</td>
+              <td>{{ item.dosePerTime || '--' }}袋</td>
+              <td>一天{{ item.frequency || '--' }}次</td>
+              <td>{{ item.dosePerDay || '--' }}袋</td>
+              <td>{{ item.firstDay || '--' }}次</td>
+              <td>{{ item.quantity || '--' }}袋</td>
+              <td>{{ item.prescriptionRemark || '--' }}</td>
+              <td>{{ item.amount || '--' }}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
 
       <!-- 费用统计 -->
       <div class="table-cost">
-        <div class="cost-item">处方费用:¥15.25</div>
-        <div class="cost-item">营养配置费:¥5</div>
-        <div class="cost-item total">总计:¥20.25</div>
+        <div class="cost-item">处方费用:¥{{ totalCost.nutritionCost }}</div>
+        <div class="cost-item">营养配置费:¥{{ totalCost.configCost }}</div>
+        <div class="cost-item total">总计:¥{{ totalCost.total }}</div>
       </div>
     </div>
 
@@ -139,12 +138,26 @@
           <div class="analysis-detail">
             <!-- 左侧表格 -->
             <div class="table-container">
-              <el-table :data="nutritionData" border>
-                <el-table-column label="三大营养素" prop="name" width="120" />
-                <el-table-column label="质量 (g)" prop="mass" width="120" align="center" />
-                <el-table-column label="热量占比" prop="ratio" width="120" align="center" />
-                <el-table-column label="参考值" prop="reference" min-width="120" align="center" />
-              </el-table>
+              <div class="table-wrapper">
+                <table class="custom-table">
+                  <thead>
+                    <tr>
+                      <th width="120" class="fixed-width">三大营养素</th>
+                      <th width="120" class="fixed-width">质量 (g)</th>
+                      <th width="120" class="fixed-width">热量占比</th>
+                      <th width="120" class="fixed-width">参考值</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr v-for="(item, index) in nutritionData" :key="index">
+                      <td>{{ item.name }}</td>
+                      <td>{{ item.mass }}</td>
+                      <td>{{ item.ratio }}</td>
+                      <td>{{ item.reference }}</td>
+                    </tr>
+                  </tbody>
+                </table>
+              </div>
               <div class="extra-info">
                 <div class="info-item">
                   <span class="label">氮含量:</span>
@@ -179,72 +192,149 @@
         </div>
       </el-tab-pane>
       <el-tab-pane label="营养数据分析" name="data">
-        <el-table :data="nutritionAnalysisData" border>
-          <el-table-column label="元素类型" prop="type" width="120" align="center" />
-          <el-table-column label="元素名称" prop="name" width="120" align="center" />
-          <el-table-column label="单位" prop="unit" width="80" align="center" />
-          <el-table-column label="肠内营养含量/天" align="center">
-            <el-table-column label="当前处方" prop="current" width="120" align="center" />
-            <el-table-column label="历史处方" prop="history" width="120" align="center" />
-          </el-table-column>
-          <el-table-column label="肠外营养含量/天" prop="parenteral" width="140" align="center" />
-          <el-table-column label="膳食营养含量/天" prop="dietary" width="140" align="center" />
-          <el-table-column label="营养设定" prop="target" width="120" align="center" />
-        </el-table>
+        <div class="table-wrapper table-fixed-header">
+          <table class="custom-table">
+            <thead>
+              <tr>
+                <th width="120" class="fixed-width">元素类型</th>
+                <th width="120" class="fixed-width">元素名称</th>
+                <th width="80" class="fixed-width">单位</th>
+                <th colspan="2" class="fixed-width text-center">肠内营养含量/天</th>
+                <th width="140" class="fixed-width">肠外营养含量/天</th>
+                <th width="140" class="fixed-width">膳食营养含量/天</th>
+                <th width="120" class="fixed-width">营养设定</th>
+              </tr>
+              <tr>
+                <th colspan="3"></th>
+                <th width="120" class="fixed-width">当前处方</th>
+                <th width="120" class="fixed-width">历史处方</th>
+                <th colspan="3"></th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr v-for="(item, index) in nutritionAnalysisData" :key="index">
+                <td v-if="isFirstInGroup(item, index)" :rowspan="getGroupRowSpan(item.type)">{{ item.type }}</td>
+                <td>{{ item.name }}</td>
+                <td>{{ item.unit }}</td>
+                <td>{{ item.current }}</td>
+                <td>{{ item.history || '--' }}</td>
+                <td>{{ item.parenteral || '--' }}</td>
+                <td>{{ item.dietary || '--' }}</td>
+                <td>{{ item.target }}</td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
       </el-tab-pane>
     </el-tabs>
   </el-card>
 </template>
 
 <script setup lang="ts">
-import {ref, onMounted} from 'vue';
+import { ref, onMounted, watch, computed } from 'vue';
 import * as echarts from 'echarts';
+import { useHistoryStore } from '@/store/modules/history';
+import {delEnteralNutrition} from '@/api/patients/nutrition';
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
+const historyStore = useHistoryStore();
 
 const activeTab = ref('nutrition');
 const pieChart = ref(null);
 
-// 营养产品数据
-const productData = ref([
-  {
-    product: '孕妇营养包(1-12月)',
-    dosage: '1.0000',
-    mealTime: '08:00,12:00,18:00',
-    firstDay: 2,
-    dailyDosage: '3.0000',
-    specification: '840.0000g/袋',
-    usage: '口服',
-    liquidVolume: '100.0000',
-    concentration: '0.9900',
-    energyDensity: '0.0410',
-    note: '--',
-    dailyCalories: '12.3000',
-    amount: '5.25'
-  }
-]);
+// 营养产品表格数据
+const tableNutritionData = ref([]);
+// 耗材表格数据
+const consumableListData = ref([]);
+const status = ref({
+  paymentStatus: '1',
+  delFlag: false,
+  stopFlag: false,
+  prescriptionDate:null,
+  settlementId: '0'
+});
 
-// 耗材数据
-const materialData = ref([
-  {
-    material: '肠内营养空白合剂',
-    specification: '3g/袋',
-    dosage: '1.0000',
-    dailyDosage: '3.000',
-    firstDay: 2,
-    quantity: '5.0000',
-    amount: '10.00'
-  }
-]);
+const delResult = ref({});
+
+const deleteNutrition = () => {
+  delEnteralNutrition(status.value.settlementId).then(() => {
+    delResult.value[status.value.settlementId]=true;
+    status.value.delFlag=true;
+    let row=tableNutritionData.value[0];
+    row.delFlag=true;
+    proxy?.$modal.msgSuccess('作废成功');
+  });
+};
+
+
+// 监听 store 数据变化
+watch(
+  () => historyStore.historyList,
+  (newVal: any) => {
+    if (newVal) {
+      // 如果是数组,取第一个元素的数据
+      if (Array.isArray(newVal)) {
+        tableNutritionData.value = newVal[0]?.nutritionList || [];
+        consumableListData.value = newVal[0]?.consumableList || [];       
+      } else {
+        // 如果是单个对象,直接取数据
+        tableNutritionData.value = newVal?.nutritionList || [];
+        consumableListData.value = newVal?.consumableList || [];
+      }
+
+      if(tableNutritionData.value.length>0){
+          let row=tableNutritionData.value[0];
+          let delFlag=delResult.value[row.settlementId];
+
+          status.value = {
+            paymentStatus: row.paymentStatus,
+            delFlag: delFlag!=null?delFlag:row.delFlag,
+            stopFlag: row.stopFlag,
+            prescriptionDate: row.prescriptionDate,
+            settlementId: row.settlementId
+          }
+        }        
+    } else {
+      // 如果没有数据,设置为空数组
+      tableNutritionData.value = [];
+      consumableListData.value = [];
+    }
+  },
+  { immediate: true, deep: true }
+);
+
+// 计算总费用
+const totalCost = computed(() => {
+  // 营养产品费用
+  const nutritionCost = tableNutritionData.value.reduce((sum, item) => {
+    return sum + (Number(item.amount) || 0);
+  }, 0);
+
+  // 耗材费用
+  const consumableCost = consumableListData.value.reduce((sum, item) => {
+    return sum + (Number(item.amount) || 0);
+  }, 0);
+
+  // 配置费用固定为 5 元
+  const configCost = 5;
+
+  return {
+    nutritionCost: nutritionCost.toFixed(2),
+    consumableCost: consumableCost.toFixed(2),
+    configCost: configCost.toFixed(2),
+    total: (nutritionCost + consumableCost + configCost).toFixed(2)
+  };
+});
 
 // 营养素分析数据
 const nutritionData = ref([
   {
-    name: '蛋白质',
+    name: '蛋白质11',
     mass: '0.729',
     ratio: '23.71%',
     reference: '10% - 15%'
   },
   {
-    name: '脂肪',
+    name: '脂肪22',
     mass: '0.39',
     ratio: '28.54%',
     reference: '20% - 30%'
@@ -257,14 +347,27 @@ const nutritionData = ref([
   }
 ]);
 
+// 判断是否是组内第一个元素
+const isFirstInGroup = (item: any, index: number) => {
+  if (index === 0) return true;
+  return item.type !== nutritionAnalysisData.value[index - 1].type;
+};
+
+// 获取组的行数
+const getGroupRowSpan = (type: string) => {
+  return nutritionAnalysisData.value.filter((item) => item.type === type).length;
+};
+ 
+
 // 营养数据分析数据
 const nutritionAnalysisData = ref([
+  // 热能及三大营养素
   {
     type: '热能及三大营养素',
     name: '热量',
     unit: 'kcal',
     current: '12.3',
-    history: '12.3',
+    history: '60.42',
     parenteral: '--',
     dietary: '--',
     target: '897.3'
@@ -274,7 +377,7 @@ const nutritionAnalysisData = ref([
     name: '热能',
     unit: 'kj',
     current: '51.45',
-    history: '51.45',
+    history: '252.72',
     parenteral: '--',
     dietary: '--',
     target: '3754.3'
@@ -284,7 +387,7 @@ const nutritionAnalysisData = ref([
     name: '碳水化合物',
     unit: 'g',
     current: '1.278',
-    history: '1.28',
+    history: '6.91',
     parenteral: '--',
     dietary: '--',
     target: '112.16'
@@ -294,7 +397,7 @@ const nutritionAnalysisData = ref([
     name: '蛋白质',
     unit: 'g',
     current: '0.729',
-    history: '0.73',
+    history: '3.88',
     parenteral: '--',
     dietary: '--',
     target: '50'
@@ -304,17 +407,18 @@ const nutritionAnalysisData = ref([
     name: '脂肪',
     unit: 'g',
     current: '0.39',
-    history: '0.39',
+    history: '0.78',
     parenteral: '--',
     dietary: '--',
     target: '19.94'
   },
+  // 常量元素
   {
     type: '常量元素',
     name: '钠',
     unit: 'mg',
     current: '9',
-    history: '9',
+    history: '51',
     parenteral: '--',
     dietary: '--',
     target: '1500'
@@ -388,6 +492,220 @@ const nutritionAnalysisData = ref([
     parenteral: '--',
     dietary: '--',
     target: '7.5'
+  },
+  // 微量元素
+  {
+    type: '微量元素',
+    name: '硒',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '60'
+  },
+  {
+    type: '微量元素',
+    name: '铜',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '0.8'
+  },
+  {
+    type: '微量元素',
+    name: '锰',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '4.5'
+  },
+  {
+    type: '微量元素',
+    name: '碘',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '120'
+  },
+  {
+    type: '微量元素',
+    name: '氟',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '1.5'
+  },
+  {
+    type: '微量元素',
+    name: '铬',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '30'
+  },
+  {
+    type: '微量元素',
+    name: '钼',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '100'
+  },
+  // 脂肪酸
+  {
+    type: '脂肪酸',
+    name: '多不饱和脂肪酸',
+    unit: 'g',
+    current: '0.0033',
+    history: '0.01',
+    parenteral: '--',
+    dietary: '--',
+    target: '--'
+  },
+  // 维生素
+  {
+    type: '维生素',
+    name: '维生素A',
+    unit: 'μgRAE',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '700'
+  },
+  {
+    type: '维生素',
+    name: '维生素D',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '10'
+  },
+  {
+    type: '维生素',
+    name: '维生素E',
+    unit: 'mgα-TE',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '14'
+  },
+  {
+    type: '维生素',
+    name: '维生素K',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '80'
+  },
+  {
+    type: '维生素',
+    name: '维生素B1',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '1.2'
+  },
+  {
+    type: '维生素',
+    name: '维生素B2',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '1.2'
+  },
+  {
+    type: '维生素',
+    name: '维生素B6',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '1.4'
+  },
+  {
+    type: '维生素',
+    name: '维生素B12',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '2.4'
+  },
+  {
+    type: '维生素',
+    name: '烟酸(尼克酸)',
+    unit: 'mgNE',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '12'
+  },
+  {
+    type: '维生素',
+    name: '维生素C',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '100'
+  },
+  {
+    type: '维生素',
+    name: '生物素',
+    unit: 'μg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '40'
+  },
+  {
+    type: '维生素',
+    name: '泛酸',
+    unit: 'mg',
+    current: '0',
+    history: '--',
+    parenteral: '--',
+    dietary: '--',
+    target: '5'
+  },
+  // 其他
+  {
+    type: '其他',
+    name: '膳食纤维',
+    unit: 'g',
+    current: '0.363',
+    history: '1.45',
+    parenteral: '--',
+    dietary: '--',
+    target: '25'
   }
 ]);
 
@@ -460,12 +778,10 @@ onMounted(() => {
   });
 });
 
-
 const emit = defineEmits(['goBack']);
 const goBack = () => {
-  emit('goBack')
+  emit('goBack');
 };
-
 </script>
 
 <style lang="scss" scoped>
@@ -655,4 +971,94 @@ const goBack = () => {
     font-size: 12px;
   }
 }
+
+.table-wrapper {
+  margin-bottom: 20px;
+  overflow-x: auto;
+
+  &.table-fixed-header {
+    position: relative;
+    height: calc(100vh - 400px); // 调整合适的高度
+    overflow-y: auto;
+
+    thead {
+      position: sticky;
+      top: 0;
+      z-index: 1;
+
+      tr:first-child th {
+        position: sticky;
+        top: 0;
+        background: #f5f7fa;
+        border-bottom: none;
+      }
+
+      tr:last-child th {
+        position: sticky;
+        top: 41px; // 第一行表头的高度
+        background: #f5f7fa;
+      }
+    }
+
+    tbody {
+      tr:first-child td {
+        border-top: none;
+      }
+    }
+  }
+
+  // 添加横向滚动条的样式
+  &::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background-color: #909399;
+    border-radius: 4px;
+  }
+
+  &::-webkit-scrollbar-track {
+    background-color: #f5f7fa;
+  }
+}
+
+.custom-table {
+  width: max-content;
+  min-width: 100%;
+  border-collapse: separate; // 改为 separate 以支持固定表头
+  border-spacing: 0;
+  border: 1px solid #ebeef5;
+
+  th,
+  td {
+    padding: 12px 8px;
+    border: 1px solid #ebeef5;
+    text-align: center;
+    font-size: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    background-color: #fff; // 添加背景色防止内容透明
+
+    &.text-center {
+      text-align: center;
+    }
+  }
+
+  th {
+    background-color: #f5f7fa;
+    color: #303133;
+    font-weight: 600;
+    height: 40px; // 固定表头高度
+  }
+
+  tbody tr:hover {
+    background-color: #f5f7fa;
+
+    td {
+      background-color: #f5f7fa;
+    }
+  }
+}
 </style>

+ 227 - 130
src/views/patients/enteralNutrition/components/table/ConfigureTable.vue

@@ -5,10 +5,14 @@
       <el-table-column label="营养产品" min-width="300">
         <template #default="{row}">
           <div class="product-group">
-            <div v-for="(product, index) in row.products" :key="index" class="product-row" style="margin-bottom:5px;">
+            <div v-for="(product, index) in row.products" :key="index" class="product-row" style="margin-bottom: 5px">
               <el-select v-model="product.nutritionProductId" placeholder="请选择" @change="changeProductSelect">
-                <el-option v-for="pn in productNutritionList" :key="pn.id" :label="pn.productName"
-                  :value="pn.id+'_'+row.sn+'_'+index"></el-option>
+                <el-option
+                  v-for="pn in productNutritionList"
+                  :key="pn.id"
+                  :label="pn.productName"
+                  :value="pn.id + '_' + row.sn + '_' + index"
+                ></el-option>
               </el-select>
               <div class="action-buttons">
                 <el-button class="icon-btn" @click="addProduct(row)">
@@ -30,8 +34,12 @@
         <template #default="{row}">
           <div class="dosage-group">
             <div v-for="(product, index) in row.products" :key="index" class="dosage-input">
-              <el-input v-model="product.dosePerTime" placeholder="请输入" class="input-center"  
-              @input="dosePerTimeInput(product.dosePerTime,row.sn+'_'+index)"/>
+              <el-input
+                v-model="product.dosePerTime"
+                placeholder="请输入"
+                class="input-center"
+                @input="dosePerTimeInput(product.dosePerTime, row.sn + '_' + index)"
+              />
               <span class="unit">g</span>
             </div>
           </div>
@@ -42,42 +50,72 @@
           <div class="time-slots">
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[0].checked" @change="changeTimeSelection(row.timeSlots[0].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[0].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[0].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[0].checked" @change="changeTimeSelection(row.timeSlots[0].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[0].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[0].checked"
+                />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[1].checked" @change="changeTimeSelection(row.timeSlots[1].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[1].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[1].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[1].checked" @change="changeTimeSelection(row.timeSlots[1].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[1].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[1].checked"
+                />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[2].checked" @change="changeTimeSelection(row.timeSlots[2].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[2].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[2].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[2].checked" @change="changeTimeSelection(row.timeSlots[2].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[2].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[2].checked"
+                />
               </div>
             </div>
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[3].checked" @change="changeTimeSelection(row.timeSlots[3].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[3].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[3].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[3].checked" @change="changeTimeSelection(row.timeSlots[3].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[3].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[3].checked"
+                />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[4].checked" @change="changeTimeSelection(row.timeSlots[4].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[4].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[4].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[4].checked" @change="changeTimeSelection(row.timeSlots[4].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[4].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[4].checked"
+                />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[5].checked" @change="changeTimeSelection(row.timeSlots[5].checked,row.sn)"/>
-                <el-time-select v-model="row.timeSlots[5].time" start="00:00" step="00:30" end="23:30"
-                  placeholder="选择时间" :disabled="!row.timeSlots[5].checked" 
-                  />
+                <el-checkbox v-model="row.timeSlots[5].checked" @change="changeTimeSelection(row.timeSlots[5].checked, row.sn)" />
+                <el-time-select
+                  v-model="row.timeSlots[5].time"
+                  start="00:00"
+                  step="00:30"
+                  end="23:30"
+                  placeholder="选择时间"
+                  :disabled="!row.timeSlots[5].checked"
+                />
               </div>
             </div>
           </div>
@@ -85,12 +123,12 @@
       </el-table-column>
       <el-table-column label="频次" width="120" align="center">
         <template #default="{row}">
-          <span>一天{{row.frequency}}次</span>
+          <span>一天{{ row.frequency }}次</span>
         </template>
       </el-table-column>
       <el-table-column label="首日" width="150" align="center">
         <template #default="{row}">
-          <el-input v-model="row.firstDay" @input="firstDayInput(row.firstDay,row.sn)">
+          <el-input v-model="row.firstDay" @input="firstDayInput(row.firstDay, row.sn)">
             <template #append>次</template>
           </el-input>
         </template>
@@ -98,14 +136,15 @@
       <el-table-column label="用量/日" width="100" align="center">
         <template #default="{row}">
           <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
-            <span>{{product.dosePerDay }}</span>             
+            <span>{{ product.dosePerDay }}</span>
+            <span class="unit">{{ product.productSpecUnit }}</span>
           </div>
         </template>
       </el-table-column>
       <el-table-column label="使用天数" width="200" align="center">
         <template #default="{row}">
           <div class="daily-size">
-            <el-input-number v-model="row.usageDays" :min="1" @change="changeUsageDays(row.usageDays,row.sn)"/>
+            <el-input-number v-model="row.usageDays" :min="1" @change="changeUsageDays(row.usageDays, row.sn)" />
           </div>
         </template>
       </el-table-column>
@@ -113,7 +152,8 @@
       <el-table-column label="用量/总" width="100" align="center">
         <template #default="{row}">
           <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
-            <span>{{product.totalDose }}</span>            
+            <span>{{ product.totalDose }}</span>
+            <span class="unit">{{ product.productSpecUnit }}</span>
           </div>
         </template>
       </el-table-column>
@@ -121,23 +161,22 @@
       <el-table-column label="规格" width="100" align="center">
         <template #default="{row}">
           <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
-            <span>{{product.specification }}</span>             
+            <span>{{ product.specification }}</span>
           </div>
         </template>
       </el-table-column>
 
       <el-table-column label="用法" width="150" align="center">
         <template #default="{row}">
-          <el-select v-model="row.usage" placeholder="请选择" >
-            <el-option v-for="dict in default_usage" :key="dict.value" :label="dict.label"
-              :value="dict.value"></el-option>
+          <el-select v-model="row.usage" placeholder="请选择" @change="changeUsage(row.usage, row.sn)">
+            <el-option v-for="dict in default_usage" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
           </el-select>
         </template>
       </el-table-column>
 
       <el-table-column label="制剂液量/次" width="170" align="center">
         <template #default="{row}">
-          <el-input v-model="row.preparationVolumePerTime">
+          <el-input v-model="row.preparationVolumePerTime" @change="changePreparationVolumePerTime(row.preparationVolumePerTime, row.sn)">
             <template #append>ml</template>
           </el-input>
         </template>
@@ -154,7 +193,7 @@
 
       <el-table-column label="能量密度/次" width="200" align="center">
         <template #default="{row}">
-          <el-input v-model="row.energyDensityPerTime">
+          <el-input v-model="row.energyDensityPerTime" @change="changeEnergyDensityPerTime(row.energyDensityPerTime, row.sn)">
             <template #append>kcal/ml</template>
           </el-input>
         </template>
@@ -162,25 +201,29 @@
 
       <el-table-column label="处方备注" width="200" align="center">
         <template #default="{row}">
-          <template v-for="(product, index) in row.products" >
-            <el-input v-model="product.prescriptionRemark" :maxlength="100" :show-word-limit="true" />
-          </template>          
+          <template v-for="(product, index) in row.products">
+            <el-input
+              v-model="product.prescriptionRemark"
+              :maxlength="100"
+              :show-word-limit="true"
+              @change="changePrescriptionRemark(product.prescriptionRemark, row.sn)"
+            />
+          </template>
         </template>
       </el-table-column>
 
       <el-table-column label="每日热量" width="100" align="center">
         <template #default="{row}">
           <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
-            <span>{{product.dailyCalories }}</span>             
+            <span>{{ product.dailyCalories }}</span>
           </div>
         </template>
       </el-table-column>
 
-
       <el-table-column label="金额" width="100" align="center">
-        <template #default="{row}"> 
+        <template #default="{row}">
           <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
-            <span>{{product.amount }}</span>
+            <span>{{ product.amount }}</span>
           </div>
         </template>
       </el-table-column>
@@ -208,37 +251,59 @@
 <script setup lang="ts">
 import {ref} from 'vue';
 import {Plus, Delete, CirclePlus} from '@element-plus/icons-vue';
-import { listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition } from '@/api/patients/nutrition';
-import { EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm } from '@/api/patients/nutrition/types';
-import { listNutrition, listAllNutrition  } from '@/api/warehouse/productNutrition/index';
-import { NutritionVO, NutritionQuery, NutritionForm } from '@/api/warehouse/productNutrition/types';
-import { it } from 'node:test';
+import {listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition} from '@/api/patients/nutrition';
+import {EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm} from '@/api/patients/nutrition/types';
+import {listNutrition, listAllNutrition} from '@/api/warehouse/productNutrition/index';
+import {NutritionVO, NutritionQuery, NutritionForm} from '@/api/warehouse/productNutrition/types';
+
+const prop = defineProps({
+  modelValue: {
+    type: String,
+    default: () => undefined
+  }
+});
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { default_usage } = toRefs < any > (proxy ?.useDict('default_usage'));
+watch(
+  () => prop.modelValue,
+  async (val: string) => {
+    if (!val || val.trim().length == 0) {
+      return;
+    }
 
-let productNutritionList=ref<NutritionVO[]>([]);
+    let arr = JSON.parse(val);
+    if (Array.isArray(arr) && arr.length == 0) {
+      tableData.value = [];
+    }
+  },
+  {deep: true, immediate: true}
+);
+
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
+const {default_usage} = toRefs<any>(proxy?.useDict('default_usage'));
+
+let productNutritionList = ref<NutritionVO[]>([]);
+let tableId = ref<number>(0);
 
 const getProductNutritionList = async () => {
   const res = await listAllNutrition();
   productNutritionList.value = res.rows;
-}
-
+};
 
 interface TimeSlot {
   checked: boolean;
   time: string;
-} 
+}
 
-interface TableRow  extends EnteralNutritionForm {
+interface TableRow extends EnteralNutritionForm {
   products: EnteralNutritionForm[];
   timeSlots: TimeSlot[];
   sn: number;
+  tableId: number;
 }
 
 // 创建默认行数据的函数
 const createDefaultRow = (): TableRow => ({
-  products: [{specification:'g',totalDose:'g',dosePerDay:'g'}],
+  products: [{specification: '', totalDose: '', dosePerDay: ''}],
   timeSlots: [
     {checked: true, time: '08:00'},
     {checked: false, time: '08:30'},
@@ -247,10 +312,11 @@ const createDefaultRow = (): TableRow => ({
     {checked: true, time: '18:00'},
     {checked: false, time: '18:30'}
   ],
-  usageDays:1,
-  frequency:3,
-  firstDay:1,
-  sn:0,
+  usageDays: 1,
+  frequency: 3,
+  firstDay: 2,
+  sn: 0,
+  tableId: tableId.value++
 });
 
 const emit = defineEmits(['change']);
@@ -261,29 +327,33 @@ const tableData = ref<TableRow[]>([createDefaultRow()]);
 // 新增行
 const addNewRow = () => {
   let row: TableRow = createDefaultRow();
-  row.sn = tableData.value.length ;
+  row.sn = tableData.value.length;
   tableData.value.push(row);
-  emit('change',JSON.stringify(tableData.value))
+  emit('change', JSON.stringify(tableData.value));
 };
 
 const changeProductSelect = async (id: string) => {
-  let arr=id.split('_');
+  let arr = id.split('_');
   for (let item of productNutritionList.value) {
     if (item.id.toString() != arr[0]) {
-      continue
+      continue;
     }
-    
+
     let row = tableData.value[Number(arr[1])];
     let pt = row.products[Number(arr[2])];
     pt.specification=item.productSpec;
     pt.dosePerTime=null;     
     pt.dailyCalories=null;
+    pt.productSpecUnit=item.productSpecUnit;
     pt.amount=null; 
     row.usage=item.defaultUsage;
-    pt.totalDose='0'+item.productSpecUnit;
-    pt.dosePerDay='0'+item.productSpecUnit;
+    pt.totalDose='0' ;
+    pt.dosePerDay='0';
+    pt.firstDay=Math.ceil(row.frequency/2.0);
     break;
   }
+
+  emit('change', JSON.stringify(tableData.value));
 };
 
 // 新增产品输入框
@@ -299,117 +369,145 @@ const removeProduct = (row: TableRow, index: number) => {
     if (rowIndex !== -1) {
       tableData.value.splice(rowIndex, 1);
     }
+    emit('change', JSON.stringify(tableData.value));
   } else {
     // 否则只删除当前输入框
     row.products.splice(index, 1);
   }
 };
- 
 
 // 复制行
 const copyRow = (index: number) => {
   const newRow = JSON.parse(JSON.stringify(tableData.value[index]));
+  newRow.tableId = tableId.value++;
   tableData.value.splice(index + 1, 0, newRow);
-  emit('change',JSON.stringify(tableData.value))
+  tableData.value.forEach((item, index) => {
+    item.sn = index;
+    item.products.forEach((ch, idx) => {
+      if (ch.nutritionProductId) {
+        let arr = ch.nutritionProductId.split('_');
+        ch.nutritionProductId = arr[0] + '_' + index + '_' + idx;
+      }
+    });
+  });
+  emit('change', JSON.stringify(tableData.value));
 };
 
 // 删除行
 const deleteRow = (index: number) => {
-  if (tableData.value.length === 1) return;
   tableData.value.splice(index, 1);
-  emit('change',JSON.stringify(tableData.value))
+
+  tableData.value.forEach((item, index) => {
+    item.sn = index;
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const changeUsageDays = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changeUsage = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
 };
- 
-const changeUsageDays = (value: string, sn:string) => { 
-  changeTimeSelection(value,sn);
-  emit('change',JSON.stringify(tableData.value))  
+
+const changePreparationVolumePerTime = (value: string, sn:string) => { 
+  changeTimeSelection(null,sn);
 }
 
-const firstDayInput = (value: string, sn:string) => { 
-  changeTimeSelection(value,sn);  
-  emit('change',JSON.stringify(tableData.value))
+const changeEnergyDensityPerTime = (value: string, sn:string) => { 
+  changeTimeSelection(null,sn);
 }
 
-const changeTimeSelection = (value: string, sn:string) => {
+const changePrescriptionRemark = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const firstDayInput = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changeTimeSelection = (value: string, sn: string) => {
   let row = tableData.value[sn];
-  let frequency=0;
-  let oldFrequency=row.frequency;
-  row.timeSlots.forEach(item=>{
-    if(item.checked){
+  let frequency = 0;
+  row.timeSlots.forEach((item) => {
+    if (item.checked) {
       frequency++;
     }
   })
   row.frequency=frequency;
-  if(oldFrequency!==frequency){
-    emit('change',JSON.stringify(tableData.value))
+  if(value!=null){
+    row.firstDay=Math.ceil(row.frequency/2.0);
   }
 
-  row.products.forEach(pt => {
+  row.products.forEach((pt) => {
     if (!pt.dosePerTime || pt.dosePerTime.trim().length == 0) {
       return;
     }
-    let arr=pt.nutritionProductId.split('_');
+    let arr = pt.nutritionProductId.split('_');
 
     for (let item of productNutritionList.value) {
-    if (item.id.toString() != arr[0]) {
-      continue
-    }    
-    
-    
-    let val=item.calorie*row.frequency*Number(pt.dosePerTime)/100.0
-    pt.dailyCalories=val.toFixed(4);
+      if (item.id.toString() != arr[0]) {
+        continue;
+      }
 
-    val=(row.frequency*(row.usageDays-1)+row.firstDay)*item.configSalePrice*Number(pt.dosePerTime)
-    pt.amount=Math.round(val*100)/100;
+      let val = (item.calorie * row.frequency * Number(pt.dosePerTime)) / 100.0;
+      pt.dailyCalories = val.toFixed(4);
 
-    val=(row.frequency*(row.usageDays-1)+row.firstDay)*Number(pt.dosePerTime)
-    pt.totalDose=val.toFixed(4)+item.productSpecUnit;
+      let firstDay = Number(row.firstDay.toString());
+      val = (row.frequency * (row.usageDays - 1) + firstDay) * item.configSalePrice * Number(pt.dosePerTime);
+      pt.amount = Math.round(val * 100) / 100;
 
-    val=row.frequency*Number(pt.dosePerTime)
-    pt.dosePerDay=val.toFixed(4)+item.productSpecUnit;
-    break;
-  }
+      val = (row.frequency * (row.usageDays - 1) + firstDay) * Number(pt.dosePerTime);
+      pt.totalDose = val.toFixed(4);
 
-  });  
+      val = row.frequency * Number(pt.dosePerTime);
+      pt.dosePerDay = val.toFixed(4);
+      break;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
 };
 
-const dosePerTimeInput = (value: string, str:string) => {
-  let arr=str.split('_');
+const dosePerTimeInput = (value: string, str: string) => {
+  let arr = str.split('_');
   let row = tableData.value[Number(arr[0])];
   let pt = row.products[Number(arr[1])];
-  if(!pt.dosePerTime||pt.dosePerTime.trim().length==0){
-    pt.dailyCalories=null;
-    pt.amount=null; 
+  if (!pt.dosePerTime || pt.dosePerTime.trim().length == 0) {
+    pt.dailyCalories = null;
+    pt.amount = null;
     return;
   }
 
-  arr=pt.nutritionProductId.split('_');
+  arr = pt.nutritionProductId.split('_');
   for (let item of productNutritionList.value) {
     if (item.id.toString() != arr[0]) {
-      continue
-    }    
-    
-    
-    let val=item.calorie*row.frequency*Number(pt.dosePerTime)/100.0
-    pt.dailyCalories=val.toFixed(4);
+      continue;
+    }
+
+    let val = (item.calorie * row.frequency * Number(pt.dosePerTime)) / 100.0;
+    pt.dailyCalories = val.toFixed(4);
 
-    val=(row.frequency*(row.usageDays-1)+row.firstDay)*item.configSalePrice*Number(pt.dosePerTime)
-    pt.amount=Math.round(val*100)/100;
+    let firstDay = Number(row.firstDay.toString());
+    val = (row.frequency * (row.usageDays - 1) + firstDay) * item.configSalePrice * Number(pt.dosePerTime);
+    pt.amount = Math.round(val * 100) / 100;
 
-    val=(row.frequency*(row.usageDays-1)+row.firstDay)*Number(pt.dosePerTime)
-    pt.totalDose=val.toFixed(4)+item.productSpecUnit;
+    val = (row.frequency * (row.usageDays - 1) + firstDay) * Number(pt.dosePerTime);
+    pt.totalDose = val.toFixed(4);
 
-    val=row.frequency*Number(pt.dosePerTime)
-    pt.dosePerDay=val.toFixed(4)+item.productSpecUnit;
+    val = row.frequency * Number(pt.dosePerTime);
+    pt.dosePerDay = val.toFixed(4);
     break;
   }
-};
 
+  emit('change', JSON.stringify(tableData.value));
+};
 
-onMounted(() => { 
+onMounted(() => {
   getProductNutritionList();
 });
-
 </script>
 
 <style lang="scss" scoped>
@@ -628,8 +726,7 @@ onMounted(() => {
   }
 }
 
-
 :deep(.daily-size div) {
   width: 100px;
-} 
+}
 </style>

+ 498 - 83
src/views/patients/enteralNutrition/components/table/MaterialTable.vue

@@ -5,10 +5,9 @@
       <el-table-column label="耗材" min-width="300">
         <template #default="{row}">
           <div class="material-group">
-            <div class="material-row" style="margin-bottom:10px;">
+            <div class="material-row" style="margin-bottom: 10px">
               <el-select v-model="row.consumableId" placeholder="请选择" @change="changeConsumableSelect">
-                <el-option v-for="pn in suppliesList" :key="pn.id" :label="pn.suppliesName"
-                  :value="pn.id+'_'+row.sn"></el-option>
+                <el-option v-for="pn in suppliesList" :key="pn.id" :label="pn.suppliesName" :value="pn.id + '_' + row.sn"></el-option>
               </el-select>
             </div>
           </div>
@@ -17,7 +16,7 @@
       <el-table-column label="规格" width="100" align="center">
         <template #default="{row}">
           <div class="spec-group">
-            <span>{{ row.specification }}</span>            
+            <span>{{ row.specification }}</span>
           </div>
         </template>
       </el-table-column>
@@ -25,17 +24,17 @@
         <template #default="{row}">
           <div class="dosage-group">
             <div class="dosage-input">
-              <el-input v-model="row.dosePerTime" placeholder="请输入" class="input-center" >
+              <el-input v-model="row.dosePerTime" placeholder="请输入" class="input-center" @input="dosePerTimeInput(row.dosePerTime, row.sn)">
                 <template #append>袋</template>
-              </el-input>              
-            </div>             
+              </el-input>
+            </div>
           </div>
         </template>
       </el-table-column>
-       
+
       <el-table-column label="频次" width="100" align="center">
         <template #default="{row}">
-          <span>一天{{row.frequency}}次</span>
+          <span>一天{{ row.frequency }}次</span>
         </template>
       </el-table-column>
       <el-table-column label="用量/日" width="100" align="center">
@@ -63,7 +62,7 @@
 
       <el-table-column label="处方备注" width="200" align="center">
         <template #default="{row}">
-          <el-input v-model="row.prescriptionRemark" :maxlength="100" :show-word-limit="true" />        
+          <el-input v-model="row.prescriptionRemark" :maxlength="100" :show-word-limit="true" @change="prescriptionRemark" />
         </template>
       </el-table-column>
 
@@ -72,138 +71,463 @@
           <span>{{ row.amount }}</span>
         </template>
       </el-table-column>
-
     </el-table>
     <div class="table-footer">
       <div class="left-buttons">
         <el-button type="primary" plain>登为个人模板</el-button>
         <el-button type="primary" plain>批量添加营养品</el-button>
-        <el-button type="primary" plain>处方模板</el-button>
+        <el-button type="primary" plain @click="openTemplate">处方模板</el-button>
       </div>
       <div class="right-summary">
         <div class="summary-list">
           <div class="summary-item">
             <span class="label">处方费用:</span>
-            <span class="value">¥0.00</span>
+            <span class="value">¥{{ nutritionalFee + otherlFee }}</span>
           </div>
           <div class="summary-item">
             <span class="label">营养配置费:</span>
-            <span class="value">¥39.00</span>
+            <span class="value">¥{{ prescriptionFee }}</span>
           </div>
           <div class="summary-item total">
             <span class="label">总计:</span>
-            <span class="value">¥39.00</span>
+            <span class="value">¥{{ totalFee + otherlFee }}</span>
           </div>
         </div>
       </div>
     </div>
+
+    <!-- 处方模板弹窗 -->
+    <el-dialog v-model="dialogVisible" title="选择处方模板" width="60%" :close-on-click-modal="false" class="template-dialog">
+      <!-- 提示信息 -->
+      <div class="tip-box">如部分模版未显示请检查预包装库位产品库存,无库存则含该产品的协定方模板将不再显示</div>
+
+      <div class="search-box">
+        <el-input v-model="searchValue" placeholder="请输入名称" class="search-input">
+          <template #append>
+            <el-button type="primary">查询</el-button>
+          </template>
+        </el-input>
+      </div>
+
+      <div class="dialog-content">
+        <!-- 左侧树形控件 -->
+        <div class="left-tree">
+          <el-tree
+            ref="treeRef"
+            :data="treeData"
+            :props="defaultProps"
+            node-key="id"
+            :default-expanded-keys="['1']"
+            show-checkbox
+            @check="handleCheck"
+          />
+        </div>
+
+        <!-- 右侧表格 -->
+        <div class="right-table">
+          <div class="table-wrapper">
+            <table class="custom-table">
+              <thead>
+                <tr>
+                  <th width="60">组号</th>
+                  <th>产品名称</th>
+                  <th width="120">用量/次</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(item, index) in templateTableData" :key="index">
+                  <td>{{ index + 1 }}</td>
+                  <td>{{ item.productName }}</td>
+                  <td>{{ item.dosePerTime }}{{ item.unit }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+
+      <div class="selected-count">已选 ({{ selectedCount }})</div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import {ref,defineProps} from 'vue';
-import {  listAll  } from '@/api/warehouse/suppliesManage';
-import { SuppliesManageVO } from '@/api/warehouse/suppliesManage/types';
-import { listNutritionConsumable, getNutritionConsumable, delNutritionConsumable, addNutritionConsumable, updateNutritionConsumable } from '@/api/patients/nutritionConsumable';
-import {NutritionConsumableForm } from '@/api/patients/nutritionConsumable/types';
-import { string } from 'vue-types';
-import { json } from 'stream/consumers';
-
-const emit = defineEmits(['update:modelValue']);
-
-const prop=defineProps({
+import {ref, defineProps} from 'vue';
+import {listAll} from '@/api/warehouse/suppliesManage';
+import {SuppliesManageVO} from '@/api/warehouse/suppliesManage/types';
+import {
+  listNutritionConsumable,
+  getNutritionConsumable,
+  delNutritionConsumable,
+  addNutritionConsumable,
+  updateNutritionConsumable
+} from '@/api/patients/nutritionConsumable';
+import {NutritionConsumableForm} from '@/api/patients/nutritionConsumable/types';
+
+let prescriptionFee = ref(1.0);
+let nutritionalFee = ref(0.0);
+let otherlFee = ref(0.0);
+let totalFee = ref(1.0);
+
+const emit = defineEmits(['change']);
+
+const prop = defineProps({
   modelValue: {
     type: String,
     default: () => undefined
-  },
+  }
 });
 
-
-interface TableRow extends NutritionConsumableForm {  
-  sn?: number;   
+interface TableRow extends NutritionConsumableForm {
+  sn?: number;
+  tableId?: number;
 }
 
 // 示例数据
 const tableData = ref<TableRow[]>([
-  {           
-    firstDay:1,
-    frequency:3,
-    dosePerTime:1,
-    dosePerDay:3,
-    usageDays:1,
-    quantity:1,
-    sn: 0    
-  }     
+  {
+    firstDay: 1,
+    frequency: 3,
+    dosePerTime: 1,
+    dosePerDay: 3,
+    usageDays: 1,
+    quantity: 1,
+    sn: 0,
+    tableId: 0
+  }
 ]);
 
-
-let suppliesList=ref<SuppliesManageVO[]>([]);
- 
+let suppliesList = ref<SuppliesManageVO[]>([]);
 
 const getSuppliesList = async () => {
   const res = await listAll();
   suppliesList.value = res.rows;
-}
-
- 
-
-// 复制行
-const copyRow = (index: number) => {
-  const newRow = JSON.parse(JSON.stringify(tableData.value[index]));
-  tableData.value.splice(index + 1, 0, newRow);
-};
-
-// 删除行
-const deleteRow = (index: number) => {
-  if (tableData.value.length === 1) return;
-  tableData.value.splice(index, 1);
 };
 
 const changeConsumableSelect = async (id: string) => {
-  let arr=id.split('_');
+  let arr = id.split('_');
   for (let item of suppliesList.value) {
+    let row = tableData.value[Number(arr[1])];
     if (item.id.toString() != arr[0]) {
-      continue
+      otherlFee.value += item.sellPrice * row.quantity;
+      continue;
     }
-    
-    
-    let row = tableData.value[Number(arr[1])];
-    row.specification=item.suppliesSpec;
-    row.amount=item.sellPrice;
+
+    let dosePerTime = 0;
+    if (row.dosePerTime) {
+      dosePerTime = row.dosePerTime;
+    }
+
+    row.specification = item.suppliesSpec;
+    row.dosePerDay = row.frequency * dosePerTime;
+    row.quantity = ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime;
+
+    if (row.consumableId) {
+      let consumableId = row.consumableId.split('_')[0];
+      for (let item of suppliesList.value) {
+        if (item.id.toString() != consumableId) {
+          continue;
+        }
+
+        row.amount = item.sellPrice * row.quantity;
+        break;
+      }
+    }
+
     break;
   }
-};
 
+  otherlFee.value = 0;
+  tableData.value.forEach((row) => {
+    if (row.amount) {
+      otherlFee.value += row.amount;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
 
 watch(
   () => prop.modelValue,
   async (val: string) => {
-    if(val&&val.trim().length>0){
-      let configData=JSON.parse(val);
-    const tableData = ref<TableRow[]>([]);
-
-    configData.forEach((item:any) => { 
-      let row: TableRow = {        
-        frequency: item.frequency,         
-        usageDays: item.usageDays,
-        firstDay: item.firstDay,
-        sn: tableData.value.length
+    if (!val || val.trim().length == 0) {
+      return;
+    }
+
+    let configData = JSON.parse(val);
+    let tableDataMap = {};
+    tableData.value.forEach((item: any) => {
+      tableDataMap[item.tableId] = item;
+    });
+
+    const newTableList = ref<TableRow[]>([]);
+    prescriptionFee.value = 0;
+    totalFee.value = 0;
+    otherlFee.value = 0;
+    nutritionalFee.value = 0;
+    prescriptionFee.value += configData.length;
+    totalFee.value += configData.length;
+
+    configData.forEach((item: any) => {
+      item.products.forEach((pd) => {
+        if (pd.amount) {
+          nutritionalFee.value += pd.amount;
+          totalFee.value += pd.amount;
+        }
+      });
+
+      let row: TableRow = {
+        frequency: Number(item.frequency),
+        usageDays: Number(item.usageDays),
+        firstDay: Number(item.firstDay),
+        dosePerTime: 1,
+        tableId: item.tableId,
+        sn: newTableList.value.length
       };
-      row.dosePerTime=0;
 
-      tableData.value.push(row);
+      let tmpData = tableDataMap[item.tableId];
+      if (tmpData) {
+        row = tmpData;
+      }
+      let dosePerTime = 0;
+      if (row.dosePerTime) {
+        dosePerTime = row.dosePerTime;
+      }
+
+      row.frequency = Number(item.frequency);
+      row.usageDays = Number(item.usageDays);
+      row.firstDay = Number(item.firstDay);
+      row.dosePerDay = item.frequency * dosePerTime;
+      row.quantity = ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime;
+
+      if (row.consumableId) {
+        let consumableId = row.consumableId.split('_')[0];
+        for (let item of suppliesList.value) {
+          if (item.id.toString() != consumableId) {
+            continue;
+          }
+
+          row.amount = item.sellPrice * row.quantity;
+          break;
+        }
+      }
+
+      if (row.amount) {
+        otherlFee.value += row.amount;
+      }
+
+      newTableList.value.push(row);
     });
-    }
-    
-    console.log(configData, "?????*************????????????????");
+
+    tableData.value = newTableList.value;
+    emit('change', JSON.stringify(tableData.value));
   },
-  { deep: true, immediate: true }
+  {deep: true, immediate: true}
 );
 
-onMounted(() => { 
-  getSuppliesList();  
-});
+const dosePerTimeInput = (value: string, sn: number) => {
+  let row = tableData.value[sn];
+  let dosePerTime = 0;
+  if (row.dosePerTime) {
+    dosePerTime = row.dosePerTime;
+  }
+  row.dosePerDay = row.frequency * dosePerTime;
+  row.quantity = ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime;
+  otherlFee.value = 0;
+
+  if (row.consumableId) {
+    let consumableId = row.consumableId.split('_')[0];
+    for (let item of suppliesList.value) {
+      if (item.id.toString() != consumableId) {
+        otherlFee.value += item.sellPrice * row.quantity;
+        continue;
+      }
+
+      row.amount = item.sellPrice * row.quantity;
+      otherlFee.value += row.amount;
+      break;
+    }
+  }
+
+  otherlFee.value = 0;
+  tableData.value.forEach((row) => {
+    if (row.amount) {
+      otherlFee.value += row.amount;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const prescriptionRemark = (value: string) => {
+  emit('change', JSON.stringify(tableData.value));
+};
+
+// 弹窗显示控制
+const dialogVisible = ref(false);
+
+// 搜索值
+const searchValue = ref('');
+// 选中的节点
+const selectedNode = ref('');
+// 已选数量
+const selectedCount = ref(2);
+
+// 树形控件数据
+const treeData = ref([
+  {
+    id: '1',
+    label: '基础公共模板',
+    children: [
+      {
+        id: '1-1',
+        label: '精氨酸组件'
+      },
+      {
+        id: '1-2',
+        label: '谷氨酰胺组件'
+      },
+      {
+        id: '1-3',
+        label: '免疫营养组件'
+      },
+      {
+        id: '1-4',
+        label: '肠内营养组件'
+      },
+      {
+        id: '1-5',
+        label: '肠外营养组件'
+      }
+    ]
+  },
+  {
+    id: '2',
+    label: '万颗星营养智能医院',
+    children: [
+      {
+        id: '2-1',
+        label: '中链甘油三酯MCT组件'
+      },
+      {
+        id: '2-2',
+        label: '口服补糖盐饮品'
+      },
+      {
+        id: '2-3',
+        label: '膳食纤维组件'
+      },
+      {
+        id: '2-4',
+        label: 'ERAS专用碳水化合物饮品'
+      },
+      {
+        id: '2-5',
+        label: '白蛋白肽组件'
+      },
+      {
+        id: '2-6',
+        label: '乳清蛋白组件'
+      },
+      {
+        id: '2-7',
+        label: '益生菌组件'
+      },
+      {
+        id: '2-8',
+        label: '鱼油蛋白组件'
+      }
+    ]
+  },
+  {
+    id: '3',
+    label: '个人模板',
+    children: [
+      {
+        id: '3-1',
+        label: '术前营养支持方案'
+      },
+      {
+        id: '3-2',
+        label: '术后恢复营养方案'
+      },
+      {
+        id: '3-3',
+        label: '重症营养支持方案'
+      }
+    ]
+  },
+  {
+    id: '4',
+    label: '科室模板',
+    children: [
+      {
+        id: '4-1',
+        label: '外科营养支持方案'
+      },
+      {
+        id: '4-2',
+        label: '内科营养支持方案'
+      },
+      {
+        id: '4-3',
+        label: 'ICU营养支持方案'
+      },
+      {
+        id: '4-4',
+        label: '肿瘤科营养支持方案'
+      }
+    ]
+  }
+]);
+
+// 树形控件配置
+const defaultProps = {
+  children: 'children',
+  label: 'label'
+};
+
+// 模板表格数据
+const templateTableData = ref([
+  {
+    productName: '氨基酸型复配营养粉2',
+    dosePerTime: '5.0000',
+    unit: 'g'
+  },
+  {
+    productName: '肠内营养全营养复合膜',
+    dosePerTime: '1.0000',
+    unit: '袋'
+  }
+]);
+
+// 树节点选中事件
+const handleCheck = (data: any, checked: any) => {
+  console.log(data, checked);
+  // 这里处理节点选中逻辑
+};
+
+// 打开处方模板
+const openTemplate = () => {
+  dialogVisible.value = true;
+};
+
+// 确认选择模板
+const handleConfirm = () => {
+  dialogVisible.value = false;
+  // 这里处理选中模板后的逻辑
+};
 
+onMounted(() => {
+  getSuppliesList();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -419,4 +743,95 @@ onMounted(() => {
     }
   }
 }
+
+.template-dialog {
+  :deep(.el-dialog__body) {
+    padding: 0 20px;
+  }
+
+  .tip-box {
+    background-color: #e6f7ff;
+    padding: 12px 16px;
+    color: #606266;
+    font-size: 14px;
+    margin-bottom: 16px;
+  }
+
+  .search-box {
+    margin-bottom: 16px;
+
+    .search-input {
+      width: 300px;
+    }
+  }
+
+  .dialog-content {
+    display: flex;
+    min-height: 350px;
+    border: 1px solid #ebeef5;
+
+    .left-tree {
+      width: 220px;
+      padding: 12px;
+      border-right: 1px solid #ebeef5;
+      max-height: 350px;
+      overflow-y: auto;
+
+      :deep(.el-tree-node__content) {
+        height: 32px;
+      }
+
+      :deep(.el-tree-node__content:hover) {
+        background-color: #f5f7fa;
+      }
+
+      :deep(.el-tree-node.is-current > .el-tree-node__content) {
+        background-color: #f5f7fa;
+      }
+    }
+
+    .right-table {
+      flex: 1;
+      padding: 12px;
+      overflow: auto;
+
+      .table-wrapper {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .selected-count {
+    margin: 16px 0;
+    color: #409eff;
+    font-size: 14px;
+  }
+}
+
+.custom-table {
+  width: 100%;
+  border-collapse: collapse;
+  border: 1px solid #ebeef5;
+
+  th,
+  td {
+    padding: 12px 8px;
+    border: 1px solid #ebeef5;
+    text-align: center;
+    font-size: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  th {
+    background-color: #f5f7fa;
+    color: #303133;
+    font-weight: 600;
+  }
+
+  tbody tr:hover {
+    background-color: #f5f7fa;
+  }
+}
 </style>

+ 837 - 0
src/views/patients/enteralNutrition/components/table/MaterialTable2.vue

@@ -0,0 +1,837 @@
+<template>
+  <div class="material-table">
+    <el-table :data="tableData" border :max-height="600">
+      <el-table-column type="index" label="组号" width="60" align="center" />
+      <el-table-column label="耗材" min-width="300">
+        <template #default="{row}">
+          <div class="material-group">
+            <div class="material-row" style="margin-bottom: 10px">
+              <el-select v-model="row.consumableId" placeholder="请选择" @change="changeConsumableSelect">
+                <el-option v-for="pn in suppliesList" :key="pn.id" :label="pn.suppliesName" :value="pn.id + '_' + row.sn"></el-option>
+              </el-select>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="规格" width="100" align="center">
+        <template #default="{row}">
+          <div class="spec-group">
+            <span>{{ row.specification }}</span>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="用量/次" width="200" align="center">
+        <template #default="{row}">
+          <div class="dosage-group">
+            <div class="dosage-input">
+              <el-input v-model="row.dosePerTime" placeholder="请输入" class="input-center" @input="dosePerTimeInput(row.dosePerTime, row.sn)">
+                <template #append>袋</template>
+              </el-input>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="频次" width="100" align="center">
+        <template #default="{row}">
+          <span>一天{{ row.frequency }}次</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="用量/日" width="100" align="center">
+        <template #default="{row}">
+          <span>{{ row.dosePerDay }}</span>
+          <span class="unit">袋</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="使用天数" width="100" align="center">
+        <template #default="{row}">
+          <span>{{ row.usageDays }}天</span>
+        </template>
+      </el-table-column> -->
+      <el-table-column label="首日" width="80" align="center">
+        <template #default="{row}">
+          <span>{{ row.firstDay }}次</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="数量" width="80" align="center">
+        <template #default="{row}">
+          <span>{{ row.quantity }}袋</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="处方备注" width="200" align="center">
+        <template #default="{row}">
+          <el-input v-model="row.prescriptionRemark" :maxlength="100" :show-word-limit="true" @change="prescriptionRemark" />
+        </template>
+      </el-table-column>
+
+      <el-table-column label="金额" width="80" align="center">
+        <template #default="{row}">
+          <span>{{ row.amount }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="table-footer">
+      <div class="left-buttons">
+        <el-button type="primary" plain>登为个人模板</el-button>
+        <el-button type="primary" plain>批量添加营养品</el-button>
+        <el-button type="primary" plain @click="openTemplate">处方模板</el-button>
+      </div>
+      <div class="right-summary">
+        <div class="summary-list">
+          <div class="summary-item">
+            <span class="label">处方费用:</span>
+            <span class="value">¥{{ nutritionalFee + otherlFee }}</span>
+          </div>
+          <div class="summary-item">
+            <span class="label">营养配置费:</span>
+            <span class="value">¥{{ prescriptionFee }}</span>
+          </div>
+          <div class="summary-item total">
+            <span class="label">总计:</span>
+            <span class="value">¥{{ totalFee + otherlFee }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 处方模板弹窗 -->
+  <el-dialog v-model="dialogVisible" title="选择处方模板" width="60%" :close-on-click-modal="false" class="template-dialog">
+    <!-- 提示信息 -->
+    <div class="tip-box">如部分模版未显示请检查预包装库位产品库存,无库存则含该产品的协定方模板将不再显示</div>
+
+    <div class="search-box">
+      <el-input v-model="searchValue" placeholder="请输入名称" class="search-input">
+        <template #append>
+          <el-button type="primary">查询</el-button>
+        </template>
+      </el-input>
+    </div>
+
+    <div class="dialog-content">
+      <!-- 左侧树形控件 -->
+      <div class="left-tree">
+        <el-tree
+          ref="treeRef"
+          :data="treeData"
+          :props="defaultProps"
+          node-key="id"
+          :default-expanded-keys="['1']"
+          show-checkbox
+          @check="handleCheck"
+        />
+      </div>
+
+      <!-- 右侧表格 -->
+      <div class="right-table">
+        <div class="table-wrapper">
+          <table class="custom-table">
+            <thead>
+              <tr>
+                <th width="60">组号</th>
+                <th>产品名称</th>
+                <th width="120">用量/次</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr v-for="(item, index) in templateTableData" :key="index">
+                <td>{{ index + 1 }}</td>
+                <td>{{ item.productName }}</td>
+                <td>{{ item.dosePerTime }}{{ item.unit }}</td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+
+    <div class="selected-count">已选 ({{ selectedCount }})</div>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import {ref, defineProps, watch, onMounted} from 'vue';
+import {listAll} from '@/api/warehouse/suppliesManage';
+import {SuppliesManageVO} from '@/api/warehouse/suppliesManage/types';
+import {
+  listNutritionConsumable,
+  getNutritionConsumable,
+  delNutritionConsumable,
+  addNutritionConsumable,
+  updateNutritionConsumable
+} from '@/api/patients/nutritionConsumable';
+import {NutritionConsumableForm} from '@/api/patients/nutritionConsumable/types';
+
+let prescriptionFee = ref(1.0);
+let nutritionalFee = ref(0.0);
+let otherlFee = ref(0.0);
+let totalFee = ref(1.0);
+
+const emit = defineEmits(['change']);
+
+const prop = defineProps({
+  modelValue: {
+    type: String,
+    default: () => undefined
+  }
+});
+
+interface TableRow extends NutritionConsumableForm {
+  sn?: number;
+  tableId?: number;
+}
+
+// 示例数据
+const tableData = ref<TableRow[]>([
+  {
+    firstDay: 2,
+    frequency: 3,
+    dosePerTime: 1,
+    dosePerDay: 3,
+    usageDays: 0,
+    quantity: 0,
+    sn: 0,
+    tableId: 0
+  }
+]);
+
+let suppliesList = ref<SuppliesManageVO[]>([]);
+
+const getSuppliesList = async () => {
+  const res = await listAll();
+  suppliesList.value = res.rows;
+};
+
+const changeConsumableSelect = async (id: string) => {
+  let arr = id.split('_');
+  for (let item of suppliesList.value) {
+    let row = tableData.value[Number(arr[1])];
+    if (item.id.toString() != arr[0]) {
+      otherlFee.value += item.sellPrice * row.quantity;
+      continue;
+    }
+
+    let dosePerTime = 0;
+    if (row.dosePerTime) {
+      dosePerTime = Number(row.dosePerTime);
+    }
+
+    row.specification = item.suppliesSpec;
+    row.dosePerDay = row.frequency * dosePerTime;
+    row.quantity = Math.max(0, ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime);
+
+    if (row.consumableId) {
+      let consumableId = row.consumableId.split('_')[0];
+      for (let item of suppliesList.value) {
+        if (item.id.toString() != consumableId) {
+          continue;
+        }
+
+        row.amount = item.sellPrice * row.quantity;
+        break;
+      }
+    }
+
+    break;
+  }
+
+  otherlFee.value = 0;
+  tableData.value.forEach((row) => {
+    if (row.amount) {
+      otherlFee.value += row.amount;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+watch(
+  () => prop.modelValue,
+  async (val: string) => {
+    if (!val || val.trim().length == 0) {
+      return;
+    }
+
+    let configData = JSON.parse(val);
+    let tableDataMap = {};
+    tableData.value.forEach((item: any) => {
+      tableDataMap[item.tableId] = item;
+    });
+
+    const newTableList = ref<TableRow[]>([]);
+    prescriptionFee.value = 0;
+    totalFee.value = 0;
+    otherlFee.value = 0;
+    nutritionalFee.value = 0;
+    prescriptionFee.value += configData.length;
+    totalFee.value += configData.length;
+
+    configData.forEach((item: any) => {
+      item.products.forEach((pd) => {
+        if (pd.amount) {
+          nutritionalFee.value += pd.amount;
+          totalFee.value += pd.amount;
+        }
+      });
+
+      let row: TableRow = {
+        frequency: Number(item.frequency),
+        usageDays: Number(item.usageDays),
+        firstDay: Number(item.firstDay),
+        dosePerTime: 1,
+        tableId: item.tableId,
+        sn: newTableList.value.length
+      };
+
+      let tmpData = tableDataMap[item.tableId];
+      if (tmpData) {
+        row = tmpData;
+      }
+      let dosePerTime = 0;
+      if (row.dosePerTime) {
+        dosePerTime = Number(row.dosePerTime);
+      }
+
+      row.frequency = Number(item.frequency);
+      row.usageDays = Number(item.usageDays);
+      row.firstDay = Number(item.firstDay);
+      row.dosePerDay = item.frequency * dosePerTime;
+      row.quantity = Math.max(0, ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime);
+
+      if (row.consumableId) {
+        let consumableId = row.consumableId.split('_')[0];
+        for (let item of suppliesList.value) {
+          if (item.id.toString() != consumableId) {
+            continue;
+          }
+
+          row.amount = item.sellPrice * row.quantity;
+          break;
+        }
+      }
+
+      if (row.amount) {
+        otherlFee.value += row.amount;
+      }
+
+      newTableList.value.push(row);
+    });
+
+    tableData.value = newTableList.value;
+    emit('change', JSON.stringify(tableData.value));
+  },
+  {deep: true, immediate: true}
+);
+
+const dosePerTimeInput = (value: string, sn: number) => {
+  let row = tableData.value[sn];
+  let dosePerTime = 0;
+  if (row.dosePerTime) {
+    dosePerTime = row.dosePerTime;
+  }
+  row.dosePerDay = row.frequency * dosePerTime;
+  row.quantity = Math.max(0, ((row.usageDays - 1) * row.frequency + row.firstDay) * dosePerTime);
+  otherlFee.value = 0;
+
+  if (row.consumableId) {
+    let consumableId = row.consumableId.split('_')[0];
+    for (let item of suppliesList.value) {
+      if (item.id.toString() != consumableId) {
+        otherlFee.value += item.sellPrice * row.quantity;
+        continue;
+      }
+
+      row.amount = item.sellPrice * row.quantity;
+      otherlFee.value += row.amount;
+      break;
+    }
+  }
+
+  otherlFee.value = 0;
+  tableData.value.forEach((row) => {
+    if (row.amount) {
+      otherlFee.value += row.amount;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const prescriptionRemark = (value: string) => {
+  emit('change', JSON.stringify(tableData.value));
+};
+
+// 弹窗显示控制
+const dialogVisible = ref(false);
+
+// 搜索值
+const searchValue = ref('');
+// 选中的节点
+const selectedNode = ref('');
+// 已选数量
+const selectedCount = ref(2);
+
+// 树形控件数据
+const treeData = ref([
+  {
+    id: '1',
+    label: '基础公共模板',
+    children: [
+      {
+        id: '1-1',
+        label: '精氨酸组件'
+      },
+      {
+        id: '1-2',
+        label: '谷氨酰胺组件'
+      },
+      {
+        id: '1-3',
+        label: '免疫营养组件'
+      },
+      {
+        id: '1-4',
+        label: '肠内营养组件'
+      },
+      {
+        id: '1-5',
+        label: '肠外营养组件'
+      }
+    ]
+  },
+  {
+    id: '2',
+    label: '万颗星营养智能医院',
+    children: [
+      {
+        id: '2-1',
+        label: '中链甘油三酯MCT组件'
+      },
+      {
+        id: '2-2',
+        label: '口服补糖盐饮品'
+      },
+      {
+        id: '2-3',
+        label: '膳食纤维组件'
+      },
+      {
+        id: '2-4',
+        label: 'ERAS专用碳水化合物饮品'
+      },
+      {
+        id: '2-5',
+        label: '白蛋白肽组件'
+      },
+      {
+        id: '2-6',
+        label: '乳清蛋白组件'
+      },
+      {
+        id: '2-7',
+        label: '益生菌组件'
+      },
+      {
+        id: '2-8',
+        label: '鱼油蛋白组件'
+      }
+    ]
+  },
+  {
+    id: '3',
+    label: '个人模板',
+    children: [
+      {
+        id: '3-1',
+        label: '术前营养支持方案'
+      },
+      {
+        id: '3-2',
+        label: '术后恢复营养方案'
+      },
+      {
+        id: '3-3',
+        label: '重症营养支持方案'
+      }
+    ]
+  },
+  {
+    id: '4',
+    label: '科室模板',
+    children: [
+      {
+        id: '4-1',
+        label: '外科营养支持方案'
+      },
+      {
+        id: '4-2',
+        label: '内科营养支持方案'
+      },
+      {
+        id: '4-3',
+        label: 'ICU营养支持方案'
+      },
+      {
+        id: '4-4',
+        label: '肿瘤科营养支持方案'
+      }
+    ]
+  }
+]);
+
+// 树形控件配置
+const defaultProps = {
+  children: 'children',
+  label: 'label'
+};
+
+// 模板表格数据
+const templateTableData = ref([
+  {
+    productName: '氨基酸型复配营养粉2',
+    dosePerTime: '5.0000',
+    unit: 'g'
+  },
+  {
+    productName: '肠内营养全营养复合膜',
+    dosePerTime: '1.0000',
+    unit: '袋'
+  }
+]);
+
+// 树节点选中事件
+const handleCheck = (data: any, checked: any) => {
+  console.log(data, checked);
+  // 这里处理节点选中逻辑
+};
+
+// 打开处方模板
+const openTemplate = () => {
+  dialogVisible.value = true;
+};
+
+// 确认选择模板
+const handleConfirm = () => {
+  dialogVisible.value = false;
+  // 这里处理选中模板后的逻辑
+};
+
+onMounted(() => {
+  getSuppliesList();
+});
+</script>
+
+<style lang="scss" scoped>
+.material-table {
+  :deep(.el-table) {
+    // 表头样式
+    .el-table__header {
+      th {
+        background-color: #f5f7fa;
+        color: #606266;
+        font-weight: 500;
+        padding: 8px 0;
+        height: 40px;
+      }
+    }
+
+    // 表格行样式
+    .el-table__row {
+      td {
+        padding: 4px 8px;
+      }
+    }
+  }
+
+  // 耗材输入框组样式
+  .material-group {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+
+    .material-row {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      .el-input {
+        flex: 1;
+      }
+    }
+  }
+
+  // 规格组样式
+  .spec-group {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    padding: 8px 0;
+
+    span {
+      color: #606266;
+      font-size: 14px;
+    }
+  }
+
+  // 用量输入框组样式
+  .dosage-group {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+
+    .dosage-input {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 4px;
+
+      .input-center {
+        width: 100px;
+
+        :deep(.el-input__inner) {
+          text-align: center;
+        }
+      }
+
+      .unit {
+        color: #909399;
+        font-size: 14px;
+        flex-shrink: 0;
+      }
+    }
+  }
+
+  // 餐次时间样式
+  .time-slots {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+
+    .time-row {
+      display: flex;
+      gap: 8px;
+
+      .time-group {
+        display: flex;
+        align-items: center;
+        gap: 4px;
+
+        :deep(.el-checkbox) {
+          margin-right: 0;
+          .el-checkbox__label {
+            display: none;
+          }
+        }
+
+        :deep(.el-time-select) {
+          .el-input {
+            width: 120px;
+          }
+          .el-input__wrapper {
+            padding: 0 8px;
+            border-radius: 2px;
+            box-shadow: 0 0 0 1px #dcdfe6 inset;
+            &:hover:not(.is-disabled) {
+              box-shadow: 0 0 0 1px #c0c4cc inset;
+            }
+            &.is-disabled {
+              background-color: #f5f7fa;
+              box-shadow: 0 0 0 1px #e4e7ed inset;
+              .el-input__inner {
+                color: #909399;
+                -webkit-text-fill-color: #909399;
+              }
+            }
+          }
+          .el-input__inner {
+            height: 32px;
+            text-align: center;
+            font-size: 14px;
+            color: #606266;
+          }
+        }
+      }
+    }
+  }
+
+  // 操作按钮样式
+  :deep(.el-button--link) {
+    height: 28px;
+    padding: 0 8px;
+    font-size: 13px;
+
+    &.el-button--primary {
+      color: var(--el-color-primary);
+      &:hover {
+        color: var(--el-color-primary-light-3);
+      }
+    }
+
+    &.el-button--danger {
+      color: var(--el-color-danger);
+      &:hover {
+        color: var(--el-color-danger-light-3);
+      }
+    }
+  }
+
+  .table-footer {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-top: 16px;
+
+    .left-buttons {
+      display: flex;
+      gap: 12px;
+
+      .el-button {
+        height: 32px;
+        padding: 0 16px;
+        font-size: 14px;
+      }
+    }
+
+    .right-summary {
+      min-width: 200px;
+
+      .summary-list {
+        display: flex;
+        flex-direction: column;
+        align-items: flex-end;
+        gap: 4px;
+
+        .summary-item {
+          display: flex;
+          align-items: center;
+          justify-content: flex-end;
+          gap: 8px;
+          text-align: right;
+
+          .label {
+            font-size: 16px;
+            color: #606266;
+          }
+
+          .value {
+            font-size: 20px;
+            color: #606266;
+            min-width: 80px;
+            text-align: right;
+          }
+
+          &.total {
+            margin-top: 4px;
+
+            .label,
+            .value {
+              color: #f56c6c;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.template-dialog {
+  :deep(.el-dialog__body) {
+    padding: 0 20px;
+  }
+
+  .tip-box {
+    background-color: #e6f7ff;
+    padding: 12px 16px;
+    color: #606266;
+    font-size: 14px;
+    margin-bottom: 16px;
+  }
+
+  .search-box {
+    margin-bottom: 16px;
+
+    .search-input {
+      width: 300px;
+    }
+  }
+
+  .dialog-content {
+    display: flex;
+    min-height: 350px;
+    border: 1px solid #ebeef5;
+
+    .left-tree {
+      width: 220px;
+      padding: 12px;
+      border-right: 1px solid #ebeef5;
+      max-height: 350px;
+      overflow-y: auto;
+
+      :deep(.el-tree-node__content) {
+        height: 32px;
+      }
+
+      :deep(.el-tree-node__content:hover) {
+        background-color: #f5f7fa;
+      }
+
+      :deep(.el-tree-node.is-current > .el-tree-node__content) {
+        background-color: #f5f7fa;
+      }
+    }
+
+    .right-table {
+      flex: 1;
+      padding: 12px;
+      overflow: auto;
+
+      .table-wrapper {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .selected-count {
+    margin: 16px 0;
+    color: #409eff;
+    font-size: 14px;
+  }
+}
+
+.custom-table {
+  width: 100%;
+  border-collapse: collapse;
+  border: 1px solid #ebeef5;
+
+  th,
+  td {
+    padding: 12px 8px;
+    border: 1px solid #ebeef5;
+    text-align: center;
+    font-size: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  th {
+    background-color: #f5f7fa;
+    color: #303133;
+    font-weight: 600;
+  }
+
+  tbody tr:hover {
+    background-color: #f5f7fa;
+  }
+}
+</style>

+ 395 - 110
src/views/patients/enteralNutrition/components/table/NutritionTable.vue

@@ -1,11 +1,20 @@
 <template>
   <div class="nutrition-table">
     <!-- 停嘱日期 (仅长期医嘱显示) -->
-    <div v-if="type === 'long-term'" class="stop-date">
+    <div class="stop-date">
       <span class="required">*</span>
       <span class="label">停嘱日期</span>
-      <el-date-picker v-model="stopDate" type="date" placeholder="请选择" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
-      <span class="days">(使用天数:0天)</span>
+      <el-date-picker
+        v-model="stopDate"
+        type="date"
+        placeholder="请选择"
+        format="YYYY-MM-DD"
+        value-format="YYYY-MM-DD"
+        :clearable="false"
+        @change="handleStopDateChange"
+        :disabled-date="disabledBeforeToday"
+      />
+      <span class="days">(使用天数:{{ daysDifference }}天)</span>
     </div>
 
     <el-table :data="tableData" border>
@@ -13,50 +22,43 @@
       <el-table-column label="营养产品" min-width="300">
         <template #default="{row}">
           <div class="product-group">
-            <template v-if="!row.isNewRow">
-              <div v-for="(product, index) in row.products" :key="index" class="product-row">
-                <el-input v-model="product.value" placeholder="请输入" />
-                <div class="action-buttons">
-                  <el-button class="icon-btn" @click="addProduct(row)">
-                    <el-icon><Plus /></el-icon>
-                  </el-button>
-                  <el-button class="icon-btn" @click="removeProduct(row, index)">
-                    <el-icon><Delete /></el-icon>
-                  </el-button>
-                </div>
+            <div v-for="(product, index) in row.products" :key="index" class="product-row">
+              <el-select v-model="product.nutritionProductId" placeholder="请选择" @change="changeProductSelect">
+                <el-option
+                  v-for="pn in productNutritionList"
+                  :key="pn.id"
+                  :label="pn.productName"
+                  :value="pn.id + '_' + row.sn + '_' + index"
+                ></el-option>
+              </el-select>
+              <div class="action-buttons">
+                <el-button class="icon-btn" @click="addProduct(row)">
+                  <el-icon>
+                    <Plus />
+                  </el-icon>
+                </el-button>
+                <el-button class="icon-btn" @click="removeProduct(row, index)">
+                  <el-icon>
+                    <Delete />
+                  </el-icon>
+                </el-button>
               </div>
-            </template>
-            <template v-else>
-              <div v-for="(product, index) in row.products" :key="index" class="product-row">
-                <el-input v-model="product.value" placeholder="请选择" />
-                <div class="action-buttons">
-                  <el-button class="icon-btn" @click="addProduct(row)">
-                    <el-icon><Plus /></el-icon>
-                  </el-button>
-                  <el-button class="icon-btn" @click="removeProduct(row, index)">
-                    <el-icon><Delete /></el-icon>
-                  </el-button>
-                </div>
-              </div>
-            </template>
+            </div>
           </div>
         </template>
       </el-table-column>
       <el-table-column label="用量/次" width="150" align="center">
         <template #default="{row}">
           <div class="dosage-group">
-            <template v-if="!row.isNewRow">
-              <div v-for="(product, index) in row.products" :key="index" class="dosage-input">
-                <el-input v-model="product.dosage" placeholder="请输入" class="input-center" />
-                <span class="unit">g</span>
-              </div>
-            </template>
-            <template v-else>
-              <div v-for="(product, index) in row.products" :key="index" class="dosage-input">
-                <el-input v-model="product.dosage" placeholder="请输入" class="input-center" />
-                <span class="unit">g</span>
-              </div>
-            </template>
+            <div v-for="(product, index) in row.products" :key="index" class="dosage-input">
+              <el-input
+                v-model="product.dosePerTime"
+                placeholder="请输入"
+                class="input-center"
+                @input="dosePerTimeInput(product.dosePerTime, row.sn + '_' + index)"
+              />
+              <span class="unit">g</span>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -65,34 +67,34 @@
           <div class="time-slots">
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[0].checked" />
+                <el-checkbox v-model="row.timeSlots[0].checked" @change="changeTimeSelection(row.timeSlots[0].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[0].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[0].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[1].checked" />
+                <el-checkbox v-model="row.timeSlots[1].checked" @change="changeTimeSelection(row.timeSlots[1].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[1].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[1].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[2].checked" />
+                <el-checkbox v-model="row.timeSlots[2].checked" @change="changeTimeSelection(row.timeSlots[2].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[2].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[2].checked"
                 />
@@ -100,34 +102,34 @@
             </div>
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[3].checked" />
+                <el-checkbox v-model="row.timeSlots[3].checked" @change="changeTimeSelection(row.timeSlots[3].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[3].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[3].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[4].checked" />
+                <el-checkbox v-model="row.timeSlots[4].checked" @change="changeTimeSelection(row.timeSlots[4].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[4].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[4].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[5].checked" />
+                <el-checkbox v-model="row.timeSlots[5].checked" @change="changeTimeSelection(row.timeSlots[5].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[5].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[5].checked"
                 />
@@ -137,26 +139,98 @@
         </template>
       </el-table-column>
       <el-table-column label="频次" width="120" align="center">
-        <template #default>
-          <span>一天3次</span>
+        <template #default="{row}">
+          <span>一天{{ row.frequency }}次</span>
         </template>
       </el-table-column>
+
+      <el-table-column label="首日" width="150" align="center">
+        <template #default="{row}">
+          <el-input v-model="row.firstDay" @input="firstDayInput(row.firstDay, row.sn)">
+            <template #append>次</template>
+          </el-input>
+        </template>
+      </el-table-column>
+
       <el-table-column label="用量/日" width="150" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
+            <span>{{ product.dosePerDay }}</span>
+            <span class="unit">{{ product.productSpecUnit }}</span>
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="规格" width="100" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
+            <span>{{ product.specification }}</span>
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="用法" width="150" align="center">
+        <template #default="{row}">
+          <el-select v-model="row.usage" placeholder="请选择" @change="changeUsage(row.usage, row.sn)">
+            <el-option v-for="dict in default_usage" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+          </el-select>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="制剂液量/次" width="170" align="center">
+        <template #default="{row}">
+          <el-input v-model="row.preparationVolumePerTime" @change="changePreparationVolumePerTime(row.preparationVolumePerTime, row.sn)">
+            <template #append>ml</template>
+          </el-input>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="制剂浓度/次" width="100" align="center">
         <template #default="{row}">
           <div class="daily-dosage">
-            <span>{{ row.dailyDosage }}</span>
-            <span class="unit">袋</span>
+            <span>{{ row.preparationConcentrationPerTime }}</span>
+            <span class="unit">%</span>
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="使用天数" width="120" align="center">
+
+      <el-table-column label="能量密度/次" width="200" align="center">
+        <template #default="{row}">
+          <el-input v-model="row.energyDensityPerTime" @change="changeEnergyDensityPerTime(row.energyDensityPerTime, row.sn)">
+            <template #append>kcal/ml</template>
+          </el-input>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="处方备注" width="200" align="center">
         <template #default="{row}">
-          <div class="days">
-            <span>{{ row.days }}</span>
-            <span class="unit">天</span>
+          <template v-for="(product, index) in row.products">
+            <el-input
+              v-model="product.prescriptionRemark"
+              :maxlength="100"
+              :show-word-limit="true"
+              @change="changePrescriptionRemark(product.prescriptionRemark, row.sn)"
+            />
+          </template>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="每日热量" width="100" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
+            <span>{{ product.dailyCalories }}</span>
           </div>
         </template>
       </el-table-column>
+
+      <el-table-column label="金额" width="100" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage" v-for="(product, index) in row.products" :key="index">
+            <span>{{ product.amount }}</span>
+          </div>
+        </template>
+      </el-table-column>
+
       <el-table-column label="操作" width="160" align="center" fixed="right">
         <template #default="{$index}">
           <div class="operation-cell">
@@ -168,7 +242,9 @@
     </el-table>
     <div class="table-footer">
       <el-button type="primary" class="add-prescription-btn" @click="addNewRow">
-        <el-icon><CirclePlus /></el-icon>
+        <el-icon>
+          <CirclePlus />
+        </el-icon>
         开具多处方
       </el-button>
     </div>
@@ -178,65 +254,103 @@
 <script setup lang="ts">
 import {ref, defineProps} from 'vue';
 import {Plus, Delete, CirclePlus} from '@element-plus/icons-vue';
+import {listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition} from '@/api/patients/nutrition';
+import {EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm} from '@/api/patients/nutrition/types';
+import {listNutrition, listAllNutrition} from '@/api/warehouse/productNutrition/index';
+import {NutritionVO, NutritionQuery, NutritionForm} from '@/api/warehouse/productNutrition/types';
 
-const props = defineProps<{
-  type?: 'normal' | 'long-term';
-}>();
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
+const {default_usage} = toRefs<any>(proxy?.useDict('default_usage'));
+
+let productNutritionList = ref<NutritionVO[]>([]);
+let tableId = ref<number>(0);
+
+const prop = defineProps({
+  prescriptionDate: {
+    type: String,
+    default: () => undefined
+  }
+});
+
+const emit = defineEmits(['change']);
 
 // 停嘱日期
 const stopDate = ref('');
+const daysDifference = ref(0);
 
 interface TimeSlot {
   checked: boolean;
   time: string;
 }
 
-interface Product {
-  value: string;
-  dosage: string;
-}
-
-interface TableRow {
-  products: Product[];
+interface TableRow extends EnteralNutritionForm {
+  products: EnteralNutritionForm[];
   timeSlots: TimeSlot[];
-  dailyDosage: number;
-  days: number;
-  isNewRow?: boolean;
+  sn: number;
+  tableId: number;
 }
 
 // 创建默认行数据的函数
-const createDefaultRow = (isNew: boolean = false): TableRow => ({
-  products: isNew
-    ? [{value: '', dosage: ''}]
-    : [
-        {value: '', dosage: ''},
-        {value: '', dosage: ''},
-        {value: '', dosage: ''}
-      ],
+const createDefaultRow = (): TableRow => ({
+  products: [{specification: '', totalDose: '', dosePerDay: ''}],
   timeSlots: [
-    {checked: false, time: '08:00'},
+    {checked: true, time: '08:00'},
     {checked: false, time: '08:30'},
-    {checked: false, time: '12:00'},
+    {checked: true, time: '12:00'},
     {checked: false, time: '12:30'},
-    {checked: false, time: '18:00'},
+    {checked: true, time: '18:00'},
     {checked: false, time: '18:30'}
   ],
-  dailyDosage: 2.0,
-  days: 1,
-  isNewRow: isNew
+  usageDays: 0,
+  frequency: 3,
+  firstDay: 2,
+  sn: 0,
+  tableId: tableId.value++
 });
 
 // 示例数据
-const tableData = ref<TableRow[]>([createDefaultRow(), createDefaultRow()]);
+const tableData = ref<TableRow[]>([createDefaultRow()]);
+
+const changeProductSelect = async (id: string) => {
+  let arr = id.split('_');
+  for (let item of productNutritionList.value) {
+    if (item.id.toString() != arr[0]) {
+      continue;
+    }
+
+    let row = tableData.value[Number(arr[1])];
+    let pt = row.products[Number(arr[2])];
+    pt.specification=item.productSpec;
+    pt.dosePerTime=null;     
+    pt.dailyCalories=null;
+    pt.productSpecUnit=item.productSpecUnit;
+    pt.amount=null; 
+    row.usage=item.defaultUsage;
+    pt.totalDose='0' ;
+    pt.dosePerDay='0';
+    pt.firstDay=Math.ceil(row.frequency/2.0);
+    break;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const getProductNutritionList = async () => {
+  const res = await listAllNutrition();
+  productNutritionList.value = res.rows;
+};
 
 // 新增行
 const addNewRow = () => {
-  tableData.value.push(createDefaultRow(true));
+  let row: TableRow = createDefaultRow();
+  row.sn = tableData.value.length;
+  tableData.value.push(row);
+  emit('change', JSON.stringify(tableData.value));
 };
 
 // 新增产品输入框
 const addProduct = (row: TableRow) => {
-  row.products.push({value: '', dosage: ''});
+  row.products.push({});
 };
 
 // 删除产品输入框
@@ -247,35 +361,206 @@ const removeProduct = (row: TableRow, index: number) => {
     if (rowIndex !== -1) {
       tableData.value.splice(rowIndex, 1);
     }
+    emit('change', JSON.stringify(tableData.value));
   } else {
     // 否则只删除当前输入框
     row.products.splice(index, 1);
   }
 };
 
-// 选择产品
-const selectProduct = (row: TableRow, index: number) => {
-  // TODO: 实现产品选择逻辑
-  console.log('选择产品', row, index);
-};
-
-// 删除产品
-const deleteProduct = (row: TableRow, index: number) => {
-  // TODO: 实现产品删除逻辑
-  console.log('删除产品', row, index);
-};
-
 // 复制行
 const copyRow = (index: number) => {
   const newRow = JSON.parse(JSON.stringify(tableData.value[index]));
+  newRow.tableId = tableId.value++;
   tableData.value.splice(index + 1, 0, newRow);
+  tableData.value.forEach((item, index) => {
+    item.sn = index;
+    item.products.forEach((ch, idx) => {
+      if (ch.nutritionProductId) {
+        let arr = ch.nutritionProductId.split('_');
+        ch.nutritionProductId = arr[0] + '_' + index + '_' + idx;
+      }
+    });
+  });
+  emit('change', JSON.stringify(tableData.value));
 };
 
 // 删除行
 const deleteRow = (index: number) => {
-  if (tableData.value.length === 1) return;
   tableData.value.splice(index, 1);
+
+  tableData.value.forEach((item, index) => {
+    item.sn = index;
+  });
+
+  emit('change', JSON.stringify(tableData.value));
 };
+
+const changeUsageDays = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changeUsage = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changePreparationVolumePerTime = (value: string, sn:string) => { 
+  changeTimeSelection(null,sn);
+}
+
+const changeEnergyDensityPerTime = (value: string, sn:string) => { 
+  changeTimeSelection(null,sn);
+}
+
+const changePrescriptionRemark = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const firstDayInput = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changeTimeSelection = (value: string, sn: string) => {
+  let row = tableData.value[sn];
+  let frequency = 0;
+  row.timeSlots.forEach((item) => {
+    if (item.checked) {
+      frequency++;
+    }
+  });
+  row.frequency = frequency;
+  if (value != null) {
+    row.firstDay = Math.ceil(row.frequency / 2.0);
+  }
+
+  row.products.forEach((pt) => {
+    if (!pt.dosePerTime || pt.dosePerTime.trim().length == 0) {
+      return;
+    }
+    let arr = pt.nutritionProductId.split('_');
+
+    for (let item of productNutritionList.value) {
+      if (item.id.toString() != arr[0]) {
+        continue;
+      }
+
+      let val = (item.calorie * row.frequency * Number(pt.dosePerTime)) / 100.0;
+      pt.dailyCalories = val.toFixed(4);
+
+      let firstDay = Number(row.firstDay.toString());
+      val = (row.frequency * (daysDifference.value - 1) + firstDay) * item.configSalePrice * Number(pt.dosePerTime);
+      val = Math.max(0, val);
+      pt.amount = Math.round(val * 100) / 100;
+
+      val = (row.frequency * (daysDifference.value - 1) + firstDay) * Number(pt.dosePerTime);
+      val = Math.max(0, val);
+      pt.totalDose = val.toFixed(4);
+
+      val = row.frequency * Number(pt.dosePerTime);
+      pt.dosePerDay = val.toFixed(4);
+      break;
+    }
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const dosePerTimeInput = (value: string, str: string) => {
+  let arr = str.split('_');
+  let row = tableData.value[Number(arr[0])];
+  let pt = row.products[Number(arr[1])];
+  if (!pt.dosePerTime || pt.dosePerTime.trim().length == 0) {
+    pt.dailyCalories = null;
+    pt.amount = null;
+    return;
+  }
+
+  arr = pt.nutritionProductId.split('_');
+  for (let item of productNutritionList.value) {
+    if (item.id.toString() != arr[0]) {
+      continue;
+    }
+
+    let val = (item.calorie * row.frequency * Number(pt.dosePerTime)) / 100.0;
+    pt.dailyCalories = val.toFixed(4);
+
+    let firstDay = Number(row.firstDay.toString());
+    val = (row.frequency * (daysDifference.value - 1) + firstDay) * item.configSalePrice * Number(pt.dosePerTime);
+    val = Math.max(0, val);
+    pt.amount = Math.round(val * 100) / 100;
+
+    val = (row.frequency * (daysDifference.value - 1) + firstDay) * Number(pt.dosePerTime);
+    val = Math.max(0, val);
+    pt.totalDose = val.toFixed(4);
+
+    val = row.frequency * Number(pt.dosePerTime);
+    pt.dosePerDay = val.toFixed(4);
+    break;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+// 禁用今天之前的日期
+const disabledBeforeToday = (date) => {
+  const start = new Date(prop.prescriptionDate);
+  const today = new Date();
+  // 设置时间为今天凌晨,确保比较的是日期而非具体时间
+  today.setHours(0, 0, 0, 0);
+  today.setDate(start.getDate() + 1);
+  return date < today;
+};
+
+watch(
+  () => prop.prescriptionDate,
+  async (val: string) => {
+    if (!val || val.trim().length == 0 || !stopDate.value || stopDate.value.trim().length == 0) {
+      return;
+    }
+
+    const end = new Date(stopDate.value);
+    const start = new Date(prop.prescriptionDate);
+    if (start >= end) {
+      stopDate.value = '';
+    } else {
+      handleStopDateChange(stopDate.value);
+    }
+  },
+  {deep: true, immediate: true}
+);
+
+const handleStopDateChange = (dateVal: any) => {
+  // 定义两个日期
+  const end = new Date(dateVal);
+  const start = new Date(prop.prescriptionDate);
+
+  // 计算时间差(毫秒)
+  const timeDifference = end.getTime() - start.getTime();
+
+  // 将毫秒差转换为天数差
+  daysDifference.value = timeDifference / (1000 * 60 * 60 * 24) + 1;
+  if (daysDifference.value <= 0) {
+    daysDifference.value = 0;
+    stopDate.value = '';
+  }
+
+  tableData.value.forEach((item) => {
+    item.stopDate = stopDate.value;
+    item.prescriptionDate = prop.prescriptionDate;
+    item.usageDays = daysDifference.value;
+  });
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+onMounted(() => {
+  getProductNutritionList();
+  tableData.value.forEach((item) => {
+    item.stopDate = stopDate.value;
+    item.prescriptionDate = prop.prescriptionDate;
+    item.usageDays = daysDifference.value;
+  });
+});
 </script>
 
 <style lang="scss" scoped>

+ 477 - 78
src/views/patients/enteralNutrition/components/table/PackageTable.vue

@@ -5,13 +5,16 @@
       <el-table-column label="营养产品" min-width="300">
         <template #default="{row}">
           <div class="product-group">
-            <div v-for="(product, index) in row.products" :key="index" class="product-row">
-              <el-input v-model="product.value" placeholder="请选择" />
+            <div class="product-row" style="margin-bottom: 5px">
+              <el-select v-model="row.nutritionProductId" placeholder="请选择" @change="changeProductSelect">
+                <el-option v-for="pn in productNutritionList" :key="pn.id" :label="pn.productName" :value="pn.id + '_' + row.sn"></el-option>
+              </el-select>
+
               <div class="action-buttons">
                 <el-button class="icon-btn" @click="addProduct(row)">
                   <el-icon><Plus /></el-icon>
                 </el-button>
-                <el-button class="icon-btn" @click="deleteProduct(row, index)">
+                <el-button class="icon-btn" @click="deleteProduct(row, row.sn)">
                   <el-icon><Delete /></el-icon>
                 </el-button>
               </div>
@@ -22,8 +25,8 @@
       <el-table-column label="数量" width="150" align="center">
         <template #default="{row}">
           <div class="quantity-group">
-            <div v-for="(product, index) in row.products" :key="index" class="quantity-input">
-              <el-input v-model="product.quantity" placeholder="请输入" class="input-center" />
+            <div class="quantity-input">
+              <el-input v-model="row.quantity" placeholder="请输入" class="input-center" @change="changeQuantityInput" />
               <span class="unit">袋</span>
             </div>
           </div>
@@ -32,8 +35,8 @@
       <el-table-column label="用量/次" width="150" align="center">
         <template #default="{row}">
           <div class="dosage-group">
-            <div v-for="(product, index) in row.products" :key="index" class="dosage-input">
-              <el-input v-model="product.dosage" placeholder="请输入" class="input-center" />
+            <div class="dosage-input">
+              <el-input v-model="row.dosePerTime" placeholder="请输入" class="input-center" @input="dosePerTimeInput(row.dosePerTime, row.sn)" />
               <span class="unit">g</span>
             </div>
           </div>
@@ -44,34 +47,34 @@
           <div class="time-slots">
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[0].checked" />
+                <el-checkbox v-model="row.timeSlots[0].checked" @change="changeTimeSelection(row.timeSlots[0].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[0].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[0].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[1].checked" />
+                <el-checkbox v-model="row.timeSlots[1].checked" @change="changeTimeSelection(row.timeSlots[1].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[1].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[1].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[2].checked" />
+                <el-checkbox v-model="row.timeSlots[2].checked" @change="changeTimeSelection(row.timeSlots[2].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[2].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[2].checked"
                 />
@@ -79,34 +82,34 @@
             </div>
             <div class="time-row">
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[3].checked" />
+                <el-checkbox v-model="row.timeSlots[3].checked" @change="changeTimeSelection(row.timeSlots[3].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[3].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[3].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[4].checked" />
+                <el-checkbox v-model="row.timeSlots[4].checked" @change="changeTimeSelection(row.timeSlots[4].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[4].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[4].checked"
                 />
               </div>
               <div class="time-group">
-                <el-checkbox v-model="row.timeSlots[5].checked" />
+                <el-checkbox v-model="row.timeSlots[5].checked" @change="changeTimeSelection(row.timeSlots[5].checked, row.sn)" />
                 <el-time-select
                   v-model="row.timeSlots[5].time"
-                  start="06:00"
+                  start="00:00"
                   step="00:30"
-                  end="22:00"
+                  end="23:30"
                   placeholder="选择时间"
                   :disabled="!row.timeSlots[5].checked"
                 />
@@ -116,35 +119,75 @@
         </template>
       </el-table-column>
       <el-table-column label="频次" width="120" align="center">
-        <template #default>
-          <span>一天3次</span>
+        <template #default="{row}">
+          <span>一天{{ row.frequency }}次</span>
         </template>
       </el-table-column>
-      <el-table-column label="首日" width="100" align="center">
+      <el-table-column label="首日" width="150" align="center">
         <template #default="{row}">
-          <div class="first-day">
-            <el-input-number v-model="row.firstDayTimes" :min="1" :max="10" size="small" controls-position="right" />
-            <span class="unit">次</span>
-          </div>
+          <el-input v-model="row.firstDay" @input="firstDayInput(row.firstDay, row.sn)">
+            <template #append>次</template>
+          </el-input>
         </template>
       </el-table-column>
       <el-table-column label="用量/日" width="100" align="center">
         <template #default="{row}">
-          <span>{{ row.dailyDosage }}g</span>
+          <div class="daily-dosage">
+            <span>{{ row.dosePerDay }}</span>
+            <span class="unit">{{ row.productSpecUnit }}</span>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="使用天数" width="200" align="center">
+        <template #default="{row}">
+          <div class="daily-size">
+            <el-input-number v-model="row.usageDays" :min="1" @change="changeUsageDays(row.usageDays, row.sn)" />
+          </div>
         </template>
       </el-table-column>
-      <el-table-column label="使用天数" width="120" align="center">
+
+      <el-table-column label="规格" width="100" align="center">
         <template #default="{row}">
-          <div class="days">
-            <el-input-number v-model="row.days" :min="1" :max="99" size="small" controls-position="right" />
-            <span class="unit">天</span>
+          <div class="daily-dosage">
+            <span>{{ row.specification }}</span>
           </div>
         </template>
       </el-table-column>
+
+      <el-table-column label="用法" width="150" align="center">
+        <template #default="{row}">
+          <el-select v-model="row.usage" placeholder="请选择" @change="changeUsage(row.usage, row.sn)">
+            <el-option v-for="dict in default_usage" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+          </el-select>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="处方备注" width="200" align="center">
+        <template #default="{row}">
+          <el-input v-model="row.prescriptionRemark" :maxlength="100" :show-word-limit="true" @change="changePrescriptionRemark(row.usage, row.sn)" />
+        </template>
+      </el-table-column>
+
+      <el-table-column label="每日热量" width="100" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage">
+            <span>{{ row.dailyCalories }}</span>
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="金额" width="100" align="center">
+        <template #default="{row}">
+          <div class="daily-dosage">
+            <span>{{ row.amount }}</span>
+          </div>
+        </template>
+      </el-table-column>
+
       <el-table-column label="操作" width="160" align="center" fixed="right">
         <template #default="{$index}">
           <div class="operation-cell">
-            <el-button type="primary" link @click="copyRow($index)">复制餐次</el-button>
+            <!-- <el-button type="primary" link @click="copyRow($index)">复制餐次</el-button> -->
             <el-button type="danger" link @click="deleteRow($index)">删除</el-button>
           </div>
         </template>
@@ -162,79 +205,183 @@
       <div class="left-buttons">
         <el-button type="primary" plain>存为个人模板</el-button>
         <el-button type="primary" plain>批量添加营养品</el-button>
-        <el-button type="primary" plain>处方模板</el-button>
+        <el-button type="primary" plain @click="openPreTemplate()">处方模板</el-button>
       </div>
       <div class="right-summary">
         <div class="summary-list">
           <div class="summary-item">
             <span class="label">处方费用:</span>
-            <span class="value">¥0.00</span>
+            <span class="value">¥{{ nutritionalFee }}</span>
           </div>
           <div class="summary-item total">
             <span class="label">总计:</span>
-            <span class="value">¥0.00</span>
+            <span class="value">¥{{ totalFee }}</span>
           </div>
         </div>
       </div>
     </div>
   </div>
+
+  <!-- 处方模板弹窗 -->
+  <el-dialog v-model="dialogVisible" title="选择处方模板" width="60%" :close-on-click-modal="false" class="template-dialog">
+    <!-- 提示信息 -->
+    <div class="tip-box">如部分模版未显示请检查预包装库位产品库存,无库存则含该产品的协定方模板将不再显示</div>
+
+    <div class="search-box">
+      <el-input v-model="searchValue" placeholder="请输入名称" class="search-input">
+        <template #append>
+          <el-button type="primary">查询</el-button>
+        </template>
+      </el-input>
+    </div>
+
+    <div class="dialog-content">
+      <!-- 左侧单选框区域 -->
+      <div class="left-panel">
+        <el-radio-group v-model="selectedTemplate" direction="vertical" @change="handleTemplateChange">
+          <el-radio label="基础公共模版">基础公共模版</el-radio>
+          <el-radio label="个人模版">个人模版</el-radio>
+        </el-radio-group>
+      </div>
+
+      <!-- 右侧表格区域 -->
+      <div class="right-panel">
+        <table class="custom-table">
+          <thead>
+            <tr>
+              <th>产品名称</th>
+              <th width="120">数量</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(item, index) in templateTableData" :key="index">
+              <td>{{ item.productName }}</td>
+              <td>{{ item.dosePerTime }}{{ item.unit }}</td>
+            </tr>
+            <tr v-if="templateTableData.length === 0">
+              <td colspan="3" class="no-data">暂无数据</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+
+    <div class="selected-info">
+      <span class="selected-count">已选 ({{ selectedCount }})</span>
+    </div>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </span>
+    </template>
+  </el-dialog>
 </template>
 
 <script setup lang="ts">
 import {ref} from 'vue';
 import {Plus, Delete, CirclePlus} from '@element-plus/icons-vue';
+import {listEnteralNutrition, getEnteralNutrition, delEnteralNutrition, addEnteralNutrition, updateEnteralNutrition} from '@/api/patients/nutrition';
+import {EnteralNutritionVO, EnteralNutritionQuery, EnteralNutritionForm} from '@/api/patients/nutrition/types';
+import {listNutrition, listAllNutrition} from '@/api/warehouse/productNutrition/index';
+import {NutritionVO, NutritionQuery, NutritionForm} from '@/api/warehouse/productNutrition/types';
+
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
+const {default_usage} = toRefs<any>(proxy?.useDict('default_usage'));
+
+let productNutritionList = ref<NutritionVO[]>([]);
+let tableId = ref<number>(0);
+
+let nutritionalFee = ref(0.0);
+let totalFee = ref(0.0);
+
+// 弹窗显示控制
+const dialogVisible = ref(false);
+
+// 搜索值
+const searchValue = ref('');
+
+// 选中的模板
+const selectedTemplate = ref('基础公共模版');
+
+// 已选数量
+const selectedCount = ref(0);
+
+// 添加单选框选择变化处理方法
+const handleTemplateChange = (value) => {
+  console.log('选中的模板类型:', value);
+  // value 就是选中的值:"基础公共模版" 或 "个人模版"
+
+  // 根据选择更新右侧表格数据
+  if (value === '基础公共模版') {
+    templateTableData.value = [
+      {productName: '基础产品1', dosePerTime: '5.0000', unit: 'g'},
+      {productName: '基础产品2', dosePerTime: '1.0000', unit: '袋'},
+      {productName: '基础产品2', dosePerTime: '1.0000', unit: '袋'},
+      {productName: '基础产品2', dosePerTime: '1.0000', unit: '袋'}
+    ];
+    selectedCount.value = templateTableData.value.length;
+  } else if (value === '个人模版') {
+    templateTableData.value = [
+      {productName: '个人产品1', dosePerTime: '3.0000', unit: 'g'},
+      {productName: '个人产品2', dosePerTime: '2.0000', unit: '袋'}
+    ];
+    selectedCount.value = templateTableData.value.length;
+  }
+};
+
+// 模板表格数据
+const templateTableData = ref([
+  {
+    productName: '氨基酸型复配营养粉1',
+    dosePerTime: '5.0000',
+    unit: 'g'
+  },
+  {
+    productName: '肠内营养专用配置袋',
+    dosePerTime: '1.0000',
+    unit: '袋'
+  }
+]);
 
 interface TimeSlot {
   checked: boolean;
   time: string;
 }
 
-interface Product {
-  value: string;
-  quantity: string;
-  dosage: string;
-}
-
-interface TableRow {
-  products: Product[];
+interface TableRow extends EnteralNutritionForm {
+  products: EnteralNutritionForm[];
   timeSlots: TimeSlot[];
-  firstDayTimes: number;
-  dailyDosage: number;
-  days: number;
+  sn: number;
+  tableId: number;
 }
 
 // 创建默认行数据
 const createDefaultRow = (): TableRow => ({
-  products: [
-    {
-      value: '',
-      quantity: '',
-      dosage: ''
-    }
-  ],
+  products: [{specification: '', dosePerDay: ''}],
   timeSlots: [
-    {checked: false, time: '08:00'},
+    {checked: true, time: '08:00'},
     {checked: false, time: '08:30'},
-    {checked: false, time: '12:00'},
+    {checked: true, time: '12:00'},
     {checked: false, time: '12:30'},
-    {checked: false, time: '18:00'},
+    {checked: true, time: '18:00'},
     {checked: false, time: '18:30'}
   ],
-  firstDayTimes: 2,
-  dailyDosage: 2.0,
-  days: 1
+  usageDays: 1,
+  frequency: 3,
+  firstDay: 2,
+  sn: 0,
+  tableId: tableId.value++
 });
 
 // 表格数据
 const tableData = ref<TableRow[]>([createDefaultRow()]);
+const emit = defineEmits(['change']);
 
 // 添加产品输入框
 const addProduct = (row: TableRow) => {
-  row.products.push({
-    value: '',
-    quantity: '',
-    dosage: ''
-  });
+  addRow();
 };
 
 // 删除产品输入框
@@ -245,28 +392,177 @@ const deleteProduct = (row: TableRow, index: number) => {
     if (rowIndex !== -1) {
       tableData.value.splice(rowIndex, 1);
     }
+    totalFee.value = 0.0;
+    nutritionalFee.value = 0.0;
   } else {
     // 否则只删除当前输入框
     row.products.splice(index, 1);
   }
 };
 
-// 复制行
-const copyRow = (index: number) => {
-  const newRow = JSON.parse(JSON.stringify(tableData.value[index]));
-  tableData.value.splice(index + 1, 0, newRow);
-};
-
 // 删除行
 const deleteRow = (index: number) => {
-  if (tableData.value.length === 1) return;
   tableData.value.splice(index, 1);
+  tableData.value.forEach((item, index) => {
+    item.sn = index;
+  });
+
+  if (tableData.value.length == 0) {
+    totalFee.value = 0.0;
+    nutritionalFee.value = 0.0;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
 };
 
 // 添加行
 const addRow = () => {
-  tableData.value.push(createDefaultRow());
+  let row: TableRow = createDefaultRow();
+  row.sn = tableData.value.length;
+  tableData.value.push(row);
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const getProductNutritionList = async () => {
+  const res = await listAllNutrition();
+  productNutritionList.value = res.rows;
+};
+
+const changeQuantityInput = async () => {
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const dosePerTimeInput = (value: string, sn: number) => {
+  let row = tableData.value[sn];
+
+  if (!row.dosePerTime || row.dosePerTime.trim().length == 0) {
+    row.dailyCalories = null;
+    row.amount = null;
+    return;
+  }
+
+  totalFee.value = 0.0;
+  nutritionalFee.value = 0.0;
+
+  let arr = row.nutritionProductId.split('_');
+  for (let item of productNutritionList.value) {
+    if (item.id.toString() != arr[0]) {
+      continue;
+    }
+
+    let val = (item.calorie * row.frequency * Number(row.dosePerTime)) / 100.0;
+    row.dailyCalories = val.toFixed(4);
+
+    let firstDay = Number(row.firstDay.toString());
+    val = (row.frequency * (row.usageDays - 1) + firstDay) * item.configSalePrice * Number(row.dosePerTime);
+    row.amount = Math.round(val * 100) / 100;
+    totalFee.value += row.amount;
+    nutritionalFee.value += row.amount;
+
+    val = row.frequency * Number(row.dosePerTime);
+    row.dosePerDay = val.toFixed(4);
+    break;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const changeProductSelect = async (id: string) => {
+  let arr = id.split('_');
+  for (let item of productNutritionList.value) {
+    if (item.id.toString() != arr[0]) {
+      continue;
+    }
+
+    let row = tableData.value[Number(arr[1])];
+    row.specification=item.productSpec;
+    row.dosePerTime=null;     
+    row.dailyCalories=null;
+    row.amount=null; 
+    row.productSpecUnit=item.productSpecUnit;
+    row.usage=item.defaultUsage;     
+    row.dosePerDay='0';
+    row.firstDay=Math.ceil(row.frequency/2.0);
+    break;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+const changeUsageDays = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
 };
+
+const changeUsage = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changePrescriptionRemark = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const firstDayInput = (value: string, sn: string) => {
+  changeTimeSelection(null, sn);
+};
+
+const changeTimeSelection = (value: string, sn: string) => {
+  let row = tableData.value[sn];
+  let frequency = 0;
+  row.timeSlots.forEach((item) => {
+    if (item.checked) {
+      frequency++;
+    }
+  });
+  row.frequency = frequency;
+
+  if (value != null) {
+    row.firstDay = Math.ceil(row.frequency / 2.0);
+  }
+
+  if (!row.dosePerTime || row.dosePerTime.trim().length == 0) {
+    return;
+  }
+
+  totalFee.value = 0.0;
+  nutritionalFee.value = 0.0;
+  let arr = row.nutritionProductId.split('_');
+
+  for (let item of productNutritionList.value) {
+    if (item.id.toString() != arr[0]) {
+      continue;
+    }
+
+    let val = (item.calorie * row.frequency * Number(row.dosePerTime)) / 100.0;
+    row.dailyCalories = val.toFixed(4);
+
+    let firstDay = Number(row.firstDay.toString());
+    val = (row.frequency * (row.usageDays - 1) + firstDay) * item.configSalePrice * Number(row.dosePerTime);
+
+    row.amount = Math.round(val * 100) / 100;
+    totalFee.value += row.amount;
+    nutritionalFee.value += row.amount;
+
+    val = row.frequency * Number(row.dosePerTime);
+    row.dosePerDay = val.toFixed(4);
+    break;
+  }
+
+  emit('change', JSON.stringify(tableData.value));
+};
+
+// 预包装的处方模板
+const openPreTemplate = () => {
+  dialogVisible.value = true;
+};
+
+// 确认选择模板
+const handleConfirm = () => {
+  dialogVisible.value = false;
+  // 这里处理选中模板后的逻辑
+};
+onMounted(() => {
+  getProductNutritionList();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -504,4 +800,107 @@ const addRow = () => {
     }
   }
 }
+
+.template-dialog {
+  :deep(.el-dialog__body) {
+    padding: 0 20px;
+  }
+
+  .tip-box {
+    background-color: #e6f7ff;
+    padding: 12px 16px;
+    color: #606266;
+    font-size: 14px;
+    margin-bottom: 16px;
+  }
+
+  .search-box {
+    margin-bottom: 16px;
+
+    .search-input {
+      width: 300px;
+    }
+  }
+
+  .dialog-content {
+    display: flex;
+    gap: 20px;
+    min-height: 400px;
+    border: 1px solid #ebeef5;
+
+    .left-panel {
+      width: 200px;
+      padding: 20px;
+      border-right: 1px solid #ebeef5;
+
+      :deep(.el-radio-group) {
+        display: flex;
+        gap: 16px;
+      }
+
+      :deep(.el-radio) {
+        margin-right: 0;
+        height: auto;
+
+        .el-radio__label {
+          font-size: 14px;
+          color: #606266;
+        }
+      }
+    }
+
+    .right-panel {
+      flex: 1;
+      padding: 16px;
+      overflow: auto;
+    }
+  }
+
+  .selected-info {
+    margin: 16px 0;
+
+    .selected-count {
+      color: #409eff;
+      font-size: 14px;
+    }
+  }
+
+  :deep(.el-dialog__footer) {
+    padding: 16px 20px;
+    border-top: 1px solid #ebeef5;
+  }
+}
+
+.custom-table {
+  width: 100%;
+  border-collapse: collapse;
+  border: 1px solid #ebeef5;
+
+  th,
+  td {
+    padding: 12px 8px;
+    border: 1px solid #ebeef5;
+    text-align: center;
+    font-size: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  th {
+    background-color: #f5f7fa;
+    color: #303133;
+    font-weight: 600;
+  }
+
+  tbody tr:hover {
+    background-color: #f5f7fa;
+  }
+
+  .no-data {
+    color: #909399;
+    text-align: center;
+    padding: 40px;
+  }
+}
 </style>

+ 4 - 6
src/views/patients/enteralNutrition/history.vue

@@ -1,21 +1,19 @@
 <template>
   <div class="history-container">
-    <LeftCard />
-    <RightCard  @goBack="goBack"/>
+    <LeftCard :patientInfo="patientInfo" />
+    <RightCard @goBack="goBack" />
   </div>
 </template>
 
 <script setup lang="ts">
 import LeftCard from './components/history/LeftCard.vue';
 import RightCard from './components/history/RightCard.vue';
-const { patientInfo } = defineProps<{ patientInfo: any }>()
-
+const {patientInfo} = defineProps<{patientInfo: any}>();
 
 const emit = defineEmits(['change']);
 const goBack = () => {
-  emit('change','enteralNutrition')
+  emit('change', 'enteralNutrition');
 };
-
 </script>
 
 <style lang="scss" scoped>

+ 3 - 3
src/views/patients/enteralNutrition/index.vue

@@ -2,7 +2,7 @@
   <div class="container">
     <div class="main-content">
       <!-- 左侧卡片 -->
-      <LeftCard  @gotoTop="goHistoryPrescription"/>
+      <LeftCard @gotoTop="goHistoryPrescription" :patientInfo="patientInfo" />
       <!-- 右侧卡片 -->
       <RightCard />
     </div>
@@ -13,13 +13,13 @@
 import LeftCard from './components/LeftCard.vue';
 import RightCard from './components/RightCard.vue';
 
+const {patientInfo} = defineProps<{patientInfo: any}>();
 // 解决bug核心
 const emit = defineEmits(['change']);
 
 const goHistoryPrescription = () => {
-  emit('change', 'enteralNutritionHistory')
+  emit('change', 'enteralNutritionHistory');
 };
-
 </script>
 
 <style lang="scss" scoped>

+ 72 - 8
src/views/patients/evaluation/add.vue

@@ -3,11 +3,14 @@
     <el-card>
       <!-- 新增顶部操作栏 -->
       <el-row>
-        <el-col :span="4">
-          <el-button type="primary" plain @click="close" style="margin-right: 20px;margin-left: 30px;">返回</el-button>
-          <span style="font-size: 14px;  vertical-align: middle;">{{ screeningName }}</span>
+        <el-col :span="1" style="margin-right: 10px;">
+          <el-button type="primary" plain @click="close" >返回</el-button>           
+        </el-col>         
+        <el-col :span="22">           
+          <span style="font-size: 14px; vertical-align: middle">{{ screeningName }}</span>
+          <div style="font-size: 14px; vertical-align: middle">来源:{{ sourceName }}</div>          
         </el-col>
-      </el-row>
+      </el-row> 
     </el-card>
 
     <el-card>
@@ -423,6 +426,40 @@
           </div>
         </template>
         <!-- 其他:人体测量、膳食状况、营养生化检查 -->
+
+
+        <!--通过文件导入的输入框 -->
+      <template v-if="item.questionType=='inputBox'">
+        <div style="margin-bottom: 10px; padding-top: 20px;" class="zebra-row">
+          <el-row>
+            <el-col :span="24">
+              <span style="color: #f56c6c; margin-right: 4px;">*</span>
+              <span style="font-weight: bold;">{{idx + 1 + (form.baseInfo&&form.baseInfo.contentList&&form.baseInfo.contentList.length>0?1:0) +'. '+ item.title }}</span>
+            </el-col>
+          </el-row>
+          <el-row style="margin-bottom: 20px;margin-top: 8px;">
+            <el-col :span="24">
+              <el-form :inline="true" label-width="100px">
+                <el-form-item>
+                  <template v-if="item.questionChildType=='inputNumber'">
+                    <el-input clearable v-model="item.value" :disabled="isView" type="number" @change="changeInputNumber"/>
+                  </template>
+                  <template v-else-if="item.questionChildType=='inputText'">
+                    <el-input  v-model="item.value" :disabled="isView" />
+                  </template>
+                  <template v-else-if="item.questionChildType=='calculated'">
+                    <el-input  v-model="item.value" :disabled="true" />
+                  </template>
+                  <template v-else>
+                    <el-input  v-model="item.value" :disabled="isView" />
+                  </template>
+                  </el-form-item>
+              </el-form>
+            </el-col>
+          </el-row>
+        </div>
+      </template>
+      <!--通过文件导入的输入框 -->
       </template>
     </el-card>
 
@@ -451,6 +488,7 @@ let paramForm = ref<EvaluationForm>({});
  
 
 const screeningName = ref(''); 
+const sourceName = ref('');
 const { patientInfo } = defineProps<{ patientInfo: any }>()
 const emit = defineEmits(['change'])
 let form=ref<ScreeningAssessmentConfigForm>({});   
@@ -469,11 +507,13 @@ const getList = async () => {
         const res =await getEvaluation(id as string);
         const content =JSON.parse(res.data.content);
         screeningName.value = content.name;
+        sourceName.value = content.source;
         Object.assign(form.value, content);
     } else {
         isView.value = false;
         const res = await getScreeningAssessmentConfig(id as string);
         screeningName.value = res.data.name;
+        sourceName.value = res.data.source;
         Object.assign(form.value, res.data);
     }
 }
@@ -538,8 +578,8 @@ const submitForm = async () => {
         }
     }
 
-  buttonLoading.value = true;
-  paramForm.value.content = JSON.stringify(form.value);
+  buttonLoading.value = true; 
+  paramForm.value.content =window.btoa(encodeURI(JSON.stringify(form.value))) ;
   if (paramForm.value.id) {
     await updateEvaluation(paramForm.value).finally(() => buttonLoading.value = false);
   } else {
@@ -575,8 +615,32 @@ const selectScaleQuestions = async (idx:number, n:number) => {
     }
 }
 
-
-
+const changeInputNumber = async (val:number) => {  
+  let conclusion=form.value.conclusion;
+  if(!conclusion||conclusion.length<=0){
+    return;
+  }
+  let conclusionObj=JSON.parse(conclusion); 
+  let formula=conclusionObj.formula;
+  if(!formula||formula.length<=0){ 
+    return;
+  }   
+  let arr=formula.split(/[;;]+/);
+  formula=arr[0].toString();
+  let otherInfo=form.value.otherInfo;  
+
+  let argArr=[];
+  otherInfo.forEach(item=>{
+    arr=item.title.split(/[\\((]+/);
+    if(formula.indexOf(arr[0])>=0){
+      formula=formula.replaceAll(arr[0],'Q'+item.qid);
+      argArr.push('Q'+item.qid+'='+item.value);
+    }
+    
+    
+    console.log( argArr,'++++++++++++++++++',formula);
+  });
+}
 
 onMounted(() => {
   paramForm.value.patientId=patientInfo.id;

+ 5 - 0
src/views/patients/evaluation/index.vue

@@ -307,5 +307,10 @@ onMounted(() => {
   font-size: 16px;
   height: 48px;
   border-radius: 8px;  
+  white-space: normal;
+    word-wrap: break-word;
+    text-align: center;
+    line-height: 1.2;
+    padding: 8px 12px;
 }
 </style>

+ 575 - 0
src/views/patients/hospitalRecord/index.vue

@@ -0,0 +1,575 @@
+<template>
+  <div class="hospital-record-layout">
+    <!-- 顶部搜索区域 -->
+    <div class="top-search">
+      <div class="search-container">
+        <!-- 日期范围 -->
+        <div class="search-item">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="-"
+            start-placeholder="2024-11-19"
+            end-placeholder="2025-07-30"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            size="default"
+            style="width: 280px"
+          />
+        </div>
+
+        <!-- 科室 -->
+        <div class="search-item">
+          <span class="search-label">科室</span>
+          <el-select v-model="selectedDept" placeholder="请选择" clearable style="width: 120px">
+            <el-option label="口腔科" value="oral" />
+            <el-option label="营养科" value="nutrition" />
+            <el-option label="内科" value="internal" />
+          </el-select>
+        </div>
+
+        <!-- 就诊类型 -->
+        <div class="search-item">
+          <span class="search-label">就诊类型</span>
+          <el-select v-model="visitType" placeholder="请选择" clearable style="width: 120px">
+            <el-option label="门诊" value="outpatient" />
+            <el-option label="住院" value="inpatient" />
+          </el-select>
+        </div>
+
+        <!-- 搜索框 -->
+        <div class="search-item">
+          <el-input v-model="searchKeyword" placeholder="姓名/姓名/门诊号/住院号" style="width: 200px" />
+        </div>
+
+        <!-- 按钮组 -->
+        <div class="search-buttons">
+          <el-button type="primary">查询</el-button>
+          <el-button>重置</el-button>
+        </div>
+      </div>
+    </div>
+
+    <!-- 主体内容区域 -->
+    <div class="main-content">
+      <!-- 左侧时间线区域 -->
+      <div class="left-timeline">
+        <!-- 左侧虚线 -->
+        <div class="timeline-line"></div>
+
+        <div class="timeline-content">
+          <!-- 日期节点 -->
+          <div class="timeline-node date-node">
+            <div class="node-marker">1</div>
+            <div class="node-content">
+              <div class="date-label">2024-11-19</div>
+            </div>
+          </div>
+
+          <!-- 口腔科形节点 -->
+          <div class="timeline-node normal-node">
+            <div class="node-content">
+              <div class="record-header">
+                <span class="record-type">口腔矫形</span>
+                <span class="doctor-info">主管医生:系统管理员</span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 营养筛查节点(蓝色标记) -->
+          <div class="timeline-node active-node">
+            <div class="node-blue-marker active"></div>
+            <div class="node-content">
+              <div class="record-type active">营养筛查</div>
+
+              <div class="record-list">
+                <div class="record-item">
+                  <div class="record-time">2025-07-07 17:57:07</div>
+                  <div class="record-doctor">筛查医生/护士:yingpai</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>1.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-07-07 17:53:39</div>
+                  <div class="record-doctor">筛查医生/护士:yingpai</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>0.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-05-27 10:34:40</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>4.00分,患者存在营养风险,开始制定营养支持/治疗计划</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2025-01-03 10:35:09</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>2.00分,每周复查营养风险筛查</div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2024-11-19 09:24:56</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail">
+                    <strong>营养不良筛查工具(MUST):</strong
+                    >4.00分,患者风险:治疗转介营养师。营养师方案评估应动用处方规划程序,监测和审视营养支持计划;除非营养师常规临床检查:护理营养支持,但若病房没有住院,请与医院营养小区安排一次,护理之后和社区每月一次。
+                  </div>
+                </div>
+
+                <div class="record-item">
+                  <div class="record-time">2024-11-19 09:24:50</div>
+                  <div class="record-doctor">筛查医生/护士:系统管理员</div>
+                  <div class="record-detail"><strong>NRS2002营养风险筛查:</strong>3.00分,患者存在营养风险,开始制定营养支持/治疗计划</div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- 营养评估节点 -->
+          <div class="timeline-node normal-node">
+            <div class="node-blue-marker"></div>
+            <div class="node-content">
+              <div class="record-type">营养评估</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 右侧导航区域 -->
+      <div class="right-navigation">
+        <el-timeline>
+          <el-timeline-item v-for="(navItem, index) in navigationItems" :key="index" :type="navItem.active ? 'primary' : 'info'" size="normal">
+            <div class="nav-timeline-content" :class="{active: navItem.active}" @click="handleNavClick(navItem.key)">
+              <span class="nav-title">{{ navItem.label }}</span>
+            </div>
+          </el-timeline-item>
+        </el-timeline>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="HospitalRecord">
+import {ref} from 'vue';
+
+// 响应式数据
+const dateRange = ref(['2024-11-19', '2025-07-30']);
+const selectedDept = ref('');
+const visitType = ref('');
+const searchKeyword = ref('');
+
+// 右侧导航数据
+const navigationItems = ref([
+  {
+    key: 'screening',
+    label: '营养筛查',
+    active: false
+  },
+  {
+    key: 'evaluation',
+    label: '营养评估',
+    active: false
+  },
+  {
+    key: 'diagnosis',
+    label: '营养诊断',
+    active: false
+  },
+  {
+    key: 'prescription',
+    label: '营养处方',
+    active: true
+  },
+  {
+    key: 'meal',
+    label: '营养膳食',
+    active: false
+  }
+]);
+
+// 导航点击处理
+const handleNavClick = (key: string) => {
+  // 重置所有项的激活状态
+  navigationItems.value.forEach((item) => {
+    item.active = false;
+  });
+
+  // 设置当前点击项为激活状态
+  const currentItem = navigationItems.value.find((item) => item.key === key);
+  if (currentItem) {
+    currentItem.active = true;
+  }
+
+  console.log('导航切换到:', key);
+  // 这里可以添加具体的页面切换逻辑
+};
+</script>
+
+<style lang="scss" scoped>
+.hospital-record-layout {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background: #f5f5f5;
+
+  .top-search {
+    background: white;
+    padding: 16px 20px;
+    .search-container {
+      display: flex;
+      align-items: center;
+      flex-wrap: nowrap;
+      gap: 60px;
+
+      .search-item {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        flex-shrink: 0;
+
+        .search-label {
+          font-size: 14px;
+          color: #606266;
+          white-space: nowrap;
+        }
+      }
+
+      .search-buttons {
+        display: flex;
+        gap: 8px;
+        margin-left: auto;
+        flex-shrink: 0;
+      }
+    }
+  }
+
+  .main-content {
+    flex: 1;
+    display: flex;
+    overflow: hidden;
+
+    .left-timeline {
+      flex: 1;
+      background: white;
+      overflow-y: auto;
+      padding: 20px;
+      border-right: 1px solid #e4e7ed;
+      position: relative;
+
+      .timeline-line {
+        position: absolute;
+        left: 40px;
+        top: 0;
+        height: 100%;
+        width: 0;
+        border-left: 2px dashed #d0d7de;
+        z-index: 1;
+      }
+
+      .timeline-content {
+        position: relative;
+        z-index: 2;
+      }
+
+      .timeline-node {
+        position: relative;
+        margin-bottom: 16px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .node-marker {
+          position: absolute;
+          left: 6px;
+          top: -4px;
+          width: 30px;
+          height: 30px;
+          border-radius: 50%;
+          background: white;
+          border: 2px solid #e4e7ed;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 12px;
+          font-weight: bold;
+          color: #606266;
+          z-index: 3;
+
+          &.active {
+            background: #409eff;
+            border-color: #409eff;
+            color: white;
+            width: 22px;
+            height: 22px;
+            left: 30px;
+          }
+        }
+
+        .node-blue-marker {
+          width: 20px;
+          height: 15px;
+          border-radius: 0 50% 50% 0;
+          background: #76b4f4;
+          position: absolute;
+          left: 20px;
+          top: 5px;
+          z-index: 3;
+        }
+
+        .node-content {
+          margin-left: 60px;
+
+          .date-label {
+            font-size: 16px;
+            font-weight: 500;
+            color: #303133;
+          }
+
+          .record-header {
+            display: flex;
+            align-items: center;
+            gap: 20px;
+            margin-bottom: 8px;
+
+            .record-type {
+              margin-bottom: 0;
+            }
+
+            .doctor-info {
+              margin-bottom: 0;
+              color: #909399;
+              font-size: 13px;
+            }
+          }
+
+          .record-type {
+            font-size: 14px;
+            color: #333;
+            margin-bottom: 8px;
+
+            &.active {
+              color: #409eff;
+              background: #f0f9ff;
+              padding: 8px 12px;
+              border-radius: 4px;
+              display: inline-block;
+            }
+          }
+
+          .doctor-info {
+            font-size: 14px;
+            color: #606266;
+            margin-bottom: 16px;
+          }
+
+          .record-list {
+            .record-item {
+              margin-bottom: 16px;
+              padding: 12px 0;
+              border-bottom: 1px solid #f0f0f0;
+
+              &:last-child {
+                border-bottom: none;
+              }
+
+              .record-time {
+                font-size: 13px;
+                color: #909399;
+                margin-bottom: 4px;
+              }
+
+              .record-doctor {
+                font-size: 13px;
+                color: #606266;
+                margin-bottom: 8px;
+              }
+
+              .record-detail {
+                font-size: 14px;
+                color: #303133;
+                line-height: 1.5;
+
+                strong {
+                  color: #303133;
+                  font-weight: 600;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      // 特殊节点样式
+      .date-node {
+        .node-marker {
+          background: #f5f7fa;
+          border-color: #909399;
+          color: #606266;
+        }
+      }
+
+      .normal-node {
+        .node-marker {
+          background: white;
+          border-color: #e4e7ed;
+        }
+      }
+
+      .active-node {
+        .node-marker {
+          background: #409eff;
+          border: none;
+          color: white;
+          width: 24px;
+          height: 12px;
+          left: 29px;
+          border-radius: 12px;
+        }
+      }
+    }
+
+    .center-divider {
+      width: 80px;
+      background: #f8f9fa;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border-left: 1px solid #e4e7ed;
+      border-right: 1px solid #e4e7ed;
+
+      .divider-text {
+        writing-mode: vertical-lr;
+        font-size: 14px;
+        color: #909399;
+      }
+    }
+
+    .right-navigation {
+      width: 160px;
+      background: white;
+
+      // 自定义右侧时间线样式
+      :deep(.el-timeline) {
+        padding-left: 16px;
+        margin-top: 10px;
+
+        .el-timeline-item {
+          padding-bottom: 20px;
+
+          &:last-child {
+            padding-bottom: 0;
+
+            .el-timeline-item__tail {
+              display: none;
+            }
+          }
+
+          .el-timeline-item__node {
+            width: 8px;
+            height: 8px;
+            left: -4px;
+            border: 1px solid #e4e7ed;
+            background: white;
+
+            &.el-timeline-item__node--primary {
+              border-color: #409eff;
+              background: #409eff;
+            }
+
+            &.el-timeline-item__node--info {
+              border-color: #e4e7ed;
+              background: #e4e7ed;
+            }
+          }
+
+          .el-timeline-item__tail {
+            left: -1px;
+            border-left: 1px solid #e4e7ed;
+          }
+
+          .el-timeline-item__content {
+            top: -4px;
+          }
+        }
+      }
+
+      .nav-timeline-content {
+        cursor: pointer;
+        padding: 2px 0;
+        transition: all 0.3s ease;
+
+        .nav-title {
+          font-size: 14px;
+          color: #606266;
+          font-weight: normal;
+          line-height: 1.2;
+          white-space: nowrap;
+        }
+
+        &:hover {
+          .nav-title {
+            color: #409eff;
+          }
+        }
+
+        &.active {
+          .nav-title {
+            color: #409eff;
+            font-weight: 500;
+          }
+        }
+      }
+    }
+  }
+
+  // 滚动条样式
+  :deep(.left-timeline::-webkit-scrollbar) {
+    width: 6px;
+  }
+
+  :deep(.left-timeline::-webkit-scrollbar-track) {
+    background: #f1f1f1;
+    border-radius: 3px;
+  }
+
+  :deep(.left-timeline::-webkit-scrollbar-thumb) {
+    background: #c1c1c1;
+    border-radius: 3px;
+
+    &:hover {
+      background: #a8a8a8;
+    }
+  }
+}
+</style>

+ 279 - 300
src/views/patients/medicalRecord/index.vue

@@ -1,118 +1,112 @@
 <template>
-    <div class="app-container">
-        <div class="left-menu">
-            <div class="back-button" @click="goBack">
-                <el-icon>
-                    <ArrowLeft />
-                </el-icon>
-                返回患者列表
-            </div>
-            <el-menu :default-active="activeMenu" class="medical-menu" @select="handleSelect">
-                <el-menu-item index="medicalRecord">
-                    <span>营养病例</span>
-                </el-menu-item>
-                <el-menu-item index="visitRecord">
-                    <span>就诊记录</span>
-                </el-menu-item>
-                <el-menu-item index="checkLabel">
-                    <span>检查指标</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionScreeningAdd" v-show="false">
-                    <span>营养筛查新增</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionScreening">
-                    <span>营养筛查</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionEvaluationAdd" v-show="false">
-                    <span>营养评估新增</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionEvaluation">
-                    <span>营养评估</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionDiagnosis">
-                    <span>营养诊断</span>
-                </el-menu-item>
-                <el-sub-menu index="nutritionIntervention">
-                    <template #title>营养干预</template>
-                    <el-menu-item index="nutritionSetting">营养设定</el-menu-item>
-                    <el-menu-item index="enteralNutrition">肠内营养</el-menu-item>
-                    <el-menu-item index="enteralNutritionHistory" v-show="false">肠内营养历史处方</el-menu-item>
-                    <el-menu-item index="parenteralNutrition">肠外营养</el-menu-item>
-                    <el-menu-item index="dietTherapy">膳食治疗</el-menu-item>
-                </el-sub-menu>
-                <el-menu-item index="nutritionMonitor">
-                    <span>营养监测</span>
-                </el-menu-item>
-                <el-menu-item index="nutritionEducation">
-                    <span>营养宣教</span>
-                </el-menu-item>
-            </el-menu>
-            <div class="search-bar">
-                <el-input v-model="searchValue" placeholder="姓名/床号/门诊号" size="small" class="search-input" @keyup.enter="handleSearch" />
-                <el-button size="small" type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
-            </div>
-            <div class="patient-tabs">
-                <el-tabs v-model="patientTab" size="small" stretch @tab-change="handleTabChange">
-                    <el-tab-pane :label="`待诊${waitingCount}`" name="wait" />
-                    <el-tab-pane :label="`诊中${treatingCount}`" name="doing" />
-                    <el-tab-pane :label="`已诊${treatedCount}`" name="done" />
-                </el-tabs>
-            </div>
-            <div class="patient-table">
-                <el-table :data="patientList" border size="small" height="80%" :show-header="true" class="table-patients" :row-class-name="tableRowClassName" @row-dblclick="handleRowClick">
-                    <el-table-column prop="name" label="姓名" width="60" />
-                    <el-table-column prop="gender" label="性别" width="50" />
-                    <el-table-column prop="age" label="年龄" width="80" />
-                </el-table>
-            </div>
-            <div class="nav-btns">
-                <el-button size="small" @click="handlePrev">上一位</el-button>
-                <el-button size="small" type="primary" @click="handleNext">下一位</el-button>
-            </div>
+  <div class="app-container">
+    <div class="left-menu">
+      <div class="back-button" @click="goBack">
+        <el-icon>
+          <ArrowLeft />
+        </el-icon>
+        返回患者列表
+      </div>
+      <el-menu :default-active="activeMenu" class="medical-menu" @select="handleSelect">
+        <el-menu-item index="medicalRecord">
+          <span>营养病例</span>
+        </el-menu-item>
+        <el-menu-item index="visitRecord">
+          <span>就诊记录</span>
+        </el-menu-item>
+        <el-menu-item index="checkLabel">
+          <span>检查指标</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionScreeningAdd" v-show="false">
+          <span>营养筛查新增</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionScreening">
+          <span>营养筛查</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionEvaluationAdd" v-show="false">
+          <span>营养评估新增</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionEvaluation">
+          <span>营养评估</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionDiagnosis">
+          <span>营养诊断</span>
+        </el-menu-item>
+        <el-sub-menu index="nutritionIntervention">
+          <template #title>营养干预</template>
+          <el-menu-item index="nutritionSetting">营养设定</el-menu-item>
+          <el-menu-item index="enteralNutrition">肠内营养</el-menu-item>
+          <el-menu-item index="enteralNutritionHistory" v-show="false">肠内营养历史处方</el-menu-item>
+          <el-menu-item index="parenteralNutrition">肠外营养</el-menu-item>
+          <el-menu-item index="dietTherapy">膳食治疗</el-menu-item>
+        </el-sub-menu>
+        <el-menu-item index="nutritionMonitor">
+          <span>营养监测</span>
+        </el-menu-item>
+        <el-menu-item index="nutritionEducation">
+          <span>营养宣教</span>
+        </el-menu-item>
+      </el-menu>
+      <div class="search-bar">
+        <el-input v-model="searchValue" placeholder="姓名/床号/门诊号" size="small" class="search-input" @keyup.enter="handleSearch" />
+        <el-button size="small" type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
+      </div>
+      <div class="patient-tabs">
+        <el-tabs v-model="patientTab" size="small" stretch @tab-change="handleTabChange">
+          <el-tab-pane :label="`待诊${waitingCount}`" name="wait" />
+          <el-tab-pane :label="`诊中${treatingCount}`" name="doing" />
+          <el-tab-pane :label="`已诊${treatedCount}`" name="done" />
+        </el-tabs>
+      </div>
+      <div class="patient-table">
+        <el-table
+          :data="patientList"
+          border
+          size="small"
+          height="80%"
+          :show-header="true"
+          class="table-patients"
+          :row-class-name="tableRowClassName"
+          @row-click="handleRowClick"
+        >
+          <el-table-column prop="name" label="姓名" width="60" />
+          <el-table-column prop="gender" label="性别" width="50" />
+          <el-table-column prop="age" label="年龄" width="80" />
+        </el-table>
+      </div>
+      <div class="nav-btns">
+        <el-button size="small" @click="handlePrev">上一位</el-button>
+        <el-button size="small" type="primary" @click="handleNext">下一位</el-button>
+      </div>
+    </div>
+    <div class="main-content">
+      <el-row>
+        <div class="patient-left-info">
+          <el-row class="info-row">
+            <span class="name">{{ patientInfo.name }}</span>
+            <span class="age">{{ patientInfo.age }}</span>
+            <span class="gender-icon" :class="patientInfo.gender === '男' ? 'male' : 'female'">
+              {{ patientInfo.gender === '男' ? '♂' : '♀' }}
+            </span>
+          </el-row>
+          <el-row style="margin-top: 10px; margin-left: 10%">
+            <el-button size="small" @click="handleEdit">编辑</el-button>
+            <el-button size="small" type="primary" @click="handleDetail">详情</el-button>
+          </el-row>
         </div>
-        <div class="main-content">
-            <el-row :gutter="20">
-                <el-col :span="4">
-                    <div class="patient-left-info">
-                        <el-row class="info-row">
-                            <span class="name">{{ patientInfo.name }}</span>
-                            <span class="age">{{ patientInfo.age }}</span>
-                            <span class="gender-icon" :class="patientInfo.gender === '男' ? 'male' : 'female'">
-                                {{ patientInfo.gender === '男' ? '♂' : '♀' }}
-                            </span>
-                        </el-row>
-                        <el-row style="margin-top: 10px; margin-left: 10%">
-                            <el-button size="small" @click="handleEdit">编辑</el-button>
-                            <el-button size="small" type="primary" @click="handleDetail">详情</el-button>
-                        </el-row>
-                    </div>
-                </el-col>
-
-                <el-col :span="20">
-                    <div class="patient-right-info">
-                        <el-row>
-                            <el-col :span="24">
-                                <span>门诊号:{{ patientInfo.outpatientNo }}</span>
-                                <span style="margin-left: 30px">科室:{{ patientInfo.deptName }}</span>
-                                <!-- <span style="margin-left: 30px" v-if="patientInfo.type == '1'">床号:{{ patientInfo.bedNo|| '--' }}</span> -->
-                                <span style="margin-left: 30px">营养诊断:{{useStore.diagnosisInfo?.diagnosisLabelStr|| '--' }}</span>
-                            </el-col>
-                        </el-row>
-                        <el-row>
-                            <el-col>
-                                <el-tooltip effect="light" :content=" stripHtml(useStore.diagnosisInfo?.consultantResult) || '--'" placement="top">
-                                    <span class="truncate-text">
-                                        会诊结果:{{ stripHtml(useStore.diagnosisInfo?.consultantResult) || '--' }}
-                                    </span>
-                                </el-tooltip>
-                            </el-col>
-                        </el-row>
-                    </div>
-                </el-col>
-
-            </el-row>
-            <component :is="currentComponent" v-if="currentComponent" :patient-info="patientInfo" @change="handleSelect" />
+        <div class="patient-right-info">
+          <el-row>
+            <span>门诊号:{{ patientInfo.outpatientNo }}</span>
+            <span style="margin-left: 30px">科室:{{ patientInfo.deptName }}</span>
+            <span style="margin-left: 30px">营养诊断:</span>
+          </el-row>
+          <el-row>
+            <span>会诊结果:</span>
+          </el-row>
         </div>
+      </el-row>
+      <component :is="currentComponent" v-if="currentComponent" :patient-info="patientInfo" @change="handleSelect" />
+    </div>
 
         <!-- 患者详情弹窗 -->
         <DetailDialog v-model:visible="showDetailDialog" :detail-data="detailData" :physical-activity-dict="physicalActivityDict" @close="handleDetailClose" />
@@ -134,60 +128,59 @@
     import { useNutritionDiagnosisStore } from '@/store/modules/diagnosis';
     const useStore = useNutritionDiagnosisStore();
 
-    // 动态导入组件
-    const MedicalRecord = defineAsyncComponent(() => import('./levelMenu.vue'));
-    const NutritionScreening = defineAsyncComponent(() => import('@/views/patients/screening/index.vue'));
-    const NutritionEvaluation = defineAsyncComponent(() => import('@/views/patients/evaluation/index.vue'));
-    const NutriDiagnosis = defineAsyncComponent(() => import('@/views/patients/nutriDiagnosis/index.vue'));
-    const CheckLabel = defineAsyncComponent(() => import('@/views/patients/checkLabel/index.vue'));
-    const NutritionScreeningAdd = defineAsyncComponent(() => import('@/views/patients/screening/add.vue'));
-    const NutritionEvaluationAdd = defineAsyncComponent(() => import('@/views/patients/evaluation/add.vue'));
-    const NutritionEducation = defineAsyncComponent(() => import('@/views/patients/nutritionEducation/index.vue'));
-    const NutritionSetting = defineAsyncComponent(() => import('@/views/patients/nutritionSetting/index.vue'));
-    const DietTherapy = defineAsyncComponent(() => import('@/views/patients/dietTherapy/index.vue'));
-    const ParenteralNutrition = defineAsyncComponent(() => import('@/views/patients/parenteralNutrition/index.vue'));
-    const EnteralNutrition = defineAsyncComponent(() => import('@/views/patients/enteralNutrition/index.vue'));
-    const EnteralNutritionHistory = defineAsyncComponent(() => import('@/views/patients/enteralNutrition/history.vue'));
-
-    const componentMap = {
-        medicalRecord: MedicalRecord,
-        checkLabel: CheckLabel,
-        nutritionDiagnosis: NutriDiagnosis,
-        nutritionScreening: NutritionScreening,
-        nutritionScreeningAdd: NutritionScreeningAdd,
-        nutritionEvaluation: NutritionEvaluation,
-        nutritionEvaluationAdd: NutritionEvaluationAdd,
-        nutritionEducation: NutritionEducation,
-        nutritionSetting: NutritionSetting,
-        dietTherapy: DietTherapy,
-        parenteralNutrition: ParenteralNutrition,
-        enteralNutrition: EnteralNutrition,
-        enteralNutritionHistory: EnteralNutritionHistory,
-    };
-    const currentComponent = ref(componentMap['medicalRecord']);
-    const route = useRoute();
-    const router = useRouter();
-    const { proxy } = getCurrentInstance() as any;
+// 动态导入组件
+const MedicalRecord = defineAsyncComponent(() => import('./levelMenu.vue'));
+const NutritionScreening = defineAsyncComponent(() => import('@/views/patients/screening/index.vue'));
+const NutritionEvaluation = defineAsyncComponent(() => import('@/views/patients/evaluation/index.vue'));
+const NutriDiagnosis = defineAsyncComponent(() => import('@/views/patients/nutriDiagnosis/index.vue'));
+const CheckLabel = defineAsyncComponent(() => import('@/views/patients/checkLabel/index.vue'));
+const NutritionScreeningAdd = defineAsyncComponent(() => import('@/views/patients/screening/add.vue'));
+const NutritionEvaluationAdd = defineAsyncComponent(() => import('@/views/patients/evaluation/add.vue'));
+const NutritionEducation = defineAsyncComponent(() => import('@/views/patients/nutritionEducation/index.vue'));
+const NutritionSetting = defineAsyncComponent(() => import('@/views/patients/nutritionSetting/index.vue'));
+const DietTherapy = defineAsyncComponent(() => import('@/views/patients/dietTherapy/index.vue'));
+const ParenteralNutrition = defineAsyncComponent(() => import('@/views/patients/parenteralNutrition/index.vue'));
+const EnteralNutrition = defineAsyncComponent(() => import('@/views/patients/enteralNutrition/index.vue'));
+const EnteralNutritionHistory = defineAsyncComponent(() => import('@/views/patients/enteralNutrition/history.vue'));  
+
+const componentMap = {
+  medicalRecord: MedicalRecord,
+  checkLabel: CheckLabel,
+  nutritionDiagnosis: NutriDiagnosis,
+  nutritionScreening: NutritionScreening,
+  nutritionScreeningAdd: NutritionScreeningAdd,
+  nutritionEvaluation: NutritionEvaluation,
+  nutritionEvaluationAdd: NutritionEvaluationAdd,
+  nutritionEducation: NutritionEducation,
+  nutritionSetting: NutritionSetting,
+  dietTherapy: DietTherapy,
+  parenteralNutrition: ParenteralNutrition,
+  enteralNutrition: EnteralNutrition,
+  enteralNutritionHistory: EnteralNutritionHistory,
+};
+const currentComponent = ref(componentMap['medicalRecord']);
+const route = useRoute();
+const router = useRouter();
+const {proxy} = getCurrentInstance() as any;
 
     const activeMenu = ref('medicalRecord');
 
-    const patientInfo = ref({
-        id: '',
-        name: '',
-        age: '',
-        gender: '',
-        type: '',
-        deptId: '',
-        deptName: '',
-        outpatientNo: '',
-        height: '',
-        weight: '',
-        bmi: '',
-        activity: '',
-        bedNo: ''
-
-
-    });
+const patientInfo = ref({
+  id: '',
+  name: '',
+  age: '',
+  gender: '',
+  type: '',
+  deptId: '',
+  deptName: '',
+  outpatientNo: '',
+  height: '',
+  weight: '',
+  bmi: '',
+  activity: '',
+
+ 
+});
 
     // 弹窗控制
     const showDetailDialog = ref(false);
@@ -197,20 +190,19 @@
     const showEditDialog = ref(false);
     const editData = ref({} as any);
 
-    const handleSelect = (key: string, other ? : string[]) => {
-        if (key == 'nutritionScreeningAdd' || key == 'nutritionEvaluationAdd') {
-            activeMenu.value = key.replace('Add', '');
-        }
-        if (key == 'enteralNutritionHistory') {
-            activeMenu.value = key.replace('History', '');
-        } else {
-            activeMenu.value = key;
-        }
-        currentComponent.value = componentMap[key] || null;
-        patientInfo.value['other'] = other;
-        patientInfo.value.type = route.query.type;
-        patientInfo.value.outpatientNo = route.query.outpatientNo;
-    };
+const handleSelect = (key: string, other?: string[]) => {
+  if (key == 'nutritionScreeningAdd' || key == 'nutritionEvaluationAdd') {
+    activeMenu.value = key.replace('Add', '');
+  } if(key=='enteralNutritionHistory'){            
+            activeMenu.value = key.replace('History',''); 
+  }else {
+    activeMenu.value = key;
+  }
+  currentComponent.value = componentMap[key] || null;
+  patientInfo.value['other'] = other;
+  patientInfo.value.type = route.query.type;
+  patientInfo.value.outpatientNo = route.query.outpatientNo;
+};
 
     const goBack = () => {
         router.back();
@@ -363,77 +355,74 @@
     const treatingCount = ref(0);
     const treatedCount = ref(0);
 
-    const getList = async () => {
-        loading.value = true;
-        try {
-            const params: any = {
-                pageNum: 1,
-                pageSize: 10
-            };
-            if (searchValue.value) {
-                params.searchFlag = searchValue.value;
-            }
-            const res = await listTreatmentUser(params);
-            patientList.value = (res.rows || []).map(item => ({
-                id: item.id,
-                name: item.treatName,
-                type: item.type,
-                deptId: item.doorId,
-                deptName: item.deptName,
-                outpatientNo: item.outpatientNo,
-                gender: item.sex === '0' ? '男' : item.sex === '1' ? '女' : '',
-                age: item.age,
-                BMI: item.bmi,
-                height: item.height,
-                weight: item.weight,
-                activity: item.activity,
-                bedNo: item.bedNo
-            }));
-
-            waitingCount.value = patientList.value.length;
-            // 如果有患者数据,根据路由参数或默认选中第一个
-            if (patientList.value.length > 0) {
-                const { id } = route.query;
-                if (typeof id === 'string') {
-                    const targetIndex = patientList.value.findIndex(patient => patient.id === id);
-                    if (targetIndex !== -1) {
-                        patientInfo.value = patientList.value[targetIndex];
-                        patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
-                        patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
-                        currentPatientIndex.value = targetIndex;
-                    } else {
-                        patientInfo.value = patientList.value[0];
-                        patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
-                        patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
-                        currentPatientIndex.value = 0;
-                    }
-                } else {
-                    patientInfo.value = patientList.value[0];
-                    patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
-                    patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
-                    currentPatientIndex.value = 0;
-                }
-            }
-            loading.value = false;
-        } catch (error) {
-            console.error('获取列表失败:', error);
-            loading.value = false;
-        }
+const getList = async () => {
+  loading.value = true;
+  try {
+    const params: any = {
+      pageNum: 1,
+      pageSize: 10
     };
+    if (searchValue.value) {
+      params.searchFlag = searchValue.value;
+    }
+    const res = await listTreatmentUser(params);
+    patientList.value = (res.rows || []).map(item => ({
+      id: item.id,
+      name: item.treatName,
+      type: item.type,
+      deptId: item.doorId,
+      deptName: item.deptName,
+      outpatientNo: item.outpatientNo,
+      gender: item.sex === '0' ? '男' : item.sex === '1' ? '女' : '',
+      age: item.age,
+      BMI: item.bmi,
+      height: item.height,
+      weight: item.weight,
+      activity: item.activity,
+    }));
+    waitingCount.value = patientList.value.length;
+    // 如果有患者数据,根据路由参数或默认选中第一个
+    if (patientList.value.length > 0) {
+      const { id } = route.query;
+      if (typeof id === 'string') {
+        const targetIndex = patientList.value.findIndex(patient => patient.id === id);
+        if (targetIndex !== -1) {
+          patientInfo.value = patientList.value[targetIndex];
+          patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
+          patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
+          currentPatientIndex.value = targetIndex;
+        } else {
+          patientInfo.value = patientList.value[0];
+          patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
+          patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
+          currentPatientIndex.value = 0;
+        }
+      } else {
+        patientInfo.value = patientList.value[0];
+        patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
+        patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
+        currentPatientIndex.value = 0;
+      }
+    }
+    loading.value = false;
+  } catch (error) {
+    console.error('获取列表失败:', error);
+    loading.value = false;
+  }
+};
 
     const handleSearch = () => {
         getList();
     };
 
-    onMounted(() => {
-        // 获取体力活动字典
-        const { physical_activity } = toRefs(proxy ?.useDict('physical_activity'));
-        physicalActivityDict.value = physical_activity ?.value || [];
-        patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
-        patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
-        getList();
-        getDiagnosisList();
-    });
+onMounted(() => {
+  // 获取体力活动字典
+  const { physical_activity } = toRefs(proxy?.useDict('physical_activity'));
+  physicalActivityDict.value = physical_activity?.value || [];
+  patientInfo.value.type = typeof route.query.type === 'string' ? route.query.type : '';
+  patientInfo.value.outpatientNo = typeof route.query.outpatientNo === 'string' ? route.query.outpatientNo : '';
+  getList();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -511,28 +500,28 @@
         overflow-x: auto;
     }
 
-    .table-patients {
-        table-layout: fixed;
-        width: 100%;
-        min-width: 220px;
-        white-space: nowrap;
-
-        .el-table__header th {
-            background: #f5f7fa;
-            color: #409eff;
-            font-weight: bold;
-            font-size: 13px;
-            padding: 4px 0;
-        }
-
-        .el-table__row {
-            cursor: pointer;
-        }
-
-        .el-table__row.current-row {
-            background: #eaf4ff !important;
-        }
-    }
+.table-patients {
+  table-layout: fixed;
+  width: 100%;
+  min-width: 220px;
+  white-space: nowrap;
+
+  .el-table__header th {
+    background: #f5f7fa;
+    color: #409eff;
+    font-weight: bold;
+    font-size: 13px;
+    padding: 4px 0;
+  }
+
+  .el-table__row {
+    cursor: pointer;
+  }
+
+  .el-table__row.current-row {
+    background: #eaf4ff !important;
+  }
+}
 
     .nav-btns {
         display: flex;
@@ -575,44 +564,34 @@
         background: #fff;
     }
 
-    .info-row {
-        display: flex;
-        align-items: center;
-        white-space: nowrap;
-    }
-
-    .info-row .name {
-        font-size: 22px;
-    }
-
-    .info-row .age {
-        margin-left: 30px;
-    }
-
-    .gender-icon {
-        margin-left: 30px;
-        font-size: 25px;
-        font-weight: bold;
-        line-height: 1;
-        display: inline-block;
-        vertical-align: middle;
-    }
-
-    .gender-icon.female {
-        color: #ff4949;
-    }
+.info-row {
+  display: flex;
+  align-items: center;
+  white-space: nowrap;
+}
+
+.info-row .name {
+  font-size: 22px;
+}
+
+.info-row .age {
+  margin-left: 30px;
+}
+
+.gender-icon {
+  margin-left: 30px;
+  font-size: 25px;
+  font-weight: bold;
+  line-height: 1;
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.gender-icon.female {
+  color: #ff4949;
+}
 
     .gender-icon.male {
         color: #409eff;
     }
-
-    .truncate-text {
-        display: -webkit-box;
-        -webkit-line-clamp: 2;
-        -webkit-box-orient: vertical;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        line-height: 1.5;
-        max-height: 3em;
-    }
 </style>

+ 243 - 249
src/views/patients/nutritionSetting/addForm.vue

@@ -27,7 +27,7 @@
                 <el-col :span="3" v-if="title === '烧伤公式'"><span>烧伤面积:</span>
                     <el-input v-model="form.burnArea" style="width: 120px;" /> <span>%</span>
                 </el-col>
-                <el-col :span="3"><span class="required">*</span><span>体重:</span>
+                <el-col :span="3"><span>体重:</span>
                     <el-input v-model="form.weight" style="width: 120px;" /> <span>kg</span>
                 </el-col>
                 <el-col :span="3"><span>上臂围:</span>
@@ -39,7 +39,7 @@
                     <el-input v-model="form.calfCircumference" style="width: 120px;" /> <span>cm</span>
                 </el-col>
                 <el-col :span="20" style="text-align:right;">
-                    <el-button type="primary" class="recalc-btn" @click="()=>recalculate(form.settingType,form.gender, form.age, form.height, form.weight, form.activity, form.stressType,form.burnArea)">重新计算</el-button>
+                    <el-button type="primary" class="recalc-btn" @click="()=>recalculate(form.gender, form.age, form.height, form.weight, form.activity, form.stressType)">重新计算</el-button>
                 </el-col>
             </el-row>
         </div>
@@ -258,281 +258,275 @@
                             </el-form-item>
                         </el-col>
 
-                        <el-col :span="8">
-                            <el-form-item label="维生素D:" class="right-label">
-                                <el-input v-model="form.vitaminD" style="width: 200px;" />
-                                <span class="unit">μg/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="维生素E:" class="right-label">
-                                <el-input v-model="form.vitaminE" style="width: 200px;" />
-                                <span class="unit">mgα-TE/d</span>
-                            </el-form-item>
-                        </el-col>
-                    </el-row>
-                    <el-row :gutter="20" style="margin-top: 8px;">
-                        <el-col :span="8">
-                            <el-form-item label="维生素K:" class="right-label">
-                                <el-input v-model="form.vitaminK" style="width: 200px;" />
-                                <span class="unit">μg/d</span>
-                            </el-form-item>
-                        </el-col>
-                    </el-row>
-                </div>
-                <!-- 水溶性维生素 -->
-                <div class="group-card">
-                    <div class="group-title">水溶性维生素</div>
-                    <el-row :gutter="20">
-                        <el-col :span="8">
-                            <el-form-item label="维生素B1:" class="right-label">
-                                <el-input v-model="form.vitaminBOne" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="维生素B2:" class="right-label">
-                                <el-input v-model="form.vitaminBTwo" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="维生素B6:" class="right-label">
-                                <el-input v-model="form.vitaminBSix" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-
-                    </el-row>
-                    <el-row :gutter="20" style="margin-top: 8px;">
-                        <el-col :span="8">
-                            <el-form-item label="维生素B12:" class="right-label">
-                                <el-input v-model="form.vitaminBTwelve" style="width: 200px;" />
-                                <span class="unit">μg/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="烟酸(尼克酸):" class="right-label">
-                                <el-input v-model="form.niacin" style="width: 200px;" />
-                                <span class="unit">mgNE/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="维生素C:" class="right-label">
-                                <el-input v-model="form.vitaminC" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-                    </el-row>
-                    <el-row :gutter="20" style="margin-top: 8px;">
-                        <el-col :span="8">
-                            <el-form-item label="叶酸:" class="right-label">
-                                <el-input v-model="form.folicAcid" style="width: 200px;" />
-                                <span class="unit">μgDFE/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="生物素:" class="right-label">
-                                <el-input v-model="form.biotin" style="width: 200px;" />
-                                <span class="unit">μg/d</span>
-                            </el-form-item>
-                        </el-col>
-                        <el-col :span="8">
-                            <el-form-item label="胆碱:" class="right-label">
-                                <el-input v-model="form.choline" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-                    </el-row>
-                    <el-row :gutter="20" style="margin-top: 8px;">
-                        <el-col :span="8">
-                            <el-form-item label="泛酸:" class="right-label">
-                                <el-input v-model="form.pantothenicAcid" style="width: 200px;" />
-                                <span class="unit">mg/d</span>
-                            </el-form-item>
-                        </el-col>
-
-                    </el-row>
-                </div>
-                <!-- 其他 -->
-                <div class="group-card">
-                    <div class="group-title">其他</div>
-                    <el-row :gutter="20">
-                        <el-col :span="8">
-                            <el-form-item label="膳食纤维:" class="right-label">
-                                <el-input v-model="form.dietaryFiber" style="width: 200px;" />
-                                <span class="unit">g/d</span>
-                            </el-form-item>
-                        </el-col>
-                    </el-row>
-                </div>
-            </el-form>
-            <div class="flex justify-center gap-2 mt-4">
-                <el-button :loading="buttonLoading" type="primary" @click="submitForm">保 存</el-button>
-            </div>
+            <el-col :span="8">
+              <el-form-item label="维生素D:" class="right-label">
+                <el-input v-model="form.vitaminD" style="width: 200px" />
+                <span class="unit">μg/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="维生素E:" class="right-label">
+                <el-input v-model="form.vitaminE" style="width: 200px" />
+                <span class="unit">mgα-TE/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" style="margin-top: 8px">
+            <el-col :span="8">
+              <el-form-item label="维生素K:" class="right-label">
+                <el-input v-model="form.vitaminK" style="width: 200px" />
+                <span class="unit">μg/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
         </div>
-
+        <!-- 水溶性维生素 -->
+        <div class="group-card">
+          <div class="group-title">水溶性维生素</div>
+          <el-row :gutter="20">
+            <el-col :span="8">
+              <el-form-item label="维生素B1:" class="right-label">
+                <el-input v-model="form.vitaminBOne" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="维生素B2:" class="right-label">
+                <el-input v-model="form.vitaminBTwo" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="维生素B6:" class="right-label">
+                <el-input v-model="form.vitaminBSix" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" style="margin-top: 8px">
+            <el-col :span="8">
+              <el-form-item label="维生素B12:" class="right-label">
+                <el-input v-model="form.vitaminBTwelve" style="width: 200px" />
+                <span class="unit">μg/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="烟酸(尼克酸):" class="right-label">
+                <el-input v-model="form.niacin" style="width: 200px" />
+                <span class="unit">mgNE/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="维生素C:" class="right-label">
+                <el-input v-model="form.vitaminC" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" style="margin-top: 8px">
+            <el-col :span="8">
+              <el-form-item label="叶酸:" class="right-label">
+                <el-input v-model="form.folicAcid" style="width: 200px" />
+                <span class="unit">μgDFE/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="生物素:" class="right-label">
+                <el-input v-model="form.biotin" style="width: 200px" />
+                <span class="unit">μg/d</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="胆碱:" class="right-label">
+                <el-input v-model="form.choline" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" style="margin-top: 8px">
+            <el-col :span="8">
+              <el-form-item label="泛酸:" class="right-label">
+                <el-input v-model="form.pantothenicAcid" style="width: 200px" />
+                <span class="unit">mg/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+        <!-- 其他 -->
+        <div class="group-card">
+          <div class="group-title">其他</div>
+          <el-row :gutter="20">
+            <el-col :span="8">
+              <el-form-item label="膳食纤维:" class="right-label">
+                <el-input v-model="form.dietaryFiber" style="width: 200px" />
+                <span class="unit">g/d</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </el-form>
+      <div class="flex justify-center gap-2 mt-4">
+        <el-button :loading="buttonLoading" type="primary" @click="submitForm">保 存</el-button>
+      </div>
     </div>
+  </div>
 </template>
 
 <script setup lang="ts">
-    import { ref, toRefs, getCurrentInstance } from 'vue';
-    import { addSetting, updateSetting, recalculateValue } from '@/api/patients/nutritionSetting';
-    import type { FormInstance } from 'element-plus';
-    const { proxy } = getCurrentInstance() as any;
-    const { physical_activity, stress_level } = toRefs(proxy ?.useDict('physical_activity', 'stress_level'));
-    const emit = defineEmits(['change'])
-    const props = defineProps({
-        form: { type: Object, required: true },
-        rules: { type: Object, required: true },
-        buttonLoading: { type: Boolean, required: true },
-        cancel: { type: Function, required: true },
-        handleCancelOne: { type: Function, required: true },
-        title: { type: String }
-    });
-    const { handleCancelOne, form, rules } = props;
-    const { title } = toRefs(props);
-    const showFlag = ref('false');
-    const buttonLoading = ref(false);
-    const NutritionSettingFormRef = ref < FormInstance > ();
-
+import {ref, toRefs, getCurrentInstance} from 'vue';
+import {addSetting, updateSetting, recalculateValue} from '@/api/patients/nutritionSetting';
+import type {FormInstance} from 'element-plus';
+const {proxy} = getCurrentInstance() as any;
+const {physical_activity, stress_level} = toRefs(proxy?.useDict('physical_activity', 'stress_level'));
+const emit = defineEmits(['change']);
+const props = defineProps({
+  form: {type: Object, required: true},
+  rules: {type: Object, required: true},
+  buttonLoading: {type: Boolean, required: true},
+  cancel: {type: Function, required: true},
+  handleCancelOne: {type: Function, required: true},
+  title: {type: String}
+});
+const {handleCancelOne, form, rules} = props;
+const {title} = toRefs(props);
+const showFlag = ref('false');
+const buttonLoading = ref(false);
+const NutritionSettingFormRef = ref<FormInstance>();
 
-    const recalculate = async (settingType,gender, age, height, weight, activity, stressType,burnArea) => {
-        const res = await recalculateValue({settingType, gender, age, height, weight, activity, stressType ,burnArea});
-        
+    const recalculate = async (gender, age, height, weight, activity, stressType) => {
+        const res = await recalculateValue({ gender, age, height, weight, activity, stressType });
         if (res) {
             const keepSettingType = form.settingType;
             Object.assign(form, res);
             form.settingType = keepSettingType; // 恢复settingType的值
+
         }
         showFlag.value = 'true';
     };
 
-    /** 提交按钮 */
-    const submitForm = async () => {
-        if (!NutritionSettingFormRef.value) return;
-        await NutritionSettingFormRef.value.validate(async (valid: boolean) => {
-            if (valid) {
-                buttonLoading.value = true;
-                try {
-                    if (form.id) {
-                        await updateSetting(form);
-                    } else {
-                        console.log(form.settingType);
-                        await addSetting(form);
-                    }
-                    proxy ?.$modal.msgSuccess('操作成功');
-                    onBack();
-                } catch (error) {
-                    console.error('提交失败', error);
-                } finally {
-                    buttonLoading.value = false;
-                }
-            }
-        });
-    };
-    const close = async () => {
-
-    };
+/** 提交按钮 */
+const submitForm = async () => {
+  if (!NutritionSettingFormRef.value) return;
+  await NutritionSettingFormRef.value.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.id) {
+          await updateSetting(form);
+        } else {
+          console.log(form.settingType);
+          await addSetting(form);
+        }
+        proxy?.$modal.msgSuccess('操作成功');
+        onBack();
+      } catch (error) {
+        console.error('提交失败', error);
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+const close = async () => {};
 
-    const onBack = () => {
-        showFlag.value = 'false';
-        emit('change', 'nutritionSetting');
-        handleCancelOne();
-    };
+const onBack = () => {
+  showFlag.value = 'false';
+  emit('change', 'nutritionSetting');
+  handleCancelOne();
+};
 </script>
 
 <style scoped>
-    .page-current {
-        display: flex;
-        align-items: center;
-        padding: 20px;
-        background: #fff;
-        border-bottom: 1px solid #e6e6e6;
-    }
+.page-current {
+  display: flex;
+  align-items: center;
+  padding: 20px;
+  background: #fff;
+  border-bottom: 1px solid #e6e6e6;
+}
 
-    .question-main {
-        padding: 20px 0 100px;
-    }
+.question-main {
+  padding: 20px 0 100px;
+}
 
-    .info-card-bee {
-        margin-bottom: 16px;
-        background: #f8fafc;
-        border-radius: 8px;
-        box-shadow: 0 2px 8px #f0f1f2;
-        padding: 18px 24px 10px 24px;
-        border: none;
-    }
+.info-card-bee {
+  margin-bottom: 16px;
+  background: #f8fafc;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px #f0f1f2;
+  padding: 18px 24px 10px 24px;
+  border: none;
+}
 
-    .info-title {
-        font-weight: bold;
-        font-size: 16px;
-        color: #409EFF;
-        margin-bottom: 8px;
-        padding-left: 2px;
-    }
+.info-title {
+  font-weight: bold;
+  font-size: 16px;
+  color: #409eff;
+  margin-bottom: 8px;
+  padding-left: 2px;
+}
 
-    .bee-title {
-        border-left: 4px solid #409EFF;
-        padding-left: 8px;
-        font-size: 16px;
-        font-weight: bold;
-        color: #409EFF;
-    }
+.bee-title {
+  border-left: 4px solid #409eff;
+  padding-left: 8px;
+  font-size: 16px;
+  font-weight: bold;
+  color: #409eff;
+}
 
-    .info-row {
-        background: #f8fafc;
-        font-size: 16px;
-        padding: 0 0 0 0;
-        margin: 0;
-    }
+.info-row {
+  background: #f8fafc;
+  font-size: 16px;
+  padding: 0 0 0 0;
+  margin: 0;
+}
 
-    .required {
-        color: #f56c6c;
-        margin-right: 2px;
-    }
+.required {
+  color: #f56c6c;
+  margin-right: 2px;
+}
 
-    .recalc-btn {
-        margin-left: 12px;
-    }
+.recalc-btn {
+  margin-left: 12px;
+}
 
-    .tips-box {
-        color: #ff5e4c;
-        margin: 16px 0;
-        font-size: 14px;
-    }
+.tips-box {
+  color: #ff5e4c;
+  margin: 16px 0;
+  font-size: 14px;
+}
 
-    .group-card {
-        background: #fff;
-        border-radius: 8px;
-        margin-bottom: 24px;
-        box-shadow: 0 2px 8px #f0f1f2;
-        padding: 16px 24px;
-    }
+.group-card {
+  background: #fff;
+  border-radius: 8px;
+  margin-bottom: 24px;
+  box-shadow: 0 2px 8px #f0f1f2;
+  padding: 16px 24px;
+}
 
-    .group-title {
-        background: #f5f7fa;
-        padding: 8px 16px;
-        font-weight: bold;
-        border-radius: 4px;
-        margin-bottom: 16px;
-        font-size: 16px;
-    }
+.group-title {
+  background: #f5f7fa;
+  padding: 8px 16px;
+  font-weight: bold;
+  border-radius: 4px;
+  margin-bottom: 16px;
+  font-size: 16px;
+}
 
-    .unit {
-        margin-left: 4px;
-        color: #888;
-    }
+.unit {
+  margin-left: 4px;
+  color: #888;
+}
 
-    .right-label .el-form-item__label {
-        text-align: right !important;
-        justify-content: flex-end;
-        display: flex;
-    }
+.right-label .el-form-item__label {
+  text-align: right !important;
+  justify-content: flex-end;
+  display: flex;
+}
 
-    ::v-deep .el-form-item__label {
-        text-align: right !important;
-        justify-content: flex-end;
-        display: flex;
-    }
-</style>
+::v-deep .el-form-item__label {
+  text-align: right !important;
+  justify-content: flex-end;
+  display: flex;
+}
+</style>

+ 93 - 92
src/views/patients/nutritionSetting/index.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="p-2">
-        <div v-show="type == 'list'">
+        <div v-show="type=='list'">
             <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
                 <div v-show="showSearch" class="mb-[10px]">
                     <el-card shadow="hover">
@@ -19,7 +19,7 @@
                                 </el-select>
                             </el-form-item>
                             <el-form-item>
-                                <el-input v-model="queryParams.searchValue" placeholder="门诊/住院号、医生" style="width: 240px" clearable />
+                                <el-input v-model="queryParams.searchValue" placeholder="门诊/住院号、医生" style="width: 240px; " clearable />
                             </el-form-item>
                             <el-form-item>
                                 <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -44,13 +44,13 @@
                     <el-table-column label="时间" align="center" prop="createTime" />
                     <el-table-column label="看诊类型" align="center" prop="type">
                         <template #default="scope">
-                            <span>{{ getDictLabel(treatment_user_type, scope.row.type) || '--' }}</span>
+                            <span>{{getDictLabel(treatment_user_type ,scope.row.type )|| '--' }}</span>
                         </template>
                     </el-table-column>
                     <el-table-column label="门诊/住院号" align="center" prop="outpatientNo" />
                     <el-table-column label="营养设定方式" align="center" prop="settingType">
                         <template #default="scope">
-                            <span>{{ getDictLabel(nutrition_setting_type, scope.row.settingType) || '--' }}</span>
+                            <span>{{getDictLabel(nutrition_setting_type ,scope.row.settingType )|| '--' }}</span>
                         </template>
                     </el-table-column>
                     <el-table-column label="所需热量(kcal/d)" align="center" prop="caloriesKcalPerDay" />
@@ -71,29 +71,30 @@
         </div>
         <div>
             <el-dialog :title="'选择营养设定'" v-model="dialog.visible" width="737px" append-to-body :close-on-click-modal="false" :show-close="true" class="nutrition-setting-dialog">
-                <el-row :gutter="20" style="margin-bottom: 10px">
+                <el-row :gutter="20" style="margin-bottom: 10px;">
                     <el-col :span="12">
-                        <el-button :class="['setting-type-btn', form.settingType === '1' ? 'active' : '']" style="width: 100%; height: 46px" @click="handleSelectSettingType('1', 'BEE')">BEE</el-button>
+                        <el-button :class="['setting-type-btn', form.settingType === '1' ? 'active' : '']" style="width: 100%; height: 46px;" @click="handleSelectSettingType('1', 'BEE')">BEE</el-button>
                     </el-col>
                     <el-col :span="12">
-                        <el-button :class="['setting-type-btn', form.settingType === '2' ? 'active' : '']" style="width: 100%; height: 46px" @click="handleSelectSettingType('2', 'DRIS')">DRIS</el-button>
+                        <el-button :class="['setting-type-btn', form.settingType === '2' ? 'active' : '']" style="width: 100%; height: 46px;" @click="handleSelectSettingType('2', 'DRIS')">DRIS</el-button>
                     </el-col>
                 </el-row>
                 <el-row :gutter="20">
                     <el-col :span="12">
-                        <el-button :class="['setting-type-btn', form.settingType === '3' ? 'active' : '']" style="width: 100%; height: 46px" @click="handleSelectSettingType('3', '拇指测法')">拇指测法</el-button>
+                        <el-button :class="['setting-type-btn', form.settingType === '3' ? 'active' : '']" style="width: 100%; height: 46px;" @click="handleSelectSettingType('3','拇指测法')">拇指测法</el-button>
                     </el-col>
                     <el-col :span="12">
-                        <el-button :class="['setting-type-btn', form.settingType === '4' ? 'active' : '']" style="width: 100%; height: 46px" @click="handleSelectSettingType('4', '烧伤公式')">烧伤公式</el-button>
+                        <el-button :class="['setting-type-btn', form.settingType === '4' ? 'active' : '']" style="width: 100%; height: 46px;" @click="handleSelectSettingType('4','烧伤公式')">烧伤公式</el-button>
                     </el-col>
                 </el-row>
             </el-dialog>
         </div>
-        <div v-show="type == 'addForm'">
+        <div v-show="type=='addForm'">
             <!-- 添加或修改营养设定对话框 -->
             <add-form :form="form" :rules="rules" :buttonLoading="buttonLoading" :cancel="cancel" :handleCancelOne="handleCancelOne" :title="title" />
         </div>
     </div>
+
 </template>
 
 <script setup name="NutritionSetting" lang="ts">
@@ -102,44 +103,44 @@
     import { listSetting, getSetting, delSetting, addSetting, updateSetting } from '@/api/patients/nutritionSetting';
     import { NutritionSettingVO, NutritionSettingQuery, NutritionSettingForm } from '@/api/patients/nutritionSetting/types';
     import AddForm from './addForm.vue';
-    const emit = defineEmits(['change']);
+    const emit = defineEmits(['change'])
     import { log } from 'vxe-table';
 
-    const { proxy } = getCurrentInstance();
-    const { treatment_user_type, nutrition_setting_type } = toRefs(proxy ?.useDict('treatment_user_type', 'nutrition_setting_type'));
+const {proxy} = getCurrentInstance();
+const {treatment_user_type, nutrition_setting_type} = toRefs(proxy?.useDict('treatment_user_type', 'nutrition_setting_type'));
 
-    const settingList = ref < NutritionSettingVO[] > ([]);
-    const buttonLoading = ref(false);
-    const loading = ref(true);
-    const showSearch = ref(true);
-    const ids = ref < Array < string | number >> ([]);
-    const single = ref(true);
-    const multiple = ref(true);
-    const total = ref(0);
-    const type = ref('list');
-    const title = ref('');
+const settingList = ref<NutritionSettingVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const type = ref('list');
+const title = ref('');
 
-    const queryFormRef = ref();
-    const NutritionSettingFormRef = ref();
+const queryFormRef = ref();
+const NutritionSettingFormRef = ref();
 
-    const dialog = reactive({
-        visible: false,
-        title: ''
-    });
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
 
-    const props = defineProps({
-        patientInfo: {
-            type: Object,
-            required: true,
-            default: () => ({
-                id: '',
-                name: '',
-                age: '',
-                deptId: '',
-                gender: ''
-            })
-        }
-    });
+const props = defineProps({
+  patientInfo: {
+    type: Object,
+    required: true,
+    default: () => ({
+      id: '',
+      name: '',
+      age: '',
+      deptId: '',
+      gender: ''
+    })
+  }
+});
 
     const initFormData: NutritionSettingForm = {
         id: undefined,
@@ -188,7 +189,6 @@
         settingType: undefined,
         type: props.patientInfo ?.type,
         deptId: props.patientInfo ?.deptId,
-        patientId: props.patientInfo ?.id,
         deptName: undefined,
         outpatientNo: props.patientInfo ?.outpatientNo,
         bmi: props.patientInfo ?.bmi,
@@ -200,13 +200,14 @@
         lowHip: undefined,
         burnArea: undefined,
         gender: props.patientInfo ?.gender,
-        age: props.patientInfo ?.age
+        age: props.patientInfo ?.age,
+
     };
 
     const form = ref({ ...initFormData });
     const queryParams = ref({
         patientId: props.patientInfo ?.id,
-        dateRange: [],
+        dateRange: [new Date(), new Date()],
         type: '',
         settingType: '',
         searchValue: '',
@@ -215,8 +216,12 @@
     });
 
     const rules = ref({
-        height: [{ required: true, message: '身高不能为空', trigger: 'blur' }],
-        activity: [{ required: true, message: '体力活动不能为空', trigger: 'change' }],
+        height: [
+            { required: true, message: '身高不能为空', trigger: 'blur' }
+        ],
+        activity: [
+            { required: true, message: '体力活动不能为空', trigger: 'change' }
+        ],
         // 只有在 BEE 公式下才校验应激状态
         stressType: [{
             required: true,
@@ -232,37 +237,34 @@
         }]
     });
 
-    // 事件函数区
-    const handleDelete = async (row ? : NutritionSettingVO) => {
-        const _ids = row ?.id || ids.value;
-        await proxy ?.$modal.confirm('是否确认删除营养设定编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
-        await delSetting(_ids);
-        proxy ?.$modal.msgSuccess('删除成功');
-        await getList();
-    };
+// 事件函数区
+const handleDelete = async (row?: NutritionSettingVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除营养设定编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delSetting(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
 
     const handleExport = () => {
-        proxy ?.download(
-            '/patients/nutritionSetting/export', {
-                ...queryParams.value
-            },
-            `setting_${new Date().getTime()}.xlsx`
-        );
+        proxy ?.download('/patients/nutritionSetting/export', {
+            ...queryParams.value
+        }, `setting_${new Date().getTime()}.xlsx`)
     };
 
-    const handleSelectSettingType = (settingType, typeName) => {
-        form.value.settingType = settingType;
-        title.value = typeName;
-        dialog.visible = false;
-        nextTick(() => {
-            type.value = 'addForm';
-        });
-    };
+const handleSelectSettingType = (settingType, typeName) => {
+  form.value.settingType = settingType;
+  title.value = typeName;
+  dialog.visible = false;
+  nextTick(() => {
+    type.value = 'addForm';
+  });
+};
 
     // 字典label工具
     function getDictLabel(dictList: any[], value: string) {
         if (!dictList || !Array.isArray(dictList)) return value || '--';
-        const found = dictList.find((item) => item.value === value);
+        const found = dictList.find(item => item.value === value);
         return found ? found.label : value || '--';
     }
 
@@ -294,7 +296,7 @@
         handleQuery();
     };
     const handleSelectionChange = (selection: NutritionSettingVO[]) => {
-        ids.value = selection.map((item) => item.id);
+        ids.value = selection.map(item => item.id);
         single.value = selection.length != 1;
         multiple.value = !selection.length;
     };
@@ -315,32 +317,31 @@
         loading.value = true;
         //  这里补充你的获取数据逻辑
         const res = await listSetting(queryParams.value);
-
         settingList.value = res.rows || [];
         total.value = res.total;
         loading.value = false;
     };
 
-    onMounted(() => {
-        getList();
-    });
+onMounted(() => {
+  getList();
+});
 </script>
 <style scoped>
-    .nutrition-setting-dialog .setting-type-btn {
-        font-size: 18px;
-        border: 1.5px solid #409eff;
-        color: #409eff;
-        background: #fff;
-        transition:
-            background 0.2s,
-            color 0.2s;
-    }
+.nutrition-setting-dialog .setting-type-btn {
+  font-size: 18px;
+  border: 1.5px solid #409eff;
+  color: #409eff;
+  background: #fff;
+  transition:
+    background 0.2s,
+    color 0.2s;
+}
 
-    .nutrition-setting-dialog .setting-type-btn.active,
-    .nutrition-setting-dialog .setting-type-btn:active,
-    .nutrition-setting-dialog .setting-type-btn:focus {
-        background: #e6f0ff;
-        color: #409eff;
-        border-color: #409eff;
-    }
-</style>
+.nutrition-setting-dialog .setting-type-btn.active,
+.nutrition-setting-dialog .setting-type-btn:active,
+.nutrition-setting-dialog .setting-type-btn:focus {
+  background: #e6f0ff;
+  color: #409eff;
+  border-color: #409eff;
+}
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 617 - 628
src/views/patients/screening/add.vue


+ 95 - 97
src/views/patients/screening/index.vue

@@ -1,14 +1,19 @@
 <template>
   <div class="p-2">
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
-      :leave-active-class="proxy?.animate.searchAnimate.leave">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item prop="screeningTime">
-              <el-date-picker v-model="screeningTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
-                range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
+              <el-date-picker
+                v-model="screeningTime"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                type="daterange"
+                range-separator="-"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              ></el-date-picker>
             </el-form-item>
             <el-form-item label="看诊类型" prop="visitType">
               <el-select v-model="queryParams.visitType" placeholder="请选择" clearable>
@@ -38,8 +43,7 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd"
-              v-hasPermi="['system:screening:add']">新增营养筛查</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:screening:add']">新增营养筛查</el-button>
           </el-col>
           <!-- <el-col :span="1.5">
             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
@@ -59,7 +63,7 @@
 
       <el-table v-loading="loading" border :data="screeningList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="时间" align="center" prop="screeningTime"  />
+        <el-table-column label="时间" align="center" prop="screeningTime" />
         <el-table-column label="看诊类型" align="center" prop="visitType" />
         <el-table-column label="门诊/住院号" align="center" prop="patientNo" />
         <el-table-column label="营养筛查类型" align="center" prop="configName" />
@@ -76,42 +80,41 @@
               <el-button link type="primary" icon="Print" @click="handleUpdate(scope.row)">打印</el-button>
             </el-tooltip> -->
             <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
-                v-hasPermi="['system:screening:remove']">删除</el-button>
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:screening:remove']">删除</el-button>
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
 
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize" @pagination="getList" />
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 添加或修改营养筛查对话框 -->
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
       <div class="screening-type-grid">
-            <el-button
-              v-for="item in screeningAssessmentConfigList"
-              :key="item.configId"
-              type="primary"
-              plain 
-              class="screening-type-btn"              
-              @click="handleScreeningSelect(item.configId)"
-              style="width: 350px;margin-left:0px">            
-              {{ item.name }}
-            </el-button>
-          </div>       
+        <el-button
+          v-for="item in screeningAssessmentConfigList"
+          :key="item.configId"
+          type="primary"
+          plain
+          class="screening-type-btn"
+          @click="handleScreeningSelect(item.configId)"
+          style="width: 350px; margin-left: 0px"
+        >
+          {{ item.name }}
+        </el-button>
+      </div>
     </el-dialog>
   </div>
 </template>
 
 <script setup name="Screening" lang="ts">
-import { listScreening, getScreening, delScreening, addScreening, updateScreening } from '@/api/patients/screening';
-import { ScreeningVO, ScreeningQuery, ScreeningForm } from '@/api/patients/screening/types';
-import { listScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
-import { ScreeningAssessmentConfigQuery,ScreeningAssessmentConfigVO } from '@/api/system/screeningAssessmentConfig/types';
+import {listScreening, getScreening, delScreening, addScreening, updateScreening} from '@/api/patients/screening';
+import {ScreeningVO, ScreeningQuery, ScreeningForm} from '@/api/patients/screening/types';
+import {listScreeningAssessmentConfig} from '@/api/system/screeningAssessmentConfig';
+import {ScreeningAssessmentConfigQuery, ScreeningAssessmentConfigVO} from '@/api/system/screeningAssessmentConfig/types';
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { treatment_user_type } = toRefs<any>(proxy?.useDict('treatment_user_type'));
+const {proxy} = getCurrentInstance() as ComponentInternalInstance;
+const {treatment_user_type} = toRefs<any>(proxy?.useDict('treatment_user_type'));
 
 const screeningTime = ref<[DateModelType, DateModelType]>(['', '']);
 const screeningList = ref<ScreeningVO[]>([]);
@@ -126,8 +129,8 @@ const screeningAssessmentConfigList = ref<ScreeningAssessmentConfigVO[]>([]);
 const router = useRouter();
 const queryFormRef = ref<ElFormInstance>();
 const screeningFormRef = ref<ElFormInstance>();
-const { patientInfo } = defineProps<{ patientInfo: any }>()
-const emit = defineEmits(['change'])
+const {patientInfo} = defineProps<{patientInfo: any}>();
+const emit = defineEmits(['change']);
 
 const dialog = reactive<DialogOption>({
   visible: false,
@@ -144,8 +147,8 @@ const initFormData: ScreeningForm = {
   screeningScore: undefined,
   screeningConclusion: undefined,
   paymentStatus: undefined,
-  status: undefined,
-}
+  status: undefined
+};
 const data = reactive<PageData<ScreeningForm, ScreeningQuery>>({
   form: {...initFormData},
   queryParams: {
@@ -160,103 +163,91 @@ const data = reactive<PageData<ScreeningForm, ScreeningQuery>>({
     screeningConclusion: undefined,
     paymentStatus: undefined,
     status: undefined,
-    params: {
-    }
+    params: {}
   },
   rules: {
-    id: [
-      { required: true, message: "主键ID不能为空", trigger: "blur" }
-    ],
-    configId: [
-      { required: true, message: "营养筛查类型不能为空", trigger: "blur" }
-    ],
-    screeningTime: [
-      { required: true, message: "筛查时间不能为空", trigger: "blur" }
-    ],
-    visitType: [
-      { required: true, message: "看诊类型不能为空", trigger: "change" }
-    ],
-    patientNo: [
-      { required: true, message: "门诊/住院号不能为空", trigger: "blur" }
-    ],
+    id: [{required: true, message: '主键ID不能为空', trigger: 'blur'}],
+    configId: [{required: true, message: '营养筛查类型不能为空', trigger: 'blur'}],
+    screeningTime: [{required: true, message: '筛查时间不能为空', trigger: 'blur'}],
+    visitType: [{required: true, message: '看诊类型不能为空', trigger: 'change'}],
+    patientNo: [{required: true, message: '门诊/住院号不能为空', trigger: 'blur'}]
   }
 });
 
-const { queryParams, form, rules } = toRefs(data);
+const {queryParams, form, rules} = toRefs(data);
 
 /** 查询筛查/评估配置列表 */
 const getScreeningConfigList = async () => {
-  let param=ref<ScreeningAssessmentConfigQuery>({
+  let param = ref<ScreeningAssessmentConfigQuery>({
     pageNum: 1,
     type: '1',
-    pageSize: -1,
+    pageSize: -1
   });
   const res = await listScreeningAssessmentConfig(param.value);
-  screeningAssessmentConfigList.value = res.rows;   
-}
+  screeningAssessmentConfigList.value = res.rows;
+};
 
 /** 查询营养筛查列表 */
 const getList = async () => {
-  loading.value = true;    
-  queryParams.value.patientId= patientInfo.id; 
+  loading.value = true;
+  queryParams.value.patientId = patientInfo.id;
   const res = await listScreening(proxy?.addDateRange(queryParams.value, screeningTime.value));
   screeningList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 /** 取消按钮 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 表单重置 */
 const reset = () => {
   form.value = {...initFormData};
   screeningFormRef.value?.resetFields();
-}
+};
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 重置按钮操作 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: ScreeningVO[]) => {
-  ids.value = selection.map(item => item.id);
+  ids.value = selection.map((item) => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "选择营养筛查";
-}
+  dialog.title = '选择营养筛查';
+};
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: ScreeningVO) => {
   reset();
-  const _id = row?.id || ids.value[0]
+  const _id = row?.id || ids.value[0];
   const res = await getScreening(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "修改营养筛查";
-}
+  dialog.title = '修改营养筛查';
+};
 
 const handleView = async (row?: ScreeningVO) => {
-  emit('change', 'nutritionScreeningAdd',window.btoa(row.id.toString()))
-}
-
+  emit('change', 'nutritionScreeningAdd', window.btoa(row.id.toString()));
+};
 
 /** 提交按钮 */
 const submitForm = () => {
@@ -264,52 +255,54 @@ const submitForm = () => {
     if (valid) {
       buttonLoading.value = true;
       if (form.value.id) {
-        await updateScreening(form.value).finally(() =>  buttonLoading.value = false);
+        await updateScreening(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addScreening(form.value).finally(() =>  buttonLoading.value = false);
+        await addScreening(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("操作成功");
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: ScreeningVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除营养筛查编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('是否确认删除营养筛查编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
   await delScreening(_ids);
-  proxy?.$modal.msgSuccess("删除成功");
+  proxy?.$modal.msgSuccess('删除成功');
   await getList();
-}
+};
 
 /** 导出按钮操作 */
 const handleExport = () => {
-  proxy?.download('system/screening/export', {
-    ...queryParams.value
-  }, `screening_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/screening/export',
+    {
+      ...queryParams.value
+    },
+    `screening_${new Date().getTime()}.xlsx`
+  );
+};
 
-const handleScreeningSelect = (configId:number) => {   
-  emit('change', 'nutritionScreeningAdd',configId)
-} 
+const handleScreeningSelect = (configId: number) => {
+  emit('change', 'nutritionScreeningAdd', configId);
+};
 
 function formatDate(date) {
-  const pad = n => n < 10 ? '0' + n : n;
-  return date.getFullYear() + '-' +
-    pad(date.getMonth() + 1) + '-' +
-    pad(date.getDate())   ;
+  const pad = (n) => (n < 10 ? '0' + n : n);
+  return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate());
 }
 
 onMounted(() => {
   getList();
-  getScreeningConfigList(); 
-  
-  let now=new Date();
-  let start=formatDate(now);
-  let end=formatDate(new Date(now.getTime()-24*60*60*1000*30));
-  screeningTime.value=[end+' 00:00:00',start+' 23:59:59'];  
+  getScreeningConfigList();
+
+  let now = new Date();
+  let start = formatDate(now);
+  let end = formatDate(new Date(now.getTime() - 24 * 60 * 60 * 1000 * 30));
+  screeningTime.value = [end + ' 00:00:00', start + ' 23:59:59'];
 });
 </script>
 
@@ -323,6 +316,11 @@ onMounted(() => {
   font-weight: normal;
   font-size: 16px;
   height: 48px;
-  border-radius: 8px;  
+  border-radius: 8px;
+  white-space: normal;
+  word-wrap: break-word;
+  text-align: center;
+  line-height: 1.2;
+  padding: 8px 12px;
 }
 </style>

+ 147 - 108
src/views/system/screeningAssessmentConfig/add.vue

@@ -4,10 +4,11 @@
     <el-row type="flex" align="middle" justify="space-between" style="margin-bottom: 16px;">
       <el-col :span="4">
         <el-button type="primary" plain @click="close" style="margin-right: 20px;margin-left: 30px;">返回</el-button>
-        <span style="font-size: 14px;  vertical-align: middle;">新增量表</span>
+        <span style="font-size: 14px;  vertical-align: middle;">{{ isEdit ? '修改量表' : '新增量表' }}</span>
       </el-col>
       <el-col :span="4" style="text-align: right;">
         <!-- <el-button type="primary" plain @click="handlePreview" style="margin-right: 8px;">预览</el-button> -->
+        <el-button type="primary" @click="editConclusion" style="margin-right: 0px;" v-if="isEdit">编辑结论</el-button>
         <el-button type="primary" @click="submitForm" style="margin-right: 30px;">保存</el-button>
       </el-col>
     </el-row>
@@ -101,12 +102,12 @@
               <el-form label-width="100px" label-position="left" :model="form" ref="screeningAssessmentConfigFormRef">
                 <el-form-item label="量表类型:" required>
                   <el-radio-group v-model="form.type">
-                    <el-radio :label="item.label" :value="item.value" v-for="item in scale_type" :key="item.value"/>                     
+                    <el-radio :label="item.label" :value="item.value" v-for="item in scale_type" :key="item.value" />
                   </el-radio-group>
-            </el-form-item>             
+                </el-form-item>
                 <el-form-item label="适用条件:" required>
                   <!-- 可留空或加说明 -->
-            </el-form-item>            
+                </el-form-item>
                 <el-form-item label="性别:" style="margin-left: 20px;">
                   <el-radio-group v-model="form.gender" style="margin-left: -50px;">
                     <el-radio label="不限" value="-1">不限</el-radio>
@@ -126,9 +127,9 @@
                 <el-form-item label="备注:">
                   <el-input type="textarea" v-model="form.remark" maxlength="500" show-word-limit placeholder="请输入"
                     :rows="3" />
-            </el-form-item>
-          </el-form>
-      </div>
+                </el-form-item>
+              </el-form>
+            </div>
 
             <div v-if="baseInfo.questionType" @click="selectBaseInfo" :class="{ selectedDiv: baseInfo.selected }">
               <span>&nbsp;</span>
@@ -174,14 +175,14 @@
 
               <div style="border-bottom: 1px solid #e5e7eb;width: 98%;" v-if="other.questionChildType == 'matrixScale'">
 
-        <el-row :gutter="10" class="mb8">
+                <el-row :gutter="10" class="mb8">
                   <el-col :span="3" style="margin-top: 30px;">
-          </el-col>
+                  </el-col>
                   <template v-for="clt in other.contentList">
                     <template v-if="clt.optionFlag">
                       <el-col :span="1" align="center" style="margin-top: 30px;margin-left: 30px;">
                         {{ clt.optionContent }}
-          </el-col>
+                      </el-col>
                     </template>
                   </template>
                 </el-row>
@@ -192,7 +193,7 @@
                       <template v-if="clt.labelFlag">
                         标题{{ idx + 1 }}:{{ clt.label }}
                       </template>
-          </el-col>
+                    </el-col>
                     <template v-for="clt2, idx in other.contentList">
                       <el-col :span="1" align="center" style="margin-left: 30px;">
                         <template v-if="clt2.optionFlag && clt.labelFlag">
@@ -202,8 +203,8 @@
                         </template>
                       </el-col>
                     </template>
-        </el-row>
-      </template>
+                  </el-row>
+                </template>
               </div>
 
               <div style="border-bottom: 1px solid #e5e7eb;width: 98%;" :class="{ selectedDiv: other.selected }"
@@ -212,7 +213,7 @@
                 <template v-if="other.questionType != 'questionTypeSelection'">
                   <el-row :gutter="10" class="mb8">
                     <el-col :span="12">
-                      <el-form-item :label="(index + 1 + (baseInfo.contentList.length>0?1:0)) + '. 标题:'" required>
+                      <el-form-item :label="(index + 1 + (baseInfo.contentList.length > 0 ? 1 : 0)) + '. 标题:'" required>
                         <el-input readonly style="width: 100%;" v-model="other.title" size="large" />
                       </el-form-item>
                     </el-col>
@@ -225,11 +226,11 @@
                     <el-col :span="4">
                     </el-col>
                   </el-row>
-          </template>
+                </template>
                 <template v-if="other.questionType == 'questionTypeSelection'">
                   <el-row :gutter="10" class="mb8">
                     <el-col :span="12">
-                      <el-form-item :label="(index + 1 + (baseInfo.contentList.length>0?1:0)) + '. 标题:'" required>
+                      <el-form-item :label="(index + 1 + (baseInfo.contentList.length > 0 ? 1 : 0)) + '. 标题:'" required>
                         <el-input style="width: 100%;" v-model="other.title" placeholder="请输入标题" size="large"
                           show-word-limit maxlength="150" />
                       </el-form-item>
@@ -394,7 +395,7 @@
                               </el-input>
                             </el-radio>
                           </el-radio-group>
-        </el-form-item>
+                        </el-form-item>
                       </el-col>
                       <el-col :span="2" align="center" style="margin-top: -5px;">
                         <miniImageUpload :limit="1" v-model="ctl.img" />
@@ -431,7 +432,7 @@
                               <template #append><el-button
                                   @click="handleMatrixScaleDelete(index, idx, true)">-</el-button></template>
                             </el-input>
-        </el-form-item>
+                          </el-form-item>
                         </template>
                       </el-col>
                       <el-col :span="4" align="center" style="margin-top: -5px;">
@@ -447,7 +448,7 @@
                       <el-col :span="3" align="center" style="margin-top: -5px;">
                         <el-input style="width: 60px;" size="large" v-model="ctl.score" />
                       </el-col>
-                      <el-col :span="3" align="left" style="margin-top: -5px;">                        
+                      <el-col :span="3" align="left" style="margin-top: -5px;">
                         <el-checkbox v-model="ctl.allowFillBlank" />
                       </el-col>
                       <el-col :span="4" align="center">
@@ -468,7 +469,7 @@
                               <template #append><el-button
                                   @click="handleOtherBtnDelete(index, idx)">-</el-button></template>
                             </el-input>
-        </el-form-item>
+                          </el-form-item>
                         </div>
                       </el-col>
                       <el-col :span="2" align="center" style="margin-top: -5px;">
@@ -500,7 +501,7 @@
                               </el-input>
                             </el-radio>
                           </el-radio-group>
-        </el-form-item>
+                        </el-form-item>
                       </el-col>
                       <el-col :span="2" align="center" style="margin-top: -5px;">
                         <el-input style="width: 60px;" v-model="ctl.score" size="large" />
@@ -530,7 +531,7 @@
                             <template #append><el-button
                                 @click="handleOtherBtnDelete(index, idx)">-</el-button></template>
                           </el-input>
-        </el-form-item>
+                        </el-form-item>
                       </el-col>
                       <el-col :span="2" align="center" style="margin-top: -5px;">
                         <el-input style="width: 60px;" v-model="ctl.score" size="large" />
@@ -589,37 +590,84 @@
                   <el-button type="primary" icon="Delete" plain @click="deleteOtherInfo(index)">删除</el-button>
                 </div>
                 <span>&nbsp;</span>
-        </div>
-      </template>
+              </div>
+            </template>
 
 
           </el-scrollbar>
         </el-main>
       </el-container>
     </el-container>
+
+
+    <!--  修改结论 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
+      <el-row style="margin-bottom: 5px;" v-for="(item, index) in conclusionList">
+        <el-col :span="4">
+          <el-input v-model="item.min" placeholder="请输入" />
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">分</div>
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">-</div>
+        </el-col>
+        <el-col :span="4">
+          <el-input v-model="item.max" placeholder="请输入" />
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">分</div>
+        </el-col>
+        <el-col :span="9">
+          <el-input v-model="item.text" placeholder="请输入结论" type="textarea" />
+        </el-col>
+        <el-col :span="1">
+        </el-col>
+        <el-col :span="2">
+          <div class="action-buttons">
+            <el-button class="icon-btn" @click="addRow()">
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </el-button>
+            <el-button class="icon-btn" @click="delRow(index)">
+              <el-icon>
+                <Delete />
+              </el-icon>
+            </el-button>
+          </div>
+        </el-col>
+      </el-row>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitConclusionForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="ScreeningAssessmentConfig" lang="ts">
 import { ref, onMounted, onUnmounted } from 'vue';
-import { listScreeningAssessmentConfig, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
+import { updateConclusion, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
 import { ScreeningAssessmentConfigVO, ScreeningAssessmentConfigQuery, ScreeningAssessmentConfigForm, QuestionAttrVO, QuestionVO } from '@/api/system/screeningAssessmentConfig/types';
-import { fa } from 'element-plus/es/locale/index.mjs';
+
 
 const route = useRoute();
 const showSystemParam = ref(true);
 const showQuestionType = ref(true);
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { arrangement_method, score_method ,scale_type} = toRefs<any>(proxy?.useDict('scale_type','arrangement_method', 'score_method'));
+const { arrangement_method, score_method, scale_type } = toRefs<any>(proxy?.useDict('scale_type', 'arrangement_method', 'score_method'));
 
-const screeningAssessmentConfigList = ref<ScreeningAssessmentConfigVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
+const conclusionList = ref([{ min: null, max: null, text: null }]);
+const isEdit = ref(false);
+
 
 const baseInfo = ref<QuestionVO>({
   questionId: '',
@@ -631,12 +679,11 @@ const baseInfo = ref<QuestionVO>({
 });
 const otherInfo = ref<QuestionVO[]>([]);
 
-const queryFormRef = ref<ElFormInstance>();
 const screeningAssessmentConfigFormRef = ref<ElFormInstance>();
 
 const dialog = reactive<DialogOption>({
   visible: false,
-  title: ''
+  title: '修改结论'
 });
 
 const initFormData: ScreeningAssessmentConfigForm = {
@@ -645,6 +692,7 @@ const initFormData: ScreeningAssessmentConfigForm = {
   gender: '-1',
   age: '-1',
   name: undefined,
+  conclusion: undefined,
   description: undefined,
   remark: undefined,
   status: undefined,
@@ -687,78 +735,68 @@ const { queryParams, form, rules } = toRefs(data);
 const getList = async () => {
   const eId = route.params && route.params.id;
   const vId = route.params && route.params.vid;
-  let id=undefined;
-  if(vId){    
-    id=vId;
-  }else{    
-    id=eId;
+  let id = undefined;
+  if (vId) {
+    id = vId;
+  } else {
+    id = eId;
   }
 
-  if (id&&id!='0') {
-  loading.value = true;
+  if (id && id != '0') {
+    isEdit.value = true;
+    loading.value = true;
     const res = await getScreeningAssessmentConfig(id as string);
     Object.assign(form.value, res.data);
-    if(res.data.baseInfo){
+    if(form.value.conclusion){
+      conclusionList.value=JSON.parse(form.value.conclusion);
+      form.value.conclusion=null;
+    }   
+
+    if (res.data.baseInfo) {
       baseInfo.value = res.data.baseInfo;
     }
-    if(res.data.otherInfo){
-      otherInfo.value = res.data.otherInfo;      
-      otherInfo.value.forEach(item=>{
-        item.increment = item.contentList.length;                 
+    if (res.data.otherInfo) {
+      otherInfo.value = res.data.otherInfo;
+      otherInfo.value.forEach(item => {
+        item.increment = item.contentList.length;
       });
     }
-  loading.value = false;
+    loading.value = false;
   }
 }
 
-/** 取消按钮 */
-const cancel = () => {
-  reset();
-  dialog.visible = false;
+const addRow = () => {
+  conclusionList.value.push({ min: null, max: null, text: null });
 }
 
-/** 表单重置 */
-const reset = () => {
-  form.value = { ...initFormData };
-  screeningAssessmentConfigFormRef.value?.resetFields();
+const submitConclusionForm = async () => {
+  let param = ref<ScreeningAssessmentConfigForm>({});
+  param.value.conclusion = JSON.stringify(conclusionList.value);
+  param.value.configId=form.value.configId;
+  await updateConclusion(param.value);
+  proxy?.$modal.msgSuccess("操作成功");
+  dialog.visible = false;
 }
 
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  getList();
-}
 
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  handleQuery();
-}
 
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: ScreeningAssessmentConfigVO[]) => {
-  ids.value = selection.map(item => item.configId);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const delRow = (index: number) => {
+  conclusionList.value.splice(index, 1);
 }
 
-/** 新增按钮操作 */
-const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = "添加筛查/评估配置";
+/** 取消按钮 */
+const cancel = () => {
+  dialog.visible = false;
 }
 
-/** 修改按钮操作 */
-const handleUpdate = async (row?: ScreeningAssessmentConfigVO) => {
-  reset();
-  const _configId = row?.configId || ids.value[0]
-  const res = await getScreeningAssessmentConfig(_configId);
-  Object.assign(form.value, res.data);
+/**  编辑结论  */
+const editConclusion = () => {
   dialog.visible = true;
-  dialog.title = "修改筛查/评估配置";
 }
 
+
+
+
 /** 提交按钮 */
 const submitForm = () => {
   screeningAssessmentConfigFormRef.value?.validate(async (valid: boolean) => {
@@ -768,8 +806,8 @@ const submitForm = () => {
         return;
       }
 
-      let baseNum=!baseInfo.value.questionType?0:1;
-      let lineNum=0;
+      let baseNum = !baseInfo.value.questionType ? 0 : 1;
+      let lineNum = 0;
       for (let item of otherInfo.value) {
         lineNum++;
         if (!item.title || item.title.trim().length < 1) {
@@ -793,10 +831,10 @@ const submitForm = () => {
         }
       }
 
-      if(baseInfo.value.questionType&&baseInfo.value.questionType.length>0){
+      if (baseInfo.value.questionType && baseInfo.value.questionType.length > 0) {
         form.value.baseInfo = baseInfo.value;
       }
-      if(otherInfo.value&&otherInfo.value.length>0){
+      if (otherInfo.value && otherInfo.value.length > 0) {
         form.value.otherInfo = otherInfo.value;
       }
       buttonLoading.value = true;
@@ -811,26 +849,7 @@ const submitForm = () => {
   });
 }
 
-/** 删除按钮操作 */
-const handleDelete = async (row?: ScreeningAssessmentConfigVO) => {
-  const _configIds = row?.configId || ids.value;
-  await proxy?.$modal.confirm('是否确认删除筛查/评估配置编号为"' + _configIds + '"的数据项?').finally(() => loading.value = false);
-  await delScreeningAssessmentConfig(_configIds);
-  proxy?.$modal.msgSuccess("删除成功");
-  await getList();
-}
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download('system/screeningAssessmentConfig/export', {
-    ...queryParams.value
-  }, `screeningAssessmentConfig_${new Date().getTime()}.xlsx`)
-}
 
-/** 预览按钮操作 */
-const handlePreview = () => {
-  // Implementation of handlePreview function
-}
 
 /** 关闭按钮操作 */
 const close = () => {
@@ -1381,7 +1400,7 @@ function handleMatrixScaleAdd(index: number, childIndex: number, isLeft: boolean
       nameEn: 'matrix' + other.increment.toString(),
       score: '0.0',
       optionContent: undefined,
-      optionFlag: !isLeft  ,
+      optionFlag: !isLeft,
     });
     return;
   }
@@ -1398,13 +1417,13 @@ function handleMatrixScaleAdd(index: number, childIndex: number, isLeft: boolean
 
   if (effectiveNum == other.contentList.length) {
     other.contentList.push({
-      labelFlag: isLeft  ,
+      labelFlag: isLeft,
       label: undefined,
       value: undefined,
       nameEn: 'matrix' + other.increment.toString(),
       score: '0.0',
       optionContent: undefined,
-      optionFlag: !isLeft  ,
+      optionFlag: !isLeft,
     });
   } else {
     let o = other.contentList[effectiveNum];
@@ -1445,7 +1464,7 @@ function handleMatrixScaleDelete(index: number, childIndex: number, isLeft: bool
       if (!child.optionFlag) {
         other.contentList = [];
       } else {
-        child.labelFlag = false;     
+        child.labelFlag = false;
       }
     } else {
       if (!child.labelFlag) {
@@ -1787,4 +1806,24 @@ ul .el-upload--picture-card {
 :deep(#jffs label) {
   font-weight: normal;
 }
+
+.action-buttons {
+  display: flex;
+  gap: 1px;
+
+  .icon-btn {
+    padding: 6px;
+    height: 32px;
+    border: 1px solid #dcdfe6;
+
+    &:hover {
+      border-color: var(--el-color-primary);
+      color: var(--el-color-primary);
+    }
+
+    .el-icon {
+      font-size: 14px;
+    }
+  }
+}
 </style>

+ 209 - 14
src/views/system/screeningAssessmentConfig/index.vue

@@ -1,17 +1,19 @@
 <template>
   <div class="p-2">
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="模板类型" prop="type">
               <el-select v-model="queryParams.type" placeholder="请选择" clearable>
-                <el-option v-for="dict in scale_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                <el-option v-for="dict in scale_type" :key="dict.value" :label="dict.label"
+                  :value="dict.value"></el-option>
               </el-select>
-            </el-form-item>             
+            </el-form-item>
             <el-form-item label="模板名称" prop="name">
               <el-input v-model="queryParams.name" placeholder="请输入" clearable @keyup.enter="handleQuery" />
-            </el-form-item>            
+            </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -25,7 +27,12 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary"   icon="Plus" @click="handleAdd" v-hasPermi="['system:screeningAssessmentConfig:add']">新增量表</el-button>
+            <el-button type="primary" icon="Plus" @click="handleAdd"
+              v-hasPermi="['system:screeningAssessmentConfig:add']">新增量表</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Upload" @click="handleImport"
+              v-hasPermi="['system:diseaseLabel:import']">快速导入JSON</el-button>
           </el-col>
           <!-- <el-col :span="1.5">
             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:screeningAssessmentConfig:edit']">修改</el-button>
@@ -40,14 +47,15 @@
         </el-row>
       </template>
 
-      <el-table v-loading="loading" border :data="screeningAssessmentConfigList" >
+      <el-table v-loading="loading" border :data="screeningAssessmentConfigList">
         <!-- <el-table-column type="selection" width="55" align="center" />        @selection-change="handleSelectionChange" -->
         <el-table-column label="模板名称" align="center" prop="name" />
-        <el-table-column label="模板类型" align="center" prop="typeName" />        
+        <el-table-column label="模板类型" align="center" prop="typeName" />
         <el-table-column label="创建人" align="center" prop="createByName" />
         <el-table-column label="状态" align="center" width="100">
           <template #default="scope">
-            <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+            <el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
+              @change="handleStatusChange(scope.row)"></el-switch>
           </template>
         </el-table-column>
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -56,19 +64,22 @@
               <el-button link type="primary" icon="View" @click="handleView(scope.row)">预览</el-button>
             </el-tooltip> -->
             <el-tooltip content="编辑" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(false,scope.row)" v-hasPermi="['system:screeningAssessmentConfig:edit']">编辑</el-button>
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(false,scope.row)"
+                v-hasPermi="['system:screeningAssessmentConfig:edit']">编辑</el-button>
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
-              <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:screeningAssessmentConfig:remove']">删除</el-button>
+              <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"
+                v-hasPermi="['system:screeningAssessmentConfig:remove']">删除</el-button>
             </el-tooltip>
             <el-tooltip content="修改结论" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleReviseConclusion(scope.row)">修改结论</el-button>
+              <el-button link type="primary" icon="Edit" @click="editConclusion(scope.row)">修改结论</el-button>
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
 
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 添加或修改筛查/评估配置对话框 -->
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@@ -86,7 +97,7 @@
           <el-input v-model="form.description" placeholder="请输入量表说明" />
         </el-form-item>
         <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -96,12 +107,83 @@
         </div>
       </template>
     </el-dialog>
+
+
+    <!--  修改结论 -->
+    <el-dialog :title="dialog2.title" v-model="dialog2.visible" width="1000px" append-to-body>
+      <el-row style="margin-bottom: 5px;" v-for="(item, index) in conclusionList">
+        <el-col :span="4">
+          <el-input v-model="item.min" placeholder="请输入" />
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">分</div>
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">-</div>
+        </el-col>
+        <el-col :span="4">
+          <el-input v-model="item.max" placeholder="请输入" />
+        </el-col>
+        <el-col :span="1">
+          <div style="margin-top: 5px;">分</div>
+        </el-col>
+        <el-col :span="9">
+          <el-input v-model="item.text" placeholder="请输入结论" type="textarea" />
+        </el-col>
+        <el-col :span="1">
+        </el-col>
+        <el-col :span="2">
+          <div class="action-buttons">
+            <el-button class="icon-btn" @click="addRow()">
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </el-button>
+            <el-button class="icon-btn" @click="delRow(index)">
+              <el-icon>
+                <Delete />
+              </el-icon>
+            </el-button>
+          </div>
+        </el-col>
+      </el-row>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitConclusionForm">确 定</el-button>
+          <el-button @click="cancelConclusion">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+
+    <!-- 上传excel -->
+    <el-dialog :title="dialogFile.title" v-model="dialogFile.visible" width="700px" append-to-body>
+      <el-form-item label="量表类型:" required>
+        <el-radio-group v-model="uploadType">
+          <el-radio :label="item.label" :value="item.value" v-for="item in scale_type" :key="item.value" />
+        </el-radio-group>
+      </el-form-item>
+      <el-upload drag :action="uploadFileUrl" :headers="headers" :file-list="fileList" accept=".json"
+        ref="imageUploadRef" :on-success="handleUploadSuccess" :before-upload="handleBeforeUpload"
+        :data="{type: uploadType}"
+        :on-error="handleUploadError">
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">
+          将文件拖到此处,或 <em>点击上传</em>
+        </div>
+      </el-upload>
+      <div class="el-upload__tip">
+        请上传 .json 标准格式文件
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="ScreeningAssessmentConfig" lang="ts">
-import { listScreeningAssessmentConfig,changeConfigStatus, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
+import { updateConclusion,listScreeningAssessmentConfig,changeConfigStatus, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
 import { ScreeningAssessmentConfigVO, ScreeningAssessmentConfigQuery, ScreeningAssessmentConfigForm } from '@/api/system/screeningAssessmentConfig/types';
+import { globalHeaders } from '@/utils/request';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const {scale_type} = toRefs<any>(proxy?.useDict('scale_type'));
@@ -115,15 +197,71 @@ const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
 const router = useRouter();
+const configId = ref<string>('0');
 
 const queryFormRef = ref<ElFormInstance>();
 const screeningAssessmentConfigFormRef = ref<ElFormInstance>();
 
+const baseUrl = import.meta.env.VITE_APP_BASE_API;
+const uploadFileUrl = ref(baseUrl + '/system/screeningAssessmentConfig/importFile'); 
+const headers = ref(globalHeaders());
+const fileList = ref<any[]>([]);
+const imageUploadRef = ref<ElUploadInstance>();
+const uploadType = ref('1'); 
+
+const dialogFile = reactive<DialogOption>({
+  visible: false,
+  title: '请上传'
+});
+
+/** 导出按钮操作 */
+const handleImport = () => {
+  dialogFile.visible = true;
+  fileList.value = [];
+}
+
+// 上传失败
+const handleUploadError = () => {
+  proxy?.$modal.msgError('上传Excel失败');
+  proxy?.$modal.closeLoading();
+};
+
+// 上传成功回调
+const handleUploadSuccess = (res: any, file: UploadFile) => {
+  if (res.code === 200) {
+    dialogFile.visible = false;
+    fileList.value = [];
+    proxy?.$modal.closeLoading();
+    getList();
+  } else {
+    proxy?.$modal.msgError(res.msg);
+    proxy?.$modal.closeLoading();
+  }
+  fileList.value = [];
+};
+
+
+/** 上传前loading加载 */
+const handleBeforeUpload = (file: any) => {
+   const isLt = file.size / 1024 / 1024 < 10;
+    if (!isLt) {
+      proxy?.$modal.msgError(`上传文件大小不能超过10MB!`);
+      return false;
+    }       
+  proxy?.$modal.loading('正在上传文件,请稍候...');
+  return true;
+};
+
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
 
+const dialog2 = reactive<DialogOption>({
+  visible: false,
+  title: '修改结论'
+});
+
 const initFormData: ScreeningAssessmentConfigForm = {
   configId: undefined,
   type: undefined,
@@ -163,6 +301,42 @@ const data = reactive<PageData<ScreeningAssessmentConfigForm, ScreeningAssessmen
 
 const { queryParams, form, rules } = toRefs(data);
 
+const conclusionList = ref([{ min: null, max: null, text: null }]);
+
+const addRow = () => {
+  conclusionList.value.push({ min: null, max: null, text: null });
+}
+
+const submitConclusionForm = async () => {
+  let param = ref<ScreeningAssessmentConfigForm>({});
+  param.value.conclusion = JSON.stringify(conclusionList.value);
+  param.value.configId=configId.value;
+  await updateConclusion(param.value);
+  proxy?.$modal.msgSuccess("操作成功");
+  dialog2.visible = false;
+  getList();
+}
+
+/** 取消按钮 */
+const cancelConclusion = () => {
+  dialog2.visible = false;
+}
+
+const delRow = (index: number) => {
+  conclusionList.value.splice(index, 1);
+}
+
+/**  编辑结论  */
+const editConclusion = (row?: ScreeningAssessmentConfigVO) => {
+  dialog2.visible = true;
+  if(row.conclusion){
+      conclusionList.value=JSON.parse(row.conclusion);  
+      configId.value=row.configId.toString();     
+    }else{
+      conclusionList.value=[{ min: null, max: null, text: null }];      
+    }
+}
+
 /** 查询筛查/评估配置列表 */
 const getList = async () => {
   loading.value = true;
@@ -284,3 +458,24 @@ onMounted(() => {
   getList();
 });
 </script>
+<style scoped>
+.action-buttons {
+  display: flex;
+  gap: 1px;
+
+  .icon-btn {
+    padding: 6px;
+    height: 32px;
+    border: 1px solid #dcdfe6;
+
+    &:hover {
+      border-color: var(--el-color-primary);
+      color: var(--el-color-primary);
+    }
+
+    .el-icon {
+      font-size: 14px;
+    }
+  }
+}
+</style>

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است