fix(outbound+trans): 修复POST接口错误数据清洗导致的sku/quantity字段被清除Bug,并新增出库审批工作流全链路
This commit is contained in:
@ -84,4 +84,12 @@ export function batchCreateUser(data: any[]) {
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// ★ 获取可指定审批人列表(SUPERVISOR / SUPER_ADMIN 且 status=active)
|
||||
export function getApproversList() {
|
||||
return request({
|
||||
url: '/v1/auth/users/approvers',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@ -77,4 +77,49 @@ export function getOutboundList(params: any) {
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交出库申请单(申请人 → 审批流)
|
||||
*/
|
||||
export function submitOutboundRequest(data: {
|
||||
items: Array<{
|
||||
material_type?: string
|
||||
name: string
|
||||
spec_model: string
|
||||
warehouse_location?: string
|
||||
quantity: number
|
||||
}>
|
||||
remark: string
|
||||
}) {
|
||||
return request({
|
||||
url: '/v1/outbound/request',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出库审批申请单列表
|
||||
* @param params 支持 status, page, limit
|
||||
*/
|
||||
export function getApprovalRequestList(params: { status?: number | ''; page?: number; limit?: number }) {
|
||||
return request({
|
||||
url: '/v1/outbound/request',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批(通过 / 驳回)出库申请单
|
||||
* @param id 审批单ID
|
||||
* @param data action: 'approve' | 'reject',reject 时需传 reject_reason
|
||||
*/
|
||||
export function approveRequest(id: number, data: { action: 'approve' | 'reject'; reject_reason?: string }) {
|
||||
return request({
|
||||
url: `/v1/outbound/request/${id}/approve`,
|
||||
method: 'patch',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -150,6 +150,16 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: 'OutboundList',
|
||||
component: () => import('@/views/outbound/index.vue'),
|
||||
meta: { title: '出库记录' }
|
||||
},
|
||||
{
|
||||
path: 'approval',
|
||||
name: 'OutboundApproval',
|
||||
component: () => import('@/views/outbound/approval/index.vue'),
|
||||
meta: {
|
||||
title: '出库审批',
|
||||
icon: 'Stamp',
|
||||
roles: ['SUPER_ADMIN', 'SUPERVISOR']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -37,6 +37,9 @@
|
||||
<el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="success" :icon="Printer" :disabled="selectedItems.length === 0" @click="handlePreview">
|
||||
生成预览 & 打印
|
||||
</el-button>
|
||||
<el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="primary" :icon="Plus" :disabled="selectedItems.length === 0" @click="openRequestDialog">
|
||||
提交出库申请
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -289,6 +292,80 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ★ 出库申请 Dialog -->
|
||||
<el-dialog
|
||||
v-model="requestDialogVisible"
|
||||
title="提交出库申请"
|
||||
width="700px"
|
||||
destroy-on-close
|
||||
class="no-print-content"
|
||||
>
|
||||
<el-alert
|
||||
title="请确认以下物料申请清单,填写申请原因后提交"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 16px;"
|
||||
/>
|
||||
|
||||
<el-table :data="validSelectedItems" border size="small" style="margin-bottom: 16px;">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="类型" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="getTypeTag(row.type)">{{ row.typeLabel }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="standard" label="规格" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="warehouse_location" label="库位" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="export_quantity" label="计划数量" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<span style="color: #F56C6C; font-weight: bold;">{{ row.export_quantity }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="* 指定审批人" required>
|
||||
<el-select
|
||||
v-model="requestApproverId"
|
||||
placeholder="请选择审批人"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="user in approvers"
|
||||
:key="user.id"
|
||||
:label="`${user.username} (${user.role === 'SUPER_ADMIN' ? '超级管理员' : '主管'})`"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请原因" required>
|
||||
<el-input
|
||||
v-model="requestRemark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请填写出库申请原因(必填)"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="requestDialogVisible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="requestSubmitting"
|
||||
@click="confirmSubmitRequest"
|
||||
>
|
||||
确认提交
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<div id="print-area">
|
||||
<div class="print-header">
|
||||
<h1>IRIS出库拣货确认单</h1>
|
||||
@ -358,6 +435,8 @@ import { ElMessage, ElTable, ElMessageBox } from 'element-plus'
|
||||
import { getAllStock, getStockList, printSelectionList } from '@/api/inbound/stock'
|
||||
import { getBomList, getBomDetail } from '@/api/bom'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { submitOutboundRequest } from '@/api/outbound'
|
||||
import { getApproversList } from '@/api/auth'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@ -381,6 +460,13 @@ const previewVisible = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
const printLoading = ref(false)
|
||||
|
||||
// ★ 出库申请相关
|
||||
const requestDialogVisible = ref(false)
|
||||
const requestRemark = ref('')
|
||||
const requestApproverId = ref<number | null>(null)
|
||||
const approvers = ref<any[]>([])
|
||||
const requestSubmitting = ref(false)
|
||||
|
||||
const allStockData = ref<any[]>([])
|
||||
const stockList = ref<any[]>([]) // 服务端分页数据
|
||||
const stockTotal = ref(0)
|
||||
@ -795,6 +881,67 @@ const handlePreview = () => {
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
// ★ 出库申请
|
||||
const openRequestDialog = () => {
|
||||
if (validSelectedItems.value.length === 0) {
|
||||
ElMessage.warning('请先添加物品并填写计划出库数量')
|
||||
return
|
||||
}
|
||||
requestRemark.value = ''
|
||||
requestApproverId.value = null
|
||||
loadApprovers()
|
||||
requestDialogVisible.value = true
|
||||
}
|
||||
|
||||
// ★ 加载可指定审批人列表
|
||||
const loadApprovers = async () => {
|
||||
try {
|
||||
const res: any = await getApproversList()
|
||||
approvers.value = res.data || []
|
||||
} catch (e) {
|
||||
console.error('加载审批人列表失败', e)
|
||||
approvers.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const confirmSubmitRequest = async () => {
|
||||
const trimmed = requestRemark.value.trim()
|
||||
if (!trimmed) {
|
||||
ElMessage.warning('请填写申请原因')
|
||||
return
|
||||
}
|
||||
if (!requestApproverId.value) {
|
||||
ElMessage.warning('请选择指定审批人')
|
||||
return
|
||||
}
|
||||
|
||||
requestSubmitting.value = true
|
||||
try {
|
||||
const payload: any = {
|
||||
items: validSelectedItems.value.map(item => ({
|
||||
material_type: item.typeLabel || item.type || '',
|
||||
name: item.name || '',
|
||||
spec_model: item.standard || '',
|
||||
warehouse_location: item.warehouse_location || '',
|
||||
quantity: item.export_quantity || 0
|
||||
})),
|
||||
remark: trimmed,
|
||||
approver_id: requestApproverId.value
|
||||
}
|
||||
|
||||
await submitOutboundRequest(payload)
|
||||
|
||||
// 成功:关闭弹窗、清空列表、提示
|
||||
requestDialogVisible.value = false
|
||||
selectedItems.value = []
|
||||
ElMessage.success('出库申请已提交,等待主管审批!')
|
||||
} catch (err: any) {
|
||||
ElMessage.error(err?.message || err?.msg || '提交申请失败,请重试')
|
||||
} finally {
|
||||
requestSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const confirmPrint = async () => {
|
||||
previewVisible.value = false;
|
||||
|
||||
|
||||
@ -15,6 +15,68 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ★ 出库模式切换 -->
|
||||
<div class="mode-switch-bar">
|
||||
<el-radio-group v-model="outboundMode" size="default" @change="handleModeChange">
|
||||
<el-radio-button value="by-request">按单出库</el-radio-button>
|
||||
<el-radio-button value="direct">直接出库</el-radio-button>
|
||||
</el-radio-group>
|
||||
<span class="mode-hint">
|
||||
{{ outboundMode === 'by-request' ? '需先选择已审批通过的申请单' : '无需审批单,自由扫码出库' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- ★ 按单出库:审批单选择 -->
|
||||
<div v-if="outboundMode === 'by-request'" class="approval-request-select">
|
||||
<el-select
|
||||
v-model="selectedRequestId"
|
||||
placeholder="请选择已审批通过的出库申请单"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
:loading="requestsLoading"
|
||||
@change="handleRequestChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="req in approvalRequests"
|
||||
:key="req.id"
|
||||
:value="req.id"
|
||||
:label="req.request_no"
|
||||
>
|
||||
<span>{{ req.request_no }}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span>{{ req.applicant_name || '未知申请人' }}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span style="color: #909399; font-size: 13px">{{ req.remark || '无备注' }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<p class="select-tip">仅显示已通过(status=1)的审批单</p>
|
||||
</div>
|
||||
|
||||
<!-- ★ 按单出库:计划清单预览 -->
|
||||
<div v-if="outboundMode === 'by-request' && selectedRequest" class="planned-items-section">
|
||||
<div class="planned-header">
|
||||
<span class="planned-title">计划出库清单</span>
|
||||
<el-tag type="success" size="small">{{ selectedRequest.items?.length || 0 }} 种</el-tag>
|
||||
</div>
|
||||
<el-table :data="selectedRequest.items || []" border size="small" style="width: 100%;">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="类型" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" type="info">{{ row.material_type || '-' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="spec_model" label="规格" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="warehouse_location" label="库位" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="计划数量" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<span style="color: #E6A23C; font-weight: bold;">{{ row.quantity ?? '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="scan-section">
|
||||
|
||||
<div v-if="userStore.hasPermission('outbound_create:operation')" class="camera-placeholder" @click="showCamera = true">
|
||||
@ -214,7 +276,7 @@ import { ref, reactive, nextTick, onUnmounted, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Scissor, EditPen, Delete, CameraFilled, Close, Refresh, Select } from '@element-plus/icons-vue'
|
||||
import QrScanner from '@/components/QrScanner/index.vue'
|
||||
import { getStockByBarcode, submitOutbound, getOutboundList } from '@/api/outbound'
|
||||
import { getStockByBarcode, submitOutbound, getOutboundList, getApprovalRequestList } from '@/api/outbound'
|
||||
import { uploadFile } from '@/api/common/upload'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
@ -228,6 +290,12 @@ const showCamera = ref(false)
|
||||
const barcodeRef = ref()
|
||||
const formRef = ref()
|
||||
|
||||
// ★ 双轨制模式
|
||||
const outboundMode = ref<'by-request' | 'direct'>('by-request') // 'by-request' | 'direct'
|
||||
const approvalRequests = ref<any[]>([])
|
||||
const selectedRequest = ref<any>(null)
|
||||
const requestsLoading = ref(false)
|
||||
|
||||
// 签名相关
|
||||
const showSignatureDialog = ref(false)
|
||||
const signaturePreviewUrl = ref('')
|
||||
@ -258,8 +326,95 @@ const totalAmount = computed(() => {
|
||||
return cartItems.value.reduce((sum, item) => sum + (item.price * item.out_quantity), 0)
|
||||
})
|
||||
|
||||
// ★ 双轨制 computed
|
||||
const selectedRequestId = computed({
|
||||
get: () => selectedRequest.value?.id ?? null,
|
||||
set: (val) => {
|
||||
if (!val) {
|
||||
selectedRequest.value = null
|
||||
} else {
|
||||
selectedRequest.value = approvalRequests.value.find(r => r.id === val) ?? null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const plannedItems = computed(() => selectedRequest.value?.items ?? [])
|
||||
|
||||
// ★ 模式切换
|
||||
const handleModeChange = () => {
|
||||
selectedRequest.value = null
|
||||
selectedRequestId.value = null
|
||||
cartItems.value = []
|
||||
form.consumer_name = ''
|
||||
form.remark = ''
|
||||
signatureFile.value = null
|
||||
signaturePreviewUrl.value = ''
|
||||
barcodeInput.value = ''
|
||||
}
|
||||
|
||||
// ★ 加载已审批通过的申请单
|
||||
const loadApprovalRequests = async () => {
|
||||
requestsLoading.value = true
|
||||
try {
|
||||
const res: any = await getApprovalRequestList({ status: 1, page: 1, pageSize: 100 })
|
||||
approvalRequests.value = res.data?.items || []
|
||||
} catch (e) {
|
||||
console.error('加载审批单列表失败', e)
|
||||
} finally {
|
||||
requestsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleRequestChange = (val: number | null) => {
|
||||
if (!val) {
|
||||
selectedRequest.value = null
|
||||
} else {
|
||||
selectedRequest.value = approvalRequests.value.find(r => r.id === val) ?? null
|
||||
}
|
||||
// 切换申请单时清空购物车,防止已扫物品与新单据混淆
|
||||
cartItems.value = []
|
||||
signatureFile.value = null
|
||||
signaturePreviewUrl.value = ''
|
||||
}
|
||||
|
||||
// ★ 按单出库模式:校验扫码是否在计划内
|
||||
const validateAgainstPlan = (scannedName: string, scannedSpec: string, scannedQty: number): string | null => {
|
||||
const normalizedName = scannedName.trim()
|
||||
const normalizedSpec = (scannedSpec || '').trim()
|
||||
|
||||
const matchedPlan = plannedItems.value.find(plan => {
|
||||
const planName = (plan.name || '').trim()
|
||||
const planSpec = (plan.spec_model || '').trim()
|
||||
return planName === normalizedName && planSpec === normalizedSpec
|
||||
})
|
||||
|
||||
if (!matchedPlan) {
|
||||
return `该物料【${normalizedName} × ${normalizedSpec}】不在计划清单中,请检查`
|
||||
}
|
||||
|
||||
const planQty = matchedPlan.quantity ?? 0
|
||||
|
||||
// 已扫数量(去重合并)
|
||||
const alreadyScanned = cartItems.value
|
||||
.filter(ci => {
|
||||
const ciName = (ci.name || '').trim()
|
||||
const ciSpec = (ci.spec_model || '').trim()
|
||||
return ciName === normalizedName && ciSpec === normalizedSpec
|
||||
})
|
||||
.reduce((sum, ci) => sum + (ci.out_quantity || 0), 0)
|
||||
|
||||
if (alreadyScanned + scannedQty > planQty) {
|
||||
return `【${normalizedName} × ${normalizedSpec}】超出计划数量(计划: ${planQty},已扫: ${alreadyScanned},本次: ${scannedQty})`
|
||||
}
|
||||
|
||||
return null // 通过
|
||||
}
|
||||
|
||||
// --- 初始化 ---
|
||||
onMounted(() => {
|
||||
// 加载已审批通过的申请单列表
|
||||
loadApprovalRequests()
|
||||
|
||||
if (userStore.username) {
|
||||
form.operator_name = userStore.username
|
||||
operatorOptions.value.push(userStore.username)
|
||||
@ -313,15 +468,32 @@ const handleManualInput = async () => {
|
||||
const code = barcodeInput.value.trim()
|
||||
if (!code) return
|
||||
|
||||
// ★ 按单出库模式:必须先选择申请单
|
||||
if (outboundMode.value === 'by-request' && !selectedRequest.value) {
|
||||
ElMessage.warning('请先选择要出库的审批申请单')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 1. 检查购物车重复
|
||||
// 1. 检查购物车重复(直接模式走旧的追加逻辑,按单模式也复用但后续会校验)
|
||||
const existIndex = cartItems.value.findIndex(item => item.barcode === code || item.sku === code)
|
||||
if (existIndex > -1) {
|
||||
const item = cartItems.value[existIndex]
|
||||
const maxQty = parseFloat(item.available_quantity)
|
||||
|
||||
// ★ 按单模式:追加时仍需校验计划数量
|
||||
if (outboundMode.value === 'by-request') {
|
||||
const err = validateAgainstPlan(item.name, item.spec_model, 1)
|
||||
if (err) {
|
||||
ElMessage.error(err)
|
||||
if (navigator.vibrate) navigator.vibrate([200, 100, 200])
|
||||
barcodeInput.value = ''
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const maxQty = parseFloat(item.available_quantity)
|
||||
if (item.out_quantity < maxQty) {
|
||||
item.out_quantity++
|
||||
ElMessage.success(`数量+1 (当前: ${item.out_quantity})`)
|
||||
@ -343,16 +515,29 @@ const handleManualInput = async () => {
|
||||
if (availQty <= 0) {
|
||||
ElMessage.warning(`库存不足或已出库 (余: ${availQty})`)
|
||||
if (navigator.vibrate) navigator.vibrate([100, 50, 100])
|
||||
} else {
|
||||
// 加入购物车
|
||||
cartItems.value.push({
|
||||
...item,
|
||||
out_quantity: 1,
|
||||
price: parseFloat(item.price || 0)
|
||||
})
|
||||
ElMessage.success(`添加成功: ${item.name}`)
|
||||
if (navigator.vibrate) navigator.vibrate(100)
|
||||
barcodeInput.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
// ★ 按单模式:扫码加入前校验是否在计划清单内
|
||||
if (outboundMode.value === 'by-request') {
|
||||
const err = validateAgainstPlan(item.name, item.spec_model, 1)
|
||||
if (err) {
|
||||
ElMessage.error(err)
|
||||
if (navigator.vibrate) navigator.vibrate([200, 100, 200])
|
||||
barcodeInput.value = ''
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 加入购物车
|
||||
cartItems.value.push({
|
||||
...item,
|
||||
out_quantity: 1,
|
||||
price: parseFloat(item.price || 0)
|
||||
})
|
||||
ElMessage.success(`添加成功: ${item.name}`)
|
||||
if (navigator.vibrate) navigator.vibrate(100)
|
||||
barcodeInput.value = ''
|
||||
}
|
||||
} catch (error: any) {
|
||||
@ -393,6 +578,7 @@ const clearAll = () => {
|
||||
signatureFile.value = null
|
||||
signaturePreviewUrl.value = ''
|
||||
barcodeInput.value = ''
|
||||
// ★ 按单模式:仅清空购物车,保留申请单选择
|
||||
})
|
||||
}
|
||||
|
||||
@ -416,40 +602,67 @@ const submitForm = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 上传签名
|
||||
// 1. 上传签名
|
||||
const uploadRes = await uploadFile(signatureFile.value)
|
||||
const signatureUrl = uploadRes.data.url
|
||||
|
||||
const itemsPayload = cartItems.value.map(item => ({
|
||||
stock_id: item.id,
|
||||
source_table: item.source_table,
|
||||
sku: item.sku,
|
||||
barcode: item.barcode,
|
||||
quantity: item.out_quantity,
|
||||
price: item.price
|
||||
}))
|
||||
// 2. 核心保护:坚决杜绝 undefined、null 和 0
|
||||
const itemsPayload = cartItems.value.map(item => {
|
||||
// 强制确保出库数量是一个大于 0 的有效数字
|
||||
let safeQuantity = Number(item.out_quantity)
|
||||
if (isNaN(safeQuantity) || safeQuantity <= 0) {
|
||||
safeQuantity = 1 // 兜底:只要扫了码,最少出 1 个
|
||||
}
|
||||
|
||||
await submitOutbound({
|
||||
items: itemsPayload,
|
||||
return {
|
||||
stock_id: item.id || 0,
|
||||
source_table: item.source_table || '',
|
||||
// 如果原数据 sku 是空,强制塞一个默认字符串,绝不传空值给后端引发 None 报错
|
||||
sku: item.sku ? String(item.sku) : (item.barcode ? String(item.barcode) : 'NO_SKU'),
|
||||
barcode: item.barcode ? String(item.barcode) : '',
|
||||
quantity: safeQuantity,
|
||||
price: item.price ? Number(item.price) : 0
|
||||
}
|
||||
})
|
||||
|
||||
if (itemsPayload.length === 0) {
|
||||
ElMessage.warning('请至少扫描一件物料后再提交出库')
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 组装发给后端的包
|
||||
const submitPayload: any = {
|
||||
outbound_type: form.outbound_type,
|
||||
request_id: outboundMode.value === 'by-request' && selectedRequest.value ? selectedRequest.value.id : null,
|
||||
consumer_name: form.consumer_name,
|
||||
operator_name: form.operator_name,
|
||||
remark: form.remark,
|
||||
signature_path: signatureUrl
|
||||
})
|
||||
signature_path: signatureUrl,
|
||||
items: itemsPayload
|
||||
}
|
||||
|
||||
// 打印在前端控制台,你可以按 F12 在 Console 里核对这把"铁证"
|
||||
console.log('准备提交给后端的最终数据:', JSON.parse(JSON.stringify(submitPayload)))
|
||||
|
||||
// 4. 发送请求
|
||||
await submitOutbound(submitPayload)
|
||||
|
||||
ElMessage.success('出库成功')
|
||||
// 重置
|
||||
|
||||
// 5. 成功后重置页面
|
||||
cartItems.value = []
|
||||
form.consumer_name = ''
|
||||
form.remark = ''
|
||||
signatureFile.value = null
|
||||
signaturePreviewUrl.value = ''
|
||||
loadHistoryOperators()
|
||||
|
||||
// 根据你的项目实际变量重置签名组件,如果没有这句可以删掉
|
||||
if (typeof signaturePreviewUrl !== 'undefined') {
|
||||
signaturePreviewUrl.value = ''
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('提交失败')
|
||||
console.error('出库报错:', error)
|
||||
ElMessage.error('提交失败,请检查数据')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -547,6 +760,39 @@ onUnmounted(() => {
|
||||
.title-box { font-size: 16px; font-weight: bold; display: flex; align-items: center; gap: 8px; }
|
||||
.header-price { font-size: 18px; color: #F56C6C; font-weight: bold; }
|
||||
|
||||
/* ★ 双轨制模式切换 */
|
||||
.mode-switch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
.mode-hint { color: #909399; font-size: 13px; }
|
||||
|
||||
/* ★ 审批单选择 */
|
||||
.approval-request-select { margin-bottom: 16px; }
|
||||
.select-tip { margin: 6px 0 0 0; color: #909399; font-size: 12px; }
|
||||
|
||||
/* ★ 计划清单 */
|
||||
.planned-items-section {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #f0f9eb;
|
||||
border: 1px solid #e1f3d8;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.planned-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.planned-title { font-weight: bold; font-size: 14px; color: #67C23A; }
|
||||
|
||||
/* 扫码区(卡片内触发器) */
|
||||
.scan-section { margin-bottom: 20px; }
|
||||
.camera-placeholder {
|
||||
|
||||
Reference in New Issue
Block a user