初步增加, 扫码登录成功
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Alert, Button, Card, Col, Descriptions, Image, Radio, Row, Select, Space, Steps, Tag, Typography, message } from 'antd';
|
||||
import { CheckCircleOutlined, QrcodeOutlined, ReloadOutlined, ShoppingCartOutlined, WechatOutlined } from '@ant-design/icons';
|
||||
import { yybApi } from '../api/modules';
|
||||
import type { YybSelectionOptions, YybTask } from '../api/types';
|
||||
import { getUser } from '../store/auth';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const phaseIndex: Record<string, number> = { login: 0, selection: 1, payment: 2, completed: 3 };
|
||||
|
||||
export default function YybRechargePage() {
|
||||
const [task, setTask] = useState<YybTask | null>(null);
|
||||
const [options, setOptions] = useState<YybSelectionOptions | null>(null);
|
||||
const [platform, setPlatform] = useState<'android' | 'ios'>('android');
|
||||
const [points, setPoints] = useState<number>();
|
||||
const [zoneId, setZoneId] = useState<string>();
|
||||
const [roleId, setRoleId] = useState<string>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { can } = usePermissions(getUser());
|
||||
|
||||
const refresh = async () => {
|
||||
if (!task) return;
|
||||
try { setTask(await yybApi.getTask(task.id)); } catch { /* 保留当前状态 */ }
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!task || ['success', 'failed'].includes(task.status)) return;
|
||||
const timer = window.setInterval(refresh, 2500);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [task?.id, task?.status]);
|
||||
|
||||
useEffect(() => {
|
||||
if (task?.status === 'ready' && task.phase === 'selection' && !options) {
|
||||
void loadOptions(platform);
|
||||
}
|
||||
}, [task?.status, task?.phase]);
|
||||
|
||||
const loadOptions = async (nextPlatform: 'android' | 'ios', nextPoints?: number, nextZone?: string) => {
|
||||
if (!task) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await yybApi.options(task.id, nextPlatform, nextPoints, nextZone);
|
||||
setOptions(data);
|
||||
if (nextPoints === undefined && data.default_product) setPoints(data.default_product.points);
|
||||
if (nextZone === undefined && data.default_zone) setZoneId(data.default_zone.zone_id);
|
||||
setRoleId(undefined);
|
||||
} catch (error) { message.error(error instanceof Error ? error.message : '查询商品失败'); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
|
||||
const selectedProduct = useMemo(() => options?.products.find(item => item.points === points), [options, points]);
|
||||
const selectedZone = useMemo(() => options?.zones.find(item => item.zone_id === zoneId), [options, zoneId]);
|
||||
const selectedRole = useMemo(() => options?.roles.find(item => item.role_id === roleId), [options, roleId]);
|
||||
|
||||
const createTask = async () => {
|
||||
setLoading(true);
|
||||
try { setTask(await yybApi.createTask()); setOptions(null); } catch (error) { message.error(error instanceof Error ? error.message : '创建任务失败'); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
const startLogin = async (provider: 'qq' | 'wechat') => {
|
||||
if (!task) return;
|
||||
setLoading(true);
|
||||
try { setTask(await yybApi.login(task.id, provider)); } catch (error) { message.error(error instanceof Error ? error.message : '启动登录失败'); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
const submitSelection = async () => {
|
||||
if (!task || !selectedProduct || !selectedZone || !selectedRole) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
setTask(await yybApi.select(task.id, { platform, points: selectedProduct.points, product_id: selectedProduct.product_id, zone_id: selectedZone.zone_id, zone_name: selectedZone.name, role_id: selectedRole.role_id, role_name: selectedRole.name }));
|
||||
message.success('选择已保存');
|
||||
} catch (error) { message.error(error instanceof Error ? error.message : '保存选择失败'); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
const createPayment = async () => {
|
||||
if (!task) return;
|
||||
setLoading(true);
|
||||
try { setTask(await yybApi.payment(task.id)); } catch (error) { message.error(error instanceof Error ? error.message : '创建付款码失败'); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
|
||||
return <Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
<Row justify="space-between" align="middle"><Title level={3} style={{ margin: 0 }}>应用宝和平精英充值</Title><Button icon={<ReloadOutlined />} onClick={task ? refresh : createTask}>{task ? '刷新任务' : '新建充值任务'}</Button></Row>
|
||||
<Steps current={phaseIndex[task?.phase || 'login']} items={[{ title: '扫码登录' }, { title: '选择充值信息' }, { title: '微信付款' }, { title: '完成' }]} />
|
||||
{!task && <Card><Space direction="vertical"><Text>每次充值使用独立的 YYB 登录会话。</Text><Button type="primary" icon={<ShoppingCartOutlined />} onClick={createTask} loading={loading}>开始充值</Button></Space></Card>}
|
||||
{task && <>
|
||||
{task.status === 'failed' && <Alert type="error" showIcon message={task.message || '任务失败'} />}
|
||||
{task.phase === 'login' && <Card title="扫码登录"><Space direction="vertical" size={12}><Radio.Group value={task.provider || undefined} onChange={event => void startLogin(event.target.value)} disabled={loading}><Radio.Button value="qq">QQ 登录</Radio.Button><Radio.Button value="wechat"><WechatOutlined /> 微信登录</Radio.Button></Radio.Group>{task.login_qr_data && <Image width={240} preview src={`data:${task.login_qr_mime_type || 'image/jpeg'};base64,${task.login_qr_data}`} />}<Text type="secondary">{task.message}</Text></Space></Card>}
|
||||
{task.phase === 'selection' && <Card title="选择充值信息" loading={loading}><Space direction="vertical" style={{ width: '100%' }}><Radio.Group value={platform} onChange={event => { const value = event.target.value; setPlatform(value); void loadOptions(value); }}><Radio.Button value="android">Android 区</Radio.Button><Radio.Button value="ios">iOS 区</Radio.Button></Radio.Group>{options && <Row gutter={[12, 12]}><Col xs={24} md={8}><Select style={{ width: '100%' }} placeholder="点券档位" value={points} options={options.products.map(item => ({ value: item.points, label: `${item.points} 点券(${(item.price_fen / 100).toFixed(2)} 元)` }))} onChange={value => { setPoints(value); void loadOptions(platform, value, zoneId); }} /></Col><Col xs={24} md={8}><Select style={{ width: '100%' }} placeholder="区服" value={zoneId} options={options.zones.map(item => ({ value: item.zone_id, label: `${item.name}(${item.zone_id})` }))} onChange={value => { setZoneId(value); void loadOptions(platform, points, value); }} /></Col><Col xs={24} md={8}><Select style={{ width: '100%' }} placeholder="角色" value={roleId} options={options.roles.filter(item => item.ban_status !== '1').map(item => ({ value: item.role_id, label: `${item.name}(${item.role_id})` }))} onChange={setRoleId} /></Col></Row>}<Button type="primary" disabled={!selectedProduct || !selectedZone || !selectedRole} onClick={submitSelection}>确认充值信息</Button></Space></Card>}
|
||||
{task.phase === 'payment' && <Card title="微信付款"><Descriptions column={1} size="small"><Descriptions.Item label="平台">{task.platform === 'ios' ? 'iOS' : 'Android'}</Descriptions.Item><Descriptions.Item label="商品">{task.points} 点券</Descriptions.Item><Descriptions.Item label="区服">{task.zone_name}</Descriptions.Item><Descriptions.Item label="角色">{task.role_name}({task.role_id})</Descriptions.Item></Descriptions>{task.payment_qr_data ? <Space direction="vertical"><Image width={280} preview src={`data:${task.payment_qr_mime_type || 'image/png'};base64,${task.payment_qr_data}`} /><Tag icon={<QrcodeOutlined />} color="blue">请使用微信扫码付款</Tag></Space> : can('yyb:recharge') ? <Button type="primary" icon={<WechatOutlined />} onClick={createPayment} loading={loading || task.status === 'running'} disabled={task.status === 'running'}>{task.status === 'running' ? '正在生成付款码' : '生成微信付款码'}</Button> : <Text type="secondary">当前账号没有创建付款码权限。</Text>}<div><Text type="secondary">{task.message}</Text></div></Card>}
|
||||
{task.phase === 'completed' && <Alert type="success" showIcon icon={<CheckCircleOutlined />} message="充值订单已确认完成" description={task.message} />}
|
||||
</>}
|
||||
</Space>;
|
||||
}
|
||||
Reference in New Issue
Block a user