import { computed, type ComputedRef, type Ref } from 'vue' import type { ClaimDetailData } from '@/types/claim' import type { TencentLoginType } from '@/types/tencent/session' import { DEFAULT_LOGIN_TYPE } from './sessionConstants' // --------------------------------------------------------------------------- // syncLoginTypeFromDetail // --------------------------------------------------------------------------- export function syncLoginTypeFromDetail( detail: T, fallback: TencentLoginType = DEFAULT_LOGIN_TYPE, ): TencentLoginType { const nextLoginType = String(detail.session?.loginType || detail.task.loginType || '').trim() if (nextLoginType === 'wx') { return 'wx' } if (nextLoginType === 'qq') { return 'qq' } return fallback } // --------------------------------------------------------------------------- // resolveTaskStatusLabel // --------------------------------------------------------------------------- export type TaskStatusLabelContext = 'claim' | 'admin' /** * Resolves a human-readable task status label. * * The `context` parameter controls wording differences between the * consumer-facing claim page and the admin manual-redeem page. */ export function resolveTaskStatusLabel( taskStatus: string | undefined, sessionStatus: string | undefined, context: TaskStatusLabelContext, ): string { switch (taskStatus) { case 'link_generated': return context === 'admin' ? '待生成二维码' : '等待开始领取' case 'claimed': if (sessionStatus === 'scanned') { return context === 'admin' ? '客户待确认' : '确认中' } if (sessionStatus === 'logged_in' || sessionStatus === 'ready_to_redeem') { return context === 'admin' ? '已登录待复核' : '已登录' } return context === 'admin' ? '等待客户登录' : '领取中' case 'role_confirmed': return '角色已确认' case 'redeeming': return '正在兑换' case 'redeemed': return context === 'admin' ? '兑换完成' : '兑换成功' case 'waiting_inventory': return '等待库存' case 'retry_pending': return '等待重试' case 'manual_review': return '等待人工处理' case 'expired': return '链接已过期' case 'closed': return '任务已关闭' default: return sessionStatus || taskStatus || '等待中' } } // --------------------------------------------------------------------------- // roleFacts / resultFacts helpers // --------------------------------------------------------------------------- export interface FactItem { label: string value: string accent?: boolean } export interface UseSessionPresentationOptions { activityInfo: ComputedRef<{ nickname?: string role?: { roleId?: string roleName?: string area?: string ready?: boolean } | null } | null> statusLabel: ComputedRef /** Context-specific default values for roleFacts */ roleFactDefaults: { nickname: string } } export function useSessionRoleFacts(options: UseSessionPresentationOptions) { const { activityInfo, statusLabel, roleFactDefaults } = options const roleFacts = computed(() => [ { label: '登录昵称', value: activityInfo.value?.nickname || roleFactDefaults.nickname, }, { label: '当前角色', value: activityInfo.value?.role?.roleName || '后端浏览器同步中', accent: true, }, { label: '角色 ID', value: activityInfo.value?.role?.roleId || '未识别', }, { label: '所在大区', value: activityInfo.value?.role?.area || '未识别', }, ]) return { roleFacts } } export interface UseSessionResultFactsOptions { order: ComputedRef<{ platformOrderId?: string } | null> orderItem: ComputedRef<{ skuName?: string } | null> result: ComputedRef<{ resultCode?: string; resultMessage?: string } | null> /** Admin context has manualRequest.proofValue as an alternative first label */ resultFactOverrides?: { firstLabel?: string firstValue?: ComputedRef } } export function useSessionResultFacts(options: UseSessionResultFactsOptions) { const { order, orderItem, result, resultFactOverrides } = options const resultFacts = computed(() => [ { label: resultFactOverrides?.firstLabel || '订单号', value: resultFactOverrides?.firstValue?.value || order.value?.platformOrderId || '-', }, { label: '商品', value: orderItem.value?.skuName || '-', }, { label: '业务返回码', value: result.value?.resultCode || '-', }, { label: '业务消息', value: result.value?.resultMessage || '尚未兑换', }, ]) return { resultFacts } } // --------------------------------------------------------------------------- // redeemBlockedReason — builds the "why can't I redeem" explanation // --------------------------------------------------------------------------- export function buildRedeemBlockedReason(options: { detail: ComputedRef hasSession: ComputedRef loginTypeLabel: ComputedRef redeemLoading: ComputedRef roleReady: ComputedRef roleConfirmed: ComputedRef sessionNotice: ComputedRef context: 'claim' | 'admin' /** Claim-specific fields */ tokenStatus?: ComputedRef requiresSupportReview?: ComputedRef }): string { const { detail, hasSession, loginTypeLabel, redeemLoading, roleReady, roleConfirmed, sessionNotice, context, tokenStatus, requiresSupportReview, } = options if (context === 'claim') { if (!detail.value) { return '正在加载领取信息' } if (tokenStatus && tokenStatus.value !== 'active') { return '当前领取链接不可用' } } else { // admin context if (!detail.value) { return '请先创建人工兑换任务并预占库存' } } if (!hasSession.value) { return context === 'claim' ? `请先选择${loginTypeLabel.value}并初始化登录会话` : `请先生成${loginTypeLabel.value}二维码并等待客户登录` } // Claim-specific: support review flow if (context === 'claim' && requiresSupportReview?.value) { if (!roleReady.value) { return '扫码成功后,系统会自动同步登录信息,准备发给客服复核' } return '当前商品需要客服复核角色并代你发起兑换,请联系人工继续' } if (redeemLoading.value) { return '兑换任务正在执行中' } if (!roleReady.value) { return ( sessionNotice.value || (context === 'claim' ? '扫码成功后,后端正在同步角色和大区信息' : '客户登录后,系统会自动同步角色和大区信息') ) } if (!roleConfirmed.value) { return context === 'claim' ? '请先确认当前角色与大区无误,再开始兑换' : '请先让客户确认角色截图,再点击确认当前角色' } return '' }