Compare commits
2 Commits
a492ea4091
...
199c547199
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
199c547199 | ||
|
|
73f32472f3 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,3 +21,5 @@ selenium-debug.log
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
.trae/
|
||||
@ -1,44 +0,0 @@
|
||||
---
|
||||
name: 输血系统
|
||||
description: 全局匹配
|
||||
---
|
||||
|
||||
```
|
||||
# 项目基础信息
|
||||
项目名称:输血系统
|
||||
项目源码名称:前端项目:blood_vue,后端项目:blood
|
||||
技术栈:
|
||||
前端:Vue3 + TypeScript + Element Plus + Vite + Pinia + Axios +scss
|
||||
后端:springboot + mybatis + redis
|
||||
数据库: sqlserver2008
|
||||
编码语言:中文注释,提示、弹窗文案使用中文
|
||||
|
||||
# 强制编码规则
|
||||
1. 组件统一使用 <script setup> 语法糖,禁止使用Options API;单文件组件结构顺序:script setup lang="ts" → template → style
|
||||
2. 变量命名:小驼峰;常量全大写下划线;组件名大驼峰;禁止拼音命名,业务字段使用业务英文缩写
|
||||
3. TS类型:所有接口入参、返回体、表单对象必须定义interface/type,禁止any;尽量使用readonly约束只读数据
|
||||
4. 样式:统一使用 scoped,优先使用Element Plus自带样式,自定义class采用BEM命名,禁止全局污染
|
||||
5. 导入:按顺序导入:vue内置包 → 第三方依赖 → 项目组件 → 工具函数 → 静态资源;统一使用import,不使用require
|
||||
6. 每次新增组件,需要增加组件注释说明,
|
||||
|
||||
# 接口请求规范(输血系统)
|
||||
1. 所有请求必须使用项目已封装axios实例,禁止直接原生axios;统一拦截器处理token、401、500
|
||||
2. 后端返回固定结构 {code:number, msg:string, data:any},code=200或者0代表成功;业务异常统一捕获提示
|
||||
3. 接口请求必须写TS类型定义,请求参数、返回data都要定义interface;增加注释说明接口用途、业务场景
|
||||
|
||||
|
||||
# 表单 & 校验(输血业务)
|
||||
1. Element Plus ElForm,表单校验规则抽离单独rules对象;必填项添加 required:true,提示文案友好
|
||||
2. 检验项目、标本类型、状态下拉选项,统一维护枚举常量,不要硬编码写死在template
|
||||
3. 保存、提交按钮:提交期间增加 loading 状态,防止重复提交;表单提交前先触发全部校验
|
||||
|
||||
# 业务与安全约束(医疗系统)
|
||||
1. 禁止:前端硬编码密钥、token、账号密码;禁止直接拼接SQL相关参数;禁止eval危险函数
|
||||
2. 弹窗提示:操作类动作(删除、取消、作废)必须增加确认弹窗;成功/失败给出toast提示
|
||||
|
||||
# AI输出约束
|
||||
1. 输出代码优先完整片段,不要省略关键逻辑;新增组件同时补充interface类型
|
||||
3. 给出代码后,附带简短说明:改动点、注意事项、需要后端配合的字段
|
||||
4. 不要生成多余无关代码,保持简洁;如存在多种方案,优先选择和现有项目一致的写法
|
||||
5. 如修改已有代码,先展示原有代码片段,再给出修改后的代码,标注改动位置
|
||||
```
|
||||
@ -1,47 +0,0 @@
|
||||
---
|
||||
name: blood-system
|
||||
description: 输血系统前端项目编码规范与业务约束。在编写、修改、审查输血系统(blood_vue)前端代码时自动应用,涵盖组件写法、TS类型、接口请求、表单校验、安全约束等规则。
|
||||
---
|
||||
|
||||
# 项目基础信息
|
||||
|
||||
- 项目名称:输血系统
|
||||
- 前端项目:blood_vue
|
||||
- 后端项目:blood
|
||||
- 技术栈:Vue3 + TypeScript + Element Plus + Vite + Pinia + Axios + scss
|
||||
- 后端:springboot + mybatis + redis
|
||||
- 数据库:sqlserver2008
|
||||
- 编码语言:中文注释,提示、弹窗文案使用中文
|
||||
|
||||
# 强制编码规则
|
||||
|
||||
1. 组件统一使用 `<script setup>` 语法糖,禁止使用 Options API;单文件组件结构顺序:script setup lang="ts" → template → style
|
||||
2. 变量命名:小驼峰;常量全大写下划线;组件名大驼峰;禁止拼音命名,业务字段使用业务英文缩写
|
||||
3. TS类型:所有接口入参、返回体、表单对象必须定义 interface/type,禁止 any;尽量使用 readonly 约束只读数据
|
||||
4. 样式:统一使用 scoped,优先使用 Element Plus 自带样式,自定义 class 采用 BEM 命名,禁止全局污染
|
||||
5. 导入:按顺序导入:vue内置包 → 第三方依赖 → 项目组件 → 工具函数 → 静态资源;统一使用 import,不使用 require
|
||||
6. 每次新增组件,需要增加组件注释说明
|
||||
|
||||
# 接口请求规范
|
||||
|
||||
1. 所有请求必须使用项目已封装 axios 实例,禁止直接原生 axios;统一拦截器处理 token、401、500
|
||||
2. 后端返回固定结构 `{code:number, msg:string, data:any}`,code=200 或 0 代表成功;业务异常统一捕获提示
|
||||
3. 接口请求必须写 TS 类型定义,请求参数、返回 data 都要定义 interface;增加注释说明接口用途、业务场景
|
||||
|
||||
# 表单 & 校验
|
||||
|
||||
1. Element Plus ElForm,表单校验规则抽离单独 rules 对象;必填项添加 required:true,提示文案友好
|
||||
2. 检验项目、标本类型、状态下拉选项,统一维护枚举常量,不要硬编码写死在 template
|
||||
3. 保存、提交按钮:提交期间增加 loading 状态,防止重复提交;表单提交前先触发全部校验
|
||||
|
||||
# 业务与安全约束
|
||||
|
||||
1. 禁止:前端硬编码密钥、token、账号密码;禁止直接拼接 SQL 相关参数;禁止 eval 危险函数
|
||||
2. 弹窗提示:操作类动作(删除、取消、作废)必须增加确认弹窗;成功/失败给出 toast 提示
|
||||
|
||||
# AI 输出约束
|
||||
|
||||
1. 输出代码优先完整片段,不要省略关键逻辑;新增组件同时补充 interface 类型
|
||||
2. 给出代码后,附带简短说明:改动点、注意事项、需要后端配合的字段
|
||||
3. 不要生成多余无关代码,保持简洁;如存在多种方案,优先选择和现有项目一致的写法
|
||||
4. 如修改已有代码,先展示原有代码片段,再给出修改后的代码,标注改动位置
|
||||
@ -31,3 +31,11 @@ export function queryDetail(query?: Object) {
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getHospitalList(query?: Object) {
|
||||
return request({
|
||||
url: "/bus/bloodbank/handInStorage/getHospitalList",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,16 +2,13 @@
|
||||
<div class="hand-in-storage">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" plain :icon="Search" @click="handleSearch">检索</el-button>
|
||||
<!-- <el-button type="primary" plain :icon="Search" @click="handleSearch">检索</el-button> -->
|
||||
<el-button type="success" plain :icon="Plus" @click="handleAdd">新增</el-button>
|
||||
<el-button type="warning" plain :icon="Fold" @click="handleSave" :loading="saveLoading">保存</el-button>
|
||||
<el-button type="info" plain :icon="Printer">打印</el-button>
|
||||
<el-button type="primary" plain :icon="Upload">导入</el-button>
|
||||
<!-- <el-button type="primary" plain :icon="Upload">导入</el-button> -->
|
||||
<el-button type="danger" plain :icon="Close" @click="handleClose">关闭</el-button>
|
||||
</div>
|
||||
<!-- <div class="toolbar-right">
|
||||
<el-checkbox v-model="printDetail">打印明细</el-checkbox>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
@ -31,7 +28,8 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="经手人">
|
||||
<SelectTable v-model:data="mainForm.handle_person" :tableData="userList" placeholder="经手人" />
|
||||
<SelectTable v-model:data="mainForm.handle_person" :tableData="userList"
|
||||
placeholder="经手人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@ -40,9 +38,10 @@
|
||||
<el-select v-model="mainForm.source_type" placeholder="血站" style="width: 80px"
|
||||
@change="handleSourceType">
|
||||
<el-option label="血站" value="1" />
|
||||
<el-option label="其它" value="2" />
|
||||
<el-option label="医院" value="2" />
|
||||
</el-select>
|
||||
<el-input v-model="mainForm.org" placeholder="请输入来源" style="flex: 1" />
|
||||
<SelectTable v-model:data="mainForm.org" :tableData="hospitalList" placeholder="请输入血液来源"
|
||||
style="flex: 1" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -210,13 +209,13 @@ import { Search, Plus, Fold, Printer, Upload, Close, Refresh } from '@element-pl
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { useCommonDict } from '@/hooks'
|
||||
import { getBloodBreed, saveData, queryList, queryDetail } from '@/api/blood/handInStorage'
|
||||
import { getBloodBreed, saveData, queryList, queryDetail, getHospitalList } from '@/api/blood/handInStorage'
|
||||
import BloodTypeStatTable from '@/components/BloodTypeStatTable/index.vue'
|
||||
|
||||
const userInfo = useUserStore()
|
||||
const { getServerTime, bloodBreed: bloodBreedList, getBloodList, userList, getUserData } = useCommonDict()
|
||||
|
||||
const printDetail = ref(false)
|
||||
|
||||
const cancelBlood = ref(false)
|
||||
const resetBloodAfterScan = ref(false)
|
||||
const saveLoading = ref(false)
|
||||
@ -274,9 +273,26 @@ const formatBloodBreed = (code: string) => {
|
||||
return item ? item.label : code
|
||||
}
|
||||
|
||||
const hospitalList = ref<any[]>([])
|
||||
|
||||
const fetchHospitalList = () => {
|
||||
getHospitalList({ sourceType: mainForm.value.source_type }).then((res: any) => {
|
||||
hospitalList.value = (res.rows || res.data || []).map((item: any) => ({
|
||||
label: item.hospitalName || item.name || item.label,
|
||||
value: item.hospitalCode || item.code || item.value
|
||||
}))
|
||||
// 默认选中第一项
|
||||
if (hospitalList.value.length > 0 && !mainForm.value.org) {
|
||||
mainForm.value.org = hospitalList.value[0].value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSourceType = () => {
|
||||
if (mainForm.value.source_type === '1') {
|
||||
mainForm.value.org = ''
|
||||
} else if (mainForm.value.source_type === '2') {
|
||||
fetchHospitalList()
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,6 +456,10 @@ const confirmSelect = () => {
|
||||
const main = res.data.xkInstorageMain || {}
|
||||
// 数据库存的是姓名(nick_name),回显时转换回用户代号以匹配下拉选项
|
||||
main.handle_person = userList.value.find(v => v.label === main.handle_person)?.value || main.handle_person
|
||||
// 数据库存的是医院名称,回显时转换回医院代号以匹配 SelectTable 选项
|
||||
if (main.source_type === '2' && main.org) {
|
||||
main.org = hospitalList.value.find(v => v.label === main.org)?.value || main.org
|
||||
}
|
||||
mainForm.value = main
|
||||
bloodList.value = res.data.xkBloodInfoList || []
|
||||
}
|
||||
@ -455,7 +475,7 @@ const handleAdd = () => {
|
||||
occur_date: time,
|
||||
handle_person: userInfo.name,
|
||||
source_type: '1',
|
||||
org: '',
|
||||
org: hospitalList.value.length > 0 ? hospitalList.value[0].value : '',
|
||||
out_date: time,
|
||||
remark: ''
|
||||
}
|
||||
@ -508,8 +528,7 @@ const handleSave = () => {
|
||||
org: mainForm.value.org,
|
||||
out_date: mainForm.value.out_date,
|
||||
occur_date: mainForm.value.occur_date,
|
||||
// 下拉选的是用户代号,入库时写入姓名(nick_name)
|
||||
handle_person: userList.value.find(v => v.value === mainForm.value.handle_person)?.label || mainForm.value.handle_person,
|
||||
handle_person: mainForm.value.handle_person,
|
||||
remark: mainForm.value.remark,
|
||||
total_money: totalMoney
|
||||
},
|
||||
@ -520,8 +539,12 @@ const handleSave = () => {
|
||||
saveLoading.value = true
|
||||
saveData(data).then((res: any) => {
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('保存成功,入库单号:' + res.data)
|
||||
mainForm.value.bill_no = res.data
|
||||
ElMessage.success(res.msg || '保存成功,入库单号:' + res.data)
|
||||
// 保存成功后清空界面
|
||||
cancelBlood.value = false
|
||||
handleAdd()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
}).finally(() => {
|
||||
saveLoading.value = false
|
||||
@ -543,6 +566,8 @@ onMounted(() => {
|
||||
mainForm.value.occur_date = time
|
||||
mainForm.value.out_date = time
|
||||
})
|
||||
// 默认调用一次医院列表接口
|
||||
fetchHospitalList()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user