|
|
@@ -103,13 +103,21 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
limit: 10
|
|
|
});
|
|
|
|
|
|
- // 额度查询
|
|
|
+ // 额度查询 - 区分404(用户额度账户不存在)和其他错误
|
|
|
const { data: balanceData, isLoading: isLoadingBalance, refetch: refetchBalance, error: balanceError } = useQuery({
|
|
|
queryKey: ['credit-balance', userId, tenantId],
|
|
|
queryFn: async () => {
|
|
|
const res = await creditBalanceClientManager.get()[':userId'].$get({
|
|
|
param: { userId: userId.toString() }
|
|
|
});
|
|
|
+
|
|
|
+ // 处理404错误 - 用户额度账户不存在,这是正常情况
|
|
|
+ if (res.status === 404) {
|
|
|
+ // 返回null表示用户没有额度账户
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他错误情况
|
|
|
if (res.status !== 200) throw new Error('获取信用额度失败');
|
|
|
return await res.json();
|
|
|
},
|
|
|
@@ -118,14 +126,17 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
gcTime: 10 * 60 * 1000,
|
|
|
});
|
|
|
|
|
|
- // 处理额度查询错误
|
|
|
+ // 处理额度查询错误 - 只处理非404错误
|
|
|
useEffect(() => {
|
|
|
if (balanceError) {
|
|
|
toast.error(`获取信用额度失败: ${balanceError.message}`);
|
|
|
}
|
|
|
}, [balanceError]);
|
|
|
|
|
|
- // 额度变更记录查询
|
|
|
+ // 判断用户是否有额度账户
|
|
|
+ const hasCreditAccount = balanceData !== null && balanceData !== undefined;
|
|
|
+
|
|
|
+ // 额度变更记录查询 - 只在用户有额度账户时查询
|
|
|
const { data: logsData, isLoading: isLoadingLogs, error: logsError } = useQuery({
|
|
|
queryKey: ['credit-balance-logs', userId, logsQueryParams, tenantId],
|
|
|
queryFn: async () => {
|
|
|
@@ -139,7 +150,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
if (res.status !== 200) throw new Error('获取额度变更记录失败');
|
|
|
return await res.json();
|
|
|
},
|
|
|
- enabled: open && !!userId && activeTab === 'logs',
|
|
|
+ enabled: open && !!userId && activeTab === 'logs' && hasCreditAccount,
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
gcTime: 10 * 60 * 1000,
|
|
|
});
|
|
|
@@ -180,7 +191,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 设置额度mutation
|
|
|
+ // 设置额度mutation - 也用于开通额度账户
|
|
|
const setLimitMutation = useMutation({
|
|
|
mutationFn: async (data: SetLimitFormData) => {
|
|
|
const res = await creditBalanceClientManager.get()[':userId'].$put({
|
|
|
@@ -193,7 +204,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
queryClient.invalidateQueries({ queryKey: ['credit-balance', userId] });
|
|
|
queryClient.invalidateQueries({ queryKey: ['credit-balance-logs', userId] });
|
|
|
setLimitForm.reset();
|
|
|
- toast.success('额度设置成功');
|
|
|
+ toast.success(hasCreditAccount ? '额度设置成功' : '信用额度开通成功');
|
|
|
},
|
|
|
onError: (error) => {
|
|
|
toast.error(`设置失败: ${error.message}`);
|
|
|
@@ -256,7 +267,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
|
|
|
// 计算欠款信息
|
|
|
const overdueInfo = useMemo(() => {
|
|
|
- if (!balanceData) return null;
|
|
|
+ if (!balanceData || !hasCreditAccount) return null;
|
|
|
const balance = balanceData;
|
|
|
const isOverdue = balance.usedAmount > balance.totalLimit;
|
|
|
const overdueAmount = isOverdue ? balance.usedAmount - balance.totalLimit : 0;
|
|
|
@@ -266,7 +277,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
overdueAmount,
|
|
|
severity: isOverdue ? 'high' : 'none' as 'high' | 'medium' | 'low' | 'none'
|
|
|
};
|
|
|
- }, [balanceData]);
|
|
|
+ }, [balanceData, hasCreditAccount]);
|
|
|
|
|
|
// 获取对话框尺寸类名
|
|
|
const getDialogSizeClass = () => {
|
|
|
@@ -294,7 +305,6 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
|
|
|
if (!open) return null;
|
|
|
|
|
|
- const balance = balanceData;
|
|
|
const logs = logsData?.data || [];
|
|
|
const logsPagination = logsData?.pagination;
|
|
|
|
|
|
@@ -332,7 +342,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
<Skeleton className="h-4 w-32" />
|
|
|
<Skeleton className="h-4 w-48" />
|
|
|
</div>
|
|
|
- ) : balance ? (
|
|
|
+ ) : hasCreditAccount ? (
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
<div>
|
|
|
<p className="text-sm text-muted-foreground">用户ID</p>
|
|
|
@@ -344,23 +354,37 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
</div>
|
|
|
<div>
|
|
|
<p className="text-sm text-muted-foreground">额度状态</p>
|
|
|
- <Badge variant={balance.isEnabled ? 'default' : 'secondary'}>
|
|
|
- {balance.isEnabled ? '启用' : '禁用'}
|
|
|
+ <Badge variant={balanceData!.isEnabled ? 'default' : 'secondary'}>
|
|
|
+ {balanceData!.isEnabled ? '启用' : '禁用'}
|
|
|
</Badge>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p className="text-sm text-muted-foreground">最后更新</p>
|
|
|
<p className="font-medium text-sm">
|
|
|
- {format(new Date(balance.updatedAt), 'yyyy-MM-dd HH:mm')}
|
|
|
+ {format(new Date(balanceData!.updatedAt), 'yyyy-MM-dd HH:mm')}
|
|
|
</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
) : (
|
|
|
- <Alert variant="destructive">
|
|
|
- <AlertCircle className="h-4 w-4" />
|
|
|
- <AlertTitle>数据加载失败</AlertTitle>
|
|
|
- <AlertDescription>无法加载用户信用额度信息</AlertDescription>
|
|
|
- </Alert>
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div className="grid grid-cols-2 gap-4">
|
|
|
+ <div>
|
|
|
+ <p className="text-sm text-muted-foreground">用户ID</p>
|
|
|
+ <p className="font-medium">{userId}</p>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <p className="text-sm text-muted-foreground">用户名</p>
|
|
|
+ <p className="font-medium">{userName}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <Alert>
|
|
|
+ <AlertCircle className="h-4 w-4" />
|
|
|
+ <AlertTitle>用户尚未开通信用额度</AlertTitle>
|
|
|
+ <AlertDescription>
|
|
|
+ 该用户目前没有信用额度账户,是否要为其开通信用额度?
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+ </div>
|
|
|
)}
|
|
|
</CardContent>
|
|
|
</Card>
|
|
|
@@ -380,7 +404,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
<Skeleton className="h-32 w-full" />
|
|
|
<Skeleton className="h-24 w-full" />
|
|
|
</div>
|
|
|
- ) : balance ? (
|
|
|
+ ) : hasCreditAccount ? (
|
|
|
<>
|
|
|
{/* 额度统计卡片 */}
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
|
@@ -393,7 +417,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
</CardHeader>
|
|
|
<CardContent>
|
|
|
<div className="text-2xl font-bold">
|
|
|
- ¥{balance.totalLimit.toFixed(2)}
|
|
|
+ ¥{balanceData!.totalLimit.toFixed(2)}
|
|
|
</div>
|
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
|
用户可用的最大信用额度
|
|
|
@@ -410,7 +434,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
</CardHeader>
|
|
|
<CardContent>
|
|
|
<div className="text-2xl font-bold">
|
|
|
- ¥{balance.usedAmount.toFixed(2)}
|
|
|
+ ¥{balanceData!.usedAmount.toFixed(2)}
|
|
|
</div>
|
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
|
用户当前已使用的额度
|
|
|
@@ -427,7 +451,7 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
</CardHeader>
|
|
|
<CardContent>
|
|
|
<div className="text-2xl font-bold">
|
|
|
- ¥{balance.availableAmount.toFixed(2)}
|
|
|
+ ¥{balanceData!.availableAmount.toFixed(2)}
|
|
|
</div>
|
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
|
剩余可用的信用额度
|
|
|
@@ -456,274 +480,371 @@ export const CreditBalanceDialog: React.FC<CreditBalanceDialogProps> = ({
|
|
|
<div className="space-y-2">
|
|
|
<div className="flex justify-between text-sm">
|
|
|
<span>使用进度</span>
|
|
|
- <span>{((balance.usedAmount / balance.totalLimit) * 100).toFixed(1)}%</span>
|
|
|
+ <span>{((balanceData!.usedAmount / balanceData!.totalLimit) * 100).toFixed(1)}%</span>
|
|
|
</div>
|
|
|
<div className="h-2 bg-secondary rounded-full overflow-hidden">
|
|
|
<div
|
|
|
className="h-full bg-primary"
|
|
|
- style={{ width: `${Math.min((balance.usedAmount / balance.totalLimit) * 100, 100)}%` }}
|
|
|
+ style={{ width: `${Math.min((balanceData!.usedAmount / balanceData!.totalLimit) * 100, 100)}%` }}
|
|
|
/>
|
|
|
</div>
|
|
|
<div className="flex justify-between text-xs text-muted-foreground">
|
|
|
<span>0</span>
|
|
|
- <span>¥{balance.totalLimit.toFixed(2)}</span>
|
|
|
+ <span>¥{balanceData!.totalLimit.toFixed(2)}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</CardContent>
|
|
|
</Card>
|
|
|
</>
|
|
|
) : (
|
|
|
- <Alert variant="destructive">
|
|
|
- <AlertCircle className="h-4 w-4" />
|
|
|
- <AlertTitle>数据加载失败</AlertTitle>
|
|
|
- <AlertDescription>无法加载信用额度信息</AlertDescription>
|
|
|
- </Alert>
|
|
|
+ <div className="space-y-6">
|
|
|
+ <Alert>
|
|
|
+ <AlertCircle className="h-4 w-4" />
|
|
|
+ <AlertTitle>用户尚未开通信用额度</AlertTitle>
|
|
|
+ <AlertDescription>
|
|
|
+ 该用户目前没有信用额度账户,请为其设置初始信用额度以开通账户。
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+
|
|
|
+ {/* 开通额度表单 */}
|
|
|
+ <Card data-testid="open-credit-account-card">
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
+ <CreditCard className="h-4 w-4" />
|
|
|
+ 开通信用额度
|
|
|
+ </CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 为该用户设置初始信用额度以开通信用账户
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <Form {...setLimitForm}>
|
|
|
+ <form onSubmit={setLimitForm.handleSubmit(onSetLimitSubmit)} className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={setLimitForm.control}
|
|
|
+ name="totalLimit"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>初始额度</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ step="0.01"
|
|
|
+ placeholder="请输入初始额度"
|
|
|
+ data-testid="open-credit-total-limit-input"
|
|
|
+ value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
+ onChange={(e) => {
|
|
|
+ const value = e.target.value;
|
|
|
+ if (value === '') {
|
|
|
+ field.onChange('');
|
|
|
+ } else {
|
|
|
+ field.onChange(parseFloat(value));
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onBlur={field.onBlur}
|
|
|
+ name={field.name}
|
|
|
+ ref={field.ref}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>
|
|
|
+ 设置用户的初始信用额度,可以为0
|
|
|
+ </FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <FormField
|
|
|
+ control={setLimitForm.control}
|
|
|
+ name="remark"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>备注</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入开通备注(可选)" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ className="w-full"
|
|
|
+ disabled={setLimitMutation.isPending}
|
|
|
+ data-testid="open-credit-account-button"
|
|
|
+ >
|
|
|
+ {setLimitMutation.isPending ? '开通中...' : '开通信用额度'}
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
)}
|
|
|
</TabsContent>
|
|
|
|
|
|
- {/* 额度操作标签页 */}
|
|
|
+ {/* 额度操作标签页 - 只在用户有额度账户时显示 */}
|
|
|
<TabsContent value="operations" className="space-y-4">
|
|
|
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
|
- {/* 设置额度表单 */}
|
|
|
- <Card data-testid="set-limit-form-card">
|
|
|
- <CardHeader>
|
|
|
- <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
- <Settings className="h-4 w-4" />
|
|
|
- 设置额度
|
|
|
- </CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 设置用户的总信用额度
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent>
|
|
|
- <Form {...setLimitForm}>
|
|
|
- <form onSubmit={setLimitForm.handleSubmit(onSetLimitSubmit)} className="space-y-4">
|
|
|
- <FormField
|
|
|
- control={setLimitForm.control}
|
|
|
- name="totalLimit"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>总额度</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
- step="0.01"
|
|
|
- placeholder="请输入总额度"
|
|
|
- data-testid="total-limit-input"
|
|
|
- value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
- onChange={(e) => {
|
|
|
- const value = e.target.value;
|
|
|
- if (value === '') {
|
|
|
- field.onChange('');
|
|
|
- } else {
|
|
|
- field.onChange(parseFloat(value));
|
|
|
- }
|
|
|
- }}
|
|
|
- onBlur={field.onBlur}
|
|
|
- name={field.name}
|
|
|
- ref={field.ref}
|
|
|
- />
|
|
|
- </FormControl>
|
|
|
- <FormDescription>
|
|
|
- 设置用户可用的最大信用额度
|
|
|
- </FormDescription>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <FormField
|
|
|
- control={setLimitForm.control}
|
|
|
- name="remark"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>备注</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input placeholder="请输入备注(可选)" {...field} />
|
|
|
- </FormControl>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <Button
|
|
|
- type="submit"
|
|
|
- className="w-full"
|
|
|
- disabled={setLimitMutation.isPending}
|
|
|
- data-testid="set-limit-button"
|
|
|
- >
|
|
|
- {setLimitMutation.isPending ? '设置中...' : '设置额度'}
|
|
|
- </Button>
|
|
|
- </form>
|
|
|
- </Form>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
-
|
|
|
- {/* 调整额度表单 */}
|
|
|
- <Card data-testid="adjust-limit-form-card">
|
|
|
- <CardHeader>
|
|
|
- <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
- <Edit className="h-4 w-4" />
|
|
|
- 调整额度
|
|
|
- </CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 增加或减少用户的信用额度
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent>
|
|
|
- <Form {...adjustLimitForm}>
|
|
|
- <form onSubmit={adjustLimitForm.handleSubmit(onAdjustLimitSubmit)} className="space-y-4">
|
|
|
- <FormField
|
|
|
- control={adjustLimitForm.control}
|
|
|
- name="adjustAmount"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>调整金额</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
- step="0.01"
|
|
|
- placeholder="正数增加,负数减少"
|
|
|
- data-testid="adjust-amount-input"
|
|
|
- value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
- onChange={(e) => {
|
|
|
- const value = e.target.value;
|
|
|
- if (value === '') {
|
|
|
- field.onChange('');
|
|
|
- } else {
|
|
|
- field.onChange(parseFloat(value));
|
|
|
- }
|
|
|
- }}
|
|
|
- onBlur={field.onBlur}
|
|
|
- name={field.name}
|
|
|
- ref={field.ref}
|
|
|
- />
|
|
|
- </FormControl>
|
|
|
- <FormDescription>
|
|
|
- 正数表示增加额度,负数表示减少额度
|
|
|
- </FormDescription>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <FormField
|
|
|
- control={adjustLimitForm.control}
|
|
|
- name="remark"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>备注</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input placeholder="请输入调整原因(可选)" {...field} />
|
|
|
- </FormControl>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <Button
|
|
|
- type="submit"
|
|
|
- className="w-full"
|
|
|
- disabled={adjustLimitMutation.isPending}
|
|
|
- data-testid="adjust-limit-button"
|
|
|
- >
|
|
|
- {adjustLimitMutation.isPending ? '调整中...' : '调整额度'}
|
|
|
- </Button>
|
|
|
- </form>
|
|
|
- </Form>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 结账恢复额度表单 */}
|
|
|
- <Card data-testid="checkout-form-card">
|
|
|
- <CardHeader>
|
|
|
- <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
- <CheckCircle className="h-4 w-4" />
|
|
|
- 结账恢复额度
|
|
|
- </CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 手动恢复用户的信用额度(通常用于结账后)
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent>
|
|
|
- <Form {...checkoutForm}>
|
|
|
- <form onSubmit={checkoutForm.handleSubmit(onCheckoutSubmit)} className="space-y-4">
|
|
|
- <FormField
|
|
|
- control={checkoutForm.control}
|
|
|
- name="amount"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>恢复金额</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
- step="0.01"
|
|
|
- placeholder="请输入恢复金额"
|
|
|
- data-testid="checkout-amount-input"
|
|
|
- value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
- onChange={(e) => {
|
|
|
- const value = e.target.value;
|
|
|
- if (value === '') {
|
|
|
- field.onChange('');
|
|
|
- } else {
|
|
|
- field.onChange(parseFloat(value));
|
|
|
- }
|
|
|
- }}
|
|
|
- onBlur={field.onBlur}
|
|
|
- name={field.name}
|
|
|
- ref={field.ref}
|
|
|
- />
|
|
|
- </FormControl>
|
|
|
- <FormDescription>
|
|
|
- 输入要恢复的额度金额
|
|
|
- </FormDescription>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <FormField
|
|
|
- control={checkoutForm.control}
|
|
|
- name="referenceId"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>关联ID(订单号等)</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input placeholder="请输入关联ID(可选)" {...field} />
|
|
|
- </FormControl>
|
|
|
- <FormDescription>
|
|
|
- 关联的订单号或其他标识符
|
|
|
- </FormDescription>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <FormField
|
|
|
- control={checkoutForm.control}
|
|
|
- name="remark"
|
|
|
- render={({ field }) => (
|
|
|
- <FormItem>
|
|
|
- <FormLabel>备注</FormLabel>
|
|
|
- <FormControl>
|
|
|
- <Input placeholder="请输入备注(可选)" {...field} />
|
|
|
- </FormControl>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- <Button
|
|
|
- type="submit"
|
|
|
- className="w-full"
|
|
|
- disabled={checkoutMutation.isPending}
|
|
|
- data-testid="checkout-button"
|
|
|
- >
|
|
|
- {checkoutMutation.isPending ? '恢复中...' : '结账恢复额度'}
|
|
|
- </Button>
|
|
|
- </form>
|
|
|
- </Form>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
+ {hasCreditAccount ? (
|
|
|
+ <>
|
|
|
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
|
+ {/* 设置额度表单 */}
|
|
|
+ <Card data-testid="set-limit-form-card">
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
+ <Settings className="h-4 w-4" />
|
|
|
+ 设置额度
|
|
|
+ </CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 设置用户的总信用额度
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <Form {...setLimitForm}>
|
|
|
+ <form onSubmit={setLimitForm.handleSubmit(onSetLimitSubmit)} className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={setLimitForm.control}
|
|
|
+ name="totalLimit"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>总额度</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ step="0.01"
|
|
|
+ placeholder="请输入总额度"
|
|
|
+ data-testid="total-limit-input"
|
|
|
+ value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
+ onChange={(e) => {
|
|
|
+ const value = e.target.value;
|
|
|
+ if (value === '') {
|
|
|
+ field.onChange('');
|
|
|
+ } else {
|
|
|
+ field.onChange(parseFloat(value));
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onBlur={field.onBlur}
|
|
|
+ name={field.name}
|
|
|
+ ref={field.ref}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>
|
|
|
+ 设置用户可用的最大信用额度
|
|
|
+ </FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <FormField
|
|
|
+ control={setLimitForm.control}
|
|
|
+ name="remark"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>备注</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入备注(可选)" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ className="w-full"
|
|
|
+ disabled={setLimitMutation.isPending}
|
|
|
+ data-testid="set-limit-button"
|
|
|
+ >
|
|
|
+ {setLimitMutation.isPending ? '设置中...' : '设置额度'}
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ {/* 调整额度表单 */}
|
|
|
+ <Card data-testid="adjust-limit-form-card">
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
+ <Edit className="h-4 w-4" />
|
|
|
+ 调整额度
|
|
|
+ </CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 增加或减少用户的信用额度
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <Form {...adjustLimitForm}>
|
|
|
+ <form onSubmit={adjustLimitForm.handleSubmit(onAdjustLimitSubmit)} className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={adjustLimitForm.control}
|
|
|
+ name="adjustAmount"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>调整金额</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ step="0.01"
|
|
|
+ placeholder="正数增加,负数减少"
|
|
|
+ data-testid="adjust-amount-input"
|
|
|
+ value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
+ onChange={(e) => {
|
|
|
+ const value = e.target.value;
|
|
|
+ if (value === '') {
|
|
|
+ field.onChange('');
|
|
|
+ } else {
|
|
|
+ field.onChange(parseFloat(value));
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onBlur={field.onBlur}
|
|
|
+ name={field.name}
|
|
|
+ ref={field.ref}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>
|
|
|
+ 正数表示增加额度,负数表示减少额度
|
|
|
+ </FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <FormField
|
|
|
+ control={adjustLimitForm.control}
|
|
|
+ name="remark"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>备注</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入调整原因(可选)" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ className="w-full"
|
|
|
+ disabled={adjustLimitMutation.isPending}
|
|
|
+ data-testid="adjust-limit-button"
|
|
|
+ >
|
|
|
+ {adjustLimitMutation.isPending ? '调整中...' : '调整额度'}
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 结账恢复额度表单 */}
|
|
|
+ <Card data-testid="checkout-form-card">
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
|
+ <CheckCircle className="h-4 w-4" />
|
|
|
+ 结账恢复额度
|
|
|
+ </CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 手动恢复用户的信用额度(通常用于结账后)
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <Form {...checkoutForm}>
|
|
|
+ <form onSubmit={checkoutForm.handleSubmit(onCheckoutSubmit)} className="space-y-4">
|
|
|
+ <FormField
|
|
|
+ control={checkoutForm.control}
|
|
|
+ name="amount"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>恢复金额</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ step="0.01"
|
|
|
+ placeholder="请输入恢复金额"
|
|
|
+ data-testid="checkout-amount-input"
|
|
|
+ value={field.value === undefined || field.value === null || String(field.value) === '' ? '' : String(field.value || 0)}
|
|
|
+ onChange={(e) => {
|
|
|
+ const value = e.target.value;
|
|
|
+ if (value === '') {
|
|
|
+ field.onChange('');
|
|
|
+ } else {
|
|
|
+ field.onChange(parseFloat(value));
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onBlur={field.onBlur}
|
|
|
+ name={field.name}
|
|
|
+ ref={field.ref}
|
|
|
+ />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>
|
|
|
+ 输入要恢复的额度金额
|
|
|
+ </FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <FormField
|
|
|
+ control={checkoutForm.control}
|
|
|
+ name="referenceId"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>关联ID(订单号等)</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入关联ID(可选)" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormDescription>
|
|
|
+ 关联的订单号或其他标识符
|
|
|
+ </FormDescription>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <FormField
|
|
|
+ control={checkoutForm.control}
|
|
|
+ name="remark"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>备注</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input placeholder="请输入备注(可选)" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="submit"
|
|
|
+ className="w-full"
|
|
|
+ disabled={checkoutMutation.isPending}
|
|
|
+ data-testid="checkout-button"
|
|
|
+ >
|
|
|
+ {checkoutMutation.isPending ? '恢复中...' : '结账恢复额度'}
|
|
|
+ </Button>
|
|
|
+ </form>
|
|
|
+ </Form>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <Alert>
|
|
|
+ <AlertCircle className="h-4 w-4" />
|
|
|
+ <AlertTitle>用户尚未开通信用额度</AlertTitle>
|
|
|
+ <AlertDescription>
|
|
|
+ 请先在"额度概览"标签页中为该用户开通信用额度,然后才能进行额度操作。
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+ )}
|
|
|
</TabsContent>
|
|
|
|
|
|
{/* 变更记录标签页 */}
|
|
|
<TabsContent value="logs" className="space-y-4">
|
|
|
- {isLoadingLogs ? (
|
|
|
+ {!hasCreditAccount ? (
|
|
|
+ <Alert>
|
|
|
+ <AlertCircle className="h-4 w-4" />
|
|
|
+ <AlertTitle>用户尚未开通信用额度</AlertTitle>
|
|
|
+ <AlertDescription>
|
|
|
+ 请先在"额度概览"标签页中为该用户开通信用额度,然后才能查看变更记录。
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+ ) : isLoadingLogs ? (
|
|
|
<div className="space-y-2">
|
|
|
<Skeleton className="h-8 w-full" />
|
|
|
<Skeleton className="h-64 w-full" />
|