994 lines
44 KiB
Markdown
994 lines
44 KiB
Markdown
# 科研捐赠管理模块 Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 实现科研捐赠管理模块——独立的新业务模块,支持协议 CRUD、审核、状态流转、与细胞存储的双向转化。
|
||
|
||
**Architecture:** 方案 C — 独立数据库表(6 张)+ 独立后端 API/CRUD + 独立前端页面,复用基础数据表和提取公共前端组件。不侵入现有细胞存储代码。
|
||
|
||
**Tech Stack:** MySQL 5.7 · SQLAlchemy · Pydantic v2 · FastAPI · Vue 3 · Element Plus · Tailwind CSS 4 · TypeScript
|
||
|
||
**Design Spec:** `docs/superpowers/specs/2026-05-17-research-donation-module-design.md`
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
### New Files (Backend)
|
||
- `docs/sql/research_donation_tables.sql` — 6 张新建表 DDL
|
||
- `backend/app/models/research_donation.py` — 6 个 SQLAlchemy Model(独立文件避免 models.py 过大)
|
||
- `backend/app/schemas/research_donation.py` — Pydantic schemas(注意:细胞存储没有独立 schema 文件,但科研捐赠新建一个更清晰)
|
||
- `backend/app/crud/research_donation.py` — CRUD/业务逻辑
|
||
- `backend/app/api/research_donation.py` — API 路由
|
||
|
||
### Modified Files (Backend)
|
||
- `backend/app/models/__init__.py` — import 新 models
|
||
- `backend/app/api/__init__.py` — 注册新路由
|
||
- `backend/app/main.py` — include 新路由
|
||
|
||
### New Files (Frontend)
|
||
- `web-admin/src/api/research-donation.ts` — API 模块
|
||
- `web-admin/src/views/research-donation/list/index.vue` — 捐赠协议列表页
|
||
- `web-admin/src/views/research-donation/list/DonationFormDialog.vue` — 新增/编辑弹窗
|
||
- `web-admin/src/views/research-donation/list/DonationViewDialog.vue` — 查看详情弹窗
|
||
- `web-admin/src/views/research-donation/list/DonationAuditDialog.vue` — 审核弹窗
|
||
- `web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue` — 状态变更弹窗
|
||
- `web-admin/src/views/research-donation/audit/index.vue` — 审核管理页
|
||
|
||
### Modified Files (Frontend)
|
||
- `web-admin/src/router/modules/sqxp.ts` — 新增科研捐赠菜单路由
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue` — 增加「转科研」操作
|
||
|
||
### Reference Files (DO NOT MODIFY — 用于参照模式)
|
||
- `backend/app/models/models.py:707-937` — 细胞存储 Model 定义
|
||
- `backend/app/api/collection_protocol.py` — 细胞存储 API 路由模式
|
||
- `backend/app/crud/collection_protocol.py` — 细胞存储 CRUD 模式
|
||
- `web-admin/src/views/cell-storage/protocol/index.vue` — 协议列表页模式
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue` — 表单弹窗模式
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue` — 查看弹窗模式
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue` — 审核弹窗模式
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue` — 状态变更弹窗模式
|
||
- `web-admin/src/views/cell-storage/protocol-audit/index.vue` — 审核管理页模式
|
||
- `web-admin/src/api/collection-protocol.ts` — API 模块模式
|
||
- `web-admin/src/router/modules/sqxp.ts:204-252` — 细胞存储路由注册
|
||
|
||
---
|
||
|
||
## Task 1: SQL 建表脚本
|
||
|
||
**Files:**
|
||
- Create: `docs/sql/research_donation_tables.sql`
|
||
|
||
**Reference:** `docs/sql/qc_item_param_and_task_result.sql`(SQL 格式参考),`backend/app/models/models.py:707-937`(字段定义参考)
|
||
|
||
- [ ] **Step 1: 编写 6 张表的 DDL**
|
||
|
||
创建 `docs/sql/research_donation_tables.sql`,包含以下 6 张表的 `CREATE TABLE IF NOT EXISTS` 语句:
|
||
|
||
```sql
|
||
-- ============================================================
|
||
-- 科研捐赠管理模块 — 建表脚本
|
||
-- ============================================================
|
||
|
||
-- 1. 科研捐赠主表
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`donation_no` varchar(64) NOT NULL COMMENT '捐赠协议编号',
|
||
`donation_status` varchar(16) NOT NULL DEFAULT 'dsx' COMMENT '状态 dsx待生效 sxz生效中 ygq已过期 yzf已作废 zckc转细胞存储',
|
||
`donation_date` char(8) DEFAULT NULL COMMENT '捐赠日期YYYYMMDD',
|
||
`source_type` varchar(20) NOT NULL DEFAULT 'direct' COMMENT '来源类型 direct独立新建 from_storage从细胞存储转来',
|
||
`source_protocol_id` varchar(64) DEFAULT NULL COMMENT '来源协议ID(from_storage时填源细胞存储协议ID)',
|
||
`consent_form_urls` text DEFAULT NULL COMMENT '知情同意书附件URL(逗号分隔,必填)',
|
||
`attachment_urls` text DEFAULT NULL COMMENT '其他附件URL(逗号分隔)',
|
||
`collector_user_id` varchar(64) DEFAULT NULL COMMENT '采集专员用户ID',
|
||
`effective_time` datetime DEFAULT NULL COMMENT '生效时间',
|
||
`effective_desc` varchar(512) DEFAULT NULL COMMENT '生效描述',
|
||
`expire_time` datetime DEFAULT NULL COMMENT '过期时间',
|
||
`expire_desc` varchar(512) DEFAULT NULL COMMENT '过期描述',
|
||
`void_time` datetime DEFAULT NULL COMMENT '作废时间',
|
||
`void_desc` varchar(512) DEFAULT NULL COMMENT '作废描述',
|
||
`to_storage_time` datetime DEFAULT NULL COMMENT '转存储时间',
|
||
`to_storage_desc` varchar(512) DEFAULT NULL COMMENT '转存储描述',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注信息',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rd_tenant_status` (`tenant_no`, `donation_status`, `del_flag`),
|
||
KEY `idx_rd_collector` (`collector_user_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠主表';
|
||
|
||
-- 2. 科研捐赠审核记录
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation_audit` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`research_donation_id` varchar(64) NOT NULL COMMENT '科研捐赠ID',
|
||
`auditor_user_id` varchar(64) DEFAULT NULL COMMENT '审核人',
|
||
`audit_remark` varchar(1000) DEFAULT NULL COMMENT '审核备注',
|
||
`audit_status` varchar(16) NOT NULL COMMENT '审核状态 shtg审核通过 shth审核驳回',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rda_donation` (`research_donation_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠审核记录';
|
||
|
||
-- 3. 科研捐赠当事人信息
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation_party` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`research_donation_id` varchar(64) NOT NULL COMMENT '科研捐赠ID',
|
||
`customer_id` varchar(64) DEFAULT NULL COMMENT '客户ID',
|
||
`mother_name` varchar(100) DEFAULT NULL COMMENT '母亲姓名',
|
||
`mother_phone` varchar(32) DEFAULT NULL COMMENT '母亲电话',
|
||
`mother_id_card` varchar(32) DEFAULT NULL COMMENT '母亲身份证号',
|
||
`mother_birth_ym` char(6) DEFAULT NULL COMMENT '母亲出生年月YYYYMM',
|
||
`mother_ethnic` varchar(32) DEFAULT NULL COMMENT '母亲民族',
|
||
`mother_address` varchar(512) DEFAULT NULL COMMENT '母亲地址',
|
||
`father_name` varchar(100) DEFAULT NULL COMMENT '父亲姓名',
|
||
`father_phone` varchar(32) DEFAULT NULL COMMENT '父亲电话',
|
||
`father_id_card` varchar(32) DEFAULT NULL COMMENT '父亲身份证号',
|
||
`newborn_name` varchar(100) DEFAULT NULL COMMENT '新生儿姓名',
|
||
`newborn_id_card` varchar(32) DEFAULT NULL COMMENT '新生儿身份证号',
|
||
`newborn_delivery_time` datetime DEFAULT NULL COMMENT '新生儿分娩时间',
|
||
`newborn_sex` char(1) DEFAULT NULL COMMENT '新生儿性别',
|
||
`delivery_mode` varchar(16) DEFAULT NULL COMMENT '分娩方式',
|
||
`birth_order` int(11) DEFAULT NULL COMMENT '出生顺序',
|
||
`newborn_weight_gram` int(11) DEFAULT NULL COMMENT '新生儿体重(克)',
|
||
`apgar_1min` decimal(4,1) DEFAULT NULL COMMENT 'Apgar 1分钟评分',
|
||
`apgar_5min` decimal(4,1) DEFAULT NULL COMMENT 'Apgar 5分钟评分',
|
||
`collector_user_id` varchar(64) DEFAULT NULL COMMENT '采集专员用户ID',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rdp_donation` (`research_donation_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠当事人信息';
|
||
|
||
-- 4. 科研捐赠采集信息
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation_collection` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`research_donation_id` varchar(64) NOT NULL COMMENT '科研捐赠ID',
|
||
`region_id` varchar(64) DEFAULT NULL COMMENT '区域ID',
|
||
`hospital_id` varchar(64) DEFAULT NULL COMMENT '医院ID',
|
||
`medical_staff` varchar(200) DEFAULT NULL COMMENT '医护人员',
|
||
`sales_user_id` varchar(64) DEFAULT NULL COMMENT '销售人员用户ID',
|
||
`sample_collect_time` datetime DEFAULT NULL COMMENT '样本采集时间',
|
||
`maternal_blood_collect_time` datetime DEFAULT NULL COMMENT '母血采集时间',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rdc_donation` (`research_donation_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠采集信息';
|
||
|
||
-- 5. 科研捐赠样本信息(无金额字段)
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation_sample` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`research_donation_id` varchar(64) NOT NULL COMMENT '科研捐赠ID',
|
||
`sample_type_id` varchar(64) DEFAULT NULL COMMENT '样本类型ID',
|
||
`sample_code_id` varchar(64) DEFAULT NULL COMMENT '样本编码ID',
|
||
`sample_code` varchar(32) DEFAULT NULL COMMENT '样本编码',
|
||
`collect_amount` decimal(18,4) DEFAULT NULL COMMENT '采集量',
|
||
`unit_id` varchar(64) DEFAULT NULL COMMENT '单位ID',
|
||
`storage_years` int(11) DEFAULT NULL COMMENT '存储年限',
|
||
`sample_status` varchar(16) DEFAULT NULL COMMENT '样本状态',
|
||
`storage_in_date` char(8) DEFAULT NULL COMMENT '入库日期YYYYMMDD',
|
||
`detect_start_date` char(8) DEFAULT NULL COMMENT '检测开始日期YYYYMMDD',
|
||
`void_date` char(8) DEFAULT NULL COMMENT '作废日期YYYYMMDD',
|
||
`storage_expire_date` char(8) DEFAULT NULL COMMENT '存储到期日期YYYYMMDD',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rds_donation` (`research_donation_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠样本信息';
|
||
|
||
-- 6. 科研捐赠样本运输
|
||
CREATE TABLE IF NOT EXISTS `biz_research_donation_sample_transport` (
|
||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||
`research_donation_id` varchar(64) NOT NULL COMMENT '科研捐赠ID',
|
||
`sample_id` varchar(64) NOT NULL COMMENT '样本ID',
|
||
`transport_type` varchar(16) NOT NULL COMMENT 'kdys快递运输 ryxd人员携带',
|
||
`express_company_id` varchar(64) DEFAULT NULL COMMENT '快递公司ID',
|
||
`waybill_no` varchar(128) DEFAULT NULL COMMENT '快递单号',
|
||
`dest_site_id` varchar(64) DEFAULT NULL COMMENT '目的站点ID',
|
||
`express_fee` decimal(18,2) DEFAULT NULL COMMENT '快递费用(元)',
|
||
`eta_time` datetime DEFAULT NULL COMMENT '预计到达时间',
|
||
`carrier_name` varchar(100) DEFAULT NULL COMMENT '携带人员姓名',
|
||
`carrier_phone` varchar(32) DEFAULT NULL COMMENT '携带人员联系电话',
|
||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_rdst_donation` (`research_donation_id`, `del_flag`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科研捐赠样本运输';
|
||
```
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add docs/sql/research_donation_tables.sql
|
||
git commit -m "feat: 科研捐赠管理模块建表脚本"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 2: 后端 Model 定义
|
||
|
||
**Files:**
|
||
- Create: `backend/app/models/research_donation.py`
|
||
- Modify: `backend/app/models/__init__.py`
|
||
|
||
**Reference:** `backend/app/models/models.py:707-937`(细胞存储 Model 定义模式)
|
||
|
||
- [ ] **Step 1: 创建 research_donation.py Model 文件**
|
||
|
||
创建 `backend/app/models/research_donation.py`,包含 6 个 Model 类。模式完全对齐 `models.py` 中的细胞存储 Model(Column 定义风格、公共字段、default 值):
|
||
|
||
```python
|
||
"""科研捐赠管理 — 数据模型"""
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import Column, DateTime, Integer, Numeric, SmallInteger, String
|
||
from sqlalchemy.ext.declarative import declarative_base
|
||
|
||
from app.models.base import Base
|
||
|
||
|
||
class BizResearchDonation(Base):
|
||
"""科研捐赠主表 biz_research_donation"""
|
||
|
||
__tablename__ = "biz_research_donation"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
donation_no = Column(String(64), nullable=False, comment="捐赠协议编号")
|
||
donation_status = Column(String(16), nullable=False, default="dsx", comment="状态 dsx/sxz/ygq/yzf/zckc")
|
||
donation_date = Column(String(8), nullable=True, comment="捐赠日期YYYYMMDD")
|
||
source_type = Column(String(20), nullable=False, default="direct", comment="来源类型 direct/from_storage")
|
||
source_protocol_id = Column(String(64), nullable=True, comment="来源协议ID")
|
||
consent_form_urls = Column(String(4000), nullable=True, comment="知情同意书附件URL(逗号分隔)")
|
||
attachment_urls = Column(String(4000), nullable=True, comment="其他附件URL(逗号分隔)")
|
||
collector_user_id = Column(String(64), nullable=True, comment="采集专员用户ID")
|
||
effective_time = Column(DateTime, nullable=True, comment="生效时间")
|
||
effective_desc = Column(String(512), nullable=True, comment="生效描述")
|
||
expire_time = Column(DateTime, nullable=True, comment="过期时间")
|
||
expire_desc = Column(String(512), nullable=True, comment="过期描述")
|
||
void_time = Column(DateTime, nullable=True, comment="作废时间")
|
||
void_desc = Column(String(512), nullable=True, comment="作废描述")
|
||
to_storage_time = Column(DateTime, nullable=True, comment="转存储时间")
|
||
to_storage_desc = Column(String(512), nullable=True, comment="转存储描述")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注信息")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
|
||
|
||
class BizResearchDonationAudit(Base):
|
||
"""科研捐赠审核记录 biz_research_donation_audit"""
|
||
|
||
__tablename__ = "biz_research_donation_audit"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
research_donation_id = Column(String(64), nullable=False, comment="科研捐赠ID")
|
||
auditor_user_id = Column(String(64), nullable=True, comment="审核人")
|
||
audit_remark = Column(String(1000), nullable=True, comment="审核备注")
|
||
audit_status = Column(String(16), nullable=False, comment="审核状态 shtg/shth")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
|
||
|
||
class BizResearchDonationParty(Base):
|
||
"""科研捐赠当事人信息 biz_research_donation_party"""
|
||
|
||
__tablename__ = "biz_research_donation_party"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
research_donation_id = Column(String(64), nullable=False, comment="科研捐赠ID")
|
||
customer_id = Column(String(64), nullable=True, comment="客户ID")
|
||
mother_name = Column(String(100), nullable=True, comment="母亲姓名")
|
||
mother_phone = Column(String(32), nullable=True, comment="母亲电话")
|
||
mother_id_card = Column(String(32), nullable=True, comment="母亲身份证号")
|
||
mother_birth_ym = Column(String(6), nullable=True, comment="母亲出生年月YYYYMM")
|
||
mother_ethnic = Column(String(32), nullable=True, comment="母亲民族")
|
||
mother_address = Column(String(512), nullable=True, comment="母亲地址")
|
||
father_name = Column(String(100), nullable=True, comment="父亲姓名")
|
||
father_phone = Column(String(32), nullable=True, comment="父亲电话")
|
||
father_id_card = Column(String(32), nullable=True, comment="父亲身份证号")
|
||
newborn_name = Column(String(100), nullable=True, comment="新生儿姓名")
|
||
newborn_id_card = Column(String(32), nullable=True, comment="新生儿身份证号")
|
||
newborn_delivery_time = Column(DateTime, nullable=True, comment="新生儿分娩时间")
|
||
newborn_sex = Column(String(1), nullable=True, comment="新生儿性别")
|
||
delivery_mode = Column(String(16), nullable=True, comment="分娩方式")
|
||
birth_order = Column(Integer, nullable=True, comment="出生顺序")
|
||
newborn_weight_gram = Column(Integer, nullable=True, comment="新生儿体重(克)")
|
||
apgar_1min = Column(Numeric(4, 1), nullable=True, comment="Apgar 1分钟评分")
|
||
apgar_5min = Column(Numeric(4, 1), nullable=True, comment="Apgar 5分钟评分")
|
||
collector_user_id = Column(String(64), nullable=True, comment="采集专员用户ID")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
|
||
|
||
class BizResearchDonationCollection(Base):
|
||
"""科研捐赠采集信息 biz_research_donation_collection"""
|
||
|
||
__tablename__ = "biz_research_donation_collection"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
research_donation_id = Column(String(64), nullable=False, comment="科研捐赠ID")
|
||
region_id = Column(String(64), nullable=True, comment="区域ID")
|
||
hospital_id = Column(String(64), nullable=True, comment="医院ID")
|
||
medical_staff = Column(String(200), nullable=True, comment="医护人员")
|
||
sales_user_id = Column(String(64), nullable=True, comment="销售人员用户ID")
|
||
sample_collect_time = Column(DateTime, nullable=True, comment="样本采集时间")
|
||
maternal_blood_collect_time = Column(DateTime, nullable=True, comment="母血采集时间")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
|
||
|
||
class BizResearchDonationSample(Base):
|
||
"""科研捐赠样本信息 biz_research_donation_sample(无金额字段)"""
|
||
|
||
__tablename__ = "biz_research_donation_sample"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
research_donation_id = Column(String(64), nullable=False, comment="科研捐赠ID")
|
||
sample_type_id = Column(String(64), nullable=True, comment="样本类型ID")
|
||
sample_code_id = Column(String(64), nullable=True, comment="样本编码ID")
|
||
sample_code = Column(String(32), nullable=True, comment="样本编码")
|
||
collect_amount = Column(Numeric(18, 4), nullable=True, comment="采集量")
|
||
unit_id = Column(String(64), nullable=True, comment="单位ID")
|
||
storage_years = Column(Integer, nullable=True, comment="存储年限")
|
||
sample_status = Column(String(16), nullable=True, comment="样本状态")
|
||
storage_in_date = Column(String(8), nullable=True, comment="入库日期YYYYMMDD")
|
||
detect_start_date = Column(String(8), nullable=True, comment="检测开始日期YYYYMMDD")
|
||
void_date = Column(String(8), nullable=True, comment="作废日期YYYYMMDD")
|
||
storage_expire_date = Column(String(8), nullable=True, comment="存储到期日期YYYYMMDD")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
|
||
|
||
class BizResearchDonationSampleTransport(Base):
|
||
"""科研捐赠样本运输 biz_research_donation_sample_transport"""
|
||
|
||
__tablename__ = "biz_research_donation_sample_transport"
|
||
|
||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||
research_donation_id = Column(String(64), nullable=False, comment="科研捐赠ID")
|
||
sample_id = Column(String(64), nullable=False, comment="样本ID")
|
||
transport_type = Column(String(16), nullable=False, comment="kdys快递运输 ryxd人员携带")
|
||
express_company_id = Column(String(64), nullable=True, comment="快递公司ID")
|
||
waybill_no = Column(String(128), nullable=True, comment="快递单号")
|
||
dest_site_id = Column(String(64), nullable=True, comment="目的站点ID")
|
||
express_fee = Column(Numeric(18, 2), nullable=True, comment="快递费用(元)")
|
||
eta_time = Column(DateTime, nullable=True, comment="预计到达时间")
|
||
carrier_name = Column(String(100), nullable=True, comment="携带人员姓名")
|
||
carrier_phone = Column(String(32), nullable=True, comment="携带人员联系电话")
|
||
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
|
||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||
remark = Column(String(512), nullable=True, comment="备注")
|
||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||
```
|
||
|
||
- [ ] **Step 2: 在 models/__init__.py 中注册**
|
||
|
||
读取 `backend/app/models/__init__.py`,在 import 区增加:
|
||
|
||
```python
|
||
from app.models.research_donation import ( # noqa: F401
|
||
BizResearchDonation,
|
||
BizResearchDonationAudit,
|
||
BizResearchDonationParty,
|
||
BizResearchDonationCollection,
|
||
BizResearchDonationSample,
|
||
BizResearchDonationSampleTransport,
|
||
)
|
||
```
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add backend/app/models/research_donation.py backend/app/models/__init__.py
|
||
git commit -m "feat: 科研捐赠管理模块 — SQLAlchemy Model 定义"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3: 后端 CRUD 层
|
||
|
||
**Files:**
|
||
- Create: `backend/app/crud/research_donation.py`
|
||
|
||
**Reference:** `backend/app/crud/collection_protocol.py`(CRUD 模式参考,但大幅简化:无缴费逻辑)
|
||
|
||
这是后端最核心的文件,包含以下功能模块:
|
||
|
||
### 功能清单
|
||
|
||
1. **列表查询** `list_donations(db, tenant_no, page, page_size, keyword, donation_status, collector_user_id)` — 分页、关键字搜索(编号/母亲姓名)、状态过滤、数据权限
|
||
2. **详情查询** `get_donation_detail(db, donation_id)` — 主表 + 所有子资源(当事人、采集、样本+运输)
|
||
3. **创建** `create_donation(db, data, tenant_no, user_id)` — 主表 + 子资源整体创建
|
||
4. **更新** `update_donation(db, donation_id, data, tenant_no, user_id)` — 子资源 upsert(先删后插)
|
||
5. **删除** `delete_donation(db, donation_id, tenant_no)` — 软删除主表 + 所有子资源
|
||
6. **批量删除** `batch_delete_donations(db, ids, tenant_no)`
|
||
7. **审核列表** `list_audits(db, tenant_no, page, page_size, keyword, audit_status)`
|
||
8. **提交审核** `submit_audit(db, donation_id, audit_status, audit_remark, user_id, tenant_no)`
|
||
9. **状态变更** `change_status(db, donation_id, new_status, desc, user_id, tenant_no)` — 含状态机校验
|
||
10. **从细胞存储转化** `convert_from_storage(db, protocol_id, tenant_no, user_id)` — 读源协议 → 创建捐赠记录
|
||
11. **转为细胞存储** `convert_to_storage(db, donation_id, tenant_no, user_id)` — 读捐赠 → 创建协议记录
|
||
|
||
### 关键模式(参照 collection_protocol.py)
|
||
|
||
- UUID 生成:`import uuid; uuid.uuid4().hex`
|
||
- camelCase → snake_case 转换:参照 `_normalize_fields()` 模式
|
||
- 附件处理:`urls_to_attachment_list()` 复用
|
||
- 子资源 upsert:先删除旧数据 `del_flag=1`,再批量 insert 新数据
|
||
- 租户隔离:所有查询加 `tenant_no` 过滤
|
||
- 软删除:设置 `del_flag=1`,查询默认 `del_flag=0`
|
||
|
||
### 状态机
|
||
|
||
```
|
||
合法转换:
|
||
dsx → sxz(生效)
|
||
dsx → yzf(作废)
|
||
sxz → ygq(过期)
|
||
sxz → yzf(作废)
|
||
sxz → zckc(转细胞存储)
|
||
```
|
||
|
||
> 注意:审核通过后才允许从 dsx → sxz。审核驳回后状态不变。
|
||
|
||
**实现指导:** 参照 `backend/app/crud/collection_protocol.py` 的整体结构,但去掉所有 payment/payment_detail/storage_cert 相关函数。CRUD 文件预计 600-800 行。
|
||
|
||
- [ ] **Step 1: 创建 crud/research_donation.py**
|
||
|
||
编写完整的 CRUD 文件,包含上述 11 个功能。关键点:
|
||
- `create_donation` 接收一个包含所有子资源的 dict,在事务中创建主表 + party + collection + samples + transports
|
||
- `update_donation` 对子资源使用"软删旧 + 插新"的 upsert 模式
|
||
- `convert_from_storage` 读取 `BizCollectionProtocol` 及其子资源,映射到捐赠字段
|
||
- `convert_to_storage` 反向操作
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add backend/app/crud/research_donation.py
|
||
git commit -m "feat: 科研捐赠管理模块 — CRUD 业务逻辑层"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 4: 后端 API 路由
|
||
|
||
**Files:**
|
||
- Create: `backend/app/api/research_donation.py`
|
||
- Modify: `backend/app/api/__init__.py`
|
||
- Modify: `backend/app/main.py`
|
||
|
||
**Reference:** `backend/app/api/collection_protocol.py`(API 路由模式)
|
||
|
||
- [ ] **Step 1: 创建 API 路由文件**
|
||
|
||
创建 `backend/app/api/research_donation.py`:
|
||
|
||
```python
|
||
"""科研捐赠管理 — API 路由"""
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from pydantic import BaseModel, Field
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.database import get_db
|
||
from app.core.menu_guard import require_admin_or_super_admin
|
||
from app.core.response import error_response, success_response
|
||
from app.crud import research_donation as rd_svc
|
||
from app.models.models import SysUser
|
||
|
||
router = APIRouter(prefix="/web/admin", tags=["科研捐赠管理"])
|
||
```
|
||
|
||
端点定义(参照 collection_protocol.py 的装饰器模式):
|
||
|
||
| 端点 | 方法 | 路径 | 说明 |
|
||
|---|---|---|---|
|
||
| 列表 | GET | `/research-donations` | page/pageSize/keyword/donationStatus 参数 |
|
||
| 详情 | GET | `/research-donations/{id}` | 返回主表 + 所有子资源 |
|
||
| 创建 | POST | `/research-donations` | 整体提交含子资源 |
|
||
| 更新 | PUT | `/research-donations/{id}` | 整体提交含子资源 |
|
||
| 删除 | DELETE | `/research-donations/{id}` | 软删除 |
|
||
| 批量删除 | POST | `/research-donations/batch-delete` | body: `{ids: []}` |
|
||
| 审核列表 | GET | `/research-donations/audits` | page/pageSize/keyword/auditStatus |
|
||
| 提交审核 | POST | `/research-donations/{id}/audit` | body: `{auditStatus, auditRemark}` |
|
||
| 状态变更 | POST | `/research-donations/{id}/status-change` | body: `{donationStatus, desc, time}` |
|
||
| 从存储转化 | POST | `/research-donations/convert-from-storage` | body: `{protocolId}` |
|
||
| 转为存储 | POST | `/research-donations/{id}/convert-to-storage` | body: `{desc}` |
|
||
|
||
每个端点的模式:
|
||
```python
|
||
@router.get("/research-donations")
|
||
def list_research_donations(
|
||
page: int = Query(1, ge=1),
|
||
pageSize: int = Query(20, ge=1, le=200),
|
||
keyword: Optional[str] = Query(None),
|
||
donationStatus: Optional[str] = Query(None),
|
||
db: Session = Depends(get_db),
|
||
user: dict = Depends(require_admin_or_super_admin),
|
||
):
|
||
tenant_no = _get_tenant_no(db, user)
|
||
collector_user_id = _get_current_user_id(user)
|
||
result = rd_svc.list_donations(db, tenant_no, page, pageSize, keyword, donationStatus, collector_user_id)
|
||
return success_response(result)
|
||
```
|
||
|
||
- [ ] **Step 2: 注册路由**
|
||
|
||
在 `backend/app/api/__init__.py` 增加:
|
||
```python
|
||
from app.api.research_donation import router as research_donation_router
|
||
```
|
||
并在 `__all__` 列表增加 `"research_donation_router"`。
|
||
|
||
在 `backend/app/main.py` 的 import 行和 include_router 行增加 `research_donation_router`。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add backend/app/api/research_donation.py backend/app/api/__init__.py backend/app/main.py
|
||
git commit -m "feat: 科研捐赠管理模块 — API 路由定义与注册"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 5: 前端路由与 API 模块
|
||
|
||
**Files:**
|
||
- Create: `web-admin/src/api/research-donation.ts`
|
||
- Modify: `web-admin/src/router/modules/sqxp.ts`
|
||
|
||
**Reference:** `web-admin/src/api/collection-protocol.ts`(API 模式),`web-admin/src/router/modules/sqxp.ts:204-252`(路由注册模式)
|
||
|
||
- [ ] **Step 1: 创建 API 模块**
|
||
|
||
创建 `web-admin/src/api/research-donation.ts`:
|
||
|
||
```typescript
|
||
import request from '@/utils/http'
|
||
|
||
type PageList<T> = {
|
||
list: T[]
|
||
total: number
|
||
page: number
|
||
pageSize: number
|
||
totalPages: number
|
||
}
|
||
|
||
export type ResearchDonationRow = Record<string, unknown>
|
||
|
||
// 列表
|
||
export function fetchResearchDonationList(params?: {
|
||
page?: number
|
||
pageSize?: number
|
||
keyword?: string
|
||
donationStatus?: string
|
||
}) {
|
||
return request.get<PageList<ResearchDonationRow>>({
|
||
url: '/web/admin/research-donations',
|
||
params
|
||
})
|
||
}
|
||
|
||
// 详情
|
||
export function fetchResearchDonationDetail(id: string) {
|
||
return request.get<{
|
||
donation: Record<string, unknown>
|
||
party: Record<string, unknown> | null
|
||
collection: Record<string, unknown> | null
|
||
samples: Array<Record<string, unknown>> | null
|
||
attachments?: Array<Record<string, unknown>>
|
||
}>({
|
||
url: `/web/admin/research-donations/${id}`
|
||
})
|
||
}
|
||
|
||
// 创建
|
||
export function fetchCreateResearchDonation(data: Record<string, unknown>) {
|
||
return request.post({
|
||
url: '/web/admin/research-donations',
|
||
data,
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 更新
|
||
export function fetchUpdateResearchDonation(id: string, data: Record<string, unknown>) {
|
||
return request.put({
|
||
url: `/web/admin/research-donations/${id}`,
|
||
data,
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 删除
|
||
export function fetchDeleteResearchDonation(id: string) {
|
||
return request.del({
|
||
url: `/web/admin/research-donations/${id}`,
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 批量删除
|
||
export function fetchBatchDeleteResearchDonation(ids: string[]) {
|
||
return request.post({
|
||
url: '/web/admin/research-donations/batch-delete',
|
||
data: { ids },
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 审核列表
|
||
export function fetchResearchDonationAuditList(params?: {
|
||
page?: number
|
||
pageSize?: number
|
||
keyword?: string
|
||
auditStatus?: string
|
||
}) {
|
||
return request.get<PageList<Record<string, unknown>>>({
|
||
url: '/web/admin/research-donations/audits',
|
||
params
|
||
})
|
||
}
|
||
|
||
// 提交审核
|
||
export function fetchSubmitDonationAudit(
|
||
donationId: string,
|
||
data: { auditStatus: string; auditRemark?: string },
|
||
options?: { showSuccessMessage?: boolean }
|
||
) {
|
||
return request.post({
|
||
url: `/web/admin/research-donations/${donationId}/audit`,
|
||
data,
|
||
showSuccessMessage: options?.showSuccessMessage ?? true
|
||
})
|
||
}
|
||
|
||
// 状态变更
|
||
export function fetchDonationStatusChange(
|
||
donationId: string,
|
||
data: { donationStatus: string; desc?: string; time?: string }
|
||
) {
|
||
return request.post({
|
||
url: `/web/admin/research-donations/${donationId}/status-change`,
|
||
data,
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 从细胞存储转化
|
||
export function fetchConvertFromStorage(protocolId: string) {
|
||
return request.post({
|
||
url: '/web/admin/research-donations/convert-from-storage',
|
||
data: { protocolId },
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
|
||
// 转为细胞存储
|
||
export function fetchConvertToStorage(
|
||
donationId: string,
|
||
data?: { desc?: string }
|
||
) {
|
||
return request.post({
|
||
url: `/web/admin/research-donations/${donationId}/convert-to-storage`,
|
||
data: data || {},
|
||
showSuccessMessage: true
|
||
})
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 添加路由定义**
|
||
|
||
在 `web-admin/src/router/modules/sqxp.ts` 的细胞存储路由组之后(约 line 252),增加:
|
||
|
||
```typescript
|
||
{
|
||
id: '60000000000000000000000000000001',
|
||
name: 'ResearchDonation',
|
||
path: '/research-donation',
|
||
component: '/index/index',
|
||
meta: {
|
||
title: '科研捐赠管理',
|
||
icon: 'ri:hand-heart-line',
|
||
keepAlive: false,
|
||
roles: ['admin']
|
||
},
|
||
children: [
|
||
{
|
||
id: '60000000000000000000000000000002',
|
||
name: 'ResearchDonationList',
|
||
path: 'list',
|
||
component: '/research-donation/list/index',
|
||
meta: { title: '捐赠协议单', keepAlive: true }
|
||
},
|
||
{
|
||
id: '60000000000000000000000000000003',
|
||
name: 'ResearchDonationAudit',
|
||
path: 'audit',
|
||
component: '/research-donation/audit/index',
|
||
meta: { title: '捐赠协议审核', keepAlive: true }
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/api/research-donation.ts web-admin/src/router/modules/sqxp.ts
|
||
git commit -m "feat: 科研捐赠管理模块 — 前端 API 模块与路由定义"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 6: 前端 — 捐赠协议列表页
|
||
|
||
**Files:**
|
||
- Create: `web-admin/src/views/research-donation/list/index.vue`
|
||
|
||
**Reference:** `web-admin/src/views/cell-storage/protocol/index.vue`(列表页模式)
|
||
|
||
**实现指导:**
|
||
|
||
参照 `cell-storage/protocol/index.vue` 的完整模式创建列表页,关键差异:
|
||
|
||
1. **搜索条件**:关键字(编号/母亲姓名)+ 状态下拉(dsx/sxz/ygq/yzf/zckc)
|
||
2. **表格列**:捐赠编号、捐赠日期、母亲姓名、状态、来源类型、采集专员、创建时间、操作
|
||
3. **操作按钮**:新增、批量删除、导出(可选)
|
||
4. **行操作**:查看、编辑、审核、状态变更、删除
|
||
5. **状态标签颜色**:dsx=info, sxz=success, ygq=warning, yzf=danger, zckc=primary
|
||
6. **无缴费相关按钮和筛选**(与细胞存储的关键区别)
|
||
|
||
组件导入:DonationFormDialog, DonationViewDialog, DonationAuditDialog, DonationStatusChangeDialog
|
||
|
||
- [ ] **Step 1: 创建列表页**
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/views/research-donation/list/index.vue
|
||
git commit -m "feat: 科研捐赠管理模块 — 捐赠协议列表页"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 7: 前端 — 新增/编辑弹窗
|
||
|
||
**Files:**
|
||
- Create: `web-admin/src/views/research-donation/list/DonationFormDialog.vue`
|
||
|
||
**Reference:** `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue`(表单弹窗模式)
|
||
|
||
**实现指导:**
|
||
|
||
参照 ProtocolFormDialog.vue 的模式,5 个 Tab:
|
||
|
||
| Tab | 字段 | 说明 |
|
||
|---|---|---|
|
||
| 基本信息 | donationNo, donationDate, sourceType, remark | 编号自动/手动、日期、来源类型只读 |
|
||
| 当事人信息 | 母亲/父亲/新生儿全部字段 | 与细胞存储一致 |
|
||
| 采集信息 | 区域/医院/采集人/时间 | 与细胞存储一致 |
|
||
| 样本信息 | 样本类型/编码/数量/储存年限(**无 price 字段**) | 可多行添加删除 |
|
||
| 附件 | 知情同意书(**必传**)+ 其他附件 | 知情同意书独立上传区域 |
|
||
|
||
关键点:
|
||
- 宽度 `1200px`,`label-position="top"`
|
||
- 知情同意书 Tab 中分两个区域:必传的知情同意书 + 可选的其他附件
|
||
- 提交时校验 `consentFormUrls` 不能为空
|
||
- 样本 Tab 无金额/费用相关字段
|
||
|
||
- [ ] **Step 1: 创建表单弹窗组件**
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/views/research-donation/list/DonationFormDialog.vue
|
||
git commit -m "feat: 科研捐赠管理模块 — 新增/编辑弹窗(5 Tab)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 8: 前端 — 查看/审核/状态变更弹窗
|
||
|
||
**Files:**
|
||
- Create: `web-admin/src/views/research-donation/list/DonationViewDialog.vue`
|
||
- Create: `web-admin/src/views/research-donation/list/DonationAuditDialog.vue`
|
||
- Create: `web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue`
|
||
|
||
**Reference:**
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue`
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue`
|
||
- `web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue`
|
||
|
||
- [ ] **Step 1: 创建 DonationViewDialog.vue**
|
||
|
||
参照 ProtocolViewDialog.vue,5 个 Tab 只读展示。宽度 `1600px`。
|
||
|
||
- [ ] **Step 2: 创建 DonationAuditDialog.vue**
|
||
|
||
参照 ProtocolAuditDialog.vue。审核状态选择(shtg/shth)+ 备注。支持批量。
|
||
|
||
- [ ] **Step 3: 创建 DonationStatusChangeDialog.vue**
|
||
|
||
参照 ProtocolStatusChangeDialog.vue。支持的操作:
|
||
- 生效(dsx → sxz)
|
||
- 过期(sxz → ygq)
|
||
- 作废(dsx/sxz → yzf)
|
||
- **转细胞存储**(sxz → zckc)— 新增操作
|
||
|
||
每个操作有时间 + 描述字段。
|
||
|
||
- [ ] **Step 4: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/views/research-donation/list/DonationViewDialog.vue \
|
||
web-admin/src/views/research-donation/list/DonationAuditDialog.vue \
|
||
web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue
|
||
git commit -m "feat: 科研捐赠管理模块 — 查看/审核/状态变更弹窗"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 9: 前端 — 审核管理页
|
||
|
||
**Files:**
|
||
- Create: `web-admin/src/views/research-donation/audit/index.vue`
|
||
|
||
**Reference:** `web-admin/src/views/cell-storage/protocol-audit/index.vue`
|
||
|
||
- [ ] **Step 1: 创建审核管理页**
|
||
|
||
参照 protocol-audit/index.vue 模式:
|
||
- 列表展示待审核/已审核的捐赠记录
|
||
- 批量审核、批量生效、批量作废等操作
|
||
- 调用 `fetchResearchDonationAuditList` API
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/views/research-donation/audit/index.vue
|
||
git commit -m "feat: 科研捐赠管理模块 — 审核管理页"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 10: 细胞存储端 — 增加「转科研」操作
|
||
|
||
**Files:**
|
||
- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue`
|
||
|
||
**Reference:** 读取现有文件,在状态变更操作中增加 `zky`(转科研)选项
|
||
|
||
- [ ] **Step 1: 修改 ProtocolStatusChangeDialog.vue**
|
||
|
||
在现有的状态变更操作中增加「转科研」选项:
|
||
- 操作类型增加 `zky`
|
||
- 调用 `fetchConvertFromStorage(protocolId)` API(从 research-donation.ts 导入)
|
||
- 成功后刷新列表
|
||
|
||
- [ ] **Step 2: 提交**
|
||
|
||
```bash
|
||
git add web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue
|
||
git commit -m "feat: 细胞存储协议 — 增加转科研操作"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 11: 联调与验证
|
||
|
||
- [ ] **Step 1: 后端验证**
|
||
|
||
```bash
|
||
cd backend && source venv/bin/activate
|
||
python -c "from app.models.research_donation import *; print('Models OK')"
|
||
python -c "from app.api.research_donation import router; print('Router OK')"
|
||
```
|
||
|
||
- [ ] **Step 2: 前端验证**
|
||
|
||
```bash
|
||
cd web-admin && pnpm build
|
||
```
|
||
|
||
预期:构建通过,无 TS 错误。
|
||
|
||
- [ ] **Step 3: 数据库建表**
|
||
|
||
在开发环境执行 `docs/sql/research_donation_tables.sql`。
|
||
|
||
- [ ] **Step 4: 功能验证清单**
|
||
|
||
| 功能 | 验证方式 |
|
||
|------|---------|
|
||
| 列表展示 | 访问 `/#/research-donation/list` |
|
||
| 新增捐赠 | 填写 5 个 Tab,提交 |
|
||
| 知情同意书必填校验 | 不传知情同意书,提交应报错 |
|
||
| 编辑捐赠 | 修改后保存 |
|
||
| 查看详情 | 5 Tab 只读展示 |
|
||
| 删除(单个/批量) | 软删除 |
|
||
| 审核流程 | 审核页面操作 |
|
||
| 状态变更 | 生效/过期/作废 |
|
||
| 转细胞存储 | 捐赠 → 细胞存储协议 |
|
||
| 细胞存储转科研 | 细胞存储 → 科研捐赠 |
|