feat(outbound): 退回弹窗增加「补发给谁」选择器

· 勾选「需要补发」后出现「补发给谁」下拉(filterable + clearable),
  并自动带上补发数量。
· 预填策略:按原领用人 consumer_name **唯一命中**才预选;重名一律留空让现场
  自己选 —— 与借用人回填同口径,宁可多一步也不猜错人(补发单会挂到错的人名下)。
· 留空时后端回退为当前操作人,文案已写明。
· 人员名单**勾选补发时才加载**(多数退回不需要补,避免无谓请求),加载后缓存。
· 新增 api/common/users.ts 的 getActiveUsers():走中性的
  /v1/common/active-users,而不是复用借库专用路径。
This commit is contained in:
yueli
2026-09-17 12:01:12 +08:00
parent 27e5589a5e
commit 05524c988a
3 changed files with 79 additions and 6 deletions

View File

@ -0,0 +1,20 @@
import request from '@/utils/request'
/**
* 在职人员名单(id + 姓名)—— 「选择某人」类下拉框的共用数据源。
*
* 使用场景:借出/转交/归还的借用人选择、出库退回补发的「补发给谁」等。
*
* ★ 为什么不复用 transaction.ts 里的 getBorrowUsers:
* 那条路径(/transactions/borrow/users)在语义上属于借库模块,出库补发直接
* 复用会让后人困惑「出库为什么在调借库的接口」。后端两者共用同一份实现,
* 前端各走各的中性/专用入口。
*
* ★ 公司隔离与服务端同口径;只返回 id 与姓名,不含邮箱/角色/部门。
*/
export function getActiveUsers() {
return request({
url: '/v1/common/active-users',
method: 'get'
})
}

View File

@ -20,11 +20,13 @@ import request from '@/utils/request'
* reason: string 可选,退回原因
* need_reissue: boolean 可选,退回后是否自动生成补发单(默认 false)
* reissue_qty: number 可选,补发数量,默认 = return_qty
* reissue_applicant_id: number 可选,**补发给谁**;不传则回退为当前操作人
* }
*
* ★ 补发(need_reissue=true):后端直接生成一张**免审批**出库单并关联回本笔退回,
* 申请人取**当前操作人**(trans_outbound 没有申请人字段,也无指回原审批单的
* 关联,无法可靠判定原申请人;原领用人写入备注)。
* ★ 补发(need_reissue=true):后端直接生成一张**免审批**出库单并关联回本笔退回。
* 申请人优先取 reissue_applicant_id(前端选择的「补发给谁」),未传则回退为
* 当前操作人 —— trans_outbound 没有申请人字段,也无指回原审批单的关联,
* 无法可靠推断原申请人(按姓名反查会重名错绑),故把选择权交给现场。
* ★ 补发库存不足时**整笔退回一并回滚**并返回明确提示 —— 取消勾选即可只做退回过账。
*
* @returns data.reissue 为 { id, request_no, quantity } 或 null
@ -36,6 +38,7 @@ export function returnFromOutbound(data: {
reason?: string
need_reissue?: boolean
reissue_qty?: number
reissue_applicant_id?: number
}) {
return request({
url: '/inbound/stock/return-from-outbound',

View File

@ -295,6 +295,21 @@
style="width: 200px"
/>
</el-form-item>
<el-form-item label="补发给谁" v-if="returnDialog.form.need_reissue">
<el-select
v-model="returnDialog.form.reissue_applicant_id"
filterable
clearable
placeholder="默认取原领用人;找不到时留空"
style="width: 260px"
>
<el-option v-for="u in reissueUsers" :key="u.id" :label="u.name" :value="u.id" />
</el-select>
<div style="color:#909399; font-size:12px; margin-top:4px;">
补发单会挂在此人名下,出现在他的「我的申请」里。<strong>留空</strong>则挂当前操作人。
</div>
</el-form-item>
</el-form>
<template #footer>
@ -313,6 +328,7 @@ import { ref, computed, onMounted, reactive, onBeforeUnmount } from 'vue'
import { ElMessage } from 'element-plus'
import { getOutboundList } from '@/api/outbound'
import { returnFromOutbound } from '@/api/inbound/return'
import { getActiveUsers } from '@/api/common/users'
import { formatQty, normalizeQty } from '@/utils/format'
import { Picture } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
@ -530,12 +546,40 @@ const returnDialog = reactive({
// 补发:默认**不勾选** —— 有些退回是项目结束退还,根本不需要补
need_reissue: false,
reissue_qty: 1,
// 补发给谁:留空则由后端回退为当前操作人
reissue_applicant_id: null as number | null,
},
})
// 勾选补发时,默认补发量 = 本次退回量(最常见的情况),用户可再调小
const handleNeedReissueChange = (val: boolean) => {
if (val) returnDialog.form.reissue_qty = normalizeQty(returnDialog.form.return_qty) || 1
// 补发人员名单:勾选补发时才加载(多数退回不需要补,避免无谓请求)
const reissueUsers = ref<Array<{ id: number; name: string }>>([])
let reissueUsersLoaded = false
const loadReissueUsers = async () => {
if (reissueUsersLoaded) return
try {
const res: any = await getActiveUsers()
reissueUsers.value = res?.data || []
reissueUsersLoaded = true
} catch {
reissueUsers.value = []
}
}
// 勾选补发时:默认补发量 = 本次退回量,并尝试按**原领用人**预选「补发给谁」
const handleNeedReissueChange = async (val: boolean) => {
if (!val) return
returnDialog.form.reissue_qty = normalizeQty(returnDialog.form.return_qty) || 1
await loadReissueUsers()
if (returnDialog.form.reissue_applicant_id) return
// 按姓名唯一命中才预填;重名一律留空让现场自己选 —— 与借用人回填同口径,
// 宁可多一步,也不猜错人(补发单会挂到错的人名下)。
const want = String(returnDialog.row?.consumer_name || '').split('/')[0].trim()
if (!want) return
const hits = reissueUsers.value.filter(
(u) => String(u.name || '').split('/')[0].trim() === want,
)
if (hits.length === 1) returnDialog.form.reissue_applicant_id = hits[0].id
}
const openReturnDialog = (row: any) => {
@ -547,6 +591,7 @@ const openReturnDialog = (row: any) => {
returnDialog.form.reason = ''
returnDialog.form.need_reissue = false
returnDialog.form.reissue_qty = 1
returnDialog.form.reissue_applicant_id = null
returnDialog.visible = true
}
@ -558,6 +603,7 @@ const resetReturnDialog = () => {
returnDialog.form.reason = ''
returnDialog.form.need_reissue = false
returnDialog.form.reissue_qty = 1
returnDialog.form.reissue_applicant_id = null
}
const submitReturn = async () => {
@ -603,6 +649,10 @@ const submitReturn = async () => {
reason,
need_reissue: needReissue,
reissue_qty: needReissue ? reissueQty : undefined,
// 留空则由后端回退为当前操作人
reissue_applicant_id: needReissue
? (returnDialog.form.reissue_applicant_id ?? undefined)
: undefined,
})
ElMessage.success(res?.msg || '退回成功')
returnDialog.visible = false