新增:京东下单页面

This commit is contained in:
Linzm committed 2026-11-03 14:13:25 +08:00
1 parent 8e8cde9ef5
commit 007c675603
25 files changed
+3264 -25

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
/**
* 原图分类 /classify(对齐 agent image-inspect)
* - 本地:Vite 代理 `/concept-classify` → `/v1/classify`
* - 线上:直连 seetacloud
*/
export type ImageKind = 'cartoon' | 'real_photo' | 'other' | 'uncertain'
export interface ImageInspect {
imageType: ImageKind
isCartoon: boolean
personCount: number
petCount: number
reviewStatus: string
conclusion: string
}
export class InspectCancelled extends Error {
constructor() {
super('cancelled')
this.name = 'InspectCancelled'
}
}
const INSPECT_TIMEOUT_MS = 120_000
const DEFAULT_INSPECT_HOST = 'https://u252424-8570-01f615a5.cqa1.seetacloud.com:8443'
const DEFAULT_INSPECT_REMOTE_URL = `${DEFAULT_INSPECT_HOST}/v1/classify`
function asRecord(value: unknown) {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, unknown>)
: null
}
export function mapInspectImageType(raw: unknown): ImageKind {
const text = String(raw ?? '')
.trim()
.toLowerCase()
if (text === 'cartoon' || text === '1') return 'cartoon'
if (text === 'real_photo' || text === 'real' || text === '2') return 'real_photo'
if (text === 'other') return 'other'
return 'uncertain'
}
function resolveInspectUrl() {
const fromEnv = String(
(import.meta as any).env?.VITE_CONCEPT_INSPECT_URL || '',
)
.trim()
.replace(/\/+$/, '')
if (fromEnv) {
if (/\/v1\/classify$/i.test(fromEnv)) return fromEnv
return `${fromEnv}/v1/classify`
}
if ((import.meta as any).env?.DEV) return '/concept-classify'
return DEFAULT_INSPECT_REMOTE_URL
}
function resolveInspectToken() {
return String((import.meta as any).env?.VITE_CONCEPT_INSPECT_TOKEN || '').trim()
}
function countField(data: Record<string, unknown>, keys: string[]) {
for (const key of keys) {
const value = Number(data[key])
if (Number.isFinite(value)) return Math.max(0, Math.trunc(value))
}
return 0
}
function normalizeInspect(body: unknown): ImageInspect {
const root = asRecord(body) || {}
const data = asRecord(root.data) || root
const imageType = mapInspectImageType(data.image_type ?? data.imageType)
const isCartoonRaw = data.is_cartoon ?? data.isCartoon
const isCartoon =
typeof isCartoonRaw === 'boolean' ? isCartoonRaw : imageType === 'cartoon'
return {
imageType,
isCartoon,
personCount: countField(data, [
'person_count',
'personCount',
'headcount',
'head_count',
]),
petCount: countField(data, ['pet_count', 'petCount']),
reviewStatus: String(data.review_status || ''),
conclusion: String(data.conclusion || ''),
}
}
export function isCartoonImage(result: ImageInspect) {
return result.isCartoon || result.imageType === 'cartoon'
}
export function figureTypeOf(result: ImageInspect) {
return result.imageType === 'real_photo' ? 1 : 0
}
export function formatSubjectFromInspect(result: ImageInspect) {
const parts: string[] = []
if (result.personCount > 0) parts.push(`${result.personCount}人`)
if (result.petCount > 0) parts.push(`${result.petCount}宠物`)
return parts.join('+')
}
export async function inspectImage(file: File, signal?: AbortSignal) {
if (signal?.aborted) throw new InspectCancelled()
const url = resolveInspectUrl()
const token = resolveInspectToken()
const form = new FormData()
form.append('image', file, file.name || 'image.jpg')
const headers: Record<string, string> = {}
if (token) headers.Authorization = `Bearer ${token}`
const controller = new AbortController()
let timedOut = false
const timer = window.setTimeout(() => {
timedOut = true
controller.abort()
}, INSPECT_TIMEOUT_MS)
const onAbort = () => controller.abort()
signal?.addEventListener('abort', onAbort, { once: true })
try {
const response = await fetch(url, {
method: 'POST',
body: form,
headers,
signal: controller.signal,
})
if (!response.ok) {
if (response.status === 400) throw new Error('图片上传错误,请检查文件后重试')
if (response.status === 401) throw new Error('图片识别鉴权失败')
if (response.status === 413) throw new Error('图片过大,请压缩后重试')
throw new Error(`图片识别失败(${response.status})`)
}
const raw: unknown = await response.json()
return normalizeInspect(raw)
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
if (timedOut) throw new Error('图片识别超时,请稍后重试')
throw new InspectCancelled()
}
if (error instanceof TypeError) throw new Error('图片识别失败,请稍后重试')
throw error instanceof Error ? error : new Error('图片识别失败')
} finally {
window.clearTimeout(timer)
signal?.removeEventListener('abort', onAbort)
}
}