增加手工入库的功能
This commit is contained in:
parent
6910ed417a
commit
a492ea4091
47
.trae/skills/blood-system/SKILL.md
Normal file
47
.trae/skills/blood-system/SKILL.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
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. 如修改已有代码,先展示原有代码片段,再给出修改后的代码,标注改动位置
|
||||||
33
src/api/blood/handInStorage.ts
Normal file
33
src/api/blood/handInStorage.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
export function getBloodBreed(query?: Object) {
|
||||||
|
return request({
|
||||||
|
url: "/bus/bloodbank/handInStorage/getBloodBreed",
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveData(data?: Object) {
|
||||||
|
return request({
|
||||||
|
url: "/bus/bloodbank/handInStorage/saveData",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryList(query?: Object) {
|
||||||
|
return request({
|
||||||
|
url: "/bus/bloodbank/handInStorage/queryList",
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryDetail(query?: Object) {
|
||||||
|
return request({
|
||||||
|
url: "/bus/bloodbank/handInStorage/queryDetail",
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -767,6 +767,30 @@ aside {
|
|||||||
color: #FFF !important;
|
color: #FFF !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectTable 下拉表格:行高30px,单元格内容垂直居中
|
||||||
|
.select-table-dropdown {
|
||||||
|
// 覆盖 vxe 默认行高(48px)与文字行高(22px),vxe 会按该变量计算单元格内联高度
|
||||||
|
--vxe-ui-table-row-height-default: 30px;
|
||||||
|
--vxe-ui-table-row-line-height: 30px;
|
||||||
|
|
||||||
|
.vxe-table {
|
||||||
|
.vxe-header--column,
|
||||||
|
.vxe-body--column {
|
||||||
|
.vxe-cell {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
line-height: 30px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.vxe-cell--wrapper {
|
||||||
|
line-height: 30px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 滚动条
|
// 滚动条
|
||||||
.mytable-style ::-webkit-scrollbar {
|
.mytable-style ::-webkit-scrollbar {
|
||||||
width: .6rem;
|
width: .6rem;
|
||||||
|
|||||||
709
src/views/blood/handInStorage/index.vue
Normal file
709
src/views/blood/handInStorage/index.vue
Normal file
@ -0,0 +1,709 @@
|
|||||||
|
<template>
|
||||||
|
<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="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="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">
|
||||||
|
<div class="section-title">入库信息</div>
|
||||||
|
<el-form :model="mainForm" label-width="100px" label-position="left">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="入库单号">
|
||||||
|
<el-input v-model="mainForm.bill_no" disabled placeholder="自动生成" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="入库时间">
|
||||||
|
<el-date-picker v-model="mainForm.occur_date" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="入库时间" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="经手人">
|
||||||
|
<SelectTable v-model:data="mainForm.handle_person" :tableData="userList" placeholder="经手人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="血液来源">
|
||||||
|
<div class="source-box">
|
||||||
|
<el-select v-model="mainForm.source_type" placeholder="血站" style="width: 80px"
|
||||||
|
@change="handleSourceType">
|
||||||
|
<el-option label="血站" value="1" />
|
||||||
|
<el-option label="其它" value="2" />
|
||||||
|
</el-select>
|
||||||
|
<el-input v-model="mainForm.org" placeholder="请输入来源" style="flex: 1" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="血站出库时间">
|
||||||
|
<el-date-picker v-model="mainForm.out_date" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="出库时间" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="mainForm.remark" placeholder="备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-checkbox v-model="cancelBlood">取消血液</el-checkbox>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="left-panel">
|
||||||
|
<div class="left-panel-scroll">
|
||||||
|
<el-form :model="bloodForm" label-width="90px" label-position="left">
|
||||||
|
<el-form-item label="血液流水号">
|
||||||
|
<el-input v-model="bloodForm.blood_no" placeholder="请输入血液流水号" @keyup.enter="handleScan" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品号">
|
||||||
|
<el-input v-model="bloodForm.product_no" placeholder="产品号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="血液品种">
|
||||||
|
<el-select v-model="bloodForm.blood_breed" placeholder="请选择血液品种" filterable
|
||||||
|
style="width: 100%" @change="handleBreedChange">
|
||||||
|
<el-option v-for="item in bloodBreedList" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="血型">
|
||||||
|
<el-select v-model="bloodForm.blood_type" placeholder="请选择血型" style="width: 100%">
|
||||||
|
<el-option v-for="item in bloodTypeOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Rh(D)">
|
||||||
|
<el-select v-model="bloodForm.rh_blood_type" placeholder="Rh(D)" style="width: 100%">
|
||||||
|
<el-option label="+" value="+" />
|
||||||
|
<el-option label="-" value="-" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="容量">
|
||||||
|
<div class="capacity-box">
|
||||||
|
<el-input v-model="bloodForm.capacity" placeholder="容量" style="flex: 1" />
|
||||||
|
<el-input v-model="bloodForm.unit" placeholder="单位" style="width: 60px" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="有效日期">
|
||||||
|
<el-date-picker v-model="bloodForm.valid_date" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="有效日期" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="采血日期">
|
||||||
|
<el-date-picker v-model="bloodForm.pick_date" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="采血日期" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="入库价格">
|
||||||
|
<el-input-number v-model="bloodForm.price" :precision="2" :step="0.01" :min="0"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :icon="Plus" @click="handleAddBlood">添加</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-box">
|
||||||
|
<div class="section-title">汇总信息</div>
|
||||||
|
<BloodTypeStatTable :data="bloodList" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-panel">
|
||||||
|
<div class="section-title">入库列表</div>
|
||||||
|
<el-table :data="bloodList" border highlight-current-row @current-change="handleRowSelect">
|
||||||
|
<el-table-column type="index" label="序号" width="55" align="center" />
|
||||||
|
<el-table-column prop="blood_no" label="血液流水号" width="140" align="center" />
|
||||||
|
<el-table-column prop="blood_breed" label="血液品种" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatBloodBreed(row.blood_breed) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="blood_type" label="血型" width="60" align="center" />
|
||||||
|
<el-table-column prop="rh_blood_type" label="Rh(D)" width="60" align="center" />
|
||||||
|
<el-table-column prop="capacity" label="容量" width="70" align="center" />
|
||||||
|
<el-table-column prop="unit" label="单位" width="60" align="center" />
|
||||||
|
<el-table-column prop="special_deal" label="特别处理" width="80" align="center" />
|
||||||
|
<el-table-column prop="valid_date" label="有效日期" width="150" align="center" />
|
||||||
|
<el-table-column prop="pick_date" label="采血日期" width="150" align="center" />
|
||||||
|
<el-table-column prop="remark" label="备注" align="center" />
|
||||||
|
<el-table-column prop="product_no" label="产品码" width="120" align="center" />
|
||||||
|
<!-- 删除血液 -->
|
||||||
|
<el-table-column label="操作" align="center" :show-overflow-tooltip="false">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<el-button type="primary" text @click="delBlood(row, $index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog v-model="searchDialogVisible" title="入库检索" width="700px" :close-on-click-modal="false">
|
||||||
|
<el-form :model="searchForm" label-width="100px">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="入库单号">
|
||||||
|
<el-input v-model="searchForm.billNo" placeholder="入库单号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="科室">
|
||||||
|
<el-input v-model="searchForm.dept" placeholder="科室" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="入库时间">
|
||||||
|
<div class="date-range">
|
||||||
|
<el-date-picker v-model="searchForm.stime" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="开始时间" style="width: 45%" />
|
||||||
|
<span class="separator">-</span>
|
||||||
|
<el-date-picker v-model="searchForm.etime" type="datetime"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss" placeholder="结束时间" style="width: 45%" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<div class="dialog-table-box">
|
||||||
|
<el-button type="primary" :icon="Search" @click="doSearch">查询</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="resetSearch">重置</el-button>
|
||||||
|
<el-table :data="queryResultList" border highlight-current-row height="280"
|
||||||
|
@current-change="handleQueryRowSelect">
|
||||||
|
<el-table-column prop="bill_no" label="入库单号" width="140" align="center" />
|
||||||
|
<el-table-column prop="occur_date" label="入库时间" width="160" align="center" />
|
||||||
|
<el-table-column prop="handle_person" label="经手人" width="100" align="center" />
|
||||||
|
<el-table-column prop="total_money" label="总金额" width="100" align="center" />
|
||||||
|
<el-table-column prop="remark" label="备注" align="center" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="searchDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmSelect">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { Search, Plus, Fold, Printer, Upload, Close, Refresh } from '@element-plus/icons-vue'
|
||||||
|
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 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)
|
||||||
|
const searchDialogVisible = ref(false)
|
||||||
|
|
||||||
|
const selectedBloodRow = ref<any>(null)
|
||||||
|
const selectedQueryRow = ref<any>(null)
|
||||||
|
|
||||||
|
const bloodTypeOptions = [
|
||||||
|
{ label: 'A', value: 'A' },
|
||||||
|
{ label: 'B', value: 'B' },
|
||||||
|
{ label: 'O', value: 'O' },
|
||||||
|
{ label: 'AB', value: 'AB' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const mainForm = ref<any>({
|
||||||
|
bill_no: '',
|
||||||
|
occur_date: '',
|
||||||
|
handle_person: userInfo.name,
|
||||||
|
source_type: '1',
|
||||||
|
org: '',
|
||||||
|
out_date: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const bloodForm = ref<any>({
|
||||||
|
blood_no: '',
|
||||||
|
product_no: '',
|
||||||
|
blood_breed: '',
|
||||||
|
blood_type: '',
|
||||||
|
rh_blood_type: '+',
|
||||||
|
capacity: '',
|
||||||
|
unit: '',
|
||||||
|
valid_date: '',
|
||||||
|
pick_date: '',
|
||||||
|
produce_date: '',
|
||||||
|
price: 0,
|
||||||
|
special_deal: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const bloodList = ref<any[]>([])
|
||||||
|
|
||||||
|
const searchForm = ref<any>({
|
||||||
|
billNo: '',
|
||||||
|
dept: '',
|
||||||
|
stime: '',
|
||||||
|
etime: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const queryResultList = ref<any[]>([])
|
||||||
|
|
||||||
|
const formatBloodBreed = (code: string) => {
|
||||||
|
const item = bloodBreedList.value.find((b: any) => b.value === code)
|
||||||
|
return item ? item.label : code
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSourceType = () => {
|
||||||
|
if (mainForm.value.source_type === '1') {
|
||||||
|
mainForm.value.org = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBreedChange = (val: string) => {
|
||||||
|
if (val) {
|
||||||
|
getBloodBreed({ breedCode: val }).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
bloodForm.value.unit = res.data.unit || bloodForm.value.unit
|
||||||
|
bloodForm.value.product_no = res.data.station_breed_code || bloodForm.value.product_no
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入库前必填项校验:血液品种、血型、Rh(D)血型、产品号、容量、有效日期
|
||||||
|
const validateBloodForm = () => {
|
||||||
|
if (!bloodForm.value.blood_breed) {
|
||||||
|
ElMessage.warning('请选择血液品种')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!bloodForm.value.blood_type) {
|
||||||
|
ElMessage.warning('请选择血型')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!bloodForm.value.rh_blood_type) {
|
||||||
|
ElMessage.warning('请选择Rh(D)血型')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!bloodForm.value.product_no) {
|
||||||
|
ElMessage.warning('请输入产品号')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!bloodForm.value.capacity) {
|
||||||
|
ElMessage.warning('请输入容量')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!bloodForm.value.valid_date) {
|
||||||
|
ElMessage.warning('请选择有效日期')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleScan = () => {
|
||||||
|
const bloodNo = bloodForm.value.blood_no?.trim()
|
||||||
|
if (!bloodNo) return
|
||||||
|
const index = bloodList.value.findIndex(item => item.blood_no === bloodNo)
|
||||||
|
if (cancelBlood.value) {
|
||||||
|
// 取消血液:从入库列表中删除对应血液流水号数据
|
||||||
|
if (index > -1) {
|
||||||
|
const removed = bloodList.value.splice(index, 1)
|
||||||
|
if (selectedBloodRow.value === removed[0]) {
|
||||||
|
selectedBloodRow.value = null
|
||||||
|
}
|
||||||
|
ElMessage.success('已取消血液:' + bloodNo)
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('入库列表中不存在该血液流水号')
|
||||||
|
}
|
||||||
|
bloodForm.value.blood_no = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (index > -1) {
|
||||||
|
ElMessage.warning('该血液流水号已存在')
|
||||||
|
bloodForm.value.blood_no = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!validateBloodForm()) return
|
||||||
|
bloodList.value.push({ ...bloodForm.value })
|
||||||
|
if (resetBloodAfterScan.value) {
|
||||||
|
resetBloodForm()
|
||||||
|
} else {
|
||||||
|
bloodForm.value.blood_no = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddBlood = () => {
|
||||||
|
if (!bloodForm.value.blood_no) {
|
||||||
|
return ElMessage.warning('请输入血液流水号')
|
||||||
|
}
|
||||||
|
// 勾选“取消血液”时,添加按钮也执行取消删除逻辑
|
||||||
|
if (cancelBlood.value) {
|
||||||
|
handleScan()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!validateBloodForm()) return
|
||||||
|
const exists = bloodList.value.some(item => item.blood_no === bloodForm.value.blood_no)
|
||||||
|
if (exists) {
|
||||||
|
ElMessage.warning('该血液流水号已存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bloodList.value.push({ ...bloodForm.value })
|
||||||
|
resetBloodForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetBloodForm = () => {
|
||||||
|
bloodForm.value = {
|
||||||
|
blood_no: '',
|
||||||
|
product_no: '',
|
||||||
|
blood_breed: '',
|
||||||
|
blood_type: '',
|
||||||
|
rh_blood_type: '+',
|
||||||
|
capacity: '',
|
||||||
|
unit: '',
|
||||||
|
valid_date: '',
|
||||||
|
pick_date: '',
|
||||||
|
produce_date: '',
|
||||||
|
price: 0,
|
||||||
|
special_deal: '',
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRowSelect = (row: any) => {
|
||||||
|
selectedBloodRow.value = row
|
||||||
|
}
|
||||||
|
|
||||||
|
const delBlood = (row: any, index: number) => {
|
||||||
|
bloodList.value.splice(index, 1)
|
||||||
|
if (selectedBloodRow.value === row) {
|
||||||
|
selectedBloodRow.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
searchDialogVisible.value = true
|
||||||
|
searchForm.value = {
|
||||||
|
billNo: '',
|
||||||
|
dept: '',
|
||||||
|
stime: dayjs().subtract(30, 'day').format('YYYY-MM-DD') + ' 00:00:00',
|
||||||
|
etime: dayjs().format('YYYY-MM-DD') + ' 23:59:59'
|
||||||
|
}
|
||||||
|
doSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
const doSearch = () => {
|
||||||
|
queryList(searchForm.value).then((res: any) => {
|
||||||
|
queryResultList.value = res.data || []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetSearch = () => {
|
||||||
|
searchForm.value = {
|
||||||
|
billNo: '',
|
||||||
|
dept: '',
|
||||||
|
stime: '',
|
||||||
|
etime: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleQueryRowSelect = (row: any) => {
|
||||||
|
selectedQueryRow.value = row
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmSelect = () => {
|
||||||
|
if (!selectedQueryRow.value) {
|
||||||
|
ElMessage.warning('请选择一条记录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryDetail({ billNo: selectedQueryRow.value.bill_no }).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
const main = res.data.xkInstorageMain || {}
|
||||||
|
// 数据库存的是姓名(nick_name),回显时转换回用户代号以匹配下拉选项
|
||||||
|
main.handle_person = userList.value.find(v => v.label === main.handle_person)?.value || main.handle_person
|
||||||
|
mainForm.value = main
|
||||||
|
bloodList.value = res.data.xkBloodInfoList || []
|
||||||
|
}
|
||||||
|
searchDialogVisible.value = false
|
||||||
|
selectedQueryRow.value = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
getServerTime().then((time: string) => {
|
||||||
|
mainForm.value = {
|
||||||
|
bill_no: '',
|
||||||
|
occur_date: time,
|
||||||
|
handle_person: userInfo.name,
|
||||||
|
source_type: '1',
|
||||||
|
org: '',
|
||||||
|
out_date: time,
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
bloodList.value = []
|
||||||
|
resetBloodForm()
|
||||||
|
selectedBloodRow.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (!mainForm.value.occur_date) {
|
||||||
|
return ElMessage.warning('请选择入库时间')
|
||||||
|
}
|
||||||
|
if (!mainForm.value.handle_person) {
|
||||||
|
return ElMessage.warning('请选择经手人')
|
||||||
|
}
|
||||||
|
if (bloodList.value.length === 0) {
|
||||||
|
return ElMessage.warning('请添加血液信息')
|
||||||
|
}
|
||||||
|
|
||||||
|
const xkInstorageDetailList = bloodList.value.map((item, index) => ({
|
||||||
|
sid: index + 1,
|
||||||
|
bill_no: '',
|
||||||
|
blood_no: item.blood_no,
|
||||||
|
product_no: item.product_no,
|
||||||
|
price: item.price || 0,
|
||||||
|
remark: item.remark || ''
|
||||||
|
}))
|
||||||
|
|
||||||
|
const xkBloodInfoList = bloodList.value.map(item => ({
|
||||||
|
blood_no: item.blood_no,
|
||||||
|
product_no: item.product_no,
|
||||||
|
blood_breed: item.blood_breed,
|
||||||
|
blood_type: item.blood_type,
|
||||||
|
rh_blood_type: item.rh_blood_type,
|
||||||
|
capacity: item.capacity,
|
||||||
|
unit: item.unit,
|
||||||
|
valid_date: item.valid_date,
|
||||||
|
pick_date: item.pick_date,
|
||||||
|
produce_date: item.produce_date,
|
||||||
|
special_deal: item.special_deal,
|
||||||
|
Remark: item.remark || ''
|
||||||
|
}))
|
||||||
|
|
||||||
|
const totalMoney = bloodList.value.reduce((sum: number, item: any) => sum + (Number(item.price) || 0), 0)
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
xkInstorageMain: {
|
||||||
|
bill_no: '',
|
||||||
|
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,
|
||||||
|
remark: mainForm.value.remark,
|
||||||
|
total_money: totalMoney
|
||||||
|
},
|
||||||
|
xkInstorageDetailList,
|
||||||
|
xkBloodInfoList
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLoading.value = true
|
||||||
|
saveData(data).then((res: any) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage.success('保存成功,入库单号:' + res.data)
|
||||||
|
mainForm.value.bill_no = res.data
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
saveLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
ElMessageBox.confirm('确定关闭当前录入吗?未保存的数据将丢失', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
handleAdd()
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getBloodList()
|
||||||
|
getUserData()
|
||||||
|
getServerTime().then((time: string) => {
|
||||||
|
mainForm.value.occur_date = time
|
||||||
|
mainForm.value.out_date = time
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.hand-in-storage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
// height: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
// overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
flex: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
flex: none;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: #fafbfc;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
padding: 0 0 6px 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 7px;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #409eff;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
width: 380px;
|
||||||
|
flex: 0 0 380px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fafbfc;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel-scroll {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fafbfc;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
min-height: 59vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .stat-box {
|
||||||
|
// flex: none;
|
||||||
|
// flex-shrink: 0;
|
||||||
|
// padding-top: 10px;
|
||||||
|
// border-top: 1px solid #ebeef5;
|
||||||
|
// background: #fafbfc;
|
||||||
|
|
||||||
|
// .total-row {
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
// align-items: center;
|
||||||
|
// padding: 8px 12px;
|
||||||
|
// background: #ecf5ff;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// margin-top: 8px;
|
||||||
|
|
||||||
|
// .total-num {
|
||||||
|
// font-size: 18px;
|
||||||
|
// font-weight: 700;
|
||||||
|
// color: #409eff;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
.source-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capacity-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml20 {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-range {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: 0 8px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-table-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.left-panel .el-form-item__label) {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -48,8 +48,8 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
proxy: {
|
proxy: {
|
||||||
// https://cn.vitejs.dev/config/#server-proxy
|
// https://cn.vitejs.dev/config/#server-proxy
|
||||||
"/dev-api": {
|
"/dev-api": {
|
||||||
// target: "http://localhost:8907",
|
target: "http://localhost:8907",
|
||||||
target: "http://47.97.125.165:8907",
|
//target: "http://47.97.125.165:8907",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (p) => p.replace(/^\/dev-api/, ""),
|
rewrite: (p) => p.replace(/^\/dev-api/, ""),
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user