refactor(purchase): 批次概念改为基于 request_no 前缀推导,撤销数据库字段

按用户反馈:不需要新增数据库字段,现有 request_no (PUR-20260831-1021-0001)
本身就能推导批次——去掉末尾4位流水号即为批次前缀。

- 撤销 purchase_request.batch_no 字段(模型/数据库)
- 撤销 create 接口的 batch_no 参数与 generate_batch_no
- 批次标识 = request_no 去掉末尾 -XXXX 段
  getBatchKey('PUR-20260831-1021-0001') = 'PUR-20260831-1021'
- 按批次查询/批量审批接口改用 batch_key (request_no LIKE 前缀)
- 前端列表批次号列、整批弹窗、一键审批本批均基于前缀推导
- 零数据库迁移,历史数据立即可用
This commit is contained in:
yueli
2026-08-31 13:49:11 +08:00
parent 664127ee30
commit f499346f74
5 changed files with 40 additions and 63 deletions

View File

@ -52,11 +52,11 @@
</el-table-column>
<el-table-column prop="requester_name" label="申请人" width="100" />
<el-table-column prop="approver_name" label="审批人" width="100" />
<!-- 批次号列同一批提交的记录共享可点击查看整批 -->
<!-- 批次号列同一批提交的记录共享request_no 去掉末尾流水号可点击查看整批 -->
<el-table-column label="批次号" width="170">
<template #default="{ row }">
<el-link v-if="row.batch_no" type="primary" :underline="false" @click="openBatchDialog(row.batch_no)">
{{ row.batch_no }}
<el-link v-if="getBatchKey(row.request_no)" type="primary" :underline="false" @click="openBatchDialog(row.request_no)">
{{ getBatchKey(row.request_no) }}
</el-link>
<span v-else style="color:#ccc">-</span>
</template>
@ -69,7 +69,7 @@
<el-table-column label="操作" width="240" fixed="right" align="center">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="openDetailDialog(row)">详情</el-button>
<el-button v-if="row.batch_no" type="info" link size="small" @click="openBatchDialog(row.batch_no)">整批</el-button>
<el-button v-if="getBatchKey(row.request_no)" type="info" link size="small" @click="openBatchDialog(row.request_no)">整批</el-button>
<template v-if="row.status === 0 && canApprove">
<el-button type="success" link size="small" @click="handleApprove(row)">通过</el-button>
<el-button type="danger" link size="small" @click="openRejectDialog(row)">驳回</el-button>
@ -286,7 +286,7 @@
<el-dialog v-model="batchDialogVisible" title="采购批次详情" width="900px" destroy-on-close :close-on-click-modal="false" :close-on-press-escape="false">
<div style="margin-bottom: 12px;">
<span style="font-weight: 600;">批次号:</span>
<el-tag style="margin-left: 6px;">{{ currentBatchNo }}</el-tag>
<el-tag style="margin-left: 6px;">{{ currentBatchKey }}</el-tag>
<span style="margin-left: 12px; color: #909399;">共 {{ batchItems.length }} 条采购记录</span>
<el-button
v-if="canApprove && batchItems.some(i => i.status === 0)"
@ -456,25 +456,26 @@ const detail = ref<any>({})
// ★ 整批查看
const batchDialogVisible = ref(false)
const currentBatchNo = ref('')
const currentBatchKey = ref('')
const batchItems = ref<any[]>([])
// 打开整批查看弹窗
const openBatchDialog = async (batchNo: string) => {
currentBatchNo.value = batchNo
// 打开整批查看弹窗(传入任一 request_no推导批次前缀
const openBatchDialog = async (requestNo: string) => {
const batchKey = getBatchKey(requestNo)
currentBatchKey.value = batchKey
batchItems.value = []
batchDialogVisible.value = true
try {
const res: any = await getPurchaseByBatch(batchNo)
const res: any = await getPurchaseByBatch(batchKey)
batchItems.value = res.data?.items || []
} catch (err: any) {
ElMessage.error(err?.msg || '加载批次记录失败')
}
}
// 批量通过本批(按批次
// 批量通过本批(按批次前缀
const handleBatchApproveByNo = async () => {
if (!currentBatchNo.value) return
if (!currentBatchKey.value) return
const pendingCount = batchItems.value.filter(i => i.status === 0).length
if (pendingCount === 0) return ElMessage.warning('本批没有待审批记录')
try {
@ -487,9 +488,9 @@ const handleBatchApproveByNo = async () => {
batchLoading.value = true
try {
const res: any = await batchApprovePurchase({ batch_no: currentBatchNo.value, action: 'approve' })
const res: any = await batchApprovePurchase({ batch_key: currentBatchKey.value, action: 'approve' })
ElMessage.success(res?.msg || '本批已全部通过')
await openBatchDialog(currentBatchNo.value) // 刷新整批数据
await openBatchDialog(batchItems.value[0]?.request_no || currentBatchKey.value) // 刷新整批数据
await fetchData()
} catch (err: any) {
ElMessage.error(err?.msg || '批量通过失败')
@ -894,14 +895,12 @@ const syncRemarkToAll = () => {
ElMessage.success('已将第一行备注同步到所有行')
}
// ★ 生成采购批次号(与后端 generate_batch_no 格式一致)
const generateBatchNo = () => {
const now = new Date()
const pad = (n: number) => String(n).padStart(2, '0')
const dateStr = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`
const timeStr = `${pad(now.getHours())}${pad(now.getMinutes())}`
const rand = Math.random().toString(16).slice(2, 6).toUpperCase()
return `BATCH-${dateStr}-${timeStr}-${rand}`
// ★ 推导批次前缀request_no 去掉末尾"-流水号" PUR-20260831-1021-0001 PUR-20260831-1021
const getBatchKey = (requestNo: string): string => {
if (!requestNo) return ''
// 去掉末尾的 -0001 段最后5个字符- + 4位流水
const idx = requestNo.lastIndexOf('-')
return idx > 0 ? requestNo.slice(0, idx) : requestNo
}
// ★ 照片一键同步所有行:取第一行的照片深拷贝到所有行
@ -952,15 +951,12 @@ const submitForm = async () => {
}
}
// 3. 逐行提交(每行携带公共信息 + 同一批次号
// 3. 逐行提交(每行携带公共信息)
submitLoading.value = true
let successCount = 0
// ★ 生成批次号:同一批提交的所有记录共享,便于审批端按批次查看/统计
const batchNo = generateBatchNo()
try {
for (const row of validItems) {
const payload: any = {
batch_no: batchNo,
name: row.name,
spec_model: row.spec_model,
quantity: row.quantity,