HuRongxin преди 1 месец
родител
ревизия
9c516fd36d
променени са 23 файла, в които са добавени 3519 реда и са изтрити 110 реда
  1. 63 0
      src/api/parameter/basicPublicTemplate/consultantResultTemplate/index.ts
  2. 152 0
      src/api/parameter/basicPublicTemplate/consultantResultTemplate/types.ts
  3. 63 0
      src/api/parameter/basicPublicTemplate/consultantResultTemplateType/index.ts
  4. 41 0
      src/api/parameter/basicPublicTemplate/consultantResultTemplateType/types.ts
  5. 76 0
      src/api/patients/diagnosis/index.ts
  6. 116 0
      src/api/patients/diagnosis/types.ts
  7. 85 71
      src/router/index.ts
  8. 196 0
      src/views/parameter/basicPublicTemplate/consultantResultTemplate/addOrEditForm.vue
  9. 308 0
      src/views/parameter/basicPublicTemplate/consultantResultTemplate/index.vue
  10. 207 0
      src/views/parameter/basicPublicTemplate/consultantResultTemplate/templateDetail.vue
  11. 144 0
      src/views/parameter/basicPublicTemplate/consultantResultTemplateType/resultTemplateTypeDialog.vue
  12. 223 0
      src/views/patients/checkLabel/index.vue
  13. 108 0
      src/views/patients/medicalRecord/detailDialog.vue
  14. 85 0
      src/views/patients/medicalRecord/edit/familyHistoryDialog.vue
  15. 85 0
      src/views/patients/medicalRecord/edit/mainSultDialog.vue
  16. 85 0
      src/views/patients/medicalRecord/edit/pastIllnessDialog.vue
  17. 85 0
      src/views/patients/medicalRecord/edit/presentIllnessDialog.vue
  18. 162 0
      src/views/patients/medicalRecord/editDialog.vue
  19. 469 0
      src/views/patients/medicalRecord/index.vue
  20. 296 0
      src/views/patients/medicalRecord/medicalRecord.vue
  21. 104 0
      src/views/patients/nutriDiagnosis/consultantTemplateDialog.vue
  22. 351 0
      src/views/patients/nutriDiagnosis/index.vue
  23. 15 39
      src/views/workbench/treatmentUser/index.vue

+ 63 - 0
src/api/parameter/basicPublicTemplate/consultantResultTemplate/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ResultTemplateVO, ResultTemplateForm, ResultTemplateQuery } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate/types';
+
+/**
+ * 查询会诊结果模板列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listResultTemplate = (query?: ResultTemplateQuery): AxiosPromise<ResultTemplateVO[]> => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplate/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询会诊结果模板详细
+ * @param id
+ */
+export const getResultTemplate = (id: string | number): AxiosPromise<ResultTemplateVO> => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplate/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增会诊结果模板
+ * @param data
+ */
+export const addResultTemplate = (data: ResultTemplateForm) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplate',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改会诊结果模板
+ * @param data
+ */
+export const updateResultTemplate = (data: ResultTemplateForm) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplate',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除会诊结果模板
+ * @param id
+ */
+export const delResultTemplate = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplate/' + id,
+    method: 'delete'
+  });
+};

+ 152 - 0
src/api/parameter/basicPublicTemplate/consultantResultTemplate/types.ts

@@ -0,0 +1,152 @@
+export interface ResultTemplateVO {
+    /**
+     * ID
+     */
+    id: string | number;
+
+    /**
+     * 模板名称
+     */
+    templateName: string;
+
+    //   模板类型id
+    templateTypeId: string | number;
+
+    /**
+     * 模板描述
+     */
+    description: string;
+
+    /**
+    * 正文
+    */
+    content: string;
+
+    /**
+     * 创建时间
+     */
+    createTime: string;
+
+    /**
+     * 创建人姓名
+     */
+    createBy: string | number;
+
+    /**
+     * 创建人科室
+     */
+    createDept: string;
+
+    createDeptName: string;
+
+    /**
+     * 最后修改时间
+     */
+    updateTime: string;
+
+    /**
+     * 最后修改人姓名
+     */
+    updateBy: string;
+
+    /**
+     * 最后修改人科室
+     */
+    updateDept: string;
+
+    updateDeptName: string;
+}
+
+export interface ResultTemplateForm {
+    /**
+     * ID
+     */
+    id?: string | number;
+
+    /**
+     * 模板名称
+     */
+    templateName?: string;
+
+    searchValue?: string;
+
+    //   模板类型id
+    templateTypeId?: string | number;
+
+    /**
+     * 模板描述
+     */
+    description?: string;
+
+    /**
+    * 正文
+    */
+    content?: string;
+
+    /**
+ * 创建时间
+ */
+    createTime?: string;
+
+    /**
+     * 创建人姓名
+     */
+    createBy?: string | number;
+
+    /**
+     * 创建人科室
+     */
+    createDept?: string;
+
+    createDeptName?: string;
+
+    /**
+     * 最后修改时间
+     */
+    updateTime?: string;
+
+    /**
+     * 最后修改人姓名
+     */
+    updateBy?: string;
+
+    /**
+     * 最后修改人科室
+     */
+    updateDept?: string;
+
+    updateDeptName?: string;
+}
+
+export interface ResultTemplateQuery extends PageQuery {
+    /**
+     * 开始时间
+     */
+    startTime?: string;
+
+    /**
+     * 结束时间
+     */
+    endTime?: string;
+
+    templateName?: string;
+
+    searchValue?: string;
+
+    /**
+     * 创建人科室
+     */
+    createBy?: string | number;
+
+
+    //   模板类型id
+    templateTypeId?: string | number;
+
+    /**
+     * 参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/parameter/basicPublicTemplate/consultantResultTemplateType/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ResultTemplateTypeVO, ResultTemplateTypeForm, ResultTemplateTypeQuery } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType/types';
+
+/**
+ * 查询会诊结果模板类型管理列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listResultTemplateType = (query?: ResultTemplateTypeQuery): AxiosPromise<ResultTemplateTypeVO[]> => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplateType/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询会诊结果模板类型管理详细
+ * @param id
+ */
+export const getResultTemplateType = (id: string | number): AxiosPromise<ResultTemplateTypeVO> => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplateType/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增会诊结果模板类型管理
+ * @param data
+ */
+export const addResultTemplateType = (data: ResultTemplateTypeForm) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplateType',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改会诊结果模板类型管理
+ * @param data
+ */
+export const updateResultTemplateType = (data: ResultTemplateTypeForm) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplateType',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除会诊结果模板类型管理
+ * @param id
+ */
+export const delResultTemplateType = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/parameter/basicPublicTemplate/consultantResultTemplateType/' + id,
+    method: 'delete'
+  });
+};

+ 41 - 0
src/api/parameter/basicPublicTemplate/consultantResultTemplateType/types.ts

@@ -0,0 +1,41 @@
+export interface ResultTemplateTypeVO {
+    /**
+     * 
+     */
+    id: string | number;
+
+    /**
+     * 模板类型
+     */
+    templateType: string;
+
+}
+
+export interface ResultTemplateTypeForm extends BaseEntity {
+    /**
+     * 
+     */
+    id?: string | number;
+
+    /**
+     * 模板类型
+     */
+    templateType?: string;
+
+}
+
+export interface ResultTemplateTypeQuery extends PageQuery {
+
+    /**
+     * 模板类型
+     */
+    templateType?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 76 - 0
src/api/patients/diagnosis/index.ts

@@ -0,0 +1,76 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { DiagnosisVO, DiagnosisForm, DiagnosisQuery } from '@/api/patients/diagnosis/types';
+
+/**
+ * 查询营养诊断列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listDiagnosis = (query?: DiagnosisQuery): AxiosPromise<DiagnosisVO[]> => {
+    return request({
+        url: '/patients/nutritionDiagnosis/list',
+        method: 'get',
+        params: query
+    });
+};
+
+/**
+ * 查询模板列表
+ * @param query
+ * @returns {*}
+ */
+
+export const queryTemplateList = () => {
+    return request({
+        url: '/patients/nutritionDiagnosis/queryTemplateList',
+        method: 'get',
+    });
+};
+
+/**
+ * 查询营养诊断详细
+ * @param id
+ */
+export const getDiagnosis = (id: string | number): AxiosPromise<DiagnosisVO> => {
+    return request({
+        url: '/patients/nutritionDiagnosis/' + id,
+        method: 'get'
+    });
+};
+
+/**
+ * 新增营养诊断
+ * @param data
+ */
+export const addDiagnosis = (data: DiagnosisForm) => {
+    return request({
+        url: '/patients/nutritionDiagnosis',
+        method: 'post',
+        data: data
+    });
+};
+
+/**
+ * 修改营养诊断
+ * @param data
+ */
+export const updateDiagnosis = (data: DiagnosisForm) => {
+    return request({
+        url: '/patients/nutritionDiagnosis',
+        method: 'put',
+        data: data
+    });
+};
+
+/**
+ * 删除营养诊断
+ * @param id
+ */
+export const delDiagnosis = (id: string | number | Array<string | number>) => {
+    return request({
+        url: '/patients/nutritionDiagnosis/' + id,
+        method: 'delete'
+    });
+};

+ 116 - 0
src/api/patients/diagnosis/types.ts

@@ -0,0 +1,116 @@
+export interface DiagnosisVO {
+    /**
+     * 
+     */
+    id: string | number;
+
+    /**
+     * 患者id
+     */
+    treatmentUserId: string | number;
+
+    /**
+     * 会诊结果模板id
+     */
+    consultantTemplateId: string | number;
+
+    /**
+     * 营养诊断标签id
+     */
+    diagnosisLableId: string | number;
+
+    /**
+     * 诊断依据id
+     */
+    diagnosisBasisId: string | number;
+
+    /**
+     * 医嘱
+     */
+    medicalOrder: string;
+
+    createTime: string;
+
+}
+
+export interface DiagnosisForm extends BaseEntity {
+    /**
+     * 
+     */
+    id?: string | number;
+
+    dateRange?: string[];
+
+    visitType?: string;
+
+    consultationContent?: string;
+
+    /**
+     * 患者id
+     */
+    treatmentUserId?: string | number;
+
+    /**
+     * 会诊结果模板id
+     */
+    consultantTemplateId?: string | number;
+
+    /**
+     * 营养诊断标签id
+     */
+    diagnosisLableId?: string | number;
+
+    /**
+     * 诊断依据id
+     */
+    diagnosisBasisId?: string | number;
+
+    /**
+     * 医嘱
+     */
+    medicalOrder?: string;
+
+}
+
+export interface DiagnosisQuery extends PageQuery {
+
+    /**
+     * 患者id
+     */
+    treatmentUserId?: string | number;
+
+    dateRange?: string[];
+
+    visitType?: string | number;
+
+    searchValue?: string;
+
+
+    /**
+     * 会诊结果模板id
+     */
+    consultantTemplateId?: string | number;
+
+    /**
+     * 营养诊断标签id
+     */
+    diagnosisLableId?: string | number;
+
+    /**
+     * 诊断依据id
+     */
+    diagnosisBasisId?: string | number;
+
+    /**
+     * 医嘱
+     */
+    medicalOrder?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 85 - 71
src/router/index.ts

@@ -26,69 +26,83 @@ import Layout from '@/layout/index.vue';
 
 // 公共路由
 export const constantRoutes: RouteRecordRaw[] = [
-  {
-    path: '/redirect',
-    component: Layout,
-    hidden: true,
-    children: [
-      {
-        path: '/redirect/:path(.*)',
-        component: () => import('@/views/redirect/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/social-callback',
-    hidden: true,
-    component: () => import('@/layout/components/SocialCallback/index.vue')
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login.vue'),
-    hidden: true
-  },
-  {
-    path: '/register',
-    component: () => import('@/views/register.vue'),
-    hidden: true
-  },
-  {
-    path: '/:pathMatch(.*)*',
-    component: () => import('@/views/error/404.vue'),
-    hidden: true
-  },
-  {
-    path: '/401',
-    component: () => import('@/views/error/401.vue'),
-    hidden: true
-  },
-  {
-    path: '',
-    component: Layout,
-    redirect: '/index',
-    children: [
-      {
-        path: '/index',
-        component: () => import('@/views/index.vue'),
-        name: 'Index',
-        meta: { title: '首页', icon: 'dashboard', affix: true }
-      }
-    ]
-  },
-  {
-    path: '/user',
-    component: Layout,
-    hidden: true,
-    redirect: 'noredirect',
-    children: [
-      {
-        path: 'profile',
-        component: () => import('@/views/system/user/profile/index.vue'),
-        name: 'Profile',
-        meta: { title: '个人中心', icon: 'user' }
-      }
-    ]
-  }
+    {
+        path: '/redirect',
+        component: Layout,
+        hidden: true,
+        children: [
+            {
+                path: '/redirect/:path(.*)',
+                component: () => import('@/views/redirect/index.vue')
+            }
+        ]
+    },
+    {
+        path: '/patients',
+        
+        hidden: true,
+        redirect: 'noredirect',
+        children: [
+            {
+                path: 'medicalRecord',
+                component: () => import('@/views/patients/medicalRecord/index.vue'),
+                name: 'MedicalRecord',
+                meta: { title: '营养病例', icon: 'form', activeMenu: '/workbench/treatmentUser' },
+            },
+        ]
+    },
+    {
+        path: '/social-callback',
+        hidden: true,
+        component: () => import('@/layout/components/SocialCallback/index.vue')
+    },
+    {
+        path: '/login',
+        component: () => import('@/views/login.vue'),
+        hidden: true
+    },
+    {
+        path: '/register',
+        component: () => import('@/views/register.vue'),
+        hidden: true
+    },
+    {
+        path: '/:pathMatch(.*)*',
+        component: () => import('@/views/error/404.vue'),
+        hidden: true
+    },
+    {
+        path: '/401',
+        component: () => import('@/views/error/401.vue'),
+        hidden: true
+    },
+    {
+        path: '',
+        component: Layout,
+        redirect: '/index',
+        children: [
+            {
+                path: '/index',
+                component: () => import('@/views/index.vue'),
+                name: 'Index',
+                meta: { title: '首页', icon: 'dashboard', affix: true }
+            }
+        ]
+    },
+    {
+        path: '/user',
+        component: Layout,
+        hidden: true,
+        redirect: 'noredirect',
+        children: [
+            {
+                path: 'profile',
+                component: () => import('@/views/system/user/profile/index.vue'),
+                name: 'Profile',
+                meta: { title: '个人中心', icon: 'user' }
+            }
+        ]
+    }
 ];
 
 // 动态路由,基于用户权限动态去加载
@@ -100,15 +114,15 @@ export const dynamicRoutes: RouteRecordRaw[] = [
  * 创建路由
  */
 const router = createRouter({
-  history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
-  routes: constantRoutes,
-  // 刷新时,滚动条位置还原
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition) {
-      return savedPosition;
+    history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
+    routes: constantRoutes,
+    // 刷新时,滚动条位置还原
+    scrollBehavior(to, from, savedPosition) {
+        if (savedPosition) {
+            return savedPosition;
+        }
+        return { top: 0 };
     }
-    return { top: 0 };
-  }
 });
 
 export default router;

+ 196 - 0
src/views/parameter/basicPublicTemplate/consultantResultTemplate/addOrEditForm.vue

@@ -0,0 +1,196 @@
+<template>
+
+    <div class="p-2">
+        <el-button type="primary" plain size="large" @click="goBack">返回</el-button>
+        <el-card shadow="never">
+            <el-form ref="resultTemplateFormRef" :model="form" :rules="rules" label-width="100px">
+                <el-row :gutter="20">
+                    <el-col :span="10">
+                        <el-form-item label="模板名称" prop="templateName">
+                            <el-input v-model="form.templateName" placeholder="请输入模板名称" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="10">
+                        <el-form-item label="类型" prop="templateTypeId">
+                            <el-select v-model="form.templateTypeId" placeholder="请选择">
+                                <el-option v-for="item in templateTypeList" :key="item.value" :label="item.label" :value="item.value" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="5">
+                        <el-button type="primary" plain icon="Plus" @click="handleAddType">新增类型</el-button>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="10">
+                        <el-form-item label="模板描述" prop="description">
+                            <el-input v-model="form.description" type="textarea" placeholder="请输入模板描述" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+                <el-row :gutter="20">
+                    <el-col :span="20">
+                        <el-form-item label="正文" prop="content">
+                            <Editor v-model="form.content" :min-height="200" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+            </el-form>
+            <div class="flex justify-center gap-2 mt-4">
+                <el-button :loading="buttonLoading" type="primary" @click="submitForm">保 存</el-button>
+            </div>
+        </el-card>
+        <!-- 添加模板类型管理弹窗 -->
+        <ResultTemplateTypeDialog ref="resultTemplateTypeDialogRef" @success="getTemplateTypeList"/>
+    </div>
+</template>
+
+<script setup lang="ts">
+    import { getCurrentInstance, ref, reactive, onMounted } from 'vue';
+    import { useRouter, useRoute } from 'vue-router';
+    import {
+        addResultTemplate,
+        updateResultTemplate,
+        getResultTemplate
+    } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate';
+    import { ResultTemplateForm } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate/types';
+    import Editor from '@/components/Editor/index.vue';
+    import ResultTemplateTypeDialog from '../consultantResultTemplateType/resultTemplateTypeDialog.vue';
+    import { listResultTemplateType } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType';
+    import type { ResultTemplateTypeQuery } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType/types';
+
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+    const router = useRouter();
+    const route = useRoute();
+    const resultTemplateTypeDialogRef = ref();
+    const resultTemplateFormRef = ref();
+    const buttonLoading = ref(false);
+
+    interface ResultTemplateTypeOption {
+        value: string | number;
+        label: string;
+    }
+
+    const templateTypeList = ref(JSON.parse(route.query.templateTypeList as string || '[]'));
+
+    const initFormData: ResultTemplateForm = {
+        id: undefined,
+        templateName: undefined,
+        description: undefined,
+        templateTypeId: undefined,
+        content: undefined
+    }
+
+    const form = ref < ResultTemplateForm > ({ ...initFormData });
+
+    const rules = {
+        templateName: [
+            { required: true, message: "模板名称不能为空", trigger: "blur" }
+        ],
+        templateTypeId: [
+            { required: true, message: "类型不能为空", trigger: "change" }
+        ],
+        description: [
+            { required: true, message: "模板描述不能为空", trigger: "blur" }
+        ],
+        content: [
+            { required: true, message: "正文不能为空", trigger: "blur" }
+        ]
+    };
+
+        /** 获取会诊模板类型列表 */
+    const getTemplateTypeList = async () => {
+        try {
+            const params: ResultTemplateTypeQuery = {
+                pageNum: 1,
+                pageSize: 100,
+                templateType: undefined,
+                params: {}
+            };
+            const res = await listResultTemplateType(params);
+            templateTypeList.value = res.rows.map(item => ({
+                value: item.id,
+                label: item.templateType
+            }));
+        } catch (error) {
+            proxy ?.$modal.msgError("获取类型列表失败");
+            console.error("获取类型列表失败:", error);
+        }
+    }
+
+    /** 新增类型按钮操作 */
+    const handleAddType = () => {
+        resultTemplateTypeDialogRef.value.open();
+    };
+
+    // 返回按钮
+    const goBack = () => {
+        router.back();
+    };
+
+    /** 表单重置 */
+    const reset = () => {
+        form.value = { ...initFormData };
+        resultTemplateFormRef.value ?.resetFields();
+    }
+
+    /** 取消按钮 */
+    const cancel = () => {
+        router.back();
+    }
+
+    /** 提交按钮 */
+    const submitForm = () => {
+        resultTemplateFormRef.value ?.validate(async (valid: boolean) => {
+            if (valid) {
+                buttonLoading.value = true;
+                try {
+                    if (form.value.id) {
+                        await updateResultTemplate(form.value);
+                    } else {
+                        await addResultTemplate(form.value);
+                    }
+                    proxy ?.$modal.msgSuccess("操作成功");
+                    router.back();
+                } catch (error) {
+                    console.error('提交失败', error);
+                } finally {
+                    buttonLoading.value = false;
+                }
+            }
+        });
+    }
+
+    /** 获取详情 */
+    const getDetail = async (id: string | number) => {
+        try {
+            const res = await getResultTemplate(id);
+            Object.assign(form.value, res.data);
+        } catch (error) {
+            console.error('获取详情失败', error);
+            proxy ?.$modal.msgError("获取详情失败");
+        }
+    }
+
+    onMounted(async () => {
+        await getTemplateTypeList();
+        const id = route.query.id;
+        const copyData = route.query.copyData;
+
+        if (id) {
+            await getDetail(id as string);
+        } else if (copyData) {
+            try {
+                const parsedData = JSON.parse(copyData as string);
+                Object.assign(form.value, parsedData);
+            } catch (error) {
+                console.error('解析复制数据失败', error);
+                proxy ?.$modal.msgError("解析复制数据失败");
+            }
+        }
+    });
+</script>

+ 308 - 0
src/views/parameter/basicPublicTemplate/consultantResultTemplate/index.vue

@@ -0,0 +1,308 @@
+<template>
+    <div class="p-2">
+        <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" label-width="120px">
+                        <el-form-item label="开始时间">
+                            <el-date-picker v-model="queryParams.startTime" type="datetime" placeholder="选择开始时间" clearable />
+                            <el-date-picker v-model="queryParams.endTime" type="datetime" placeholder="选择结束时间" clearable />
+                        </el-form-item>
+                        <el-form-item>
+
+                        </el-form-item>
+                        <el-form-item label="创建人科室">
+                            <el-input v-model="queryParams.createBy" placeholder="请选择" clearable />
+                        </el-form-item>
+                        <el-form-item label="类型">
+                            <el-select v-model="queryParams.templateTypeId" placeholder="请选择" clearable>
+                                <el-option v-for="item in templateTypeList" :key="item.value" :label="item.label" :value="item.value" />
+                            </el-select>
+                        </el-form-item>
+                        <el-form-item>
+                            <el-input v-model="queryParams.searchValue" style="width: 300px;" placeholder="模板名称/模板描述/创建人/最后编辑人" clearable />
+                        </el-form-item>
+                        <el-form-item>
+                            <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
+                            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+                        </el-form-item>
+                    </el-form>
+                </el-card>
+            </div>
+        </transition>
+
+        <el-card shadow="never">
+            <template #header>
+                <el-row :gutter="10" class="mb8">
+                    <el-col :span="1.5">
+                        <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['basicPublicTemplate:consultantResultTemplate:add']">新增模板</el-button>
+                    </el-col>
+                    <el-col :span="1.5">
+                        <el-button type="primary" plain icon="Plus" @click="handleAddType">新增类型</el-button>
+                    </el-col>
+                    <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+                </el-row>
+            </template>
+
+            <el-table v-loading="loading" border :data="resultTemplateList" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" align="center" />
+                <el-table-column label="模板名称" align="center" prop="templateName" />
+                <el-table-column label="模板描述" align="center" prop="description" />
+                <el-table-column label="类型" align="center" prop="templateTypeId">
+                    <template #default="{ row }">
+                        {{ getTemplateTypeName(row.templateTypeId) }}
+                    </template>
+                </el-table-column>
+                <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+                <el-table-column label="创建人姓名" align="center" prop="createBy" />
+                <el-table-column label="创建人科室" align="center" prop="createDeptName" />
+                <el-table-column label="最后修改时间" align="center" prop="updateTime" width="180" />
+                <el-table-column label="最后修改人姓名" align="center" prop="updateBy" />
+                <el-table-column label="最后修改人科室" align="center" prop="updateDeptName" />
+                <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                    <template #default="scope">
+                        <el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['basicPublicTemplate:consultantResultTemplate:edit']">编辑</el-button>
+                        <el-button link type="primary" @click="handleDetail(scope.row)">详情</el-button>
+                        <el-button link type="danger" @click="handleDelete(scope.row)" v-hasPermi="['basicPublicTemplate:consultantResultTemplate:remove']">删除</el-button>
+                    </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" />
+        </el-card>
+
+        <!-- 添加模板类型管理弹窗 -->
+        <ResultTemplateTypeDialog ref="resultTemplateTypeDialogRef" @success="getTemplateTypeList" />
+    </div>
+</template>
+
+<script setup name="ResultTemplate" lang="ts">
+    import {
+        listResultTemplate,
+        getResultTemplate,
+        delResultTemplate,
+        addResultTemplate,
+        updateResultTemplate
+    } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate';
+    import { ResultTemplateVO, ResultTemplateQuery, ResultTemplateForm } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate/types';
+    import { listResultTemplateType } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType';
+    import type { ResultTemplateTypeQuery } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType/types';
+    import ResultTemplateTypeDialog from '../consultantResultTemplateType/resultTemplateTypeDialog.vue';
+
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+    const resultTemplateList = ref < ResultTemplateVO[] > ([]);
+    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);
+    import { useRouter } from 'vue-router';
+    import { json } from 'stream/consumers';
+    const router = useRouter();
+    const queryFormRef = ref < ElFormInstance > ();
+    const resultTemplateFormRef = ref < ElFormInstance > ();
+    const resultTemplateTypeDialogRef = ref();
+
+    interface ResultTemplateTypeOption {
+        value: string | number;
+        label: string;
+    }
+    const templateTypeList = ref < ResultTemplateTypeOption[] > ([]);
+
+    const dialog = reactive < DialogOption > ({
+        visible: false,
+        title: ''
+    });
+    const initFormData: ResultTemplateForm = {
+        id: undefined,
+        templateName: undefined,
+        description: undefined,
+        templateTypeId: undefined,
+        content: undefined,
+        createBy: undefined,
+        createDept: undefined,
+        createTime: undefined,
+        updateBy: undefined,
+        updateDept: undefined,
+        updateTime: undefined,
+        searchValue: undefined,
+        createDeptName: undefined,
+        updateDeptName: undefined,
+
+    }
+
+    const initQueryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        searchValue: undefined,
+        createBy: undefined,
+        templateTypeId: undefined,
+        startTime: undefined,
+        endTime: undefined,
+        params: {}
+    }
+
+    const data = reactive < PageData < ResultTemplateForm,
+        ResultTemplateQuery >> ({
+            form: { ...initFormData },
+
+            queryParams: { ...initQueryParams },
+            rules: {
+                templateName: [
+                    { required: true, message: "模板名称不能为空", trigger: "blur" }
+                ],
+                templateTypeId: [
+                    { required: true, message: "类型不能为空", trigger: "change" }
+                ],
+                description: [
+                    { required: true, message: "模板描述不能为空", trigger: "blur" }
+                ]
+            }
+        });
+
+    const { queryParams, form, rules } = toRefs(data);
+
+    /** 获取类型名称 */
+    const getTemplateTypeName = (typeId: string | number) => {
+        const type = templateTypeList.value.find(t => t.value === typeId);
+        return type ? type.label : '';
+    };
+
+    /** 查询会诊结果模板列表 */
+    const getList = async () => {
+        loading.value = true;
+        const res = await listResultTemplate(queryParams.value);
+        resultTemplateList.value = res.rows;
+        console.log(JSON.stringify(resultTemplateList.value));
+
+        total.value = res.total;
+        loading.value = false;
+    }
+
+    /** 获取会诊模板类型列表 */
+    const getTemplateTypeList = async () => {
+        try {
+            const params: ResultTemplateTypeQuery = {
+                pageNum: 1,
+                pageSize: 100,
+                templateType: undefined,
+                params: {}
+            };
+            const res = await listResultTemplateType(params);
+            templateTypeList.value = res.rows.map(item => ({
+                value: item.id,
+                label: item.templateType
+            }));
+        } catch (error) {
+            proxy ?.$modal.msgError("获取类型列表失败");
+            console.error("获取类型列表失败:", error);
+        }
+    }
+    /** 新增按钮操作 */
+    const handleAdd = () => {
+        router.push({
+            path: '/argManage/consultantResultTemplateAdd',
+            query: {
+                templateTypeList: JSON.stringify(templateTypeList.value), // 序列化数组数据
+            },
+        });
+    };
+    /** 取消按钮 */
+    const cancel = () => {
+        reset();
+        dialog.visible = false;
+    }
+
+    /** 表单重置 */
+    const reset = () => {
+        form.value = { ...initFormData };
+        resultTemplateFormRef.value ?.resetFields();
+    }
+
+    /** 搜索按钮操作 */
+    const handleQuery = () => {
+        queryParams.value.pageNum = 1;
+        getList();
+    }
+
+    /** 重置按钮操作 */
+    const resetQuery = () => {
+        queryFormRef.value ?.resetFields();
+        queryParams.value = { ...initQueryParams };
+        handleQuery();
+    }
+
+    /** 多选框选中数据 */
+    const handleSelectionChange = (selection: ResultTemplateVO[]) => {
+        ids.value = selection.map(item => item.id);
+        single.value = selection.length != 1;
+        multiple.value = !selection.length;
+    }
+
+    /** 新增类型按钮操作 */
+    const handleAddType = () => {
+        resultTemplateTypeDialogRef.value.open();
+    };
+
+    /** 查看详情按钮操作 */
+    const handleDetail = (row: ResultTemplateVO) => {
+        router.push({
+            path: '/argManage/consultantResultTemplateDetail',
+            query: { id: row.id }
+        });
+    };
+
+
+    /** 修改按钮操作 */
+    const handleUpdate = (row ? : ResultTemplateVO) => {
+        const id = row ?.id || ids.value[0];
+        router.push({
+            path: '/argManage/consultantResultTemplateAdd',
+            query: { id }
+        });
+    }
+
+
+    /** 提交按钮 */
+    const submitForm = () => {
+        resultTemplateFormRef.value ?.validate(async (valid: boolean) => {
+            if (valid) {
+                buttonLoading.value = true;
+                try {
+                    if (form.value.id) {
+                        await updateResultTemplate(form.value);
+                    } else {
+                        await addResultTemplate(form.value);
+                    }
+                    proxy ?.$modal.msgSuccess("操作成功");
+                    dialog.visible = false;
+                    await getList();
+                } catch (error) {
+                    console.error('提交失败', error);
+                } finally {
+                    buttonLoading.value = false;
+                }
+            }
+        });
+    }
+
+    /** 删除按钮操作 */
+    const handleDelete = async (row ? : ResultTemplateVO) => {
+        const _ids = row ?.id || ids.value;
+        try {
+            await proxy ?.$modal.confirm('是否确认删除该模板?');
+            await delResultTemplate(_ids);
+            proxy ?.$modal.msgSuccess("删除成功");
+            await getList();
+        } catch (error) {
+            console.error('删除失败', error);
+        }
+    }
+
+    onMounted(() => {
+        getList();
+        getTemplateTypeList(); // 初始化时加载供应商数据
+    });
+</script>

+ 207 - 0
src/views/parameter/basicPublicTemplate/consultantResultTemplate/templateDetail.vue

@@ -0,0 +1,207 @@
+<template>
+    <div class="p-2">
+        <el-button type="primary" plain size="large" @click="goBack">返回</el-button>
+        <el-card shadow="never" class="mt-4">
+            <el-form label-width="100px" class="mb-4">
+                <el-row :gutter="20">
+                    <el-col :span="16">
+                        <el-form-item label="模板名称:">
+                            <div class="form-text">{{ templateDetail.templateName }}</div>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="16">
+                        <el-form-item label="模板描述:">
+                            <div class="form-text">{{ templateDetail.description }}</div>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="8">
+                        <el-form-item label="类型:">
+                            <div class="form-text">{{ getTemplateTypeName(templateDetail.templateTypeId) }}</div>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+
+            <div class="content-section">
+                <div class="section-title">正文:</div>
+                <div class="template-content" v-html="templateDetail.content"></div>
+            </div>
+<!-- 
+            <div class="content-section">
+                <div class="section-title">变量说明</div>
+                <el-table :data="variableList" border stripe>
+                    <el-table-column prop="name" label="变量名" min-width="200"/>
+                    <el-table-column prop="description" label="说明" min-width="150"/>
+                    <el-table-column prop="example" label="示例值" min-width="150"/>
+                </el-table>
+            </div> -->
+        </el-card>
+    </div>
+</template>
+
+<script setup lang="ts">
+    import { ref, onMounted } from 'vue';
+    import { useRouter, useRoute } from 'vue-router';
+    import { getResultTemplate } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate';
+    import { ResultTemplateVO } from '@/api/parameter/basicPublicTemplate/consultantResultTemplate/types';
+    import { listResultTemplateType } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType';
+    import type { ResultTemplateTypeQuery } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType/types';
+
+    const router = useRouter();
+    const route = useRoute();
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+    const templateDetail = ref<ResultTemplateVO>({
+        id: '',
+        templateName: '',
+        description: '',
+        templateTypeId: '',
+        content: '',
+        createTime: '',
+        createBy: '',
+        createDept: '',
+        updateTime: '',
+        updateBy: '',
+        updateDept: '',
+        createDeptName: '',
+        updateDeptName: ''
+    });
+
+    interface TemplateTypeOption {
+        value: string | number;
+        label: string;
+    }
+    const templateTypeList = ref<TemplateTypeOption[]>([]);
+
+    // 变量说明列表
+    const variableList = ref([
+        { name: '${purpose}', description: '会诊目的', example: '营养评估' },
+        { name: '${height}', description: '身高', example: '170' },
+        { name: '${weight}', description: '体重', example: '65' },
+        { name: '${bmi}', description: 'BMI指数', example: '22.5' },
+        { name: '${lossWeight}', description: '近6个月体重变化', example: '-2' },
+        { name: '${lossWeightRate}', description: '体重下降率', example: '3' },
+        { name: '${totalFood24retrospectCalories}', description: '24h膳食回顾能量', example: '2000' },
+        { name: '${totalFood24retrospectProtein}', description: '24h膳食回顾蛋白质', example: '60' },
+        { name: '${alb}', description: '白蛋白', example: '35' },
+        { name: '${beforeAlb}', description: '前白蛋白', example: '180' },
+        { name: '${hgb}', description: '血红蛋白', example: '120' },
+        { name: '${nrs2002Score}', description: 'NRS2002评分', example: '3' },
+        { name: '${nrs2002Conclusion}', description: 'NRS2002结论', example: '存在营养风险' },
+        { name: '${glimResult}', description: 'GLIM营养不良评估', example: '重度营养不良' },
+        { name: '${way}', description: '营养治疗途径', example: '经口肠内营养+补充性肠外营养' },
+        { name: '${totalCalories}', description: '总能量', example: '2000' },
+        { name: '${totalProtein}', description: '总蛋白质', example: '80' }
+    ]);
+
+    /** 获取类型名称 */
+    const getTemplateTypeName = (typeId: string | number) => {
+        const type = templateTypeList.value.find(t => t.value === typeId);
+        return type ? type.label : '';
+    };
+
+    /** 获取模板类型列表 */
+    const getTemplateTypeList = async () => {
+        try {
+            const params: ResultTemplateTypeQuery = {
+                pageNum: 1,
+                pageSize: 100,
+                templateType: undefined,
+                params: {}
+            };
+            const res = await listResultTemplateType(params);
+            templateTypeList.value = res.rows.map(item => ({
+                value: item.id,
+                label: item.templateType
+            }));
+        } catch (error) {
+            proxy?.$modal.msgError("获取类型列表失败");
+            console.error("获取类型列表失败:", error);
+        }
+    };
+
+    /** 获取模板详情 */
+    const getDetail = async (id: string | number) => {
+        try {
+            const res = await getResultTemplate(id);
+            templateDetail.value = res.data;
+        } catch (error) {
+            console.error('获取详情失败', error);
+            proxy?.$modal.msgError("获取详情失败");
+        }
+    };
+
+    // 返回按钮
+    const goBack = () => {
+        router.back();
+    };
+
+    onMounted(async () => {
+        const id = route.query.id;
+        if (id) {
+            await Promise.all([
+                getTemplateTypeList(),
+                getDetail(id as string)
+            ]);
+        } else {
+            proxy?.$modal.msgError("未找到模板ID");
+            router.back();
+        }
+    });
+</script>
+
+<style scoped>
+.form-text {
+    line-height: 32px;
+    color: #606266;
+}
+
+.content-section {
+    margin-top: 20px;
+}
+
+.section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin-bottom: 16px;
+    color: #303133;
+    position: relative;
+    padding-left: 10px;
+}
+
+.section-title::before {
+    content: '';
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 4px;
+    height: 16px;
+    background-color: #409EFF;
+    border-radius: 2px;
+}
+
+.template-content {
+    padding: 16px;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    min-height: 500px;
+    width: 60%;
+    line-height: 50px;
+    background-color: #fff;
+    margin-bottom: 20px;
+}
+
+.template-content :deep(p) {
+    margin: 8px 0;
+    line-height: 1.6;
+}
+
+:deep(.el-form-item__label) {
+    font-weight: bold;
+}
+</style>

+ 144 - 0
src/views/parameter/basicPublicTemplate/consultantResultTemplateType/resultTemplateTypeDialog.vue

@@ -0,0 +1,144 @@
+<template>
+  <el-dialog :title="'会诊结果模板类型管理'" v-model="dialog.visible" width="500px" append-to-body>
+    <div class="mb-4">
+      <el-input 
+        v-model="form.templateType" 
+        placeholder="请输入" 
+        maxlength="30" 
+        show-word-limit
+        class="w-[300px] mr-2">
+        <template #prepend>{{ isEdit ? '修改类型' : '新增类型' }}</template>
+      </el-input>
+      <el-button type="primary" @click="handleSubmit">确定</el-button>
+    </div>
+
+    <el-table :data="resultTemplateList" border>
+      <el-table-column label="类型名称" prop="templateType" />
+      <el-table-column label="操作" width="150" align="center">
+        <template #default="scope">
+          <el-button link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-dialog>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, reactive, watch } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { listResultTemplateType, getResultTemplateType, delResultTemplateType, addResultTemplateType, updateResultTemplateType } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType';
+import type { ResultTemplateTypeVO, ResultTemplateTypeForm } from '@/api/parameter/basicPublicTemplate/consultantResultTemplateType/types';
+
+export default defineComponent({
+  name: 'ResultTemplateTypeDialog',
+  setup(props, { emit }) {
+    const dialog = reactive({
+      visible: false
+    });
+
+    const resultTemplateList = ref<ResultTemplateTypeVO[]>([]);
+    const isEdit = ref(false);
+
+    const form = reactive<ResultTemplateTypeForm>({
+      id: undefined,
+      templateType: ''
+    });
+
+    // 获取列表
+    const getList = async () => {
+      try {
+        const res = await listResultTemplateType({
+          pageNum: 1,
+          pageSize: 100
+        });
+        resultTemplateList.value = res.rows;
+      } catch (error) {
+        console.error('获取列表失败', error);
+      }
+    };
+
+    // 提交(新增或修改)
+    const handleSubmit = async () => {
+      if (!form.templateType) {
+        ElMessage.warning('请输入类型名称');
+        return;
+      }
+      try {
+        if (isEdit.value) {
+          await updateResultTemplateType(form);
+          ElMessage.success('修改成功');
+        } else {
+          await addResultTemplateType(form);
+          ElMessage.success('添加成功');
+        }
+        resetForm();
+        getList();
+      } catch (error) {
+        console.error(isEdit.value ? '修改失败' : '添加失败', error);
+      }
+    };
+
+    // 打开编辑
+    const handleUpdate = (row: ResultTemplateTypeVO) => {
+      isEdit.value = true;
+      form.id = row.id;
+      form.templateType = row.templateType;
+    };
+
+    // 重置表单
+    const resetForm = () => {
+      isEdit.value = false;
+      form.id = undefined;
+      form.templateType = '';
+    };
+
+    // 删除
+    const handleDelete = async (row: ResultTemplateTypeVO) => {
+      try {
+        await ElMessageBox.confirm('确认要删除该类型吗?');
+        await delResultTemplateType(row.id);
+        ElMessage.success('删除成功');
+        // 如果正在编辑被删除的项,重置表单
+        if (form.id === row.id) {
+          resetForm();
+        }
+        getList();
+      } catch (error) {
+        console.error('删除失败', error);
+      }
+    };
+
+    // 打开弹窗
+    const open = () => {
+      dialog.visible = true;
+      resetForm();
+      getList();
+    };
+
+    // 监听表单提交成功
+    watch(() => dialog.visible, (val) => {
+      if (!val) {
+        emit('success');
+      }
+    });
+
+    return {
+      dialog,
+      form,
+      isEdit,
+      resultTemplateList,
+      handleSubmit,
+      handleUpdate,
+      handleDelete,
+      open
+    };
+  }
+});
+</script>
+
+<style scoped>
+.el-input {
+  width: auto;
+}
+</style>

+ 223 - 0
src/views/patients/checkLabel/index.vue

@@ -0,0 +1,223 @@
+<template>
+    <div>
+
+        <!-- 筛选区 -->
+         <div>
+            <el-date-picker v-model="dateRange" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
+             <span style="margin-left: 30px">检验类型</span><el-select v-model="visitType" placeholder="请选择" style="width: 140px; margin-left: 10px">
+                <el-option label="门诊" value="0" />
+                <el-option label="住院" value="1" />
+            </el-select>
+           <el-input v-model="searchDoctor" placeholder="医生姓名/门诊号/住院号" style="width: 240px; " clearable />
+            <el-button type="primary" class="filter-btn" style="margin-right: 8px;" @click="handleSearch">查询</el-button>
+            <el-button class="filter-btn" @click="handleReset">重置</el-button>
+        </div>
+        <!-- <div>
+            <el-row >
+                <el-col :span="10">
+                </el-col>
+                <el-col :span="4" >
+                    <el-select v-model="checkType" placeholder="检查类型"  style="width: 260px">
+                        <el-option label="检查" value="1" />
+                        <el-option label="化验" value="2" />
+                    </el-select>
+                </el-col>
+                <el-col :span="4">
+                    <el-input v-model="searchDoctor" placeholder="申请医生/检查项目" />
+                </el-col>
+                <el-col :span="6">
+                    <el-button type="primary" class="search-btn">查询</el-button>
+                    <el-button class="reset-btn">重置</el-button>
+                </el-col>
+            </el-row>
+
+
+
+
+        </div> -->
+        <!-- 表格区 -->
+        <el-table :data="tableData" border class="check-table" style="width: 100%">
+            <el-table-column prop="item" label="检查项目" />
+            <el-table-column prop="applyDoctor" label="申请医生" />
+            <el-table-column prop="applyTime" label="申请时间" />
+            <el-table-column prop="reviewDoctor" label="审核医生" />
+            <el-table-column prop="diagnosis" label="临床诊断" />
+            <el-table-column label="操作" />
+            <template #empty>
+                <div class="empty-data">暂无数据</div>
+            </template>
+        </el-table>
+        <!-- 分页区 -->
+        <div class="pagination-bar">
+            <span>共0条</span>
+            <span class="pagination-gap">前往</span>
+            <el-input v-model="page" size="small" class="page-input" style="width: 40px;" />
+            <span class="pagination-gap">页</span>
+            <el-pagination background layout="prev, pager, next, sizes" :total="0" :page-size="10" :current-page="page" :page-sizes="[10, 20, 50, 100]" class="pagination" />
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts">
+    import { ref } from 'vue';
+    const dateRange = ref([]);
+    const checkType = ref('2');
+    const searchDoctor = ref('');
+    const showSearch = ref(true);
+    const tableData = ref([]); // 空数据
+    const page = ref(1);
+</script>
+
+<style scoped>
+    .check-indicators-container {
+        background: #f6f9fb;
+        min-height: 100vh;
+        padding: 0;
+    }
+
+    .custom-tab-bar {
+        display: flex;
+        background: #7ec0fa;
+        height: 40px;
+        align-items: stretch;
+        border-bottom: 1px solid #e0e0e0;
+        margin-bottom: 24px;
+    }
+
+    .custom-tab {
+        color: #fff;
+        font-size: 16px;
+        padding: 0 32px;
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        height: 100%;
+        transition: background 0.2s;
+        background: #7ec0fa;
+        font-weight: 500;
+    }
+
+    .custom-tab.first.active,
+    .custom-tab.active {
+        background: #4a7ebc;
+    }
+
+    .custom-tab:not(.active):hover {
+        background: #6bb0e6;
+    }
+
+    .custom-tab+.custom-tab {
+        border-left: 1px solid rgba(255, 255, 255, 0.1);
+    }
+
+    .filter-bar {
+        display: flex;
+        align-items: center;
+        padding: 24px 0 0 24px;
+        gap: 12px;
+        background: #fff;
+    }
+
+    .date-picker {
+        width: 200px
+    }
+
+    .date-picker.custom-date-picker {
+        width: 200px !important;
+        min-width: 200px !important;
+        max-width: 200px !important;
+    }
+
+    .date-picker.custom-date-picker :deep(.el-input__wrapper) {
+        height: 50px !important;
+        min-height: 50px !important;
+        border-radius: 4px !important;
+        border: 1px solid #dcdfe6 !important;
+        font-size: 14px !important;
+        background: #fff !important;
+        box-shadow: none !important;
+        padding: 0 8px !important;
+    }
+
+    .date-picker.custom-date-picker :deep(.el-input__inner) {
+        height: 32px !important;
+        line-height: 32px !important;
+        font-size: 14px !important;
+        padding: 0 4px !important;
+    }
+
+    .date-picker.custom-date-picker :deep(.el-range-separator) {
+        padding: 0 2px !important;
+        color: #c0c4cc !important;
+        font-size: 14px !important;
+    }
+
+    .date-picker.custom-date-picker :deep(.el-range-input) {
+        font-size: 14px !important;
+        padding: 0 2px !important;
+    }
+
+    .date-picker.custom-date-picker :deep(.el-icon) {
+        font-size: 16px !important;
+        color: #c0c4cc !important;
+    }
+
+    .check-type {
+        width: 160px;
+    }
+
+    .search-doctor {
+        width: 260px;
+    }
+
+    .search-btn {
+        margin-left: 8px;
+    }
+
+    .reset-btn {
+        margin-left: 0;
+    }
+
+    .check-table {
+        margin: 16px 0 0 0;
+        background: #fff;
+    }
+
+    .check-table ::v-deep .el-table__header th {
+        background: #e6f0fa;
+        color: #333;
+        font-weight: 500;
+        font-size: 15px;
+    }
+
+    .empty-data {
+        text-align: center;
+        color: #999;
+        padding: 32px 0;
+        background: #fff;
+        font-size: 16px;
+    }
+
+    .pagination-bar {
+        display: flex;
+        align-items: center;
+        background: #fff;
+        padding: 8px 24px 24px 0;
+        justify-content: flex-end;
+        gap: 8px;
+        font-size: 14px;
+        margin-top: 0;
+    }
+
+    .pagination-gap {
+        margin: 0 4px;
+    }
+
+    .page-input {
+        text-align: center;
+    }
+
+    .pagination {
+        margin-left: 8px;
+    }
+</style>

+ 108 - 0
src/views/patients/medicalRecord/detailDialog.vue

@@ -0,0 +1,108 @@
+<template>
+    <el-dialog :model-value="visible" title="查看门诊患者信息" width="900px" append-to-body @close="handleClose" class="detail-dialog-root">
+        <div class="detail-dialog-body">
+            <el-row :gutter="0">
+                <el-col :span="12">
+                    <div class="detail-row"><span class="label required">看诊类型:</span><span class="value">{{ detailData.type === '0' ? '门诊' : '住院' }}</span></div>
+                    <div class="detail-row"><span class="label">诊疗卡号:</span><span class="value">{{ detailData.treatNum || '--' }}</span></div>
+                    <div class="detail-row"><span class="label required">科室:</span><span class="value">{{ detailData.deptName || '--' }}</span></div>
+                    <div class="detail-row"><span class="label required">姓名:</span><span class="value">{{ detailData.treatName || '--' }}</span></div>
+                    <div class="detail-row"><span class="label required">身份证:</span><span class="value">{{ detailData.idCard || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">年龄:</span><span class="value">{{ detailData.age || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">身高:</span><span class="value">{{ detailData.height ? detailData.height + 'cm' : '--' }}</span></div>
+                    <div class="detail-row"><span class="label">BMI:</span><span class="value">{{ detailData.bmi || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">过敏食物:</span><span class="value">{{ detailData.allergyFoot || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">过敏药物:</span><span class="value">{{ detailData.allergyDrug || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">体力活动:</span><span class="value">{{ getDictLabel(physicalActivityDict, detailData.activity) || '--' }}</span></div>
+                </el-col>
+                <el-col :span="12">
+                    <div class="detail-row"><span class="label">门诊号:</span><span class="value">{{ detailData.outpatientNo || '--' }}</span></div>
+                    <div class="detail-row"><span class="label required">性别:</span><span class="value">{{ detailData.sex === '1' ? '男' : detailData.sex === '2' ? '女' : '--' }}</span></div>
+                    <div class="detail-row"><span class="label">出生日期:</span><span class="value">{{ detailData.birthday || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">联系电话:</span><span class="value">{{ detailData.phoneNum || '--' }}</span></div>
+                    <div class="detail-row"><span class="label">体重:</span><span class="value">{{ detailData.weight ? detailData.weight + 'kg' : '--' }}</span></div>
+                </el-col>
+            </el-row>
+        </div>
+        <template #footer>
+            <el-button @click="handleClose">关闭</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+    import { computed } from 'vue';
+    import { ElDialog, ElRow, ElCol, ElButton } from 'element-plus';
+
+    const props = defineProps({
+        visible: Boolean,
+        detailData: {
+            type: Object,
+            default: () => ({})
+        },
+        physicalActivityDict: {
+            type: Array,
+            default: () => []
+        }
+    });
+    const emit = defineEmits(['update:visible', 'close']);
+
+    function handleClose() {
+        emit('update:visible', false);
+        emit('close');
+    }
+
+    function getDictLabel(dictList: any[], value: any) {
+        if (!Array.isArray(dictList)) return value || '--';
+        const item = dictList.find((item) => item.value === value);
+        return item ? item.label : value || '--';
+    }
+
+    defineOptions({
+        name: 'DetailDialog'
+    });
+</script>
+
+<style scoped>
+    .detail-dialog-root>>>.el-dialog__body {
+        padding: 40px 40px 10px 40px;
+    }
+
+    .detail-dialog-body {
+        font-size: 14px;
+        color: #222;
+    }
+
+    .detail-row {
+        display: flex;
+        align-items: flex-start;
+        margin-bottom: 18px;
+        min-height: 22px;
+    }
+
+    .label {
+        display: inline-block;
+        width: 110px;
+        font-weight: bold;
+        color: #222;
+        text-align: right;
+        margin-right: 8px;
+        line-height: 22px;
+    }
+
+    .label.required::before {
+        content: '*';
+        color: #f56c6c;
+        margin-right: 2px;
+        font-size: 15px;
+        font-weight: bold;
+    }
+
+    .value {
+        flex: 1;
+        color: #333;
+        text-align: left;
+        word-break: break-all;
+        line-height: 22px;
+    }
+</style>

+ 85 - 0
src/views/patients/medicalRecord/edit/familyHistoryDialog.vue

@@ -0,0 +1,85 @@
+<template>
+    <el-dialog :model-value="visible" title="编辑家族史" width="600px" append-to-body @close="handleClose" class="family-history-dialog">
+        <div class="dialog-body">
+            <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
+                <el-form-item label="家族史:" prop="familyHistory">
+                    <el-input 
+                        v-model="form.familyHistory" 
+                        type="textarea" 
+                        :rows="6"
+                        placeholder="请输入家族史内容"
+                        maxlength="500"
+                        show-word-limit
+                    />
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button @click="handleClose">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ElDialog, ElForm, ElFormItem, ElInput, ElButton } from 'element-plus';
+
+const props = defineProps({
+    visible: Boolean,
+    familyHistoryData: {
+        type: String,
+        default: ''
+    }
+});
+
+const emit = defineEmits(['update:visible', 'save', 'close']);
+
+const formRef = ref();
+const form = ref({
+    familyHistory: ''
+});
+
+watch(() => props.familyHistoryData, (val) => {
+    form.value.familyHistory = val || '';
+}, { immediate: true });
+
+const rules = {
+    familyHistory: [
+        { required: true, message: '请输入家族史内容', trigger: 'blur' }
+    ]
+};
+
+function handleSave() {
+    formRef.value.validate((valid) => {
+        if (valid) {
+            emit('save', form.value.familyHistory);
+            emit('update:visible', false);
+        }
+    });
+}
+
+function handleClose() {
+    emit('update:visible', false);
+    emit('close');
+}
+
+defineOptions({
+    name: 'FamilyHistoryDialog'
+});
+</script>
+
+<style scoped>
+.family-history-dialog :deep(.el-dialog__body) {
+    padding: 20px 20px 10px 20px;
+}
+
+.dialog-body {
+    font-size: 14px;
+    color: #222;
+}
+
+.el-form-item {
+    margin-bottom: 18px;
+}
+</style>

+ 85 - 0
src/views/patients/medicalRecord/edit/mainSultDialog.vue

@@ -0,0 +1,85 @@
+<template>
+    <el-dialog :model-value="visible" title="编辑主诉" width="600px" append-to-body @close="handleClose" class="main-sult-dialog">
+        <div class="dialog-body">
+            <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
+                <el-form-item label="主诉:" prop="mainSult">
+                    <el-input 
+                        v-model="form.mainSult" 
+                        type="textarea" 
+                        :rows="6"
+                        placeholder="请输入主诉内容"
+                        maxlength="500"
+                        show-word-limit
+                    />
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button @click="handleClose">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ElDialog, ElForm, ElFormItem, ElInput, ElButton } from 'element-plus';
+
+const props = defineProps({
+    visible: Boolean,
+    mainSultData: {
+        type: String,
+        default: ''
+    }
+});
+
+const emit = defineEmits(['update:visible', 'save', 'close']);
+
+const formRef = ref();
+const form = ref({
+    mainSult: ''
+});
+
+watch(() => props.mainSultData, (val) => {
+    form.value.mainSult = val || '';
+}, { immediate: true });
+
+const rules = {
+    mainSult: [
+        { required: true, message: '请输入主诉内容', trigger: 'blur' }
+    ]
+};
+
+function handleSave() {
+    formRef.value.validate((valid) => {
+        if (valid) {
+            emit('save', form.value.mainSult);
+            emit('update:visible', false);
+        }
+    });
+}
+
+function handleClose() {
+    emit('update:visible', false);
+    emit('close');
+}
+
+defineOptions({
+    name: 'MainSultDialog'
+});
+</script>
+
+<style scoped>
+.main-sult-dialog :deep(.el-dialog__body) {
+    padding: 20px 20px 10px 20px;
+}
+
+.dialog-body {
+    font-size: 14px;
+    color: #222;
+}
+
+.el-form-item {
+    margin-bottom: 18px;
+}
+</style>

+ 85 - 0
src/views/patients/medicalRecord/edit/pastIllnessDialog.vue

@@ -0,0 +1,85 @@
+<template>
+    <el-dialog :model-value="visible" title="编辑既往史" width="600px" append-to-body @close="handleClose" class="past-illness-dialog">
+        <div class="dialog-body">
+            <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
+                <el-form-item label="既往史:" prop="pastIllness">
+                    <el-input 
+                        v-model="form.pastIllness" 
+                        type="textarea" 
+                        :rows="6"
+                        placeholder="请输入既往史内容"
+                        maxlength="500"
+                        show-word-limit
+                    />
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button @click="handleClose">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ElDialog, ElForm, ElFormItem, ElInput, ElButton } from 'element-plus';
+
+const props = defineProps({
+    visible: Boolean,
+    pastIllnessData: {
+        type: String,
+        default: ''
+    }
+});
+
+const emit = defineEmits(['update:visible', 'save', 'close']);
+
+const formRef = ref();
+const form = ref({
+    pastIllness: ''
+});
+
+watch(() => props.pastIllnessData, (val) => {
+    form.value.pastIllness = val || '';
+}, { immediate: true });
+
+const rules = {
+    pastIllness: [
+        { required: true, message: '请输入既往史内容', trigger: 'blur' }
+    ]
+};
+
+function handleSave() {
+    formRef.value.validate((valid) => {
+        if (valid) {
+            emit('save', form.value.pastIllness);
+            emit('update:visible', false);
+        }
+    });
+}
+
+function handleClose() {
+    emit('update:visible', false);
+    emit('close');
+}
+
+defineOptions({
+    name: 'PastIllnessDialog'
+});
+</script>
+
+<style scoped>
+.past-illness-dialog :deep(.el-dialog__body) {
+    padding: 20px 20px 10px 20px;
+}
+
+.dialog-body {
+    font-size: 14px;
+    color: #222;
+}
+
+.el-form-item {
+    margin-bottom: 18px;
+}
+</style>

+ 85 - 0
src/views/patients/medicalRecord/edit/presentIllnessDialog.vue

@@ -0,0 +1,85 @@
+<template>
+    <el-dialog :model-value="visible" title="编辑现病史" width="600px" append-to-body @close="handleClose" class="present-illness-dialog">
+        <div class="dialog-body">
+            <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
+                <el-form-item label="现病史:" prop="presentIllness">
+                    <el-input 
+                        v-model="form.presentIllness" 
+                        type="textarea" 
+                        :rows="6"
+                        placeholder="请输入现病史内容"
+                        maxlength="500"
+                        show-word-limit
+                    />
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button @click="handleClose">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ElDialog, ElForm, ElFormItem, ElInput, ElButton } from 'element-plus';
+
+const props = defineProps({
+    visible: Boolean,
+    presentIllnessData: {
+        type: String,
+        default: ''
+    }
+});
+
+const emit = defineEmits(['update:visible', 'save', 'close']);
+
+const formRef = ref();
+const form = ref({
+    presentIllness: ''
+});
+
+watch(() => props.presentIllnessData, (val) => {
+    form.value.presentIllness = val || '';
+}, { immediate: true });
+
+const rules = {
+    presentIllness: [
+        { required: true, message: '请输入现病史内容', trigger: 'blur' }
+    ]
+};
+
+function handleSave() {
+    formRef.value.validate((valid) => {
+        if (valid) {
+            emit('save', form.value.presentIllness);
+            emit('update:visible', false);
+        }
+    });
+}
+
+function handleClose() {
+    emit('update:visible', false);
+    emit('close');
+}
+
+defineOptions({
+    name: 'PresentIllnessDialog'
+});
+</script>
+
+<style scoped>
+.present-illness-dialog :deep(.el-dialog__body) {
+    padding: 20px 20px 10px 20px;
+}
+
+.dialog-body {
+    font-size: 14px;
+    color: #222;
+}
+
+.el-form-item {
+    margin-bottom: 18px;
+}
+</style>

+ 162 - 0
src/views/patients/medicalRecord/editDialog.vue

@@ -0,0 +1,162 @@
+<template>
+    <el-dialog :model-value="visible" title="编辑门诊患者信息" width="1100px" append-to-body @close="handleClose" class="edit-dialog-root">
+        <div class="edit-dialog-body">
+            <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" label-position="right">
+                <el-row :gutter="0">
+                    <el-col :span="12">
+                        <el-form-item label="看诊类型:" prop="type" required>
+                            <span>{{ form.type === '0' ? '门诊' : '住院' }}</span>
+                        </el-form-item>
+                        <el-form-item label="诊疗卡号:" prop="treatNum">
+                            <span>{{ form.treatNum || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="科室:" prop="deptName" required>
+                            <span>{{ form.deptName || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="姓名:" prop="treatName" required>
+                            <span>{{ form.treatName || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="身份证:" prop="idCard" required>
+                            <span>{{ form.idCard || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="年龄:" prop="age">
+                            <span>{{ form.age || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="身高:" prop="height">
+                            <el-input v-model="form.height" style="width: 80%;" />
+                            <span class="unit">cm</span>
+                        </el-form-item>
+
+
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="门诊号:" prop="outpatientNo">
+                            <span>{{ form.outpatientNo || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="性别:" prop="sex" required>
+                            <span>{{ form.sex === '1' ? '男' : form.sex === '2' ? '女' : '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="出生日期" prop="birthday">
+                            <span>{{ form.birthday || '--' }}</span>
+                        </el-form-item>
+                        <el-form-item label="联系电话:" prop="phoneNum">
+                            <el-input v-model="form.phoneNum" placeholder="请输入" />
+                        </el-form-item>
+                        <el-form-item label="体重:" prop="weight">
+                            <el-input v-model="form.weight" style="width: 80%;" />
+                            <span class="unit">kg</span>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row>
+                    <el-col :span="24">
+                        <el-form-item label="过敏食物:" prop="allergyFoot">
+                            <el-input type="textarea" v-model="form.allergyFoot" maxlength="120" show-word-limit />
+                        </el-form-item>
+                    </el-col>
+
+                </el-row>
+                <el-row>
+                    <el-col :span="24">
+                        <el-form-item label="过敏药物:" prop="allergyDrug">
+                            <el-input type="textarea" v-model="form.allergyDrug" maxlength="120" show-word-limit />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row>
+                    <el-col :span="12">
+                        <el-form-item label="体力活动:" prop="activity">
+                            <el-select v-model="form.activity" placeholder="请选择">
+                                <el-option v-for="dict in physical_activity" :key="dict.value" :label="dict.label" :value="dict.value" />
+                            </el-select>
+                        </el-form-item>
+                        <el-form-item label="床号:" prop="bedNo" v-if="form.type === '1'">
+                            <el-input v-model="form.bedNo" placeholder="请输入" />
+                        </el-form-item>
+                        <el-form-item label="病区:" prop="inpatientWard" v-if="form.type === '1'">
+                            <el-input v-model="form.inpatientWard" placeholder="请输入" />
+                        </el-form-item>
+                        <el-form-item label="入院日期:" prop="admissionDate" v-if="form.type === '1'">
+                            <el-date-picker v-model="form.admissionDate" type="date" placeholder="请选择" value-format="YYYY-MM-DD" style="width: 100%" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+        </div>
+        <template #footer>
+            <el-button type="primary" @click="handleSave">保存</el-button>
+            <el-button @click="handleClose">关闭</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+    import { ref, watch, getCurrentInstance, toRefs } from 'vue';
+    import { ElDialog, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElCol, ElRow, ElDatePicker } from 'element-plus';
+    
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+    const { physical_activity } = toRefs(proxy?.useDict('physical_activity'));
+
+    const props = defineProps({
+        visible: Boolean,
+        editData: {
+            type: Object,
+            default: () => ({})
+        }
+    });
+    const emit = defineEmits(['update:visible', 'save', 'close']);
+
+    const formRef = ref();
+    const form = ref({ ...props.editData });
+
+    watch(() => props.editData, (val) => {
+        form.value = { ...val };
+    }, { immediate: true });
+
+    const rules = {
+        phoneNum: [{ required: false, message: '请输入联系电话', trigger: 'blur' }],
+        height: [{ required: false, message: '请输入身高', trigger: 'blur' }],
+        weight: [{ required: false, message: '请输入体重', trigger: 'blur' }],
+        allergyFoot: [{ required: false, message: '请输入过敏食物', trigger: 'blur' }],
+        allergyDrug: [{ required: false, message: '请输入过敏药物', trigger: 'blur' }],
+        activity: [{ required: false, message: '请选择体力活动', trigger: 'change' }],
+    };
+
+    function handleSave() {
+        formRef.value.validate((valid) => {
+            if (valid) {
+                emit('save', form.value);
+                emit('update:visible', false);
+            }
+        });
+    }
+
+    function handleClose() {
+        emit('update:visible', false);
+        emit('close');
+    }
+
+    defineOptions({
+        name: 'EditDialog'
+    });
+</script>
+
+<style scoped>
+    .edit-dialog-root>>>.el-dialog__body {
+        padding: 40px 40px 10px 40px;
+    }
+
+    .edit-dialog-body {
+        font-size: 14px;
+        color: #222;
+    }
+
+    .unit {
+        margin-left: 8px;
+        color: #888;
+    }
+
+    .el-form-item {
+        margin-bottom: 18px;
+    }
+</style>

+ 469 - 0
src/views/patients/medicalRecord/index.vue

@@ -0,0 +1,469 @@
+<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="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="parenteralNutrition">肠外营养</el-menu-item>
+                    <el-menu-item index="dietTherapy">膳食治疗</el-menu-item>
+                </el-sub-menu>
+                <el-menu-item index="nutritionPlan">
+                    <span>营养计划</span>
+                </el-menu-item>
+                <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="50%" :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>
+                        <span style="font-size: 22px;">{{ patientInfo.name }}</span>
+                        <span style="margin-left: 30px;">{{ patientInfo.age }}</span>
+                        <span class="gender-icon" :class="patientInfo.gender === '男' ? 'male' : 'female'">
+                            {{ patientInfo.gender === '男' ? '♂' : '♀' }}
+                        </span>
+                    </el-row>
+                    <el-row>
+                        <el-button size="small" @click="handleEdit">编辑</el-button>
+                        <el-button size="small" type="primary" @click="handleDetail">详情</el-button>
+                    </el-row>
+                </div>
+                <div class="patient-right-info">
+                    <el-row>
+                        <span>门诊号:</span>
+                        <span>科室:{{ patientInfo.deptName }}</span>
+                        <span>营养诊断:</span>
+                    </el-row>
+                    <el-row>
+                        <span>会诊结果:</span>
+                    </el-row>
+                </div>
+            </el-row>
+            <component 
+                :is="currentComponent" 
+                v-if="currentComponent" 
+                :patient-info="patientInfo"
+            />
+        </div>
+
+        <!-- 患者详情弹窗 -->
+        <DetailDialog v-model:visible="showDetailDialog" :detail-data="detailData" :physical-activity-dict="physicalActivityDict" @close="handleDetailClose" />
+        <!-- 新增:编辑弹窗 -->
+        <EditDialog v-model:visible="showEditDialog" :edit-data="editData" @close="handleEditClose" @save="handleEditSave" />
+    </div>
+</template>
+
+<script setup lang="ts">
+    import { ref, onMounted, getCurrentInstance, toRefs, defineAsyncComponent } from 'vue';
+    import { useRoute, useRouter } from 'vue-router';
+    import { ArrowLeft } from '@element-plus/icons-vue';
+    import { listTreatmentUser, getTreatmentUser } from '@/api/workbench/treatmentUser';
+    import DetailDialog from './detailDialog.vue';
+    import EditDialog from './editDialog.vue';
+    
+    // 动态导入组件
+    const MedicalRecord = defineAsyncComponent(() => import('./medicalRecord.vue'));
+    const NutriDiagnosis = defineAsyncComponent(() => import('@/views/patients/nutriDiagnosis/index.vue'));
+    const CheckLabel = defineAsyncComponent(() => import('@/views/patients/checkLabel/index.vue'));
+
+    const componentMap = {
+        medicalRecord: MedicalRecord, // “营养病例”对应MedicalRecord.vue
+        checkLabel: CheckLabel, // “检查标签”对应CheckLabel.vue
+        nutritionDiagnosis: NutriDiagnosis,
+        // ... 其它映射
+    };
+    const currentComponent = ref(componentMap['medicalRecord']); // 默认显示
+    const route = useRoute();
+    const router = useRouter();
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+    const activeMenu = ref('medicalRecord');
+
+    const patientInfo = ref({
+        id: '',
+        name: '',
+        age: '',
+         gender: '',
+        deptName: '',
+       
+    } as any);
+
+    // 弹窗控制
+    const showDetailDialog = ref(false);
+    const detailData = ref({});
+    const physicalActivityDict = ref([]);
+
+    const showEditDialog = ref(false);
+    const editData = ref({} as any);
+
+    const handleSelect = (key: string) => {
+        activeMenu.value = key;
+        currentComponent.value = componentMap[key] || null;
+    };
+
+    const goBack = () => {
+        router.back();
+    };
+
+    const searchValue = ref('');
+    const patientTab = ref('wait');
+    const patientList = ref([]);
+    const loading = ref(false);
+
+    const currentPatientIndex = ref(0);
+
+    const tableRowClassName = ({ row, rowIndex }) => {
+        return rowIndex === currentPatientIndex.value ? 'current-row' : '';
+    };
+
+    const handleRowClick = (row, column, event) => {
+        const idx = patientList.value.findIndex(item => item === row);
+        patientInfo.value = row;
+        if (idx !== -1) {
+            currentPatientIndex.value = idx;
+        }
+    };
+
+    const handlePrev = () => {
+        currentComponent.value=componentMap['medicalRecord']
+        if (currentPatientIndex.value > 0) {
+            patientInfo.value = patientList.value[currentPatientIndex.value - 1];
+            currentPatientIndex.value--;
+        }
+    };
+
+    const handleNext = () => {
+       currentComponent.value=componentMap['medicalRecord']
+        patientInfo.value = patientList.value[currentPatientIndex.value + 1];
+        if (currentPatientIndex.value < patientList.value.length - 1) {
+            currentPatientIndex.value++;
+        }
+    };
+    const handleTabChange = (tab: string) => {
+        if (tab == 'wait') {
+            getList();
+        } else {
+            patientList.value = [];
+        }
+
+    }
+    // 编辑按钮处理
+    const handleEdit = async () => {
+        if (!patientInfo.value.id) {
+            proxy ?.$modal.msgError('请先选择患者');
+            return;
+        }
+
+        try {
+            // 获取患者详细信息
+            const res = await getTreatmentUser(patientInfo.value.id);
+            editData.value = res.data;
+            showEditDialog.value = true;
+        } catch (error) {
+            console.error('获取患者详情失败:', error);
+            proxy ?.$modal.msgError('获取患者详情失败');
+        }
+    };
+
+    // 详情按钮处理
+    const handleDetail = async () => {
+        if (!patientInfo.value.id) {
+            proxy ?.$modal.msgError('请先选择患者');
+            return;
+        }
+        
+        try {
+            // 获取患者详细信息
+            const res = await getTreatmentUser(patientInfo.value.id);
+            detailData.value = res.data;
+            showDetailDialog.value = true;
+        } catch (error) {
+            console.error('获取患者详情失败:', error);
+            proxy ?.$modal.msgError('获取患者详情失败');
+        }
+    };
+
+    // 详情弹窗关闭处理
+    const handleDetailClose = () => {
+        showDetailDialog.value = false;
+        detailData.value = {};
+    };
+
+    const handleEditClose = () => {
+        showEditDialog.value = false;
+        editData.value = {};
+    };
+
+    const handleEditSave = (data) => {
+        // 可在此处调用保存API,保存后刷新列表
+        showEditDialog.value = false;
+        proxy ?.$modal.msgSuccess('保存成功');
+        getList();
+    };
+
+    const waitingCount = ref(0);
+    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,
+                gender: item.sex === '1' ? '男' : item.sex === '2' ? '女' : '',
+                age: item.age
+            }));
+            waitingCount.value = patientList.value.length;
+
+            // 如果有患者数据,根据路由参数或默认选中第一个
+            if (patientList.value.length > 0) {
+                const { id } = route.query;
+                if (id) {
+                    // 查找从工作台点击进入的患者
+                    const targetIndex = patientList.value.findIndex(patient => patient.id === id);
+                    if (targetIndex !== -1) {
+                        patientInfo.value = patientList.value[targetIndex];
+                        currentPatientIndex.value = targetIndex;
+                    } else {
+                        // 如果没找到,默认选中第一个
+                        patientInfo.value = patientList.value[0];
+                        currentPatientIndex.value = 0;
+                    }
+                } else {
+                    // 没有路由参数,默认选中第一个
+                    patientInfo.value = patientList.value[0];
+                    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 || [];
+        getList();
+    });
+</script>
+
+<style lang="scss" scoped>
+    .app-container {
+        display: flex;
+        height: calc(100vh - 50px);
+        background-color: #f5f7fa;
+        margin: -10px;
+    }
+
+    .left-menu {
+        width: 220px;
+        background-color: #fff;
+        border-right: 1px solid #e6e6e6;
+        display: flex;
+        flex-direction: column;
+        padding-bottom: 12px;
+        position: relative;
+    }
+
+    .back-button {
+        padding: 16px;
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        cursor: pointer;
+        color: #409EFF;
+        border-bottom: 1px solid #e6e6e6;
+        font-size: 14px;
+
+        .el-icon {
+            font-size: 16px;
+        }
+
+        &:hover {
+            background-color: #f5f7fa;
+        }
+    }
+
+    .medical-menu {
+        border-right: none;
+
+        :deep(.el-menu-item) {
+            font-size: 14px;
+            height: 40px;
+            line-height: 40px;
+        }
+    }
+
+    .search-bar {
+        display: flex;
+        align-items: center;
+        padding: 8px 12px 0 12px;
+        gap: 6px;
+    }
+
+    .search-input {
+        flex: 1;
+    }
+
+    .search-btn {
+        margin-left: 0;
+    }
+
+    .patient-tabs {
+        padding: 0 12px;
+        margin-top: 8px;
+    }
+
+    .patient-table {
+        padding: 0 12px;
+        margin-top: 4px;
+    }
+
+    .table-patients {
+        width: 100%;
+
+        .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;
+        justify-content: space-between;
+        gap: 8px;
+        position: absolute;
+        left: 0;
+        right: 0;
+        bottom: 12px;
+        padding: 0 12px;
+        background: #fff;
+    }
+
+    .main-content {
+        flex: 1;
+        padding: 20px;
+        overflow-y: auto;
+    }
+
+    .patient-left-info {
+        background-color: #fff;
+        height: 100px;
+        padding: 20px;
+        border-radius: 4px;
+        margin-bottom: 20px;
+        width: 11%;
+
+    }
+
+    .patient-right-info {
+        background-color: #fff;
+        height: 100px;
+        padding: 20px;
+        border-radius: 4px;
+        margin-bottom: 20px;
+        width: 86%;
+        margin-left: 30px;
+
+    }
+
+
+
+
+
+    .gender-icon {
+        margin-left: 30%;
+        font-size: 25px;
+        font-weight: bold;
+    }
+
+    .gender-icon.female {
+        color: #ff4949;
+    }
+
+    .gender-icon.male {
+        color: #409eff;
+    }
+</style>

+ 296 - 0
src/views/patients/medicalRecord/medicalRecord.vue

@@ -0,0 +1,296 @@
+<template>
+    <div class="patient-info">
+        <span>营养病例</span>
+    </div>
+    <div class="custom-tab-bar">
+        <div v-for="(tab, idx) in tabs" :key="tab.value" :class="['custom-tab', { active: activeTab === tab.value, first: idx === 0 }]" @click="activeTab = tab.value">
+            {{ tab.label }}
+        </div>
+    </div>
+    <div class="content-area">
+        <div class="medical-content">
+            <div class="row">
+                <span class="label">复诊日期:</span>
+            </div>
+            <div class="row">
+                <span class="label">主诉:</span>
+                <span v-if="mainSult" class="content-text">{{ mainSult }}</span>
+                <span class="edit-link" @click="handleEditMainSult">编辑</span>
+            </div>
+            <div class="row">
+                <span class="label">现病史:</span>
+                <span v-if="presentIllness" class="content-text">{{ presentIllness }}</span>
+                <span class="edit-link" @click="handleEditPresentIllness">编辑</span>
+            </div>
+            <div class="row">
+                <span class="label">既往史:</span>
+                <span v-if="pastIllness" class="content-text">{{ pastIllness }}</span>
+                <span class="edit-link" @click="handleEditPastIllness">编辑</span>
+            </div>
+            <div class="row">
+                <span class="label">家族史:</span>
+                <span v-if="familyHistory" class="content-text">{{ familyHistory }}</span>
+                <span class="edit-link" @click="handleEditFamilyHistory">编辑</span>
+            </div>
+            <div class="row">
+                <span class="label">营养诊断:</span>
+            </div>
+            <div class="row multi-line">
+                <span class="label">营养诊断依据:</span>
+            </div>
+            <div class="row multi-line">
+                <span class="label">会诊结果:</span>
+            </div>
+
+            <div class="row">
+                <span class="label">营养医嘱:</span>
+            </div>
+            <div class="row">
+                <span class="label">知情同意书(0/5):</span>
+                <imageUpload  :limit="5" :isShowTip="false" />
+            </div>
+        </div>
+
+    </div>
+    
+    <!-- 主诉编辑对话框 -->
+    <MainSultDialog 
+        v-model:visible="showMainSultDialog" 
+        :main-sult-data="mainSult" 
+        @save="handleMainSultSave" 
+        @close="handleMainSultClose" 
+    />
+    
+    <!-- 现病史编辑对话框 -->
+    <PresentIllnessDialog 
+        v-model:visible="showPresentIllnessDialog" 
+        :present-illness-data="presentIllness" 
+        @save="handlePresentIllnessSave" 
+        @close="handlePresentIllnessClose" 
+    />
+    
+    <!-- 既往史编辑对话框 -->
+    <PastIllnessDialog 
+        v-model:visible="showPastIllnessDialog" 
+        :past-illness-data="pastIllness" 
+        @save="handlePastIllnessSave" 
+        @close="handlePastIllnessClose" 
+    />
+    
+    <!-- 家族史编辑对话框 -->
+    <FamilyHistoryDialog 
+        v-model:visible="showFamilyHistoryDialog" 
+        :family-history-data="familyHistory" 
+        @save="handleFamilyHistorySave" 
+        @close="handleFamilyHistoryClose" 
+    />
+
+</template>
+
+<script setup lang="ts">
+    import { ref } from 'vue';
+    import MainSultDialog from './edit/mainSultDialog.vue';
+    import FamilyHistoryDialog from './edit/familyHistoryDialog.vue';
+    import PastIllnessDialog from './edit/pastIllnessDialog.vue';
+    import PresentIllnessDialog from './edit/presentIllnessDialog.vue';
+    
+    const tabs = [
+        { label: '病史记录', value: 'history' },
+        { label: '检查指标', value: 'check' },
+        { label: '营养筛查', value: 'screen' },
+        { label: '营养评估', value: 'evaluate' },
+        { label: '营养诊断', value: 'diagnose' },
+        { label: '营养干预', value: 'intervene' },
+        { label: '营养膳食', value: 'diet' },
+    ];
+    const activeTab = ref('history');
+    
+    // 主诉相关
+    const mainSult = ref('');
+    const showMainSultDialog = ref(false);
+    
+    const handleEditMainSult = () => {
+        showMainSultDialog.value = true;
+    };
+    
+    const handleMainSultSave = (data: string) => {
+        mainSult.value = data;
+        // 这里可以调用API保存数据
+        console.log('保存主诉:', data);
+    };
+    
+    const handleMainSultClose = () => {
+        showMainSultDialog.value = false;
+    };
+    
+    // 现病史相关
+    const presentIllness = ref('');
+    const showPresentIllnessDialog = ref(false);
+    
+    const handleEditPresentIllness = () => {
+        showPresentIllnessDialog.value = true;
+    };
+    
+    const handlePresentIllnessSave = (data: string) => {
+        presentIllness.value = data;
+       
+    };
+    
+    const handlePresentIllnessClose = () => {
+        showPresentIllnessDialog.value = false;
+    };
+    
+    // 既往史相关
+    const pastIllness = ref('');
+    const showPastIllnessDialog = ref(false);
+    
+    const handleEditPastIllness = () => {
+        showPastIllnessDialog.value = true;
+    };
+    
+    const handlePastIllnessSave = (data: string) => {
+        pastIllness.value = data;
+        console.log('保存既往史:', data);
+    };
+    
+    const handlePastIllnessClose = () => {
+        showPastIllnessDialog.value = false;
+    };
+    
+    // 家族史相关
+    const familyHistory = ref('');
+    const showFamilyHistoryDialog = ref(false);
+    
+    const handleEditFamilyHistory = () => {
+        showFamilyHistoryDialog.value = true;
+    };
+    
+    const handleFamilyHistorySave = (data: string) => {
+        familyHistory.value = data;
+        console.log('保存家族史:', data);
+    };
+    
+    const handleFamilyHistoryClose = () => {
+        showFamilyHistoryDialog.value = false;
+    };
+</script>
+
+<style lang="scss" scoped>
+    .custom-tab-bar {
+        display: flex;
+        background: #7ec0fa;
+        height: 56px;
+        line-height: 55px;
+        align-items: stretch;
+        border-bottom: 1px solid #e0e0e0;
+        margin-bottom: 24px;
+    }
+
+    .custom-tab {
+        color: #fff;
+        font-size: 16px;
+        padding: 0 32px;
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        height: 100%;
+        transition: background 0.2s;
+        background: #7ec0fa;
+        font-weight: 500;
+    }
+
+    .patient-info {
+        background-color: #fff;
+        padding: 20px;
+        border-radius: 4px;
+        margin-bottom: 20px;
+        font-size: 24px;
+
+        :deep(.el-descriptions__label) {
+            width: 80px;
+            justify-content: flex-end;
+        }
+    }
+
+    .content-area {
+        background-color: #fff;
+        margin-top: -20px;
+        padding: 10px;
+        border-radius: 4px;
+        min-height: calc(100vh - 200px);
+    }
+
+    .custom-tab.first.active,
+    .custom-tab.active {
+        background: #4a7ebc;
+    }
+
+    .custom-tab:not(.active):hover {
+        background: #6bb0e6;
+    }
+
+    .custom-tab+.custom-tab {
+        border-left: 1px solid rgba(255, 255, 255, 0.1);
+    }
+
+    .medical-content {
+        background: #fff;
+        border-radius: 4px;
+        padding: 50px 60px 40px 60px;
+        min-height: 80vh;
+        font-size: 15px;
+        color: #222;
+    }
+
+    .row {
+        display: flex;
+        align-items: flex-start;
+        margin-bottom: 16px;
+        flex-wrap: wrap;
+    }
+
+    .label {
+        min-width: 110px;
+        color: #222;
+        font-weight: 500;
+        margin-right: 8px;
+        line-height: 28px;
+        text-align: right;
+    }
+
+    .edit-link {
+        color: #409eff;
+        margin-right: 24px;
+        cursor: pointer;
+        font-size: 14px;
+        line-height: 28px;
+    }
+    
+    .content-text {
+        flex: 1;
+        color: #333;
+        line-height: 28px;
+        margin-right: 16px;
+        word-break: break-all;
+    }
+
+    .multi-line {
+        flex-direction: column;
+        align-items: flex-start;
+        margin-bottom: 20px;
+    }
+
+    .add-btn {
+        margin-left: 12px;
+        width: 32px;
+        height: 32px;
+        border-radius: 50%;
+        background: #f5f7fa;
+        border: 1px dashed #dcdfe6;
+        color: #999;
+        font-size: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+</style>

+ 104 - 0
src/views/patients/nutriDiagnosis/consultantTemplateDialog.vue

@@ -0,0 +1,104 @@
+<template>
+    <el-dialog :model-value="visible" title="营养会诊模板" width="480px" @close="$emit('update:visible', false)" append-to-body class="consultant-template-dialog">
+        <div class="dialog-header-row">
+            <el-input v-model="searchValue" placeholder="请输入模板名称" size="small" class="search-input" @keyup.enter="handleSearch" />
+            <el-button type="primary" size="small" class="search-btn" @click="handleSearch">确定</el-button>
+        </div>
+        <el-tree :data="treeData" :props="defaultProps" node-key="id" default-expand-all class="template-tree" highlight-current :expand-on-click-node="false" :show-checkbox="false" @node-click="handleNodeClick" />
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button type="primary" @click="handleConfirm">确定</el-button>
+                <el-button @click="handleCancel">取消</el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+    import { ref, watch, onMounted } from 'vue';
+    import { queryTemplateList } from '@/api/patients/diagnosis/index';
+
+    const props = defineProps({
+        visible: Boolean
+    });
+    const emit = defineEmits(['update:visible', 'select']);
+
+    const searchValue = ref('');
+    const treeData = ref<any[]>([]);
+    const defaultProps = {
+        children: 'children',
+        label: 'label'
+    };
+
+    const handleSearch = async () => {
+        await fetchTemplates();
+    };
+
+    const handleConfirm = () => {
+        // 只传递有 template 字段的节点
+        emit('select', selectedTemplate.value?.template || '');
+        emit('update:visible', false);
+    };
+
+    const handleCancel = () => {
+        emit('update:visible', false);
+    };
+
+    const selectedTemplate = ref<any>(null);
+
+    const fetchTemplates = async () => {
+        // Fetch template list from API
+        const res = await queryTemplateList();
+        treeData.value = res.data;
+    };
+
+    const handleNodeClick = (data: any) => {
+        selectedTemplate.value = data;
+    };
+
+    watch(() => props.visible, (val) => {
+        if (val) fetchTemplates();
+    });
+
+    onMounted(() => {
+        if (props.visible) fetchTemplates();
+    });
+</script>
+
+<style scoped>
+    .consultant-template-dialog .el-dialog__body {
+        padding-top: 10px;
+        padding-bottom: 0;
+    }
+
+    .dialog-header-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 12px;
+    }
+
+    .search-input {
+        flex: 1;
+        margin-right: 8px;
+    }
+
+    .search-btn {
+        min-width: 60px;
+    }
+
+    .template-tree {
+        margin: 12px 0 24px 0;
+        min-height: 180px;
+        max-height: 260px;
+        overflow-y: auto;
+        background: #fff;
+        border: none;
+    }
+
+    .dialog-footer {
+        display: flex;
+        justify-content: center;
+        gap: 16px;
+        padding-bottom: 8px;
+    }
+</style>

+ 351 - 0
src/views/patients/nutriDiagnosis/index.vue

@@ -0,0 +1,351 @@
+<template>
+    <div class="p-2">
+        <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">
+                        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+                            <el-form-item>
+                                <el-date-picker v-model="queryParams.dateRange" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
+                            </el-form-item>
+                            <el-form-item label="检验类型">
+                                <el-select v-model="form.visitType" class="spec-unit-select">
+                                    <el-option v-for="dict in check_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+                                </el-select>
+                            </el-form-item>
+                            <el-form-item>
+                                <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>
+                                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+                            </el-form-item>
+                        </el-form>
+                    </el-card>
+                </div>
+            </transition>
+
+            <el-card shadow="never">
+                <template #header>
+                    <el-row :gutter="10" class="mb8">
+                        <el-col :span="1.5">
+                            <el-button type="primary" @click="handleAdd">新增营养诊断</el-button>
+                        </el-col>
+                        <el-col :span="1.5">
+                            <el-button disabled>打印已选病历</el-button>
+                        </el-col>
+                        <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+                    </el-row>
+                </template>
+                <el-table v-loading="loading" border :data="diagnosisList" @selection-change="handleSelectionChange">
+                    <el-table-column type="selection" width="55" align="center" />
+                    <el-table-column label="" align="center" prop="id" v-if="true" />
+                    <el-table-column label="时间" align="center" prop="createTime" />
+                    <el-table-column label="诊断依据" align="center" prop="diagnosisBasis" />
+                    <el-table-column label="看诊类型" align="center" prop="diagnosisType" />
+                    <el-table-column label="门诊/住院号" align="center" prop="outpatientNumber" />
+                    <el-table-column label="营养诊断" align="center" prop="nutritionalDiagnosis" />
+                    <el-table-column label="诊断医生" align="center" prop="diagnosisDoctor" />
+                    <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                        <template #default="scope">
+                            <el-tooltip content="修改" placement="top">
+                                <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+                            </el-tooltip>
+                            <el-tooltip content="删除" placement="top">
+                                <el-button link type="primary" icon="Delete" @click="handleDelete(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" />
+            </el-card>
+        </div>
+
+        <div v-show="type === 'addForm'">
+            <el-form ref="diagnosisFormRef" :model="form" :rules="rules" label-width="120px">
+                <el-form-item label="营养诊断:" prop="diagnosisLableId">
+                    <el-select v-model="labelList" multiple placeholder="请选择" style="width: 100%;" :disabled="true" @click="labelDialogVisible = true" class="custom-label-select" value-key="labelId">
+                        <el-option v-for="item in labelList" :key="item.labelId" :label="item.labelName" :value="item">
+                            <el-tag type="info" size="small">{{ item.labelName }}</el-tag>
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="营养诊断依据:" prop="diagnosisBasisId">
+                    <el-input v-model="form.diagnosisBasisId" placeholder="请输入营养筛查/评估结果" clearable />
+                </el-form-item>
+
+                <el-form-item label="营养会诊结论:">
+                    <div class="consultation-toolbar">
+                        <el-button type="danger" plain @click="clearContent">清空记录</el-button>
+                        <el-button @click="showTemplateDialog = true">导入模板</el-button>
+                    </div>
+                </el-form-item>
+                <el-form-item>
+                    <Editor v-model="form.consultationContent" placeholder="请输入内容..." style="min-height: 200px; width: 100%;" />
+                </el-form-item>
+                <el-form-item label="营养医嘱:" prop="medicalOrder">
+                    <el-input v-model="form.medicalOrder" placeholder="请输入" clearable />
+                </el-form-item>
+
+            </el-form>
+            <LabelDialog v-model="labelDialogVisible" :initial-selected-labels="labelList || []" @confirm="onLabelConfirm" />
+            <ConsultantTemplateDialog v-model:visible="showTemplateDialog" @select="onTemplateSelect" />
+            <div class="dialog-footer" style="text-align: center; margin-top: 100px">
+                <el-button @click="handleCancel">取 消</el-button>
+                <el-button :loading="buttonLoading" type="primary" @click="submitForm">提 交</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup name="Diagnosis" lang="ts">
+    import { listDiagnosis, getDiagnosis, delDiagnosis, addDiagnosis, updateDiagnosis } from '@/api/patients/diagnosis';
+    import { DiagnosisVO, DiagnosisQuery, DiagnosisForm } from '@/api/patients/diagnosis/types';
+    import { getTreatmentUser as fetchTreatmentUser } from '@/api/workbench/treatmentUser';
+    import LabelDialog from '@/views/warehouse/nutriProduct/labelDialog.vue';
+    const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+    const ConsultantTemplateDialog = defineAsyncComponent(() => import('./consultantTemplateDialog.vue'));
+    const { check_type } = toRefs < any > (proxy ?.useDict('check_type'));
+
+
+    const diagnosisList = ref < DiagnosisVO[] > ([]);
+    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 labelList = ref([]);
+    const detailData = ref({});
+    const labelDialogVisible = ref(false);
+
+    const queryFormRef = ref < ElFormInstance > ();
+    const diagnosisFormRef = ref < ElFormInstance > ();
+    const showTemplateDialog = ref(false);
+
+    const dialog = reactive < DialogOption > ({
+        visible: false,
+        title: ''
+    });
+    // 声明接收的 props
+    const props = defineProps({
+        patientInfo: {
+            type: Object,
+            required: true,
+            default: () => ({
+                id: '',
+                name: '',
+                age: '',
+                gender: ''
+            })
+        }
+    });
+
+    const initFormData: DiagnosisForm = {
+        id: undefined,
+        treatmentUserId: props.patientInfo ?.id, // Initialize with patient ID
+        consultantTemplateId: undefined,
+        diagnosisLableId: undefined,
+        diagnosisBasisId: undefined,
+        medicalOrder: undefined,
+        consultationContent: undefined,
+    }
+
+    const data = reactive < PageData < DiagnosisForm,
+        DiagnosisQuery >> ({
+            form: { ...initFormData },
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                visitType: undefined,
+                dateRange: undefined,
+                searchValue: undefined,
+                params: {}
+            },
+            rules: {
+                diagnosisLableId: [
+                    { required: false, message: "营养诊断不能为空", trigger: ['blur', 'change'] }
+                ],
+                medicalOrder: [
+                    { required: true, message: "医嘱不能为空", trigger: "blur" }
+                ],
+            }
+        });
+
+    const { queryParams, form, rules } = toRefs(data);
+
+    /** 查询营养诊断列表 */
+    const getList = async () => {
+        loading.value = true;
+        const res = await listDiagnosis(queryParams.value);
+        diagnosisList.value = res.rows;
+        console.log("diagnosisList", JSON.stringify(diagnosisList.value));
+
+        total.value = res.total;
+        loading.value = false;
+    }
+    // 详情按钮处理
+    const getTreatmentUser = async () => {
+        try {
+            // 获取患者详细信息
+            const res = await fetchTreatmentUser(props.patientInfo ?.id);
+            detailData.value = res.data;
+        } catch (error) {
+
+        }
+    };
+
+    /** 取消按钮 */
+    const cancel = () => {
+        reset();
+        dialog.visible = false;
+    }
+    // 标签确认回调
+    function onLabelConfirm(selectedLabels: Array < { labelId: string | number;labelName: string } > ) {
+        labelList.value = selectedLabels;
+    }
+    const onTemplateSelect = async (templateContent) => {
+        if (!templateContent) return;
+
+        try {
+            // 获取患者详细信息
+            const res = await fetchTreatmentUser(props.patientInfo ?.id);
+            const patientDetail = res.data || {};
+
+            console.log('Template content:', templateContent);
+            console.log('Patient details:', patientDetail);
+
+            // 替换模板中的占位符
+            let processedContent = templateContent;
+
+            // 定义替换规则(根据实际字段进行调整)
+            const replacements = {
+                'height': patientDetail.height || '',
+                'weight': patientDetail.weight || '',
+                'bmi': patientDetail.bmi || '',
+                'alb': patientDetail.alb || '',
+                'hgb': patientDetail.hgb || '',
+                'nrs2002Score': patientDetail.nrs2002Score || '',
+                'nrs2002Conclusion': patientDetail.nrs2002Conclusion || '',
+                'beforeAlb': patientDetail.beforeAlb || '',
+                'way': patientDetail.way || '',
+                'glimResult': patientDetail.glimResult || '',
+                'purpose': patientDetail.purpose || '',
+                'lossWeight': patientDetail.lossWeight || '',
+                'totalCalories': patientDetail.totalCalories || '',
+                'totalProtein': patientDetail.totalProtein || '',
+                'lossWeightRate': patientDetail.lossWeightRate || '',
+                'totalFood24retrospectProtein': patientDetail.totalFood24retrospectProtein || '',
+                'totalFood24retrospectCalories': patientDetail.totalFood24retrospectCalories || '',
+
+
+                // 可以根据需要添加更多替换规则
+            };
+
+            console.log('Replacements:', replacements);
+
+            // 执行替换
+            for (const [key, value] of Object.entries(replacements)) {
+                const placeholder = '${' + key + '}';
+                console.log(`Replacing ${placeholder} with ${value}`);
+                processedContent = processedContent.split(placeholder).join(value);
+            }
+
+            console.log('Processed content:', processedContent);
+
+            // 将处理后的内容赋值给表单
+            form.value.consultationContent = processedContent;
+        } catch (error) {
+            console.error('获取患者详情失败:', error);
+            proxy ?.$modal.msgError('获取患者详情失败');
+            // 如果获取详情失败,仍然使用原始模板
+            form.value.consultationContent = templateContent;
+        }
+    };
+
+    /** 表单重置 */
+    const reset = () => {
+        form.value = { ...initFormData };
+        diagnosisFormRef.value ?.resetFields();
+    }
+
+    /** 搜索按钮操作 */
+    const handleQuery = () => {
+        queryParams.value.pageNum = 1;
+        getList();
+    }
+
+    /** 重置按钮操作 */
+    const resetQuery = () => {
+        queryFormRef.value ?.resetFields();
+        handleQuery();
+    }
+
+    /** 多选框选中数据 */
+    const handleSelectionChange = (selection: DiagnosisVO[]) => {
+        ids.value = selection.map(item => item.id);
+        single.value = selection.length != 1;
+        multiple.value = !selection.length;
+    }
+
+    /** 新增按钮操作 */
+    const handleAdd = () => {
+        reset();
+        type.value = 'addForm';
+        form.value.treatmentUserId = props.patientInfo ?.id;
+    }
+    const handleCancel = () => {
+        type.value = 'list';
+    };
+    const clearContent = () => {
+        form.value.consultationContent = '';
+    }
+
+    /** 修改按钮操作 */
+    const handleUpdate = async (row ? : DiagnosisVO) => {
+        reset();
+        const _id = row ?.id || ids.value[0]
+        const res = await getDiagnosis(_id);
+        Object.assign(form.value, res.data);
+        dialog.visible = true;
+        dialog.title = "修改营养诊断";
+    }
+
+    /** 提交按钮 */
+    const submitForm = () => {
+        diagnosisFormRef.value ?.validate(async (valid: boolean) => {
+            if (valid) {
+                buttonLoading.value = true;
+                if (form.value.id) {
+                    await updateDiagnosis(form.value).finally(() => buttonLoading.value = false);
+                } else {
+                    await addDiagnosis(form.value).finally(() => buttonLoading.value = false);
+                }
+                proxy ?.$modal.msgSuccess("操作成功");
+                dialog.visible = false;
+                await getList();
+                type.value = 'list';
+            }
+
+        });
+    }
+
+    /** 删除按钮操作 */
+    const handleDelete = async (row ? : DiagnosisVO) => {
+        const _ids = row ?.id || ids.value;
+        await proxy ?.$modal.confirm('是否确认删除营养诊断编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+        await delDiagnosis(_ids);
+        proxy ?.$modal.msgSuccess("删除成功");
+        await getList();
+    }
+
+
+
+    onMounted(() => {
+        getList();
+    });
+</script>

+ 15 - 39
src/views/workbench/treatmentUser/index.vue

@@ -27,7 +27,7 @@
                         </el-form-item>
                         <el-form-item>
                             <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
-                            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']">添加患者</el-button>
+                            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workbench:treatmentUser:add']">添加患者</el-button>
                         </el-form-item>
                     </el-form>
                 </el-card>
@@ -57,7 +57,7 @@
             <div class="patient-cards-container" v-if="userList.length > 0">
                 <el-row :gutter="20">
                     <el-col v-for="(patient, index) in userList" :key="index" :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
-                        <el-card class="patient-card" shadow="hover" @click="showPatientDetail(patient)">
+                        <el-card class="patient-card" shadow="hover" @click="toPatients(patient)">
                             <div class="patient-status-bar" :class="getStatusBarClass(patient)">
                                 {{ getStatusText(patient) }}
                             </div>
@@ -205,37 +205,6 @@
                 </div>
             </template>
         </el-dialog>
-        <!-- 新增:患者详情弹窗 -->
-        <el-dialog v-model="showDetail" :visible="showDetail" title="查看门诊患者信息" width="900px" append-to-body>
-            <div style="padding: 20px 30px;">
-                <el-row :gutter="24">
-
-                    <el-col :span="12">
-                        <div><b>看诊类型:</b>{{ detailData.type === '0' ? '门诊' : '住院' }}</div>
-                        <div><b>诊疗卡号:</b>{{ detailData.treatNum || '--' }}</div>
-                        <div><b>科室:</b>{{ detailData.deptName || '--' }}</div>
-                        <div><b>姓名:</b>{{ detailData.treatName || '--' }}</div>
-                        <div><b>身份证:</b>{{ detailData.idCard || '--' }}</div>
-                        <div><b>年龄:</b>{{ detailData.age || '--' }}</div>
-                        <div><b>身高:</b>{{ detailData.height ? detailData.height + 'cm' : '--' }}</div>
-                        <div><b>BMI:</b>{{ detailData.bmi || '--' }}</div>
-                        <div><b>过敏食物:</b>{{ detailData.allergyFoot || '--' }}</div>
-                        <div><b>过敏药物:</b>{{ detailData.allergyDrug || '--' }}</div>
-                        <div><b>体力活动:</b>{{ getDictLabel(physical_activity.value, detailData.activity) || '--' }}</div>
-                    </el-col>
-                    <el-col :span="12">
-                        <div><b>门诊号:</b>{{ detailData.outpatientNo || '--' }}</div>
-                        <div><b>性别:</b>{{ detailData.sex === '1' ? '男' : detailData.sex === '2' ? '女' : '--' }}</div>
-                        <div><b>出生日期:</b>{{ detailData.birthday || '--' }}</div>
-                        <div><b>联系电话:</b>{{ detailData.phoneNum || '--' }}</div>
-                        <div><b>体重:</b>{{ detailData.weight ? detailData.weight + 'kg' : '--' }}</div>
-                    </el-col>
-                </el-row>
-            </div>
-            <template #footer>
-                <el-button @click="showDetail = false">关闭</el-button>
-            </template>
-        </el-dialog>
     </div>
 </template>
 
@@ -246,6 +215,9 @@
     import { TreatmentUserVo, TreatmentUserForm, TreatmentUserQuery } from '@/api/workbench/treatmentUser/types';
     import { log } from 'console';
     import { get } from 'http';
+    import { useRouter } from 'vue-router';
+
+    const router = useRouter();
     const { proxy } = getCurrentInstance() as ComponentInternalInstance;
     const { treatment_user_type, treatment_user_status, evaluation_status, physical_activity, user_sex } = toRefs < any > (proxy ?.useDict('treatment_user_type', 'treatment_user_status', 'evaluation_status', 'physical_activity', 'user_sex'));
     const userList = ref < TreatmentUserVo[] > ([]);
@@ -628,13 +600,17 @@
         getWardList(); // 初始化时加载病区数据
     });
 
-    const showDetail = ref(false);
     const detailData = ref({});
-    const showPatientDetail = async (patient) => {
-
-        // 这里可以根据需要请求详情接口,或直接用patient
-        detailData.value = { ...patient };
-        showDetail.value = true;
+    const toPatients = (patient: TreatmentUserVo) => {
+        router.push({
+            path: '/patients/medicalRecord',
+            query: {
+                id: patient.id,
+                type: patient.type,
+                treatName: patient.treatName,
+                outpatientNo: patient.outpatientNo
+            }
+        });
     };
 </script>
 <style scoped>