优化
This commit is contained in:
@@ -56,6 +56,8 @@ describe('CarrierRatesView price-book source contract', () => {
|
|||||||
expect(source).toContain('sourceDestinationName')
|
expect(source).toContain('sourceDestinationName')
|
||||||
expect(source).toContain('autoSelectExactWarehouseMappings')
|
expect(source).toContain('autoSelectExactWarehouseMappings')
|
||||||
expect(source).toContain("'DUPLICATE_RATE'")
|
expect(source).toContain("'DUPLICATE_RATE'")
|
||||||
|
expect(source).toContain('.channel-tabs { width: 100%; min-width: 0;')
|
||||||
|
expect(source).toContain('.channel-preview :deep(.el-table) { width: 100%; max-width: 100%; min-width: 0; }')
|
||||||
expect(source).not.toContain('tenantId')
|
expect(source).not.toContain('tenantId')
|
||||||
expect(source).not.toContain('客户选择')
|
expect(source).not.toContain('客户选择')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -847,7 +847,8 @@ onMounted(() => {
|
|||||||
.mapping-load-error { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px; border: 1px solid color-mix(in srgb, var(--cc-danger) 24%, var(--cc-border)); border-radius: 8px; background: #fff; }
|
.mapping-load-error { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px; border: 1px solid color-mix(in srgb, var(--cc-danger) 24%, var(--cc-border)); border-radius: 8px; background: #fff; }
|
||||||
.mapping-load-error strong { color: var(--cc-danger); font-size: 13px; }
|
.mapping-load-error strong { color: var(--cc-danger); font-size: 13px; }
|
||||||
.mapping-load-error p { margin: 4px 0; color: var(--cc-text); font-size: 12px; }
|
.mapping-load-error p { margin: 4px 0; color: var(--cc-text); font-size: 12px; }
|
||||||
.channel-tabs { margin-bottom: 0; }
|
.channel-tabs { width: 100%; min-width: 0; margin-bottom: 0; overflow: hidden; }
|
||||||
|
.channel-preview :deep(.el-table) { width: 100%; max-width: 100%; min-width: 0; }
|
||||||
.channel-meta { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: -4px 0 8px; color: var(--cc-muted); font-size: 12px; }
|
.channel-meta { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: -4px 0 8px; color: var(--cc-muted); font-size: 12px; }
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.table-actions :deep(.el-button) { min-height: 40px; }
|
.table-actions :deep(.el-button) { min-height: 40px; }
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
correctWeightResult,
|
correctWeightResult,
|
||||||
createWeightResult,
|
createWeightResult,
|
||||||
downloadWeightImage,
|
downloadWeightImage,
|
||||||
|
getWeightOcrRawText,
|
||||||
getWeightWorkspace,
|
getWeightWorkspace,
|
||||||
|
normalizeWeightConfidence,
|
||||||
retryWeightRecognition,
|
retryWeightRecognition,
|
||||||
uploadWeightImage,
|
uploadWeightImage,
|
||||||
} from './weightOcr'
|
} from './weightOcr'
|
||||||
@@ -23,6 +25,31 @@ describe('inbound weight OCR API', () => {
|
|||||||
vi.mocked(http.put).mockReset()
|
vi.mocked(http.put).mockReset()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('normalizes OCR confidence to the firstleg decimal contract', () => {
|
||||||
|
expect(normalizeWeightConfidence(0.5097191334)).toBe(0.50972)
|
||||||
|
expect(normalizeWeightConfidence(1.2)).toBe(1)
|
||||||
|
expect(normalizeWeightConfidence(-0.2)).toBe(0)
|
||||||
|
expect(normalizeWeightConfidence(Number.NaN)).toBeNull()
|
||||||
|
expect(normalizeWeightConfidence(null)).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('accepts current and compatible OCR raw-text response fields', () => {
|
||||||
|
const upload = {
|
||||||
|
fileId: '1',
|
||||||
|
fileName: 'weight.jpg',
|
||||||
|
contentType: 'image/jpeg',
|
||||||
|
sizeBytes: 100,
|
||||||
|
recognitionStatus: 'SUCCESS',
|
||||||
|
weightValue: 12.5,
|
||||||
|
weightUnit: 'KG',
|
||||||
|
confidence: 0.98,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
expect(getWeightOcrRawText({ ...upload, ocrRawText: ' 12.5 KG ' })).toBe('12.5 KG')
|
||||||
|
expect(getWeightOcrRawText({ ...upload, rawText: 'display: 12.5' })).toBe('display: 12.5')
|
||||||
|
expect(getWeightOcrRawText(upload)).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
it('loads the measurement workspace using the real firstleg path', async () => {
|
it('loads the measurement workspace using the real firstleg path', async () => {
|
||||||
const detail = { inboundId: '9007199254740993', weightResults: [] }
|
const detail = { inboundId: '9007199254740993', weightResults: [] }
|
||||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: detail })
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: detail })
|
||||||
@@ -61,13 +88,13 @@ describe('inbound weight OCR API', () => {
|
|||||||
ocrRawText: null,
|
ocrRawText: null,
|
||||||
ocrWeightValue: 12.5,
|
ocrWeightValue: 12.5,
|
||||||
ocrWeightUnit: 'KG',
|
ocrWeightUnit: 'KG',
|
||||||
confidence: 0.98,
|
confidence: 0.5097191334,
|
||||||
}
|
}
|
||||||
await createWeightResult('10001', '20001', createPayload)
|
await createWeightResult('10001', '20001', createPayload)
|
||||||
expect(http.post).toHaveBeenNthCalledWith(
|
expect(http.post).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
'/platform/v1/first-leg/inbounds/20001/weight-results',
|
'/platform/v1/first-leg/inbounds/20001/weight-results',
|
||||||
createPayload,
|
{ ...createPayload, confidence: 0.50972 },
|
||||||
{ params: { tenantId: '10001' } },
|
{ params: { tenantId: '10001' } },
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,4 +124,3 @@ describe('inbound weight OCR API', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,25 @@ import type {
|
|||||||
const measurementBasePath = '/platform/v1/first-leg/inbounds'
|
const measurementBasePath = '/platform/v1/first-leg/inbounds'
|
||||||
const imageBasePath = '/platform/v1/files/measurements'
|
const imageBasePath = '/platform/v1/files/measurements'
|
||||||
const maxErrorEnvelopeBytes = 1024 * 1024
|
const maxErrorEnvelopeBytes = 1024 * 1024
|
||||||
|
const confidenceScale = 100_000
|
||||||
|
|
||||||
function segment(value: string) {
|
function segment(value: string) {
|
||||||
return encodeURIComponent(value)
|
return encodeURIComponent(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeWeightConfidence(value: number | null) {
|
||||||
|
if (value == null || !Number.isFinite(value)) return null
|
||||||
|
const clamped = Math.min(1, Math.max(0, value))
|
||||||
|
return Math.round(clamped * confidenceScale) / confidenceScale
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeightOcrRawText(result: WeightImageUploadResult) {
|
||||||
|
const rawText = result.ocrRawText ?? result.rawText
|
||||||
|
if (typeof rawText !== 'string') return null
|
||||||
|
const normalized = rawText.trim()
|
||||||
|
return normalized ? normalized.slice(0, 1000) : null
|
||||||
|
}
|
||||||
|
|
||||||
async function parseBlobError(blob: Blob, status?: number) {
|
async function parseBlobError(blob: Blob, status?: number) {
|
||||||
if (!blob.type.toLowerCase().includes('json')) return null
|
if (!blob.type.toLowerCase().includes('json')) return null
|
||||||
if (blob.size > maxErrorEnvelopeBytes) {
|
if (blob.size > maxErrorEnvelopeBytes) {
|
||||||
@@ -85,9 +99,13 @@ export async function createWeightResult(
|
|||||||
inboundId: string,
|
inboundId: string,
|
||||||
payload: CreateWeightResultRequest,
|
payload: CreateWeightResultRequest,
|
||||||
) {
|
) {
|
||||||
|
const normalizedPayload = {
|
||||||
|
...payload,
|
||||||
|
confidence: normalizeWeightConfidence(payload.confidence),
|
||||||
|
}
|
||||||
const response = await http.post<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
|
const response = await http.post<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
|
||||||
`${measurementBasePath}/${segment(inboundId)}/weight-results`,
|
`${measurementBasePath}/${segment(inboundId)}/weight-results`,
|
||||||
payload,
|
normalizedPayload,
|
||||||
{ params: { tenantId } },
|
{ params: { tenantId } },
|
||||||
)
|
)
|
||||||
return unwrapApiResponse(response)
|
return unwrapApiResponse(response)
|
||||||
@@ -120,4 +138,3 @@ export async function confirmWeightResult(
|
|||||||
)
|
)
|
||||||
return unwrapApiResponse(response)
|
return unwrapApiResponse(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ describe('WeightOcrPanel contract', () => {
|
|||||||
expect(source).toContain("['image/jpeg', 'image/png', 'image/bmp']")
|
expect(source).toContain("['image/jpeg', 'image/png', 'image/bmp']")
|
||||||
expect(source).toContain('5 * 1024 * 1024')
|
expect(source).toContain('5 * 1024 * 1024')
|
||||||
expect(source).toContain('图片服务未返回 OCR 原文')
|
expect(source).toContain('图片服务未返回 OCR 原文')
|
||||||
|
expect(source).toContain('ocrRawText: getWeightOcrRawText(upload)')
|
||||||
expect(source).toContain("adjustmentReason: 'OCR 重试识别结果'")
|
expect(source).toContain("adjustmentReason: 'OCR 重试识别结果'")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
correctWeightResult,
|
correctWeightResult,
|
||||||
createWeightResult,
|
createWeightResult,
|
||||||
downloadWeightImage,
|
downloadWeightImage,
|
||||||
|
getWeightOcrRawText,
|
||||||
getWeightWorkspace,
|
getWeightWorkspace,
|
||||||
retryWeightRecognition,
|
retryWeightRecognition,
|
||||||
uploadWeightImage,
|
uploadWeightImage,
|
||||||
@@ -153,7 +154,7 @@ async function handleFileChange(event: Event) {
|
|||||||
await createWeightResult(customerId.value, props.inboundId, {
|
await createWeightResult(customerId.value, props.inboundId, {
|
||||||
fileId: String(upload.fileId),
|
fileId: String(upload.fileId),
|
||||||
ocrProvider: 'FILE_WEIGHT_OCR',
|
ocrProvider: 'FILE_WEIGHT_OCR',
|
||||||
ocrRawText: null,
|
ocrRawText: getWeightOcrRawText(upload),
|
||||||
ocrWeightValue: upload.weightValue,
|
ocrWeightValue: upload.weightValue,
|
||||||
ocrWeightUnit: upload.weightUnit,
|
ocrWeightUnit: upload.weightUnit,
|
||||||
confidence: upload.confidence,
|
confidence: upload.confidence,
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export interface WeightImageUploadResult {
|
|||||||
weightValue: number | null
|
weightValue: number | null
|
||||||
weightUnit: string | null
|
weightUnit: string | null
|
||||||
confidence: number | null
|
confidence: number | null
|
||||||
|
ocrRawText?: string | null
|
||||||
|
rawText?: string | null
|
||||||
error: string | null
|
error: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,4 +61,3 @@ export interface CorrectWeightResultRequest {
|
|||||||
export interface ConfirmWeightResultRequest {
|
export interface ConfirmWeightResultRequest {
|
||||||
version: number
|
version: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user