完善虎牙已有绑定角色确认与重新扫码换绑流程
This commit is contained in:
@@ -54,7 +54,9 @@ export default function CustomerFulfillmentPage() {
|
||||
const [loginInfo, setLoginInfo] = useState<CustomerLoginInfo | null>(null)
|
||||
const [bindingInfo, setBindingInfo] = useState<CustomerBindingInfo | null>(null)
|
||||
const [loginQrImage, setLoginQrImage] = useState('')
|
||||
const [bindingActionLoading, setBindingActionLoading] = useState(false)
|
||||
const startingRef = useRef(false)
|
||||
const hydratingBindingRef = useRef(false)
|
||||
|
||||
const selectedTask = useMemo(
|
||||
() => tasks.find((task) => task.id === selectedTaskId) || tasks[0] || null,
|
||||
@@ -182,6 +184,34 @@ export default function CustomerFulfillmentPage() {
|
||||
return () => window.clearInterval(timer)
|
||||
}, [message, orderNo, selectedTask?.bindingStatus, selectedTask?.id, selectedTask?.loginStatus])
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedTask || !orderNo || bindingInfo || hydratingBindingRef.current) return
|
||||
if (
|
||||
selectedTask.loginStatus !== 'authenticated' ||
|
||||
selectedTask.bindingStatus !== 'role_ready'
|
||||
) {
|
||||
return
|
||||
}
|
||||
hydratingBindingRef.current = true
|
||||
void pollCustomerBinding(orderNo, selectedTask.id)
|
||||
.then((response) => {
|
||||
setTasks((current) =>
|
||||
current.map((task) => (task.id === response.data.task.id ? response.data.task : task)),
|
||||
)
|
||||
if (response.data.binding) setBindingInfo(response.data.binding)
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
hydratingBindingRef.current = false
|
||||
})
|
||||
}, [
|
||||
bindingInfo,
|
||||
orderNo,
|
||||
selectedTask?.bindingStatus,
|
||||
selectedTask?.id,
|
||||
selectedTask?.loginStatus,
|
||||
])
|
||||
|
||||
async function handleStartLogin() {
|
||||
if (!selectedTask) return
|
||||
setLoading(true)
|
||||
@@ -201,6 +231,7 @@ export default function CustomerFulfillmentPage() {
|
||||
|
||||
async function handleConfirmBinding() {
|
||||
if (!selectedTask) return
|
||||
setBindingActionLoading(true)
|
||||
try {
|
||||
const response = await confirmCustomerBinding(orderNo, selectedTask.id)
|
||||
setTasks((current) =>
|
||||
@@ -210,6 +241,26 @@ export default function CustomerFulfillmentPage() {
|
||||
message.success('绑定已确认,等待客服处理')
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '确认绑定失败')
|
||||
} finally {
|
||||
setBindingActionLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStartRebind() {
|
||||
if (!selectedTask) return
|
||||
setBindingActionLoading(true)
|
||||
try {
|
||||
const response = await startCustomerBinding(orderNo, selectedTask.id, {
|
||||
forceRebind: true,
|
||||
})
|
||||
setTasks((current) =>
|
||||
current.map((task) => (task.id === response.data.task.id ? response.data.task : task)),
|
||||
)
|
||||
setBindingInfo(response.data.binding)
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '创建换绑二维码失败')
|
||||
} finally {
|
||||
setBindingActionLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +316,14 @@ export default function CustomerFulfillmentPage() {
|
||||
const bindingQrVisible =
|
||||
selectedTask?.loginStatus === 'authenticated' &&
|
||||
['qr_pending', 'scanned', 'role_ready'].includes(selectedTask.bindingStatus)
|
||||
const bindingQrPending = ['qr_pending', 'scanned'].includes(selectedTask?.bindingStatus || '')
|
||||
const bindingRole = bindingInfo?.bindGameRole || selectedTask?.bindGameRole || ''
|
||||
const bindingAccount = bindingInfo?.bindGameAccount || selectedTask?.bindGameAccount || ''
|
||||
const bindingChannel = bindingInfo?.bindGameChannel || selectedTask?.bindGameChannel || ''
|
||||
const roleChoiceRequired =
|
||||
selectedTask?.bindingStatus === 'role_ready' &&
|
||||
bindingInfo?.hasExistingBinding === true &&
|
||||
bindingInfo?.rebindRequested !== true
|
||||
|
||||
return (
|
||||
<div className="customer-fulfillment-page">
|
||||
@@ -374,32 +433,66 @@ export default function CustomerFulfillmentPage() {
|
||||
) : null}
|
||||
|
||||
{bindingQrVisible ? (
|
||||
<Card title="第二步:扫码绑定游戏角色">
|
||||
<Card
|
||||
title={
|
||||
selectedTask.bindingStatus === 'role_ready'
|
||||
? '第二步:确认游戏角色'
|
||||
: '第二步:扫码绑定游戏角色'
|
||||
}
|
||||
>
|
||||
<Space direction="vertical" align="center" size={14} style={{ width: '100%' }}>
|
||||
{bindingInfo?.qrImage ? (
|
||||
{bindingQrPending && bindingInfo?.qrImage ? (
|
||||
<Image src={bindingInfo.qrImage} alt="游戏角色绑定二维码" width={260} />
|
||||
) : (
|
||||
) : bindingQrPending ? (
|
||||
<Spin tip="生成绑定二维码..." />
|
||||
)}
|
||||
) : null}
|
||||
<Text type="secondary">
|
||||
{bindingInfo?.message ||
|
||||
config.bindingInstructions ||
|
||||
'请使用虎牙 App 扫码完成游戏角色绑定。'}
|
||||
</Text>
|
||||
{bindingInfo?.bindGameRole ? (
|
||||
{bindingRole ? (
|
||||
<Alert
|
||||
type="success"
|
||||
type={roleChoiceRequired ? 'warning' : 'success'}
|
||||
showIcon
|
||||
message={`已识别角色:${bindingInfo.bindGameRole}`}
|
||||
description={[bindingInfo.bindGameAccount, bindingInfo.bindGameChannel]
|
||||
.filter(Boolean)
|
||||
.join(' · ')}
|
||||
message={
|
||||
roleChoiceRequired
|
||||
? `当前已绑定角色:${bindingRole}`
|
||||
: `已识别角色:${bindingRole}`
|
||||
}
|
||||
description={[bindingAccount, bindingChannel].filter(Boolean).join(' · ')}
|
||||
/>
|
||||
) : null}
|
||||
{selectedTask.bindingStatus === 'role_ready' ? (
|
||||
<Button type="primary" onClick={() => void handleConfirmBinding()}>
|
||||
确认绑定角色
|
||||
</Button>
|
||||
<Space direction="vertical" align="center" size={10}>
|
||||
{roleChoiceRequired ? (
|
||||
<Text type="secondary">
|
||||
请确认这是本次需要的游戏角色;角色不正确时请重新扫码换绑。
|
||||
</Text>
|
||||
) : null}
|
||||
<Space wrap>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={bindingActionLoading}
|
||||
onClick={() => void handleConfirmBinding()}
|
||||
>
|
||||
{roleChoiceRequired
|
||||
? '使用当前角色'
|
||||
: bindingInfo?.rebindRequested
|
||||
? '确认换绑角色'
|
||||
: '确认绑定角色'}
|
||||
</Button>
|
||||
{roleChoiceRequired ? (
|
||||
<Button
|
||||
danger
|
||||
loading={bindingActionLoading}
|
||||
onClick={() => void handleStartRebind()}
|
||||
>
|
||||
角色不对,重新扫码换绑
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</Space>
|
||||
) : null}
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
@@ -31,10 +31,14 @@ export function pollCustomerLogin(orderNo: string, taskId: number) {
|
||||
)
|
||||
}
|
||||
|
||||
export function startCustomerBinding(orderNo: string, taskId: number) {
|
||||
export function startCustomerBinding(
|
||||
orderNo: string,
|
||||
taskId: number,
|
||||
options: { forceRebind?: boolean } = {},
|
||||
) {
|
||||
return apiPost<{ task: CustomerSemiAutoTask; binding: CustomerBindingInfo }>(
|
||||
`/api/v1/customer-fulfillment/${encodeURIComponent(orderNo)}/tasks/${taskId}/binding/start`,
|
||||
{},
|
||||
options.forceRebind ? { forceRebind: true } : {},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,5 +41,7 @@ export type CustomerBindingInfo = {
|
||||
bindGameAccount: string
|
||||
bindGameRole: string
|
||||
bindGameChannel: string
|
||||
hasExistingBinding?: boolean
|
||||
rebindRequested?: boolean
|
||||
message: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user