feat: 新增票据夹模块并优化 OCR 与员工画像服务
后端新增票据夹端点、数据模型和服务模块,优化 OCR 端点 Schema 和附件操作逻辑,完善员工行为画像服务和辅助函数, 前端新增票据夹视图和服务层,优化文档中心样式和侧边栏导 航,完善员工画像详情弹窗和权限控制,补充单元测试。
This commit is contained in:
@@ -4,6 +4,7 @@ export function recognizeOcrFiles(files, options = {}) {
|
||||
const formData = new FormData()
|
||||
for (const file of files) {
|
||||
formData.append('files', file)
|
||||
formData.append('receipt_ids', String(file?.receiptId || ''))
|
||||
}
|
||||
|
||||
return apiRequest('/ocr/recognize', {
|
||||
|
||||
49
web/src/services/receiptFolder.js
Normal file
49
web/src/services/receiptFolder.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { apiRequest } from './api.js'
|
||||
|
||||
function buildStatusQuery(status = 'all') {
|
||||
const normalized = String(status || 'all').trim()
|
||||
return normalized ? `?status=${encodeURIComponent(normalized)}` : ''
|
||||
}
|
||||
|
||||
export function fetchReceiptFolderItems(status = 'all') {
|
||||
return apiRequest(`/receipt-folder${buildStatusQuery(status)}`)
|
||||
}
|
||||
|
||||
export function fetchReceiptFolderDetail(receiptId) {
|
||||
return apiRequest(`/receipt-folder/${encodeURIComponent(String(receiptId || '').trim())}`)
|
||||
}
|
||||
|
||||
export function updateReceiptFolderItem(receiptId, payload = {}) {
|
||||
return apiRequest(`/receipt-folder/${encodeURIComponent(String(receiptId || '').trim())}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteReceiptFolderItem(receiptId) {
|
||||
return apiRequest(`/receipt-folder/${encodeURIComponent(String(receiptId || '').trim())}`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchReceiptFolderAsset(pathOrUrl) {
|
||||
const target = String(pathOrUrl || '').trim()
|
||||
if (!target) {
|
||||
throw new Error('票据文件地址为空。')
|
||||
}
|
||||
return apiRequest(target, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
export async function buildReceiptFile(receipt) {
|
||||
const blob = await fetchReceiptFolderAsset(receipt?.source_url || receipt?.sourceUrl)
|
||||
const fileName = String(receipt?.file_name || receipt?.fileName || 'receipt.bin').trim() || 'receipt.bin'
|
||||
const mediaType = String(receipt?.media_type || receipt?.mediaType || blob.type || 'application/octet-stream')
|
||||
const file = new File([blob], fileName, { type: mediaType })
|
||||
Object.defineProperty(file, 'receiptId', {
|
||||
value: String(receipt?.id || ''),
|
||||
enumerable: false
|
||||
})
|
||||
return file
|
||||
}
|
||||
@@ -91,6 +91,9 @@ export function deleteExpenseClaimItem(claimId, itemId) {
|
||||
export function uploadExpenseClaimItemAttachment(claimId, itemId, file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
if (file?.receiptId) {
|
||||
formData.append('receipt_id', String(file.receiptId))
|
||||
}
|
||||
|
||||
return apiRequest(`/reimbursements/claims/${encodeURIComponent(String(claimId || '').trim())}/items/${encodeURIComponent(String(itemId || '').trim())}/attachment`, {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user