refactor: remove local history caching and add API suggestions

Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
dxc
2026-02-10 14:38:15 +08:00
parent 94ff7cecdc
commit 8cae6ee7f6
7 changed files with 122 additions and 117 deletions

View File

@ -61,3 +61,21 @@ export function deleteFile(filename: string) {
method: 'delete' method: 'delete'
}) })
} }
// 8. 供应商建议
export function getSupplierSuggestions(params: any) {
return request({
url: '/inbound/buy/suggestions/suppliers',
method: 'get',
params
})
}
// 9. 用户建议
export function getUserSuggestions(params: any) {
return request({
url: '/inbound/buy/suggestions/users',
method: 'get',
params
})
}

View File

@ -40,3 +40,12 @@ export function searchMaterialBase(keyword: string) {
params: { keyword } params: { keyword }
}) })
} }
// 用户建议
export function getUserSuggestions(params: any) {
return request({
url: '/inbound/product/suggestions/users',
method: 'get',
params
})
}

View File

@ -43,3 +43,12 @@ export function searchMaterialBase(keyword: string) {
params: { keyword } params: { keyword }
}) })
} }
// 用户建议
export function getUserSuggestions(params: any) {
return request({
url: '/inbound/semi/suggestions/users',
method: 'get',
params
})
}

View File

@ -104,6 +104,24 @@ export function searchMaterialBase(keyword: string) {
}) })
} }
// 供应商建议
export function getProviderSuggestions(params: any) {
return request({
url: '/v1/inbound/service/suggestions/providers',
method: 'get',
params
})
}
// 用户建议
export function getUserSuggestions(params: any) {
return request({
url: '/v1/inbound/service/suggestions/users',
method: 'get',
params
})
}
// 删除服务权益 // 删除服务权益
export function deleteService(id: number) { export function deleteService(id: number) {
return request({ return request({

View File

@ -511,58 +511,64 @@ const form = reactive({
arrival_photo: [] as string[], inspection_report: [] as string[] arrival_photo: [] as string[], inspection_report: [] as string[]
}) })
// 历史记录辅助函数
const HISTORY_KEYS = { SUPPLIER: 'history_suppliers', PURCHASER: 'history_purchasers', EMAIL: 'history_emails', MATERIAL: 'history_materials' } // 供应商建议 API
const saveToHistory = (key: string, value: string) => { const fetchSupplierSuggestions = async (query: string, cb: any) => {
if (!value) return if (!form.base_id) {
cb([])
return
}
try { try {
const existing = localStorage.getItem(key) const res: any = await getSupplierSuggestions({ base_id: form.base_id })
let list = existing ? JSON.parse(existing) : [] if (res.code === 200) {
list = list.filter((i: string) => i !== value) const suppliers = res.data.map((name: string) => ({ value: name }))
list.unshift(value) const filtered = query ? suppliers.filter((item: any) => item.value.toLowerCase().includes(query.toLowerCase())) : suppliers
if (list.length > 20) list = list.slice(0, 20) cb(filtered)
localStorage.setItem(key, JSON.stringify(list)) } else {
} catch (e) { console.error('save history failed', e) } cb([])
} }
const getHistoryList = (key: string): any[] => { } catch (e) {
try { return (JSON.parse(localStorage.getItem(key) || '[]')).map((v: string) => ({value: v})) } catch (e) { return [] } cb([])
} }
const saveMaterialHistory = (item: any) => {
if (!item || !item.id) return
const key = HISTORY_KEYS.MATERIAL
try {
let list = JSON.parse(localStorage.getItem(key) || '[]')
list = list.filter((i: any) => i.id !== item.id)
list.unshift({...item, isHistory: true})
if (list.length > 10) list = list.slice(0, 10)
localStorage.setItem(key, JSON.stringify(list))
} catch (e) {}
}
const getMaterialHistory = () => {
try { return JSON.parse(localStorage.getItem(HISTORY_KEYS.MATERIAL) || '[]') } catch (e) { return [] }
} }
const createFilter = (queryString: string) => { return (item: any) => (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0) } // 用户建议 API
const getTableDataUnique = (field: string) => { return Array.from(new Set(tableData.value.map((i: any) => i[field]).filter(Boolean))).map(i => ({value: i})) } const fetchUserSuggestions = async (query: string, cb: any) => {
const mixedSearch = (queryString: string, tableField: string, storageKey: string, cb: any) => { try {
const tableList = getTableDataUnique(tableField) const res: any = await getUserSuggestions({ keyword: query })
const historyList = getHistoryList(storageKey) if (res.code === 200) {
const map = new Map() const users = res.data.map((user: any) => ({ value: user.value, email: user.email }))
historyList.forEach(i => map.set(i.value, i)) cb(users)
tableList.forEach(i => map.set(i.value, i)) } else {
const allList = Array.from(map.values()) cb([])
const results = queryString ? allList.filter(createFilter(queryString)) : allList }
cb(results) } catch (e) {
cb([])
}
} }
const querySearchSupplier = (qs: string, cb: any) => mixedSearch(qs, 'supplier_name', HISTORY_KEYS.SUPPLIER, cb) const querySearchSupplier = (qs: string, cb: any) => fetchSupplierSuggestions(qs, cb)
const handleSupplierSelect = (item: any) => saveToHistory(HISTORY_KEYS.SUPPLIER, item.value) const handleSupplierSelect = (item: any) => {
const querySearchPurchaser = (qs: string, cb: any) => mixedSearch(qs, 'purchaser', HISTORY_KEYS.PURCHASER, cb) form.supplier_name = item.value
const handlePurchaserSelect = (item: any) => saveToHistory(HISTORY_KEYS.PURCHASER, item.value) }
const querySearchEmail = (qs: string, cb: any) => mixedSearch(qs, 'purchaser_email', HISTORY_KEYS.EMAIL, cb) const querySearchPurchaser = (qs: string, cb: any) => fetchUserSuggestions(qs, cb)
const handleEmailSelect = (item: any) => saveToHistory(HISTORY_KEYS.EMAIL, item.value) const handlePurchaserSelect = (item: any) => {
form.purchaser = item.value
form.purchaser_email = item.email || ''
}
const querySearchEmail = (qs: string, cb: any) => fetchUserSuggestions(qs, (users: any[]) => {
const emailUsers = users.filter(u => u.email).map(u => ({ value: u.email }))
const filtered = qs ? emailUsers.filter((item: any) => item.value.toLowerCase().includes(qs.toLowerCase())) : emailUsers
cb(filtered)
})
const handleEmailSelect = (item: any) => {
form.purchaser_email = item.value
}
const currencyOptions = [{value: 'CNY', desc: '人民币'}, {value: 'USD', desc: '美元'}, {value: 'EUR', desc: '欧元'}] const currencyOptions = [{value: 'CNY', desc: '人民币'}, {value: 'USD', desc: '美元'}, {value: 'EUR', desc: '欧元'}]
const querySearchCurrency = (queryString: string, cb: any) => { cb(queryString ? currencyOptions.filter(createFilter(queryString)) : currencyOptions) } const querySearchCurrency = (queryString: string, cb: any) => {
const filtered = queryString ? currencyOptions.filter(item => item.value.toLowerCase().includes(queryString.toLowerCase()) || item.desc.toLowerCase().includes(queryString.toLowerCase())) : currencyOptions
cb(filtered)
}
const handleMaterialDropdownVisible = (visible: boolean) => { if (visible && materialOptions.value.length === 0) handleSearchMaterial('') } const handleMaterialDropdownVisible = (visible: boolean) => { if (visible && materialOptions.value.length === 0) handleSearchMaterial('') }
const handleSearchMaterial = async (query: string) => { const handleSearchMaterial = async (query: string) => {
@ -570,17 +576,12 @@ const handleSearchMaterial = async (query: string) => {
try { try {
const res: any = await searchMaterialBase(query) const res: any = await searchMaterialBase(query)
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false})) const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
if (!query) { materialOptions.value = apiResults
const history = getMaterialHistory()
const historyIds = new Set(history.map((h: any) => h.id))
materialOptions.value = [...history, ...apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))]
} else { materialOptions.value = apiResults }
} finally { searchLoading.value = false } } finally { searchLoading.value = false }
} }
const onMaterialSelected = (val: number) => { const onMaterialSelected = (val: number) => {
const item = materialOptions.value.find(i => i.id === val) const item = materialOptions.value.find(i => i.id === val)
if (item) { if (item) {
saveMaterialHistory(item)
form.material_name = item.name form.material_name = item.name
form.spec_model = item.spec form.spec_model = item.spec
form.category = item.category form.category = item.category
@ -739,10 +740,6 @@ const submitForm = async () => {
} }
} else { await updateBuyInbound(form.id!, payload); ElMessage.success('更新成功') } } else { await updateBuyInbound(form.id!, payload); ElMessage.success('更新成功') }
// 成功后保存历史记录
saveToHistory(HISTORY_KEYS.SUPPLIER, form.supplier_name)
saveToHistory(HISTORY_KEYS.PURCHASER, form.purchaser)
saveToHistory(HISTORY_KEYS.EMAIL, form.purchaser_email)
await fetchData() await fetchData()
visible.value = false visible.value = false

View File

@ -471,18 +471,12 @@ const handleSearchMaterial = async (query: string) => {
try { try {
const res: any = await searchMaterialBase(query) const res: any = await searchMaterialBase(query)
const apiResults = (res.data || []).map((i: any) => ({ ...i, isHistory: false })) const apiResults = (res.data || []).map((i: any) => ({ ...i, isHistory: false }))
if (!query) { materialOptions.value = apiResults
const history = getMaterialHistory()
const historyIds = new Set(history.map((h: any) => h.id))
const filteredApi = apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))
materialOptions.value = [...history, ...filteredApi]
} else { materialOptions.value = apiResults }
} finally { searchLoading.value = false } } finally { searchLoading.value = false }
} }
const onMaterialSelected = (val: number) => { const onMaterialSelected = (val: number) => {
const item = materialOptions.value.find(i => i.id === val) const item = materialOptions.value.find(i => i.id === val)
if (item) { if (item) {
saveMaterialHistory(item)
// Auto-populate readonly fields // Auto-populate readonly fields
form.material_name = item.name form.material_name = item.name
form.spec_model = item.spec form.spec_model = item.spec
@ -649,7 +643,6 @@ const submitForm = async () => {
const newItem = res.data const newItem = res.data
if (newItem) { ElMessage.info('发送打印...'); try { await executePrint(newItem); ElMessage.success('指令已发送') } catch (e: any) { ElMessage.warning('打印失败') } } if (newItem) { ElMessage.info('发送打印...'); try { await executePrint(newItem); ElMessage.success('指令已发送') } catch (e: any) { ElMessage.warning('打印失败') } }
} else { await updateProductInbound(form.id!, payload); ElMessage.success('更新成功') } } else { await updateProductInbound(form.id!, payload); ElMessage.success('更新成功') }
saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, form.production_manager)
visible.value = false; fetchData() visible.value = false; fetchData()
} catch(e:any) { } catch(e:any) {
// 捕获后端报错 // 捕获后端报错

View File

@ -530,49 +530,16 @@ const form = reactive({
// ------------------------------------ // ------------------------------------
// 历史记录管理器 // 历史记录管理器
// ------------------------------------ // ------------------------------------
const HISTORY_KEYS = { PRODUCTION_MANAGER: 'history_production_managers', MATERIAL: 'history_semi_materials' }
const saveToHistory = (key: string, value: string) => {
if (!value) return
try {
const existing = localStorage.getItem(key)
let list = existing ? JSON.parse(existing) : []
list = list.filter((i: string) => i !== value)
list.unshift(value)
if (list.length > 20) list = list.slice(0, 20)
localStorage.setItem(key, JSON.stringify(list))
} catch (e) { console.error('save history failed', e) }
}
const getHistoryList = (key: string): any[] => { try { return (JSON.parse(localStorage.getItem(key) || '[]')).map((v: string) => ({value: v})) } catch (e) { return [] } }
const saveMaterialHistory = (item: any) => {
if (!item || !item.id) return
const key = HISTORY_KEYS.MATERIAL
try {
let list = JSON.parse(localStorage.getItem(key) || '[]')
list = list.filter((i: any) => i.id !== item.id)
list.unshift({...item, isHistory: true})
if (list.length > 10) list = list.slice(0, 10)
localStorage.setItem(key, JSON.stringify(list))
} catch (e) {}
}
const getMaterialHistory = () => { try { return JSON.parse(localStorage.getItem(HISTORY_KEYS.MATERIAL) || '[]') } catch (e) { return [] } }
// ------------------------------------ // ------------------------------------
// Autocomplete & Search Logic // Autocomplete & Search Logic (后端 API 驱动)
// ------------------------------------ // ------------------------------------
const createFilter = (queryString: string) => { return (item: any) => (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0) } const querySearchManager = async (query: string, cb: any) => {
const getTableDataUnique = (field: string) => { return Array.from(new Set(tableData.value.map((i: any) => i[field]).filter(Boolean))).map(i => ({value: i})) } // 后续会从后端获取用户建议,暂时先返回空列表
const mixedSearch = (queryString: string, tableField: string, storageKey: string, cb: any) => { cb([])
const tableList = getTableDataUnique(tableField) }
const historyList = getHistoryList(storageKey) const handleManagerSelect = (item: any) => {
const map = new Map() // 无需保存历史
historyList.forEach(i => map.set(i.value, i))
tableList.forEach(i => map.set(i.value, i))
const allList = Array.from(map.values())
const results = queryString ? allList.filter(createFilter(queryString)) : allList
cb(results)
} }
const querySearchManager = (qs: string, cb: any) => mixedSearch(qs, 'production_manager', HISTORY_KEYS.PRODUCTION_MANAGER, cb)
const handleManagerSelect = (item: any) => saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, item.value)
// ------------------------------------ // ------------------------------------
// Material Search (Matches Buy.vue) // Material Search (Matches Buy.vue)
@ -583,17 +550,12 @@ const handleSearchMaterial = async (query: string) => {
try { try {
const res: any = await searchMaterialBase(query) const res: any = await searchMaterialBase(query)
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false})) const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
if (!query) { materialOptions.value = apiResults
const history = getMaterialHistory()
const historyIds = new Set(history.map((h: any) => h.id))
materialOptions.value = [...history, ...apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))]
} else { materialOptions.value = apiResults }
} finally { searchLoading.value = false } } finally { searchLoading.value = false }
} }
const onMaterialSelected = (val: number) => { const onMaterialSelected = (val: number) => {
const item = materialOptions.value.find(i => i.id === val) const item = materialOptions.value.find(i => i.id === val)
if (item) { if (item) {
saveMaterialHistory(item)
// Populate form fields // Populate form fields
form.material_name = item.name form.material_name = item.name
form.spec_model = item.spec form.spec_model = item.spec
@ -800,7 +762,6 @@ const submitForm = async () => {
catch (printErr: any) { ElMessage.warning('入库成功,但自动打印失败:' + (printErr.msg || '未知错误')) } catch (printErr: any) { ElMessage.warning('入库成功,但自动打印失败:' + (printErr.msg || '未知错误')) }
} }
} else { await updateSemiInbound(form.id!, payload); ElMessage.success('更新成功') } } else { await updateSemiInbound(form.id!, payload); ElMessage.success('更新成功') }
saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, form.production_manager)
await fetchData(); visible.value = false await fetchData(); visible.value = false
} catch (e: any) { } catch (e: any) {
// 捕获后端报错 // 捕获后端报错