- 新增地址类型定义,增强前端地址数据结构 - 新增地址编辑页面,支持地址智能识别和定位选点功能 - 地址编辑支持省市区选择及默认地址设置 - 新增地址列表页面,支持地址展示、删除、编辑和选择功能 - 实现售后申请页面,支持选择售后类型和退款原因 - 售后申请支持商品选择、退款金额计算和凭证上传 - 新增售后详情页面,支持售后状态展示及申请取消 - 优化页面加载和用户交互体验,增加错误提示和权限处理
240 lines
7.6 KiB
TypeScript
240 lines
7.6 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { View, Text } from '@tarojs/components';
|
||
import { Button, Loading, Card } from '@nutui/nutui-react-taro';
|
||
import { Success, Failure, Tips, User } from '@nutui/icons-react-taro';
|
||
import Taro, { useRouter } from '@tarojs/taro';
|
||
import { confirmQRLogin } from '@/api/passport/qr-login';
|
||
import { useUser } from '@/hooks/useUser';
|
||
|
||
/**
|
||
* 扫码登录确认页面
|
||
* 用于处理从二维码跳转过来的登录确认
|
||
*/
|
||
const QRConfirmPage: React.FC = () => {
|
||
const router = useRouter();
|
||
const { user, getDisplayName } = useUser();
|
||
const [loading, setLoading] = useState(false);
|
||
const [confirmed, setConfirmed] = useState(false);
|
||
const [error, setError] = useState<string>('');
|
||
const [token, setToken] = useState<string>('');
|
||
|
||
useEffect(() => {
|
||
// 从URL参数中获取token
|
||
const { qrCodeKey, token: urlToken } = router.params;
|
||
const loginToken = qrCodeKey || urlToken;
|
||
|
||
if (loginToken) {
|
||
setToken(loginToken);
|
||
} else {
|
||
setError('无效的登录链接');
|
||
}
|
||
}, [router.params]);
|
||
|
||
// 确认登录
|
||
const handleConfirmLogin = async () => {
|
||
if (!token) {
|
||
setError('缺少登录token');
|
||
return;
|
||
}
|
||
|
||
if (!user?.userId) {
|
||
setError('请先登录小程序');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
setLoading(true);
|
||
setError('');
|
||
|
||
const result = await confirmQRLogin({
|
||
token,
|
||
userId: user.userId,
|
||
platform: 'wechat',
|
||
wechatInfo: {
|
||
nickname: user.nickname,
|
||
avatar: user.avatar
|
||
}
|
||
});
|
||
|
||
if (result.success) {
|
||
setConfirmed(true);
|
||
Taro.showToast({
|
||
title: '登录确认成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
|
||
// 3秒后自动返回
|
||
setTimeout(() => {
|
||
Taro.navigateBack();
|
||
}, 3000);
|
||
} else {
|
||
setError(result.message || '登录确认失败');
|
||
}
|
||
} catch (err: any) {
|
||
setError(err.message || '登录确认失败');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
// 取消登录
|
||
const handleCancel = () => {
|
||
Taro.navigateBack();
|
||
};
|
||
|
||
// 重试
|
||
const handleRetry = () => {
|
||
setError('');
|
||
setConfirmed(false);
|
||
handleConfirmLogin();
|
||
};
|
||
|
||
return (
|
||
<View className='qr-confirm-page min-h-screen bg-gray-50'>
|
||
<View className='p-4'>
|
||
{/* 主要内容卡片 */}
|
||
<Card className='bg-white rounded-lg shadow-sm'>
|
||
<View className='p-6 text-center'>
|
||
{/* 图标 */}
|
||
<View className='mb-6'>
|
||
{loading ? (
|
||
<View className='w-16 h-16 mx-auto flex items-center justify-center'>
|
||
<Loading className='text-blue-500' />
|
||
</View>
|
||
) : confirmed ? (
|
||
<View className='w-16 h-16 mx-auto bg-green-100 rounded-full flex items-center justify-center'>
|
||
<Success className='text-green-500' />
|
||
</View>
|
||
) : error ? (
|
||
<View className='w-16 h-16 mx-auto bg-red-100 rounded-full flex items-center justify-center'>
|
||
<Failure className='text-red-500' />
|
||
</View>
|
||
) : (
|
||
<View className='w-16 h-16 mx-auto bg-blue-100 rounded-full flex items-center justify-center'>
|
||
<User size='32' className='text-blue-500' />
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
{/* 标题 */}
|
||
<Text className='text-xl font-bold text-gray-800 mb-2 block'>
|
||
{loading ? '正在确认登录...' :
|
||
confirmed ? '登录确认成功' :
|
||
error ? '登录确认失败' : '确认登录'}
|
||
</Text>
|
||
|
||
{/* 描述 */}
|
||
<Text className='text-gray-600 mb-6 block'>
|
||
{loading ? '请稍候,正在为您确认登录' :
|
||
confirmed ? '您已成功确认登录,网页端将自动登录' :
|
||
error ? error :
|
||
`确认使用 ${getDisplayName()} 登录网页端?`}
|
||
</Text>
|
||
|
||
{/* 用户信息 */}
|
||
{!loading && !confirmed && !error && user && (
|
||
<View className='bg-gray-50 rounded-lg p-4 mb-6'>
|
||
<View className='flex items-center justify-center'>
|
||
<View className='w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mr-3'>
|
||
<User className='text-blue-500' size='20' />
|
||
</View>
|
||
<View className='text-left'>
|
||
<Text className='text-sm font-medium text-gray-800 block'>
|
||
{user.nickname || user.username || '用户'}
|
||
</Text>
|
||
<Text className='text-xs text-gray-500 block'>
|
||
ID: {user.userId}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* 操作按钮 */}
|
||
<View className='flex flex-col' style={{ gap: '12px' }}>
|
||
{loading ? (
|
||
<Button
|
||
type='default'
|
||
size='large'
|
||
disabled
|
||
className='w-full'
|
||
>
|
||
确认中...
|
||
</Button>
|
||
) : confirmed ? (
|
||
<Button
|
||
type='success'
|
||
size='large'
|
||
onClick={handleCancel}
|
||
className='w-full'
|
||
>
|
||
完成
|
||
</Button>
|
||
) : error ? (
|
||
<View className='flex flex-col' style={{ gap: '8px' }}>
|
||
<Button
|
||
type='primary'
|
||
size='large'
|
||
onClick={handleRetry}
|
||
className='w-full'
|
||
>
|
||
重试
|
||
</Button>
|
||
<Button
|
||
type='default'
|
||
size='large'
|
||
onClick={handleCancel}
|
||
className='w-full'
|
||
>
|
||
取消
|
||
</Button>
|
||
</View>
|
||
) : (
|
||
<View className='mt-3'>
|
||
<Button
|
||
type='primary'
|
||
size='large'
|
||
onClick={handleConfirmLogin}
|
||
className='w-full mb-2'
|
||
disabled={!token || !user?.userId}
|
||
>
|
||
确认登录
|
||
</Button>
|
||
<Button
|
||
type='default'
|
||
size='large'
|
||
onClick={handleCancel}
|
||
className='w-full'
|
||
>
|
||
取消
|
||
</Button>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</Card>
|
||
|
||
{/* 安全提示 */}
|
||
<Card className='bg-yellow-50 border border-yellow-200 rounded-lg mt-4'>
|
||
<View className='p-4'>
|
||
<View className='flex items-start'>
|
||
<Tips className='text-yellow-600 mr-2 mt-1' size='16' />
|
||
<View>
|
||
<Text className='text-sm font-medium text-yellow-800 mb-1 block'>
|
||
安全提示
|
||
</Text>
|
||
<Text className='text-xs text-yellow-700 block'>
|
||
请确认这是您本人的登录操作。如果不是,请点击取消并检查账户安全。
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</Card>
|
||
</View>
|
||
</View>
|
||
);
|
||
};
|
||
|
||
export default QRConfirmPage;
|