采购人根据历史上传记录来

This commit is contained in:
dxc
2026-02-10 17:41:53 +08:00
parent 8ee2a9a45b
commit d594ed7ef1
4 changed files with 53 additions and 116 deletions

View File

@ -80,7 +80,7 @@ export function getUserSuggestions(params: any) {
})
}
// 10. [新增] 链接建议
// 10. 链接建议
export function getLinkSuggestions(params: any) {
return request({
url: '/inbound/buy/suggestions/links',

View File

@ -98,13 +98,6 @@
</el-tag>
</template>
<template #default="scope" v-else-if="col.prop === 'qty_stock'">
<span class="stock-num">{{ scope.row.qty_stock }}</span>
</template>
<template #default="scope" v-else-if="col.prop === 'qty_available'">
<span class="avail-num">{{ scope.row.qty_available }}</span>
</template>
<template #default="scope" v-else-if="['arrival_photo', 'inspection_report'].includes(col.prop)">
<div v-if="getImagesOnly(scope.row[col.prop]).length > 0" style="display: flex; align-items: center; justify-content: center;">
<el-image
@ -399,8 +392,10 @@
@select="handlePurchaserSelect"
>
<template #default="{ item }">
<span>{{ item.value }}</span>
<span v-if="item.email" style="float: right; color: #999; font-size: 12px; margin-left:10px">{{ item.email }}</span>
<div style="display: flex; justify-content: space-between;">
<span style="font-weight: 500">{{ item.value }}</span>
<span v-if="item.email" style="color: #999; font-size: 12px;">{{ item.email }}</span>
</div>
</template>
</el-autocomplete>
</el-form-item>
@ -453,7 +448,6 @@
</template>
</el-dialog>
<el-dialog v-model="dialogVisibleImage" append-to-body width="50%"><img style="width: 100%" :src="dialogImageUrl" alt="Preview Image" /></el-dialog>
<el-dialog v-model="cameraDialogVisible" title="拍照上传" width="500px" append-to-body destroy-on-close :close-on-click-modal="false">
<WebRtcCamera
@ -498,9 +492,6 @@ import {
import {getLabelPreview, executePrint} from '@/api/common/print'
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
// 获取环境变量中的 API Base URL用于图片拼接
const apiBaseUrl = import.meta.env.VITE_APP_BASE_API || ''
// ------------------------------------
// 状态与变量
// ------------------------------------
@ -574,7 +565,6 @@ const stockColumns = [
]
const allColumns = [...baseColumns, ...stockColumns]
const STORAGE_KEY_COLS = 'stock_buy_visible_columns_v2'
const defaultColumns = [
'material_name', 'material_type', 'category', 'spec_model', 'unit',
@ -601,7 +591,6 @@ const form = reactive({
// 1. 供应商建议 (基于 base_id)
const fetchSupplierSuggestions = async (query: string, cb: any) => {
if (!form.base_id) {
// 如果没有选物料,不给建议,或者可以给空
cb([])
return
}
@ -623,14 +612,13 @@ const handleSupplierSelect = (item: any) => {
form.supplier_name = item.value
}
// 2. 采购人建议 (全局搜索 + 系统用户)
// 2. 采购人建议 (全局,来自历史 buy 表)
const fetchUserSuggestions = async (query: string, cb: any) => {
try {
const res: any = await getUserSuggestions({ keyword: query })
if (res.code === 200) {
// 假设后端返回 [{value: '张三', email: 'zhangsan@xxx.com'}, ...]
const users = res.data.map((user: any) => ({ value: user.value, email: user.email }))
cb(users)
// res.data = [{value: '张三', email: 'xx@xx.com'}]
cb(res.data)
} else {
cb([])
}
@ -641,7 +629,7 @@ const fetchUserSuggestions = async (query: string, cb: any) => {
const querySearchPurchaser = (qs: string, cb: any) => fetchUserSuggestions(qs, cb)
const handlePurchaserSelect = (item: any) => {
form.purchaser = item.value
// 核心:选中采购人时,自动填邮箱
// 自动填邮箱
if (item.email) {
form.purchaser_email = item.email
}
@ -653,7 +641,6 @@ const fetchLinkSuggestions = async (query: string, cb: any, type: 'original' | '
try {
const res: any = await getLinkSuggestions({ base_id: form.base_id, type })
if (res.code === 200) {
// 后端返回 ['http://...', 'http://...']
const links = res.data.map((link: string) => ({ value: link }))
const filtered = query ? links.filter((item:any) => item.value.toLowerCase().includes(query.toLowerCase())) : links
cb(filtered)
@ -686,10 +673,6 @@ const onMaterialSelected = (val: number) => {
form.category = item.category
form.unit = item.unit
form.material_type = item.type
// 切换物料后,清空跟物料相关的供应商、链接,因为它们不再适用新物料
// form.supplier_name = '' // 可选:是否清空
// form.source_link = ''
// form.detail_link = ''
checkHistoryAndSetMode(item.id)
}
}
@ -699,7 +682,6 @@ const onMaterialSelected = (val: number) => {
// ------------------------------------
const validateUnique = (rule: any, value: string, callback: any) => {
if (!value) return callback()
// 前端仅做当前页面的简单重复提示,真正的校验在后端
const isDuplicate = tableData.value.some((row: any) => {
if (dialogStatus.value === 'update' && row.id === form.id) return false
if (rule.field === 'serial_number' && row.serial_number === value) return true
@ -724,12 +706,11 @@ const rules = {
// 自动计算批号逻辑
const checkHistoryAndSetMode = async (baseId: number) => {
try {
const res: any = await getBuyList({page: 1, pageSize: 1000}) // 获取最近数据
const res: any = await getBuyList({page: 1, pageSize: 1000})
const historyItems = (res.data.items || []).filter((item: any) => item.base_id === baseId)
if (historyItems.length > 0) {
modeLocked.value = true
// 找最新的那条记录
const latest = historyItems.sort((a: any, b: any) => b.id - a.id)[0]
if (latest.serial_number) {
entryMode.value = 'serial'
@ -738,7 +719,6 @@ const checkHistoryAndSetMode = async (baseId: number) => {
} else {
entryMode.value = 'batch'
form.serial_number = ''
// 自动递增批号
form.batch_number = incrementBatchNumber(latest.batch_number || '000000')
}
} else {
@ -807,7 +787,7 @@ const handleUpdate = (row: any) => {
source_link: row.source_link, detail_link: row.detail_link,
arrival_photo: row.arrival_photo || [], inspection_report: row.inspection_report || []
})
// 核心:回显图片时,使用 getImageUrl 补全路径
// 回显图片补全路径
arrivalFileList.value = form.arrival_photo.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) }))
const reports = form.inspection_report || []
const reportImgs = reports.filter(r => !isExternalLink(r))
@ -844,11 +824,9 @@ const submitForm = async () => {
}
} else { await updateBuyInbound(form.id!, payload); ElMessage.success('更新成功') }
await fetchData()
visible.value = false
} catch (e: any) {
// 重点:捕获后端唯一性校验错误
ElMessage.error(e.msg || '操作失败')
} finally { submitting.value = false }
}
@ -859,15 +837,16 @@ const submitForm = async () => {
// 图片/文件处理 (核心修复)
// ------------------------------------
// 1. 路径补全如果是http开头则直接用否则拼接 apiBaseUrl
// 1. 路径补全
const getImageUrl = (url: string) => {
if (!url) return ''
if (url.startsWith('http') || url.startsWith('https') || url.startsWith('blob:')) {
return url
}
// 拼接 API 基础路径,例如 http://localhost:5000 + /static/files/xxx.jpg
// 注意处理斜杠,防止双斜杠
const baseUrl = apiBaseUrl.endsWith('/') ? apiBaseUrl.slice(0, -1) : apiBaseUrl
// 获取 VITE_APP_BASE_API如果未设置则默认为空
const apiBase = import.meta.env.VITE_APP_BASE_API || ''
// 去除末尾斜杠
const baseUrl = apiBase.endsWith('/') ? apiBase.slice(0, -1) : apiBase
const path = url.startsWith('/') ? url : '/' + url
return baseUrl + path
}
@ -892,16 +871,14 @@ const customUpload = async (options: any, targetField: 'arrival_photo' | 'inspec
if (res.code === 200) {
const newUrl = res.data.url // 后端返回的相对路径
// 1. 存入表单数据
// 1. 存入表单数据 (相对路径)
form[targetField].push(newUrl)
// 2. 核心修复:显式更新 fileList 以确保缩略图显示
// 需要拼接完整路径用于展示
// 2. 显式更新 fileList (完整路径,用于显示)
const fullUrl = getImageUrl(newUrl)
const fileObj = { name: file.name, url: fullUrl, status: 'success', uid: file.uid }
if (targetField === 'arrival_photo') {
// 替换或追加
const idx = arrivalFileList.value.findIndex(f => f.uid === file.uid)
if (idx > -1) arrivalFileList.value[idx] = fileObj
else arrivalFileList.value.push(fileObj)
@ -925,9 +902,8 @@ const customUpload = async (options: any, targetField: 'arrival_photo' | 'inspec
const handleRemoveImage = async (uploadFile: any, targetField: 'arrival_photo' | 'inspection_report') => {
try {
// 这里需要反向查找,因为 uploadFile.url 可能是带域名的完整路径,而 form 里存的是相对路径
// 简单比对末尾文件名
const filename = uploadFile.url.split('/').pop()
// 尝试匹配完整路径或文件名
const urlToRemove = form[targetField].find(u => u.endsWith(filename)) || uploadFile.url
form[targetField] = form[targetField].filter(u => u !== urlToRemove)
@ -966,7 +942,7 @@ const handleCameraConfirm = async (file: File) => {
// 更新表单
form[field].push(newUrl)
// 更新文件列表 (使用 getImageUrl 补全显示)
// 更新文件列表 (完整路径)
const fileObj = { name: file.name, url: getImageUrl(newUrl) }
if (field === 'arrival_photo') {
arrivalFileList.value.push(fileObj)