Close WeChat QR flow when binding is confirmed
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ArrowLeft, Loader2, MessageCircle, Pause, Play, Plus, QrCode, RefreshCw, Unplug, X } from 'lucide-react';
|
||||
import { ArrowLeft, CheckCircle2, Loader2, MessageCircle, Pause, Play, Plus, QrCode, RefreshCw, Unplug, X } from 'lucide-react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -48,6 +48,7 @@ export function ChannelAccounts() {
|
||||
const [notice, setNotice] = useState('');
|
||||
const [qr, setQr] = useState<CloudWechatBindState | null>(null);
|
||||
const [qrAccountId, setQrAccountId] = useState('');
|
||||
const [connectedAccountId, setConnectedAccountId] = useState('');
|
||||
const [verifyCode, setVerifyCode] = useState('');
|
||||
const [confirmDisconnect, setConfirmDisconnect] = useState(false);
|
||||
const [pending, setPending] = useState<CloudPendingOperation[]>([]);
|
||||
@@ -75,7 +76,7 @@ export function ChannelAccounts() {
|
||||
useEffect(() => {
|
||||
if (!selected) return;
|
||||
setTargetSlug(params.get('target') ?? selected.target_agent_slug ?? '');
|
||||
setQr(null); setQrAccountId(''); setVerifyCode(''); setConfirmDisconnect(false);
|
||||
setQr(null); setQrAccountId(''); setConnectedAccountId(''); setVerifyCode(''); setConfirmDisconnect(false);
|
||||
const next = new URLSearchParams(params);
|
||||
next.set('account', selected.id);
|
||||
next.delete('target');
|
||||
@@ -121,21 +122,30 @@ export function ChannelAccounts() {
|
||||
channel_account_id: selected.id, operation_id: operationId(), expected_revision: selected.revision,
|
||||
}), enabled ? '渠道已启用。' : '渠道已暂停,账号连接仍保留。');
|
||||
};
|
||||
const applyBindState = useCallback((result: CloudWechatBindState, accountId: string) => {
|
||||
if (result.status === 'confirmed') {
|
||||
setQr(null); setQrAccountId(''); setVerifyCode(''); setNotice(''); setError('');
|
||||
setConnectedAccountId(accountId);
|
||||
} else {
|
||||
setQr(result); setQrAccountId(accountId); setConnectedAccountId('');
|
||||
}
|
||||
}, []);
|
||||
const startQr = async (force: boolean) => {
|
||||
if (!selected) return;
|
||||
setConnectedAccountId(''); setVerifyCode('');
|
||||
const result = await run('qr', () => cloudAgentsApi.call('channelAccountWechatBindStart', {
|
||||
channel_account_id: selected.id, operation_id: operationId(), force,
|
||||
}), '请使用个人微信扫码。');
|
||||
if (result && typeof result === 'object' && 'session_key' in result) {
|
||||
setQr(result as CloudWechatBindState); setQrAccountId(selected.id);
|
||||
applyBindState(result as CloudWechatBindState, selected.id);
|
||||
}
|
||||
};
|
||||
const verify = async () => {
|
||||
if (!selected || !qr || qrAccountId !== selected.id || !verifyCode.trim()) return;
|
||||
const result = await run('verify', () => cloudAgentsApi.call('channelAccountWechatBindVerification', {
|
||||
channel_account_id: selected.id, operation_id: operationId(), session_key: qr.session_key, verify_code: verifyCode.trim(),
|
||||
}), '微信连接已确认。');
|
||||
if (result && typeof result === 'object' && 'session_key' in result) setQr(result as CloudWechatBindState);
|
||||
}), '验证码已提交,请等待微信确认。');
|
||||
if (result && typeof result === 'object' && 'session_key' in result) applyBindState(result as CloudWechatBindState, selected.id);
|
||||
};
|
||||
const pollingAccountId = selected?.id ?? '';
|
||||
const pollingSessionKey = qr?.session_key ?? '';
|
||||
@@ -148,18 +158,18 @@ export function ChannelAccounts() {
|
||||
void cloudAgentsApi.call('channelAccountWechatBindStatus', { channel_account_id: pollingAccountId, session_key: pollingSessionKey })
|
||||
.then(result => {
|
||||
if (stopped || !alive.current) return;
|
||||
setQr(result);
|
||||
if (result.status === 'confirmed') void refresh();
|
||||
applyBindState(result, pollingAccountId);
|
||||
if (result.status === 'confirmed') void refresh().catch(failure => { if (alive.current) setError(errorText(failure)); });
|
||||
}).catch(failure => { if (!stopped && alive.current) setError(errorText(failure)); });
|
||||
}, 2000);
|
||||
return () => { stopped = true; window.clearInterval(timer); };
|
||||
}, [pollingAccountId, pollingSessionKey, pollingStatus, qrAccountId, refresh]);
|
||||
}, [pollingAccountId, pollingSessionKey, pollingStatus, qrAccountId, refresh, applyBindState]);
|
||||
const disconnect = async () => {
|
||||
if (!selected) return;
|
||||
await run('disconnect', () => cloudAgentsApi.call('disconnectChannelAccount', {
|
||||
channel_account_id: selected.id, operation_id: operationId(), expected_revision: selected.revision,
|
||||
}), '微信账号已断开。');
|
||||
setConfirmDisconnect(false); setQr(null);
|
||||
setConfirmDisconnect(false); setQr(null); setConnectedAccountId('');
|
||||
};
|
||||
|
||||
return <section className="h-full overflow-auto p-4 sm:p-6" data-testid="channel-accounts-page">
|
||||
@@ -201,6 +211,14 @@ export function ChannelAccounts() {
|
||||
: <Button disabled={Boolean(busy) || selected.health !== 'connected' || !selected.target_agent_slug} onClick={() => void setEnabled(true)}><Play className="mr-2 h-4 w-4" />启用</Button>}
|
||||
<Button variant="ghost" className="text-destructive" disabled={Boolean(busy)} onClick={() => setConfirmDisconnect(true)}><Unplug className="mr-2 h-4 w-4" />断开连接</Button>
|
||||
</div>
|
||||
{connectedAccountId === selected.id && <div role="status" className="flex items-start gap-3 rounded-xl bg-emerald-50 p-4 text-emerald-800">
|
||||
<CheckCircle2 className="mt-0.5 h-5 w-5 shrink-0" aria-hidden="true" />
|
||||
<div><p className="font-medium">微信已连接</p><p className="mt-1 text-sm">
|
||||
{!selected.target_agent_slug ? '请选择目标智能体并保存,再启用渠道开始对话。'
|
||||
: !selected.enabled ? '点击“启用”开启渠道,之后就可以在微信中与智能体对话。'
|
||||
: '连接已完成,可以返回微信与智能体对话。'}
|
||||
</p></div>
|
||||
</div>}
|
||||
{qr && qrAccountId === selected.id && <div className="flex flex-wrap gap-5 rounded-xl border border-border p-4" aria-label="微信扫码连接">
|
||||
<div className="flex h-48 w-48 items-center justify-center rounded-lg bg-white p-3">{qr.qrcode_url ? <CloudWechatQr value={qr.qrcode_url} /> : <Loader2 className="h-7 w-7 animate-spin" />}</div>
|
||||
<div className="min-w-52 flex-1 space-y-3"><div className="flex items-start justify-between"><div><p className="font-medium">{label(qr.status)}</p><p className="mt-1 text-xs text-muted-foreground">扫码只负责账号连接,目标智能体可稍后选择。</p>{qr.message && <p className="mt-1 text-xs text-muted-foreground">{qr.message}</p>}</div>
|
||||
|
||||
Reference in New Issue
Block a user