Browse Source

参数管理

chenying2100 1 month ago
parent
commit
d41027d64e

+ 0 - 3
.specstory/history/2025-06-24_02-08Z-调整表单label宽度以完全显示.md

@@ -2678,7 +2678,6 @@ CREATE TABLE sys_recipe_food_ingredient (
 
 ---
 
-<<<<<<< HEAD
 _**User**_
 
 帮我用样式画出span的边框
@@ -2937,5 +2936,3 @@ _**Assistant**_
 
 ---
 
-=======
->>>>>>> dev-cy/end250630

+ 0 - 159
info.txt

@@ -1,159 +0,0 @@
-食谱分类管理   
-  食谱分类名称: 
-
-  CREATE TABLE sys_recipe_category (
-    recipe_category_id     BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
-    tenant_id varchar(20) default '000000' null comment '租户编号',
-    name            VARCHAR(100) NOT NULL COMMENT '食谱分类名称',     
-    sort            INT DEFAULT 0 COMMENT '排序',
-    -- 系统字段
-    status CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
-    del_flag char default '0' null comment '删除标志(0代表存在 1代表删除)',
-    create_dept  bigint null comment '创建部门',
-    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
-    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
-    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食谱分类管理表';
-
-食谱管理
-  基本膳食
-  餐次
-  食谱名称 
-  食谱分类: 
-  食谱价格: 
-  适用疾病: 
-  忌食病症: 
-  适用科室: 
-  智能推荐:
-  制作方式:  
-
-
-CREATE TABLE sys_recipe (
-    recipe_id         BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
-    tenant_id  varchar(20) default '000000' null comment '租户编号',
-    name              VARCHAR(100) NOT NULL COMMENT '食谱名称',
-    category_id       BIGINT NOT NULL COMMENT '食谱分类ID',
-    base_diet         VARCHAR(100) DEFAULT NULL COMMENT '基本膳食',
-    meal_time         VARCHAR(100) DEFAULT NULL COMMENT '餐次',
-    price             DECIMAL(10,2) DEFAULT 0.00 COMMENT '食谱价格',
-    suitable_disease  VARCHAR(255) DEFAULT NULL COMMENT '适用疾病(可用逗号分隔多个)',
-    avoid_disease     VARCHAR(255) DEFAULT NULL COMMENT '忌食病症(可用逗号分隔多个)',
-    suitable_dept     VARCHAR(255) DEFAULT NULL COMMENT '适用科室(可用逗号分隔多个)',
-    smart_recommend   char(1) DEFAULT 0 COMMENT '智能推荐(0否 1是)',
-    cooking_method    VARCHAR(255) DEFAULT NULL COMMENT '制作方式', 
-
-    -- 系统字段
-    status CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
-    del_flag char default '0' null comment '删除标志(0代表存在 1代表删除)',
-    create_dept  bigint null comment '创建部门',
-    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
-    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
-    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食谱管理表';
-
- 
-
-CREATE TABLE sys_recipe_food_ingredient (
-    id                  BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
-    recipe_id           BIGINT NOT NULL COMMENT '食谱ID',
-    food_ingredient_id  BIGINT NOT NULL COMMENT '食材ID',
-    quantity            DECIMAL(10,2) DEFAULT 0.00 COMMENT '用量(可选,单位自定义)',     
-    calories            DECIMAL(10,2) DEFAULT 0.00 COMMENT '热量(kcal)',
-    sort                INT DEFAULT 0 COMMENT '排序(可选)',    
-    remark              VARCHAR(255) DEFAULT NULL COMMENT '备注',
-    
-    -- 系统字段
-    status CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
-    del_flag char default '0' null comment '删除标志(0代表存在 1代表删除)',
-    create_dept  bigint null comment '创建部门',
-    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
-    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
-    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食谱与食材关联表';
-
--- 食材管理表
-CREATE TABLE sys_food_ingredient (
-    food_ingredient_id     BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
-    food_category_id       BIGINT NOT NULL COMMENT '食材分类ID',
-    tenant_id varchar(20) default '000000' null comment '租户编号',
-    name                  VARCHAR(100) NOT NULL COMMENT '食材名称',
-    code                  VARCHAR(50) NOT NULL COMMENT '食材编码',
-    unit                  VARCHAR(20) NOT NULL COMMENT '单位',
-    purchase_price        DECIMAL(10,2) DEFAULT 0.00 COMMENT '入货价格(元)',
-    edible_ratio          DECIMAL(5,2) DEFAULT 100.00 COMMENT '食材可食比例构成(%)',
-    shelf_life            INT DEFAULT 0 COMMENT '保质期(天)',
-    stock_warning         DECIMAL(10,2) DEFAULT 0.00 COMMENT '库存预警值(g)',
-    expiry_warning        INT DEFAULT 0 COMMENT '保质期预警(天)',
-    description           TEXT COMMENT '食材描述',
-    
-    -- 营养成分
-    calories              DECIMAL(8,2) DEFAULT 0.00 COMMENT '热量(kcal)',
-    protein               DECIMAL(8,2) DEFAULT 0.00 COMMENT '蛋白质(g)',
-    fat                   DECIMAL(8,2) DEFAULT 0.00 COMMENT '脂肪(g)',
-    carbohydrate          DECIMAL(8,2) DEFAULT 0.00 COMMENT '碳水化合物(g)',
-    water                 DECIMAL(8,2) DEFAULT 0.00 COMMENT '水分(ml)',
-    vitamin_a             DECIMAL(8,2) DEFAULT 0.00 COMMENT '维生素A(μg)',
-    vitamin_b2            DECIMAL(8,2) DEFAULT 0.00 COMMENT '维生素B2(mg)',
-    vitamin_c             DECIMAL(8,2) DEFAULT 0.00 COMMENT '维生素C(mg)',
-    sodium                DECIMAL(8,2) DEFAULT 0.00 COMMENT '钠(mg)',
-    iron                  DECIMAL(8,2) DEFAULT 0.00 COMMENT '铁(mg)',
-    phosphorus            DECIMAL(8,2) DEFAULT 0.00 COMMENT '磷(mg)',
-    dietary_fiber         DECIMAL(8,2) DEFAULT 0.00 COMMENT '膳食纤维(g)',
-    vitamin_b1            DECIMAL(8,2) DEFAULT 0.00 COMMENT '维生素B1(mg)',
-    niacin                DECIMAL(8,2) DEFAULT 0.00 COMMENT '烟酸(mg)',
-    vitamin_e             DECIMAL(8,2) DEFAULT 0.00 COMMENT '维生素E(mg)',
-    calcium               DECIMAL(8,2) DEFAULT 0.00 COMMENT '钙(mg)',
-    potassium             DECIMAL(8,2) DEFAULT 0.00 COMMENT '钾(mg)',
-    cholesterol           DECIMAL(8,2) DEFAULT 0.00 COMMENT '胆固醇(g)',
-
-    -- 系统字段
-    status CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
-    del_flag char default '0' null comment '删除标志(0代表存在 1代表删除)',
-    create_dept  bigint null comment '创建部门',
-    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
-    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
-    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食材管理表';
- 
-
-
-
-
-          
-
-
-
-
-
-
-
-
-
-{ "calories": "kcal", "protein": "g", "fat": "g", "carbohydrate": "g", "water": "ml", "vitamin_a": "μg", "vitamin_b2": "mg", "vitamin_c": "mg", "sodium": "mg", "iron": "mg", "phosphorus": "mg", "dietary_fiber": "g", "vitamin_b1": "mg", "niacin": "mg", "vitamin_e": "mg", "calcium": "mg", "potassium": "mg", "cholesterol": "g" }
-
-[
-  { "name": "calories", "unit": "kcal", "remark": "热量" },
-  { "name": "protein", "unit": "g", "remark": "蛋白质" },
-  { "name": "fat", "unit": "g", "remark": "脂肪" },
-  { "name": "carbohydrate", "unit": "g", "remark": "碳水化合物" },
-  { "name": "water", "unit": "ml", "remark": "水分" },
-  { "name": "vitamin_a", "unit": "μg", "remark": "维生素A" },
-  { "name": "vitamin_b2", "unit": "mg", "remark": "维生素B2" },
-  { "name": "vitamin_c", "unit": "mg", "remark": "维生素C" },
-  { "name": "sodium", "unit": "mg", "remark": "钠" },
-  { "name": "iron", "unit": "mg", "remark": "铁" },
-  { "name": "phosphorus", "unit": "mg", "remark": "磷" },
-  { "name": "dietary_fiber", "unit": "g", "remark": "膳食纤维" },
-  { "name": "vitamin_b1", "unit": "mg", "remark": "维生素B1" },
-  { "name": "niacin", "unit": "mg", "remark": "烟酸" },
-  { "name": "vitamin_e", "unit": "mg", "remark": "维生素E" },
-  { "name": "calcium", "unit": "mg", "remark": "钙" },
-  { "name": "potassium", "unit": "mg", "remark": "钾" },
-  { "name": "cholesterol", "unit": "g", "remark": "胆固醇" }
-]
-
-

+ 140 - 0
prompt/check-template.txt

@@ -0,0 +1,140 @@
+# 营养表单设计UI草图
+
+## 1. 页面整体布局
+- 顶部:页面标题(可选)
+- 左侧:参数选择区(系统参数、题型选择)
+- 右侧:表单编辑区(量表类型、适用条件、量表名称、说明、备注等)
+- 下方:题目编辑区(题目内容、题型、填空项等)
+
+---
+
+## 2. 结构化UI设计
+
+```
+<template>
+  <div class="check-template-container">
+    <!-- 左侧参数选择区 -->
+    <aside class="sidebar">
+      <section class="system-params">
+        <h3>系统参数</h3>
+        <div class="param-group">
+          <div class="group-title">基本信息</div>
+          <el-button>性别</el-button>
+          <el-button>出生日期</el-button>
+          <el-button>身高</el-button>
+          <el-button>体重</el-button>
+          <el-button>BMI</el-button>
+          <el-button>营养诊断</el-button>
+          <el-button>入院日期</el-button>
+        </div>
+        <div class="param-group">
+          <div class="group-title">其他</div>
+          <el-button>人体测量</el-button>
+          <el-button>膳食状况</el-button>
+          <el-button>营养生化检查</el-button>
+        </div>
+      </section>
+      <section class="question-types">
+        <h3>题型选择</h3>
+        <el-button>单选题</el-button>
+        <el-button>多选题</el-button>
+        <el-button>填空题</el-button>
+        <el-button>量表题</el-button>
+        <el-button>矩阵量表</el-button>
+      </section>
+    </aside>
+
+    <!-- 右侧表单编辑区 -->
+    <main class="main-content">
+      <el-form label-width="100px">
+        <el-form-item label="量表类型:" required>
+          <el-radio-group v-model="form.type">
+            <el-radio label="营养筛查" />
+            <el-radio label="营养评估" />
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="适用条件:">
+          <el-row>
+            <el-col :span="12">
+              <span>性别:</span><el-link type="primary">不限</el-link>
+            </el-col>
+            <el-col :span="12">
+              <span>年龄:</span><el-link type="primary">不限</el-link>
+            </el-col>
+          </el-row>
+        </el-form-item>
+        <el-form-item label="量表名称:" required>
+          <el-input v-model="form.name" maxlength="50" show-word-limit placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="量表说明:">
+          <el-input v-model="form.desc" maxlength="50" show-word-limit placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="备注:">
+          <el-input type="textarea" v-model="form.remark" maxlength="500" show-word-limit placeholder="请输入" />
+        </el-form-item>
+      </el-form>
+
+      <!-- 题目编辑区 -->
+      <section class="question-edit">
+        <el-card>
+          <el-form label-width="80px">
+            <el-form-item label="标题:" required>
+              <el-input v-model="question.title" placeholder="请输入标题" />
+            </el-form-item>
+            <el-form-item label="填空:">
+              <el-input v-model="question.blank" placeholder="请输入填空内容" />
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </section>
+    </main>
+  </div>
+</template>
+```
+
+---
+
+## 3. 样式建议(伪代码)
+
+```
+.check-template-container {
+  display: flex;
+  background: #f8fafc;
+}
+.sidebar {
+  width: 260px;
+  background: #fff;
+  border-radius: 4px;
+  margin: 16px;
+  padding: 16px;
+  box-shadow: 0 2px 8px #f0f1f2;
+}
+.main-content {
+  flex: 1;
+  margin: 16px;
+  background: #fff;
+  border-radius: 4px;
+  padding: 24px;
+  min-width: 600px;
+}
+.question-edit {
+  margin-top: 24px;
+  background: #f0f6ff;
+  border-radius: 4px;
+  padding: 16px;
+}
+```
+
+---
+
+## 4. 交互说明
+- 左侧参数和题型按钮可拖拽或点击添加到右侧表单或题目区。
+- 右侧表单区支持表单校验、字数限制、必填项提示。
+- 题目编辑区可根据题型动态切换输入控件。
+
+---
+
+# 设计说明
+- 结构清晰,左右分栏,主次分明。
+- 采用 Element Plus 组件,风格简洁现代。
+- 适合表单设计、量表配置等场景。 

+ 63 - 0
prompt/info.txt

@@ -0,0 +1,63 @@
+筛查/评估配置   
+  量表类型: 
+  适用条件: 
+  性别: 
+  年龄:
+  量表名称:
+  量表说明:
+  备注:
+
+CREATE TABLE sys_screening_assessment_config (
+    config_id     BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
+    tenant_id  varchar(20) default '000000' null comment '租户编号',
+    type          char(1) NOT NULL COMMENT '量表类型',           -- 如:营养筛查、营养评估
+    gender        char(2) DEFAULT '-1' COMMENT '性别',         -- 男、女、不限
+    age           char(2) DEFAULT '-1' COMMENT '年龄',         -- 如:不限、18-65、>65等
+    name          VARCHAR(150) NOT NULL COMMENT '量表名称',
+    description   VARCHAR(300) DEFAULT NULL COMMENT '量表说明',
+    remark        text DEFAULT NULL COMMENT '备注',
+     
+    -- 系统字段
+    status CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
+    del_flag char default '0' null comment '删除标志(0代表存在 1代表删除)',
+    create_dept  bigint null comment '创建部门',
+    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
+    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
+    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='筛查/评估配置表';
+
+ 
+CREATE TABLE sys_screening_assessment_question(
+  question_id     BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
+  tenant_id  varchar(20) default '000000' null comment '租户编号',
+  config_id     BIGINT  COMMENT '主键ID',
+  title     VARCHAR(600) NOT NULL COMMENT '标题',
+  question_type VARCHAR(50) NOT NULL COMMENT '题型',
+  content   text COMMENT '内容',
+
+  -- 系统字段
+  create_dept  bigint null comment '创建部门',
+  create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
+  create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
+  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
+)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='题目表';
+ 
+ 
+ 
+
+
+
+
+          
+
+
+
+
+
+
+
+ 
+
+

+ 2 - 0
src/api/system/foodIngredient/types.ts

@@ -168,6 +168,8 @@ export interface FoodIngredientVO {
   nutrientRatio: string;
 
   nutrientRefVal: string;
+  
+  nutrientUnit: string;
 }
 
 export interface FoodIngredientForm extends BaseEntity {

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

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ScreeningAssessmentConfigVO, ScreeningAssessmentConfigForm, ScreeningAssessmentConfigQuery } from '@/api/system/screeningAssessmentConfig/types';
+
+/**
+ * 查询筛查/评估配置列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listScreeningAssessmentConfig = (query?: ScreeningAssessmentConfigQuery): AxiosPromise<ScreeningAssessmentConfigVO[]> => {
+  return request({
+    url: '/system/screeningAssessmentConfig/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询筛查/评估配置详细
+ * @param configId
+ */
+export const getScreeningAssessmentConfig = (configId: string | number): AxiosPromise<ScreeningAssessmentConfigVO> => {
+  return request({
+    url: '/system/screeningAssessmentConfig/' + configId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增筛查/评估配置
+ * @param data
+ */
+export const addScreeningAssessmentConfig = (data: ScreeningAssessmentConfigForm) => {
+  return request({
+    url: '/system/screeningAssessmentConfig',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改筛查/评估配置
+ * @param data
+ */
+export const updateScreeningAssessmentConfig = (data: ScreeningAssessmentConfigForm) => {
+  return request({
+    url: '/system/screeningAssessmentConfig',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除筛查/评估配置
+ * @param configId
+ */
+export const delScreeningAssessmentConfig = (configId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/screeningAssessmentConfig/' + configId,
+    method: 'delete'
+  });
+};

+ 164 - 0
src/api/system/screeningAssessmentConfig/types.ts

@@ -0,0 +1,164 @@
+export interface ScreeningAssessmentConfigVO {
+  /**
+   * 主键ID
+   */
+  configId: string | number;
+
+  /**
+   * 量表类型
+   */
+  type: string;
+
+  /**
+   * 性别
+   */
+  gender: string;
+
+  /**
+   * 年龄
+   */
+  age: string;
+
+  /**
+   * 量表名称
+   */
+  name: string;
+
+  /**
+   * 量表说明
+   */
+  description: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+}
+
+export interface QuestionAttrVO {
+  label: string;
+  value: string;
+  nameEn: string;
+  formType?: string;
+  score?: string;
+}
+
+export interface QuestionVO {
+  /**
+   * 主键ID
+   */
+  questionId: string | number;
+
+  /**
+   * 标题
+   */
+  title: string;
+
+  /**
+   * 题型
+   */
+  questionType: string;
+
+  required?:boolean
+
+  arrangement?:string
+
+  /**
+   * 内容
+  */
+  content: string;
+
+  selected?: boolean;   
+
+  contentList: Array<QuestionAttrVO>;
+}
+
+export interface ScreeningAssessmentConfigForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  configId?: string | number;
+
+  /**
+   * 量表类型
+   */
+  type?: string;
+
+  /**
+   * 性别
+   */
+  gender?: string;
+
+  /**
+   * 年龄
+   */
+  age?: string;
+
+  /**
+   * 量表名称
+   */
+  name?: string;
+
+  /**
+   * 量表说明
+   */
+  description?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+}
+
+export interface ScreeningAssessmentConfigQuery extends PageQuery {
+
+  /**
+   * 量表类型
+   */
+  type?: string;
+
+  /**
+   * 性别
+   */
+  gender?: string;
+
+  /**
+   * 年龄
+   */
+  age?: string;
+
+  /**
+   * 量表名称
+   */
+  name?: string;
+
+  /**
+   * 量表说明
+   */
+  description?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/system/screeningAssessmentQuestion/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ScreeningAssessmentQuestionVO, ScreeningAssessmentQuestionForm, ScreeningAssessmentQuestionQuery } from '@/api/system/screeningAssessmentQuestion/types';
+
+/**
+ * 查询题目列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listScreeningAssessmentQuestion = (query?: ScreeningAssessmentQuestionQuery): AxiosPromise<ScreeningAssessmentQuestionVO[]> => {
+  return request({
+    url: '/system/screeningAssessmentQuestion/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询题目详细
+ * @param questionId
+ */
+export const getScreeningAssessmentQuestion = (questionId: string | number): AxiosPromise<ScreeningAssessmentQuestionVO> => {
+  return request({
+    url: '/system/screeningAssessmentQuestion/' + questionId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增题目
+ * @param data
+ */
+export const addScreeningAssessmentQuestion = (data: ScreeningAssessmentQuestionForm) => {
+  return request({
+    url: '/system/screeningAssessmentQuestion',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改题目
+ * @param data
+ */
+export const updateScreeningAssessmentQuestion = (data: ScreeningAssessmentQuestionForm) => {
+  return request({
+    url: '/system/screeningAssessmentQuestion',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除题目
+ * @param questionId
+ */
+export const delScreeningAssessmentQuestion = (questionId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/screeningAssessmentQuestion/' + questionId,
+    method: 'delete'
+  });
+};

+ 86 - 0
src/api/system/screeningAssessmentQuestion/types.ts

@@ -0,0 +1,86 @@
+export interface ScreeningAssessmentQuestionVO {
+  /**
+   * 主键ID
+   */
+  questionId: string | number;
+
+  /**
+   * 主键ID
+   */
+  configId: string | number;
+
+  /**
+   * 标题
+   */
+  title: string;
+
+  /**
+   * 题型
+   */
+  questionType: string;
+
+  /**
+   * 内容
+   */
+  content: string;
+
+}
+
+export interface ScreeningAssessmentQuestionForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  questionId?: string | number;
+
+  /**
+   * 主键ID
+   */
+  configId?: string | number;
+
+  /**
+   * 标题
+   */
+  title?: string;
+
+  /**
+   * 题型
+   */
+  questionType?: string;
+
+  /**
+   * 内容
+   */
+  content?: string;
+
+}
+
+export interface ScreeningAssessmentQuestionQuery extends PageQuery {
+
+  /**
+   * 主键ID
+   */
+  configId?: string | number;
+
+  /**
+   * 标题
+   */
+  title?: string;
+
+  /**
+   * 题型
+   */
+  questionType?: string;
+
+  /**
+   * 内容
+   */
+  content?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 269 - 0
src/components/MiniImageUpload/index.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="component-upload-image" style="height: 40px;width: 40px;">
+    <el-upload
+      ref="imageUploadRef"
+      multiple
+      :action="uploadImgUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :limit="limit"
+      :disabled="disabled"
+      :accept="fileAccept"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      :before-remove="handleDelete"
+      :show-file-list="true"
+      :headers="headers"
+      :file-list="fileList"       
+      :class="{ hide: fileList.length >= limit }"
+    >
+      <el-icon class="avatar-uploader-icon" style="height: 10px;width: 10px;">
+        <plus/>
+      </el-icon>
+    </el-upload>
+    <!-- 上传提示 -->
+    <div v-if="showTip" class="el-upload__tip">
+      请上传
+      <template v-if="fileSize">
+        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
+      </template>
+      <template v-if="fileType">
+        格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
+      </template>
+      的文件
+    </div>
+
+    <!-- <el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
+      <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
+    </el-dialog> -->
+  </div>
+</template>
+
+<script setup lang="ts">
+import { listByIds, delOss } from '@/api/system/oss';
+import { OssVO } from '@/api/system/oss/types';
+import { propTypes } from '@/utils/propTypes';
+import { globalHeaders } from '@/utils/request';
+import { compressAccurately } from 'image-conversion';
+import { string } from 'vue-types';
+
+const props = defineProps({
+  modelValue: {
+    type: [String, Object, Array],
+    default: () => []
+  },
+  // 图片数量限制
+  limit: propTypes.number.def(5),
+  // 大小限制(MB)
+  fileSize: propTypes.number.def(5),
+  // 文件类型, 例如['png', 'jpg', 'jpeg']
+  fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),
+  // 是否显示提示
+  isShowTip: {
+    type: Boolean,
+    default: false
+  },
+  disabled: {
+    type: Boolean,
+    default: false
+  },  
+  // 是否支持压缩,默认否
+  compressSupport: {
+    type: Boolean,
+    default: false
+  },
+   
+  // 压缩目标大小,单位KB。默认300KB以上文件才压缩,并压缩至300KB以内
+  compressTargetSize: propTypes.number.def(300)
+});
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emit = defineEmits(['update:modelValue']);
+const number = ref(0);
+const uploadList = ref<any[]>([]);
+const dialogImageUrl = ref('');
+const dialogVisible = ref(false);
+
+const baseUrl = import.meta.env.VITE_APP_BASE_API;
+const uploadImgUrl = ref(baseUrl + '/api/qiniu/upload'); // 上传的图片服务器地址
+const headers = ref(globalHeaders());
+
+const fileList = ref<any[]>([]);
+const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
+
+const imageUploadRef = ref<ElUploadInstance>();
+
+// 监听 fileType 变化,更新 fileAccept
+const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));
+
+watch(
+  () => props.modelValue,
+  async (val: string) => {
+    if (val) {
+      // 首先将值转为数组
+      let list: OssVO[] = [];
+      if (Array.isArray(val)) {
+        list = val as OssVO[];
+      } else {
+        const res = await listByIds(val);
+        list = res.data;
+      }
+      // 然后将数组转为对象数组
+      fileList.value = list.map((item) => {
+        // 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
+        let itemData;
+        if (typeof item === 'string') {
+          itemData = { name: item, url: item };
+        } else {
+          // 此处name使用ossId 防止删除出现重名
+          itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
+        }
+        return itemData;
+      });
+    } else {
+      fileList.value = [];
+      return [];
+    }
+  },
+  { deep: true, immediate: true }
+);
+
+/** 上传前loading加载 */
+const handleBeforeUpload = (file: any) => {
+  let isImg = false;
+  if (props.fileType.length) {
+    let fileExtension = '';
+    if (file.name.lastIndexOf('.') > -1) {
+      fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
+    }
+    isImg = props.fileType.some((type: any) => {
+      if (file.type.indexOf(type) > -1) return true;
+      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+      return false;
+    });
+  } else {
+    isImg = file.type.indexOf('image') > -1;
+  }
+  if (!isImg) {
+    proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`);
+    return false;
+  }
+  if (file.name.includes(',')) {
+    proxy?.$modal.msgError('文件名不正确,不能包含英文逗号!');
+    return false;
+  }
+  if (props.fileSize) {
+    const isLt = file.size / 1024 / 1024 < props.fileSize;
+    if (!isLt) {
+      proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
+      return false;
+    }
+  }
+
+  //压缩图片,开启压缩并且大于指定的压缩大小时才压缩
+  if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {
+    proxy?.$modal.loading('正在上传图片,请稍候...');
+    number.value++;
+    return compressAccurately(file, props.compressTargetSize);
+  } else {
+    proxy?.$modal.loading('正在上传图片,请稍候...');
+    number.value++;
+  }
+};
+
+// 文件个数超出
+const handleExceed = () => {
+  proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
+};
+
+// 上传成功回调
+const handleUploadSuccess = (res: any, file: UploadFile) => {
+  if (res.code === 200) {
+    uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
+    uploadedSuccessfully();
+  } else {
+    number.value--;
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    imageUploadRef.value?.handleRemove(file);
+    uploadedSuccessfully();
+  }
+};
+
+// 删除图片
+const handleDelete = (file: UploadFile): boolean => {
+  const findex = fileList.value.map((f) => f.name).indexOf(file.name);
+  if (findex > -1 && uploadList.value.length === number.value) {
+    const ossId = fileList.value[findex].ossId;
+    delOss(ossId);
+    fileList.value.splice(findex, 1);
+    emit('update:modelValue', listToString(fileList.value));
+    return false;
+  }
+  return true;
+};
+
+// 上传结束处理
+const uploadedSuccessfully = () => {
+  if (number.value > 0 && uploadList.value.length === number.value) {
+    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
+    uploadList.value = [];
+    number.value = 0;
+    emit('update:modelValue', listToString(fileList.value));
+    proxy?.$modal.closeLoading();
+  }
+};
+
+// 上传失败
+const handleUploadError = () => {
+  proxy?.$modal.msgError('上传图片失败');
+  proxy?.$modal.closeLoading();
+};
+
+// 预览
+const handlePictureCardPreview = (file: any) => {
+  dialogImageUrl.value = file.url;
+  dialogVisible.value = true;
+};
+
+// 对象转成指定字符串分隔
+const listToString = (list: any[], separator?: string) => {
+  let strs = '';
+  separator = separator || ',';
+  for (const i in list) {
+    if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
+      strs += list[i].ossId + separator;
+    }
+  }
+  return strs != '' ? strs.substring(0, strs.length - 1) : '';
+};
+</script>
+
+<style lang="scss" scoped>
+// .el-upload--picture-card 控制加号部分
+:deep(.hide .el-upload--picture-card) {
+  display: none;
+}
+:deep(.el-upload--picture-card) {
+  width: 40px !important;
+  height: 40px !important;
+  line-height: 40px !important;
+}
+:deep(.el-upload-list__item) {
+  width: 40px !important;
+  height: 40px !important;
+  line-height: 40px !important;
+}
+:deep(.el-upload-list__item-delete) {
+  width: 40px !important;
+  height: 40px !important;
+  line-height: 40px !important;
+  margin-right: 16px;   
+}
+:deep(.el-upload-list__item-preview) {
+  visibility: hidden;
+}
+
+
+</style>

+ 31 - 18
src/views/system/recipe/add.vue

@@ -344,7 +344,7 @@
             <el-table-column label="疾病/部位名称" align="center" prop="labelName" />
             <el-table-column label="疾病/部位编码" align="center" prop="labelCode" />
             <el-table-column label="所属分类" align="center" prop="categoryName" />
-          </el-table>
+        </el-table>
         </div>
         <div style="margin-left: 10px;width: 100%;">
           <pagination v-show="total > -1" :total="total" v-model:page="diseaseLabelQuery.pageNum"
@@ -358,14 +358,14 @@
             <el-table-column label="疾病/部位名称" align="center" prop="labelName" />
             <el-table-column label="疾病/部位编码" align="center" prop="labelCode" />
             <el-table-column label="所属分类" align="center" prop="categoryName" />
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
-              <template #default="scope">
-                <el-tooltip content="删除" placement="top">
+          <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
+            <template #default="scope">
+              <el-tooltip content="删除" placement="top">
                   <el-button link type="danger" icon="Delete" @click="handleSelectedDelete2(scope.row)">删除</el-button>
-                </el-tooltip>
-              </template>
-            </el-table-column>
-          </el-table>
+              </el-tooltip>
+            </template>
+          </el-table-column>
+        </el-table>
         </div>
         <div style="margin-left: 10px;margin-top:36px;width: 100%;text-align: left;">
           <span style="color: rgb(118, 180, 244);">已选:{{ selecteddiseaseLabelList2.length }}项</span>
@@ -1303,7 +1303,7 @@ const getList = async () => {
   if (vRecipeId) {
     isView.value = true;
     id = vRecipeId;
-  } else {
+      } else {
     isView.value = false;
     id = recipeId;
   }
@@ -1413,21 +1413,34 @@ const nutrientAnalysisLeftData = async () => {
 
 
 const nutrientAnalysisRightData = async (jsonStr:string) => {
+  rightList.value=[];
+  if(selectedIngredientListBak.value.length<1){
+    return; 
+  }
+  
   let nutritionalDataAnalysis =  JSON.parse(jsonStr);
   nutritionalDataAnalysis.forEach((na:any) => {
     let showData={}
     selectedIngredientListBak.value.forEach(item => {
+      let quantity = Number(item.quantity?item.quantity:0) / 100;
       if(!showData['nutrientName']){
         showData['nutrientUnit'] =na.unit;
         showData['nutrientName'] = na.remark;
-        showData['nutrientQuantity'] = item[na.name]?Number(item[na.name]) : 0;
+        showData['nutrientQuantity'] = item[na.name]?Number(item[na.name])*quantity/100 : 0;
       } else{
-        showData['nutrientQuantity'] = (item[na.name]?Number(item[na.name]):0)+showData['nutrientQuantity'];
+        showData['nutrientQuantity'] = (item[na.name]?Number(item[na.name])*quantity/100:0)+showData['nutrientQuantity'];
       }       
   });
    
    rightList.value.push(JSON.parse(JSON.stringify(showData)));
-  });  
+  }); 
+  
+  rightList.value.forEach(item=>{
+    if(item.nutrientQuantity>0){
+      item.nutrientQuantity=Math.floor(item.nutrientQuantity*10000)/10000
+    }
+  })
+  
 }
 
 const nutrientAnalysisData = async () => {
@@ -1463,10 +1476,10 @@ onMounted(() => {
 }
 
 .diagnosis-tag {
-  border-left: 1px solid #409EFF;
-  border-top: 1px solid #409EFF;
-  border-bottom: 1px solid #409EFF;
-  padding: 8px 8px;
+  border-left: 1px solid #409EFF;  
+  border-top: 1px solid #409EFF;  
+  border-bottom: 1px solid #409EFF;  
+  padding: 8px 8px;   
   color: #409EFF;
   background: #f4f8ff;
   display: inline-block;
@@ -1476,8 +1489,8 @@ onMounted(() => {
   vertical-align: middle;
 }
 
-.diagnosis-tag:last-child {
-  border-right: 1px solid #409EFF;
+.diagnosis-tag:last-child {  
+  border-right: 1px solid #409EFF; 
 }
 
 .diagnosisActive {

+ 988 - 0
src/views/system/screeningAssessmentConfig/add.vue

@@ -0,0 +1,988 @@
+<template>
+  <div class="p-2">
+    <!-- 新增顶部操作栏 -->
+    <el-row type="flex" align="middle" justify="space-between" style="margin-bottom: 16px;">
+      <el-col :span="4">
+        <el-button type="primary" plain @click="close" style="margin-right: 20px;">返回</el-button>
+        <span style="font-size: 14px;  vertical-align: middle;">新增量表</span>
+      </el-col>
+      <el-col :span="4" style="text-align: right;">
+        <el-button type="primary" plain @click="handlePreview" style="margin-right: 8px;">预览</el-button>
+        <el-button type="primary" @click="submitForm">保存</el-button>
+      </el-col>
+    </el-row>
+
+    <el-container class="layout-container-demo" style="height: 100vh; min-height: 100vh;">
+      <el-aside id="leftPanel"
+        style="background:transparent; width: 450px; min-width: 300px; max-width: 500px; flex: 0 0 auto; border-right: 2px solid #e4e7ed;">
+        <div style="background:#f6f9fc; border-radius:4px; margin-bottom: 16px; padding: 16px 0;">
+          <div style="font-weight:bold; font-size:16px; padding:0 24px 8px 24px; cursor:pointer;"
+            @click="showSystemParam = !showSystemParam">
+            系统参数
+            <i class="el-icon-arrow-down" :style="{
+              float: 'right',
+              transition: 'transform 0.2s',
+              transform: showSystemParam ? 'rotate(0deg)' : 'rotate(-90deg)'
+            }"></i>
+          </div>
+          <div v-show="showSystemParam" style="padding: 16px 16px 0 16px;">
+            <div style="font-weight: bold; margin-bottom: 8px;">基本信息</div>
+            <el-row :gutter="20" style="margin-bottom: 24px;">
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('gender', '性别')">性别</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('birthDate', '出生日期')">出生日期</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('height', '身高')">身高</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('weight', '体重')">体重</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('BMI', 'BMI')">BMI</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain
+                  @click="handleBaseBtnClick('nutritionalDiagnosis', '营养诊断')">营养诊断</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain @click="handleBaseBtnClick('admissionDate', '入院日期')">入院日期</el-button>
+              </el-col>
+            </el-row>
+            <div style="font-weight: bold; margin-bottom: 8px;">其他</div>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-button class="param-btn" plain
+                  @click="handleOtherBtnClick('anthropometry', '人体测量')">人体测量</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain
+                  @click="handleOtherBtnClick('dietaryStatus', '膳食状况')">膳食状况</el-button>
+              </el-col>
+              <el-col :span="8">
+                <el-button class="param-btn" plain
+                  @click="handleOtherBtnClick('nutritionalBiochemical', '营养生化检查')">营养生化检查</el-button>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        <div style="background:#f6f9fc; border-radius:4px; margin-bottom: 16px; padding: 16px 0;">
+          <div style="font-weight:bold; font-size:16px; padding:0 24px 8px 24px; cursor:pointer;"
+            @click="showQuestionType = !showQuestionType">
+            题型选择
+            <i class="el-icon-arrow-down" :style="{
+              float: 'right',
+              transition: 'transform 0.2s',
+              transform: showQuestionType ? 'rotate(0deg)' : 'rotate(-90deg)'
+            }"></i>
+          </div>
+          <div v-show="showQuestionType" style="min-height:40px; padding:0 24px 16px 24px;">
+            <el-row :gutter="12">
+              <el-col :span="8"><el-button class="param-btn" plain>单选题</el-button></el-col>
+              <el-col :span="8"><el-button class="param-btn" plain>多选题</el-button></el-col>
+              <el-col :span="8"><el-button class="param-btn" plain>填空题</el-button></el-col>
+              <el-col :span="8"><el-button class="param-btn" plain>量表题</el-button></el-col>
+              <el-col :span="8"><el-button class="param-btn" plain>矩阵量表</el-button></el-col>
+            </el-row>
+          </div>
+        </div>
+      </el-aside>
+      <el-container>
+        <el-main style="padding-left: 20px;">
+          <el-scrollbar>
+            <div style="width: 50%;">
+              <el-form label-width="100px" label-position="left">
+                <el-form-item label="量表类型:" required>
+                  <el-radio-group v-model="form.type">
+                    <el-radio label="营养筛查" value="1" />
+                    <el-radio label="营养评估" value="2" />
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="适用条件:" required>
+                  <!-- 可留空或加说明 -->
+                </el-form-item>
+                <el-form-item label="性别:" style="margin-left: 20px;">
+                  <el-radio-group v-model="form.gender" style="margin-left: -50px;">
+                    <el-radio label="不限" value="-1">不限</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="年龄:" style="margin-left: 20px;">
+                  <el-radio-group v-model="form.age" style="margin-left: -50px;">
+                    <el-radio label="不限" style="width: 50%" value="-1">不限</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="量表名称:" required>
+                  <el-input v-model="form.name" maxlength="50" show-word-limit placeholder="请输入" />
+                </el-form-item>
+                <el-form-item label="量表说明:">
+                  <el-input v-model="form.description" maxlength="50" show-word-limit placeholder="请输入" />
+                </el-form-item>
+                <el-form-item label="备注:">
+                  <el-input type="textarea" v-model="form.remark" maxlength="500" show-word-limit placeholder="请输入"
+                    :rows="3" />
+                </el-form-item>
+              </el-form>
+            </div>
+
+            <div v-if="baseInfo.questionType" @click="selectBaseInfo" :class="{ selectedDiv: baseInfo.selected }">
+              <span>&nbsp;</span>
+              <div style="width: 50%;margin-top: 32px;">
+                <el-form label-width="100px" label-position="left">
+                  <el-form-item label="1. 标题:" required>
+                    <el-input :value="baseInfo.title" readonly />
+                  </el-form-item>
+                  <div style="margin-left: 60px;">
+                    <template v-for="ctl in baseInfo.contentList">
+                      <el-form-item label="填空:">
+                        <el-input :value="ctl.label" readonly style="margin-left: -50px;">
+                          <template #append><el-button @click="handleBaseBtnDelete(ctl.nameEn)">-</el-button></template>
+                        </el-input>
+                      </el-form-item>
+                    </template>
+                  </div>
+                </el-form>
+              </div>
+              <div style="display: flex; align-items: right;justify-content: right;padding-right: 50px;">
+                <el-button type="primary" icon="Delete" plain @click="deleteBaseInfo">删除</el-button>
+              </div>
+              <span>&nbsp;</span>
+            </div>
+
+            <template v-for="other, index in otherInfo">
+              <div style="border-bottom: 1px solid #e5e7eb;" :class="{ selectedDiv: other.selected }"
+                @click="selectOtherInfo(index)">
+                <span>&nbsp;</span>
+                <div style="width: 50%;margin-top: 32px;">
+                  <div>
+                    <el-form label-width="100px" label-position="left">
+                      <el-form-item :label="(index + 1 + baseInfo.contentList.length) + '. 标题:'" required>
+                        <el-input :value="other.title" readonly />
+                      </el-form-item>
+                      <template v-for="ctl, idx in other.contentList">
+                        <template v-if="ctl.formType == 'radio'">
+                          <div style="margin-left: -50px;width: 1024px;">
+                            <div style="background-color: #e5e7eb; margin-left: 60px;height: 40px;line-height: 40px;
+                          margin-top: 5px;margin-bottom: 15px;" v-if="idx == 0">
+                              <div style="text-align:left;margin-left: 50px;">选项(单选)</div>
+                            </div>
+                            <el-form-item>
+                              <el-radio-group>
+                                <el-radio :value="idx" :disabled="true">
+                                  选项{{ idx + 1 }}:
+                                  <el-input v-model="ctl.value" :value="ctl.label" readonly style="width: 450px;">
+                                    <template #append><el-button
+                                        @click="handleBaseBtnDelete(ctl.nameEn)">-</el-button></template>
+                                  </el-input>
+                                </el-radio>
+                              </el-radio-group>
+                            </el-form-item>
+                          </div>
+                        </template>
+                        <template v-else-if="ctl.formType == 'radioScore'">
+                          <div style="margin-left: -50px;width: 1024px;">
+                            <div style="background-color: #e5e7eb; margin-left: 60px;height: 40px;line-height: 40px;
+                          margin-top: 5px;margin-bottom: 15px;" v-if="idx == 0">
+                              <div style="display:inline;text-align:left;margin-left: 50px;">选项(单选)</div>
+                              <div style="display:inline;text-align:left;margin-left: 340px;">分值设置</div>
+                            </div>
+                            <el-form-item>
+                              <el-radio-group>
+                                <el-radio :value="idx" :disabled="true" :key="idx">
+                                  选项{{ idx + 1 }}:
+                                  <el-input v-model="ctl.value" :value="ctl.label" readonly>
+                                    <template #append><el-button
+                                        @click="handleBaseBtnDelete(ctl.nameEn)">-</el-button></template>
+                                  </el-input>
+
+                                  <el-input v-model="ctl.score" :value="idx" style="width: 50px;margin-left: 20px;" />
+                                </el-radio>
+                              </el-radio-group>
+                            </el-form-item>
+                          </div>
+                        </template>
+                        <template v-else>
+                          <div style="margin-left: 60px;">
+                            <el-form-item label="填空:">
+                              <el-input :value="ctl.label" readonly style="margin-left: -50px;">
+                                <template #append><el-button
+                                    @click="handleBaseBtnDelete(ctl.nameEn)">-</el-button></template>
+                              </el-input>
+                            </el-form-item>
+                          </div>
+                        </template>
+                      </template>
+                    </el-form>
+                  </div>
+                </div>
+                <div style="display: flex; align-items: right;justify-content: right;padding-right: 50px;">
+                  <el-button type="primary" icon="Top" plain>上移</el-button>
+                  <el-button type="primary" icon="Bottom" plain>下移</el-button>
+                  <el-button type="primary" icon="Upload" plain>置顶</el-button>
+                  <el-button type="primary" icon="Download" plain>置底</el-button>
+                  <el-button type="primary" icon="Delete" plain @click="deleteOtherInfo(index)">删除</el-button>
+                </div>
+                <span>&nbsp;</span>
+              </div>
+            </template>
+
+
+            <div style="overflow-x: scroll;">
+              <el-radio-group  size="large">
+                <el-radio-button label="New York"  />
+                <el-radio-button label="Washington"  />
+                <el-radio-button label="Los Angeles"/>
+                <el-radio-button label="Chicago"/>
+              </el-radio-group>
+            </div>
+            <div style="border-bottom: 1px solid #e5e7eb;" class="selectedDiv">
+              <span>&nbsp;</span>
+              <el-row :gutter="10" class="mb8">
+                <el-col :span="12">
+                  <el-form-item label="2. 标题:" required>
+                    <el-input readonly style="width: 100%;" />
+                    </el-form-item>
+                </el-col>
+                <el-col :span="2" align="center">
+                  <el-checkbox label="是否必填"/>               
+                </el-col>
+                <el-col :span="3">
+                  <el-select v-model="queryParams.gender" placeholder="请选择" clearable >
+                          <el-option v-for="dict in [{ label: '横向排列', value: '1' }, { label: '竖向排列', value: '2' }]"
+                            :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                        </el-select> 
+                </el-col>
+                <el-col :span="3"  >
+                  <div>                   
+                  <el-select v-model="queryParams.gender" placeholder="请选择" clearable>
+                    <el-option v-for="dict in [{ label: '横向排列', value: '1' }, { label: '竖向排列', value: '2' }]"
+                      :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                  </el-select>
+                  </div>
+                  
+                </el-col>
+                <el-col :span="6">
+                  
+                </el-col>
+              </el-row>
+              
+              <el-row :gutter="10" class="mb8" style="background-color: #e5e7eb;height: 40px;line-height: 40px;">
+                <el-col :span="12">
+                  <div style="text-align: center;">选项(单选)</div>
+                </el-col>
+                <el-col :span="2">
+                  <div style="text-align: center;">添加图片</div>
+                </el-col>
+                <el-col :span="3"> 
+                  <div style="text-align: center;">分值设置</div>
+                </el-col>
+                <el-col :span="3" > 
+                  <div style="text-align: left;">允许填空</div>
+                </el-col>
+                <el-col :span="6" > 
+                   
+                </el-col>
+              </el-row>
+
+              <el-row :gutter="10" class="mb8" style="margin-top: 20px;">
+                <el-col :span="12">
+                  <el-form-item required>
+                    <el-radio-group>
+                      <el-radio :disabled="true"  >
+                        选项1:
+                        <el-input  readonly size="large" style="width: 500px;">
+                          <template #append><el-button>-</el-button></template>
+                        </el-input>                         
+                      </el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="2" align="center" style="margin-top: -5px;">
+                  <miniImageUpload :limit="1"/>
+                </el-col>
+                <el-col :span="3" align="center">
+                  <el-input style="width: 60px;height: 30px;" value="1" size="small" />
+                </el-col>
+                <el-col :span="1" align="center">
+                  <el-checkbox/>
+                </el-col>
+                <el-col :span="8" align="center">                   
+                </el-col>
+              </el-row>
+               
+              <div style="display: flex; align-items: right;justify-content: right;padding-right: 50px;">
+                <el-button type="primary" icon="Top" plain>上移</el-button>
+                <el-button type="primary" icon="Bottom" plain>下移</el-button>
+                <el-button type="primary" icon="Upload" plain>置顶</el-button>
+                <el-button type="primary" icon="Download" plain>置底</el-button>
+                <el-button type="primary" icon="Delete" plain>删除</el-button>
+              </div>
+              <span>&nbsp;</span>
+            </div>
+
+
+
+          </el-scrollbar>
+        </el-main>
+      </el-container>
+    </el-container>
+  </div>
+</template>
+
+<script setup name="ScreeningAssessmentConfig" lang="ts">
+import { ref, onMounted, onUnmounted } from 'vue';
+import { listScreeningAssessmentConfig, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
+import { ScreeningAssessmentConfigVO, ScreeningAssessmentConfigQuery, ScreeningAssessmentConfigForm, QuestionAttrVO, QuestionVO } from '@/api/system/screeningAssessmentConfig/types';
+import { fa, tr } from 'element-plus/es/locale/index.mjs';
+import { Document } from '@element-plus/icons-vue';
+
+const showSystemParam = ref(true);
+const showQuestionType = ref(true);
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { scale_type } = toRefs<any>(proxy?.useDict('scale_type'));
+
+const screeningAssessmentConfigList = ref<ScreeningAssessmentConfigVO[]>([]);
+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 baseInfo = ref<QuestionVO>({
+  questionId: '',
+  title: '',
+  questionType: '',
+  content: '',
+  selected: false,
+  contentList: [],
+});
+const otherInfo = ref<QuestionVO[]>([]);
+const questionType = ref<QuestionVO[]>([]);
+
+const queryFormRef = ref<ElFormInstance>();
+const screeningAssessmentConfigFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ScreeningAssessmentConfigForm = {
+  configId: undefined,
+  type: '1',
+  gender: '-1',
+  age: '-1',
+  name: undefined,
+  description: undefined,
+  remark: undefined,
+  status: undefined,
+}
+const data = reactive<PageData<ScreeningAssessmentConfigForm, ScreeningAssessmentConfigQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    type: undefined,
+    gender: undefined,
+    age: undefined,
+    name: undefined,
+    description: undefined,
+    status: undefined,
+    params: {
+    }
+  },
+  rules: {
+    configId: [
+      { required: true, message: "主键ID不能为空", trigger: "blur" }
+    ],
+    type: [
+      { required: true, message: "量表类型不能为空", trigger: "change" }
+    ],
+    name: [
+      { required: true, message: "量表名称不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询筛查/评估配置列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listScreeningAssessmentConfig(queryParams.value);
+  screeningAssessmentConfigList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  screeningAssessmentConfigFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ScreeningAssessmentConfigVO[]) => {
+  ids.value = selection.map(item => item.configId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加筛查/评估配置";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ScreeningAssessmentConfigVO) => {
+  reset();
+  const _configId = row?.configId || ids.value[0]
+  const res = await getScreeningAssessmentConfig(_configId);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改筛查/评估配置";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  screeningAssessmentConfigFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.configId) {
+        await updateScreeningAssessmentConfig(form.value).finally(() => buttonLoading.value = false);
+      } else {
+        await addScreeningAssessmentConfig(form.value).finally(() => buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ScreeningAssessmentConfigVO) => {
+  const _configIds = row?.configId || ids.value;
+  await proxy?.$modal.confirm('是否确认删除筛查/评估配置编号为"' + _configIds + '"的数据项?').finally(() => loading.value = false);
+  await delScreeningAssessmentConfig(_configIds);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('system/screeningAssessmentConfig/export', {
+    ...queryParams.value
+  }, `screeningAssessmentConfig_${new Date().getTime()}.xlsx`)
+}
+
+/** 预览按钮操作 */
+const handlePreview = () => {
+  // Implementation of handlePreview function
+}
+
+/** 关闭按钮操作 */
+const close = () => {
+  // Implementation of close function
+}
+
+function handleOtherBtnClick(nameEn: string, nameCn: string) {
+  let question: QuestionVO = {
+    questionId: '',
+    title: nameCn,
+    questionType: 'otherInfo',
+    content: '',
+    contentList: [],
+  };
+  otherInfo.value.push(question);
+
+  if ('anthropometry' == nameEn) {
+    question.contentList.push({
+      label: '身高',
+      value: undefined,
+      nameEn: 'height',
+    });
+    question.contentList.push({
+      label: '当前体重',
+      value: undefined,
+      nameEn: 'currentWeight',
+    });
+    question.contentList.push({
+      label: '出生体重',
+      value: undefined,
+      nameEn: 'birthWeight',
+    });
+    question.contentList.push({
+      label: '理想体重',
+      value: undefined,
+      nameEn: 'idealWeight',
+    });
+    question.contentList.push({
+      label: 'BMI',
+      value: undefined,
+      nameEn: 'BMI',
+    });
+    question.contentList.push({
+      label: '握力',
+      value: undefined,
+      nameEn: 'gripStrength',
+    });
+    question.contentList.push({
+      label: '皮褶厚度',
+      value: undefined,
+      nameEn: 'leatherFoldThickness',
+    });
+    question.contentList.push({
+      label: '腰围',
+      value: undefined,
+      nameEn: 'waistCircumference',
+    });
+    question.contentList.push({
+      label: '上臂围',
+      value: undefined,
+      nameEn: 'upperArmCircumference',
+    });
+    question.contentList.push({
+      label: '小腿围',
+      value: undefined,
+      nameEn: 'calfCircumference',
+    });
+    question.contentList.push({
+      label: '其他测量指标',
+      value: undefined,
+      nameEn: 'otherMeasurementIndicators',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '请选择调查时间',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '三个月前',
+      formType: 'radio',
+      value: undefined,
+      nameEn: 'threeMonthsAgo',
+    });
+    question.contentList.push({
+      label: '两个月前',
+      value: undefined,
+      formType: 'radio',
+      nameEn: 'twoMonthsAgo',
+    });
+    question.contentList.push({
+      label: '一个月前',
+      formType: 'radio',
+      value: undefined,
+      nameEn: 'oneMonthsAgo',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '请填入已选调查时间体重',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '过去体重',
+      value: undefined,
+      nameEn: 'pastWeight',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '1-3个月内体重变化情况',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '近3个月体重无显著变化',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'noChangeWeight',
+    });
+    question.contentList.push({
+      label: '近3个月体重丢失>5%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'weightLoss5In3Months',
+    });
+    question.contentList.push({
+      label: '近2个月体重丢失>5%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'weightLoss5In2Months',
+    });
+    question.contentList.push({
+      label: '近1个月体重丢失>5%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'weightLoss5In1Months',
+    });
+
+    return;
+  }
+
+  if ('dietaryStatus' == nameEn) {
+    question.contentList.push({
+      label: '平素膳食餐次',
+      value: undefined,
+      nameEn: 'regularMealSchedule',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '近1周食物摄入量是否减少',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '无变化',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'noChange',
+    });
+    question.contentList.push({
+      label: '较从前减少25%~50%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'decreasedBy25To50',
+    });
+    question.contentList.push({
+      label: '较从前减少51%~75%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'decreasedBy51To75',
+    });
+    question.contentList.push({
+      label: '较从前减少76%~100%',
+      formType: 'radioScore',
+      value: undefined,
+      nameEn: 'decreasedBy76To100',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '近1周食物摄入种类(/日)',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '奶及奶制品',
+      value: undefined,
+      nameEn: 'milkAndDairyProducts',
+    });
+    question.contentList.push({
+      label: '坚果类',
+      value: undefined,
+      nameEn: 'nutCategory',
+    });
+    question.contentList.push({
+      label: '大豆类',
+      value: undefined,
+      nameEn: 'largeBeans',
+    });
+    question.contentList.push({
+      label: '肉蛋水产类',
+      value: undefined,
+      nameEn: 'meatEggAndAquaticProducts',
+    });
+    question.contentList.push({
+      label: '蔬菜类',
+      value: undefined,
+      nameEn: 'vegetables',
+    });
+    question.contentList.push({
+      label: '水果类',
+      value: undefined,
+      nameEn: 'fruitCategory',
+    });
+    question.contentList.push({
+      label: '谷薯杂豆类',
+      value: undefined,
+      nameEn: 'grainPotatoAndMixedBeans',
+    });
+    question.contentList.push({
+      label: '调料类',
+      value: undefined,
+      nameEn: 'seasoning',
+    });
+    question.contentList.push({
+      label: '水',
+      value: undefined,
+      nameEn: 'water',
+    });
+
+
+    question = {
+      questionId: '',
+      title: '近1周能量-营养素摄入分析(/日)',
+      questionType: 'otherInfo',
+      content: '',
+      contentList: [],
+    };
+    otherInfo.value.push(question);
+    question.contentList.push({
+      label: '总能量',
+      value: undefined,
+      nameEn: 'totalEnergy',
+    });
+    question.contentList.push({
+      label: '总氮量',
+      value: undefined,
+      nameEn: 'totalNitrogenContent',
+    });
+    question.contentList.push({
+      label: '氮/能量比值:1',
+      value: undefined,
+      nameEn: 'nitrogenEnergyRatio1',
+    });
+    question.contentList.push({
+      label: '蛋白质含量',
+      value: undefined,
+      nameEn: 'proteinContent',
+    });
+    question.contentList.push({
+      label: '脂肪含量',
+      value: undefined,
+      nameEn: 'fatContent',
+    });
+    question.contentList.push({
+      label: '碳水化合物含量',
+      value: undefined,
+      nameEn: 'carbohydrateContent',
+    });
+    question.contentList.push({
+      label: '蛋白质热比',
+      value: undefined,
+      nameEn: 'proteinHeatRatio',
+    });
+    question.contentList.push({
+      label: '脂肪热比',
+      value: undefined,
+      nameEn: 'protefatToHeatRatioinHeatRatio',
+    });
+    question.contentList.push({
+      label: '碳水化合物热比',
+      value: undefined,
+      nameEn: 'carbohydrateHeatRatio',
+    });
+    question.contentList.push({
+      label: '盐',
+      value: undefined,
+      nameEn: 'salt',
+    });
+    question.contentList.push({
+      label: '钠',
+      value: undefined,
+      nameEn: 'sodium',
+    });
+    return;
+  }
+}
+
+
+function handleBaseBtnClick(nameEn: string, nameCn: string) {
+  if (baseInfo.value.contentList.length < 1) {
+    baseInfo.value.title = '基本信息';
+    baseInfo.value.questionType = 'baseInfo';
+    baseInfo.value.contentList = [];
+  }
+
+  let questionAttr: QuestionAttrVO = {
+    label: nameCn,
+    value: undefined,
+    nameEn: nameEn,
+  };
+
+  if (baseInfo.value.contentList.length < 1) {
+    baseInfo.value.contentList.push(questionAttr);
+  } else {
+    let existed = false;
+    for (let item of baseInfo.value.contentList) {
+      if (item.nameEn == nameEn) {
+        existed = true;
+        break;
+      }
+    }
+
+    if (!existed) {
+      baseInfo.value.contentList.push(questionAttr);
+    }
+
+  }
+}
+
+
+function handleBaseBtnDelete(nameEn: string) {
+  let contentList = [];
+  for (let item of baseInfo.value.contentList) {
+    if (item.nameEn != nameEn) {
+      contentList.push(item);
+    }
+  }
+
+  if (contentList.length < 1) {
+    baseInfo.value.questionType = undefined;
+    baseInfo.value.contentList = [];
+  } else {
+    baseInfo.value.contentList = contentList;
+  }
+}
+
+function deleteBaseInfo() {
+  baseInfo.value.contentList = [];
+  baseInfo.value.questionType = undefined;
+}
+
+function deleteOtherInfo(index: number) {
+  let newList = []
+  for (let i = 0, len = otherInfo.value.length; i < len; i++) {
+    if (i == index) {
+      continue;
+    }
+    newList.push(otherInfo.value[i]);
+  }
+  otherInfo.value = newList;
+}
+
+
+function selectBaseInfo() {
+  baseInfo.value.selected = true;
+  otherInfo.value.forEach(item => {
+    item.selected = false;
+  });
+}
+
+function selectOtherInfo(index: number) {
+  baseInfo.value.selected = false;
+  otherInfo.value.forEach(item => {
+    item.selected = false;
+  });
+  let other = otherInfo.value[index]
+  other.selected = true;
+}
+
+const bodyWidth = ref((window.document.body.clientWidth - 500) * 0.8);
+function updateBodyWidth() {
+  let leftPanel = window.document.querySelector('#leftPanel').clientWidth;
+  bodyWidth.value = (window.document.body.clientWidth - leftPanel) * 0.93;
+}
+onMounted(() => {
+  window.addEventListener('resize', updateBodyWidth);
+  updateBodyWidth();
+});
+onUnmounted(() => {
+  window.removeEventListener('resize', updateBodyWidth);
+});
+</script>
+
+<style scoped>
+.layout-container-demo .el-header {
+  position: relative;
+  background-color: var(--el-color-primary-light-7);
+  color: var(--el-text-color-primary);
+}
+
+.layout-container-demo .el-aside {
+  color: var(--el-text-color-primary);
+  background: var(--el-color-primary-light-8);
+}
+
+.layout-container-demo .el-menu {
+  border-right: none;
+}
+
+.layout-container-demo .el-main {
+  padding: 0;
+}
+
+.layout-container-demo .toolbar {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  right: 20px;
+}
+
+.param-btn {
+  width: 100%;
+  height: 48px;
+  font-size: 13px;
+  font-weight: normal;
+  border: 1.5px solid #bfcbd9;
+  border-radius: 4px;
+  margin-bottom: 16px;
+  background: #fff;
+  color: #222;
+  letter-spacing: 1px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  padding: 0 2px;
+}
+
+@media (max-width: 1200px) {
+  .param-btn {
+    font-size: 13px;
+  }
+}
+
+@media (max-width: 800px) {
+  .param-btn {
+    font-size: 12px;
+  }
+}
+
+.selectedDiv {
+  background-color: #e8f4ff;
+}
+
+ul .el-upload--picture-card{  
+  --el-upload-picture-card-size:500px
+}
+</style>

+ 263 - 0
src/views/system/screeningAssessmentConfig/index.vue

@@ -0,0 +1,263 @@
+<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">
+            <el-form-item label="模板类型" prop="gender">
+              <el-select v-model="queryParams.gender" placeholder="请选择" clearable>
+                <el-option v-for="dict in scale_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+              </el-select>
+            </el-form-item>             
+            <el-form-item label="模板名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入" clearable @keyup.enter="handleQuery" />
+            </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"   icon="Plus" @click="handleAdd" v-hasPermi="['system:screeningAssessmentConfig:add']">新增量表</el-button>
+          </el-col>
+          <!-- <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:screeningAssessmentConfig:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:screeningAssessmentConfig:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:screeningAssessmentConfig:export']">导出</el-button>
+          </el-col> -->
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="screeningAssessmentConfigList" >
+        <!-- <el-table-column type="selection" width="55" align="center" />        @selection-change="handleSelectionChange" -->
+        <el-table-column label="模板名称" align="center" prop="name" />
+        <el-table-column label="模板类型" align="center" prop="type" />
+        <el-table-column label="模板类型" align="center" prop="type" />
+        <el-table-column label="创建人" align="center" prop="createBy" />
+        <el-table-column label="状态" align="center" prop="status" />
+        <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="View" @click="handleUpdate(scope.row)">预览</el-button>
+            </el-tooltip>
+            <el-tooltip content="编辑" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:screeningAssessmentConfig:edit']">编辑</el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:screeningAssessmentConfig:remove']">删除</el-button>
+            </el-tooltip>
+            <el-tooltip content="修改结论" placement="top">
+              <el-button link type="primary" icon="Edit" @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>
+    <!-- 添加或修改筛查/评估配置对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="screeningAssessmentConfigFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="性别" prop="gender">
+          <el-input v-model="form.gender" placeholder="请输入性别" />
+        </el-form-item>
+        <el-form-item label="年龄" prop="age">
+          <el-input v-model="form.age" placeholder="请输入年龄" />
+        </el-form-item>
+        <el-form-item label="量表名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入量表名称" />
+        </el-form-item>
+        <el-form-item label="量表说明" prop="description">
+          <el-input v-model="form.description" placeholder="请输入量表说明" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ScreeningAssessmentConfig" lang="ts">
+import { listScreeningAssessmentConfig, getScreeningAssessmentConfig, delScreeningAssessmentConfig, addScreeningAssessmentConfig, updateScreeningAssessmentConfig } from '@/api/system/screeningAssessmentConfig';
+import { ScreeningAssessmentConfigVO, ScreeningAssessmentConfigQuery, ScreeningAssessmentConfigForm } from '@/api/system/screeningAssessmentConfig/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const {scale_type } = toRefs<any>(proxy?.useDict('scale_type'));
+
+const screeningAssessmentConfigList = ref<ScreeningAssessmentConfigVO[]>([]);
+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 router = useRouter();
+
+const queryFormRef = ref<ElFormInstance>();
+const screeningAssessmentConfigFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ScreeningAssessmentConfigForm = {
+  configId: undefined,
+  type: undefined,
+  gender: undefined,
+  age: undefined,
+  name: undefined,
+  description: undefined,
+  remark: undefined,
+  status: undefined,
+}
+const data = reactive<PageData<ScreeningAssessmentConfigForm, ScreeningAssessmentConfigQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    type: undefined,
+    gender: undefined,
+    age: undefined,
+    name: undefined,
+    description: undefined,
+    status: undefined,
+    params: {
+    }
+  },
+  rules: {
+    configId: [
+      { required: true, message: "主键ID不能为空", trigger: "blur" }
+    ],
+    type: [
+      { required: true, message: "量表类型不能为空", trigger: "change" }
+    ],
+    name: [
+      { required: true, message: "量表名称不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询筛查/评估配置列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listScreeningAssessmentConfig(queryParams.value);
+  screeningAssessmentConfigList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  screeningAssessmentConfigFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ScreeningAssessmentConfigVO[]) => {
+  ids.value = selection.map(item => item.configId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  // reset();
+  // dialog.visible = true;
+  // dialog.title = "添加筛查/评估配置";
+  router.push('/argManage/screening-assessment-page/0');
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (disabled:boolean,row?: ScreeningAssessmentConfigVO) => {
+  // reset();
+  // const _configId = row?.configId || ids.value[0]
+  // const res = await getScreeningAssessmentConfig(_configId);
+  // Object.assign(form.value, res.data);
+  // dialog.visible = true;
+  // dialog.title = "修改筛查/评估配置";
+  const _configId = row?.configId || ids.value[0]
+  if(disabled){
+    router.push('/argManage/screening-assessment-view/'+_configId);
+  }else{
+    router.push('/argManage/screening-assessment-page/'+_configId);
+  } 
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  screeningAssessmentConfigFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.configId) {
+        await updateScreeningAssessmentConfig(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addScreeningAssessmentConfig(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ScreeningAssessmentConfigVO) => {
+  const _configIds = row?.configId || ids.value;
+  await proxy?.$modal.confirm('是否确认删除筛查/评估配置编号为"' + _configIds + '"的数据项?').finally(() => loading.value = false);
+  await delScreeningAssessmentConfig(_configIds);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('system/screeningAssessmentConfig/export', {
+    ...queryParams.value
+  }, `screeningAssessmentConfig_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 235 - 0
src/views/system/screeningAssessmentQuestion/index.vue

@@ -0,0 +1,235 @@
+<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">
+            <el-form-item label="主键ID" prop="configId">
+              <el-input v-model="queryParams.configId" placeholder="请输入主键ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="标题" prop="title">
+              <el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter="handleQuery" />
+            </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="['system:screeningAssessmentQuestion:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:screeningAssessmentQuestion:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:screeningAssessmentQuestion:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:screeningAssessmentQuestion:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="screeningAssessmentQuestionList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="主键ID" align="center" prop="questionId" v-if="true" />
+        <el-table-column label="主键ID" align="center" prop="configId" />
+        <el-table-column label="标题" align="center" prop="title" />
+        <el-table-column label="题型" align="center" prop="questionType" />
+        <el-table-column label="内容" align="center" prop="content" />
+        <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)" v-hasPermi="['system:screeningAssessmentQuestion:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:screeningAssessmentQuestion:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改题目对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="screeningAssessmentQuestionFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="主键ID" prop="configId">
+          <el-input v-model="form.configId" placeholder="请输入主键ID" />
+        </el-form-item>
+        <el-form-item label="标题" prop="title">
+            <el-input v-model="form.title" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="内容">
+          <editor v-model="form.content" :min-height="192"/>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ScreeningAssessmentQuestion" lang="ts">
+import { listScreeningAssessmentQuestion, getScreeningAssessmentQuestion, delScreeningAssessmentQuestion, addScreeningAssessmentQuestion, updateScreeningAssessmentQuestion } from '@/api/system/screeningAssessmentQuestion';
+import { ScreeningAssessmentQuestionVO, ScreeningAssessmentQuestionQuery, ScreeningAssessmentQuestionForm } from '@/api/system/screeningAssessmentQuestion/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const screeningAssessmentQuestionList = ref<ScreeningAssessmentQuestionVO[]>([]);
+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 queryFormRef = ref<ElFormInstance>();
+const screeningAssessmentQuestionFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ScreeningAssessmentQuestionForm = {
+  questionId: undefined,
+  configId: undefined,
+  title: undefined,
+  questionType: undefined,
+  content: undefined,
+}
+const data = reactive<PageData<ScreeningAssessmentQuestionForm, ScreeningAssessmentQuestionQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    configId: undefined,
+    title: undefined,
+    questionType: undefined,
+    content: undefined,
+    params: {
+    }
+  },
+  rules: {
+    questionId: [
+      { required: true, message: "主键ID不能为空", trigger: "blur" }
+    ],
+    title: [
+      { required: true, message: "标题不能为空", trigger: "blur" }
+    ],
+    questionType: [
+      { required: true, message: "题型不能为空", trigger: "change" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询题目列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listScreeningAssessmentQuestion(queryParams.value);
+  screeningAssessmentQuestionList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  screeningAssessmentQuestionFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ScreeningAssessmentQuestionVO[]) => {
+  ids.value = selection.map(item => item.questionId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加题目";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ScreeningAssessmentQuestionVO) => {
+  reset();
+  const _questionId = row?.questionId || ids.value[0]
+  const res = await getScreeningAssessmentQuestion(_questionId);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改题目";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  screeningAssessmentQuestionFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.questionId) {
+        await updateScreeningAssessmentQuestion(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addScreeningAssessmentQuestion(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ScreeningAssessmentQuestionVO) => {
+  const _questionIds = row?.questionId || ids.value;
+  await proxy?.$modal.confirm('是否确认删除题目编号为"' + _questionIds + '"的数据项?').finally(() => loading.value = false);
+  await delScreeningAssessmentQuestion(_questionIds);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('system/screeningAssessmentQuestion/export', {
+    ...queryParams.value
+  }, `screeningAssessmentQuestion_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>