修改:前端判断剩余生图次数
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/prod Build is passing

This commit is contained in:
Linzm committed 2026-11-03 15:52:24 +08:00
1 parent 6dd4b4a724
commit 505c585772
1 file changed
+81 -7
+81 -7
View File
@@ -197,7 +197,15 @@ const caseItems = [
{ label: config.messages.caseLabels[2], src: config.images.caseScene },
]
/**
* 剩余次数:
* - 首屏用接口 remain_count 打底
* - 之后只按「本地点成功出图」递减
* - 接口若未扣减(仍返回原值),不会把次数加回去
*/
const remainCount = ref(0)
/** 打底时已统计到的成功次数;新增成功 = 当前成功数 - baselineUsed */
const remainBaselineUsed = ref(0)
const sessionId = ref('')
const subjectName = ref('')
/** stat 返回的主体文案(订单约定主体,标签始终展示此值) */
@@ -270,6 +278,64 @@ const isEditing = computed(() => !!editingResultId.value)
const isPendingCartoon = computed(
() => Boolean(pendingPreview.value && photoInspect.value && isCartoonImage(photoInspect.value)),
)
/** 统计已消耗的生图次数:成功 task + 对话里已出的结果卡 */
function countUsedGenerations() {
const ids = new Set<string>()
const detail = sessionDetail.value as { tasks?: Array<Record<string, any>> } | null
detail?.tasks?.forEach((task) => {
const taskId = String(task?.task_id || '').trim()
if (!taskId) return
const images = Array.isArray(task.images) ? task.images : []
const hasImage = images.some((image: any) => Boolean(image?.url))
const status = String(task.status || '').toLowerCase()
if (status === 'success' || hasImage) ids.add(`task:${taskId}`)
})
messages.value.forEach((msg) => {
if (msg.type !== 'result') return
const taskId = String(msg.taskId || '').trim()
// 与 task 去重;无 taskId 的本地结果用消息 id
ids.add(taskId ? `task:${taskId}` : `msg:${msg.id}`)
})
return ids.size
}
/**
* 首屏打底:接口 remain_count 在京东侧常不随出图减少,这里当作可生图总额,
* 再按会话内已成功次数扣除;后续只减不增。
*/
function seedRemainFromApi(apiRemainRaw?: unknown) {
const apiRemain = Number(apiRemainRaw)
remainBaselineUsed.value = 0
remainCount.value = Number.isFinite(apiRemain) ? Math.max(0, apiRemain) : 0
refreshRemainByUsed()
}
/** 按已成功出图数重算剩余(只减不加) */
function refreshRemainByUsed(apiRemainRaw?: unknown) {
const used = countUsedGenerations()
const earned = Math.max(0, used - remainBaselineUsed.value)
remainCount.value = Math.max(0, remainCount.value - earned)
remainBaselineUsed.value = used
const apiRemain = Number(apiRemainRaw)
if (!Number.isFinite(apiRemain)) return
// 接口若已扣得更少,跟随接口;偏高(未扣)则忽略,避免加回次数
if (apiRemain < remainCount.value) {
remainCount.value = Math.max(0, apiRemain)
}
}
/** 生图接口成功即扣 1 次(不等待过渡动画),并抬高 baseline 避免结果入列后重复扣 */
function noteGenerationSuccess() {
remainCount.value = Math.max(0, remainCount.value - 1)
remainBaselineUsed.value += 1
}
function resetRemainQuota() {
remainCount.value = 0
remainBaselineUsed.value = 0
}
const titleSpec = computed(() => productTitleSpecFromOrder(orderStat.value))
const hasRequiredTitle = computed(() => {
if (!titleSpec.value) return true
@@ -655,17 +721,17 @@ function onGenerateStageDone(generatingId: string) {
}
if (pending.uploadId) lastUploadId.value = pending.uploadId
remainCount.value = Math.max(0, remainCount.value - 1)
// 成功扣次已在 noteGenerationSuccess;这里只把 baseline 与结果卡对齐
remainBaselineUsed.value = Math.max(remainBaselineUsed.value, countUsedGenerations())
isBusy.value = false
photoInspect.value = null
scrollToBottom()
// 仅同步剩余次数,避免整页历史重绘冲掉当前气泡
// 仅同步剩余次数;接口未扣减时不会把次数加回去
void badgeApi
.getOrderStat({})
.then((res: any) => {
const count = Number(res?.remain_count)
if (Number.isFinite(count)) remainCount.value = Math.max(0, count)
orderStat.value = res || orderStat.value
refreshRemainByUsed(res?.remain_count)
})
.catch(() => {})
}
@@ -797,6 +863,8 @@ async function startGenerate(opts: {
startedAt,
}
patchGeneratingResult(generatingId, resultUrl)
// 接口已成功出图:立刻扣剩余次数(不依赖过渡动画是否播完)
noteGenerationSuccess()
scrollToBottom()
} catch (error) {
removeGenerating(generatingId)
@@ -942,6 +1010,8 @@ function applySessionHistory(detail: ReturnType<typeof jingdongApi.parseSessionD
})
messages.value = next
if (latestUpload) lastUploadId.value = latestUpload
// 历史回填后:用接口总额 - 已成功次数
seedRemainFromApi(orderStat.value?.remain_count)
scrollToBottom()
}
@@ -949,6 +1019,7 @@ function fetchSessionDetail(sid: string) {
const id = String(sid || '').trim()
if (!id) {
sessionDetail.value = null
seedRemainFromApi(orderStat.value?.remain_count)
return Promise.resolve()
}
return jingdongApi
@@ -965,6 +1036,7 @@ function fetchSessionDetail(sid: string) {
})
.catch(() => {
sessionDetail.value = null
seedRemainFromApi(orderStat.value?.remain_count)
})
}
@@ -974,19 +1046,21 @@ function fetchOrderStat() {
.then((res: any) => {
// interceptor 已解包 data:remain_count / subject_name / type_name / session_id / prod_id ...
orderStat.value = res || {}
const count = Number(res?.remain_count)
remainCount.value = Number.isFinite(count) ? Math.max(0, count) : 0
subjectNameFromStat.value = String(res?.subject_name || '').trim()
subjectName.value = subjectNameFromStat.value
styleName.value = String(res?.style_name || '').trim()
typeName.value = String(res?.type_name || '').trim()
sessionId.value = String(res?.session_id || '').trim()
subjectMatched.value = true
// 先记下接口值;等会话历史回来后再按已生成次数扣除
const apiRemain = Number(res?.remain_count)
remainCount.value = Number.isFinite(apiRemain) ? Math.max(0, apiRemain) : 0
remainBaselineUsed.value = 0
return fetchSessionDetail(sessionId.value)
})
.catch(() => {
orderStat.value = null
remainCount.value = 0
resetRemainQuota()
subjectName.value = ''
subjectNameFromStat.value = ''
styleName.value = ''