|
|
@@ -192,21 +192,21 @@ export const DeliveryAddressManagement: React.FC = () => {
|
|
|
const getStatusBadge = (status: DeliveryAddressState) => {
|
|
|
switch (status) {
|
|
|
case DeliveryAddressState.ACTIVE:
|
|
|
- return <Badge variant="default">正常</Badge>;
|
|
|
+ return <Badge variant="default" data-testid="status-active">正常</Badge>;
|
|
|
case DeliveryAddressState.DISABLED:
|
|
|
- return <Badge variant="secondary">禁用</Badge>;
|
|
|
+ return <Badge variant="secondary" data-testid="status-disabled">禁用</Badge>;
|
|
|
case DeliveryAddressState.DELETED:
|
|
|
- return <Badge variant="destructive">删除</Badge>;
|
|
|
+ return <Badge variant="destructive" data-testid="status-deleted">删除</Badge>;
|
|
|
default:
|
|
|
- return <Badge variant="outline">未知</Badge>;
|
|
|
+ return <Badge variant="outline" data-testid="status-unknown">未知</Badge>;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const getIsDefaultBadge = (isDefault: DefaultAddressState) => {
|
|
|
return isDefault === DefaultAddressState.IS_DEFAULT ? (
|
|
|
- <Badge variant="default">默认</Badge>
|
|
|
+ <Badge variant="default" data-testid="is-default-true">默认</Badge>
|
|
|
) : (
|
|
|
- <Badge variant="outline">非默认</Badge>
|
|
|
+ <Badge variant="outline" data-testid="is-default-false">非默认</Badge>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
@@ -222,32 +222,109 @@ export const DeliveryAddressManagement: React.FC = () => {
|
|
|
return parts.join(' ');
|
|
|
};
|
|
|
|
|
|
- // 加载状态
|
|
|
- if (isLoading) {
|
|
|
- return (
|
|
|
- <div className="space-y-4">
|
|
|
- <div className="flex justify-between items-center">
|
|
|
- <Skeleton className="h-8 w-48" />
|
|
|
- <Skeleton className="h-10 w-32" />
|
|
|
- </div>
|
|
|
-
|
|
|
- <Card>
|
|
|
- <CardContent className="pt-6">
|
|
|
- <div className="space-y-3">
|
|
|
- {[...Array(5)].map((_, i) => (
|
|
|
- <div key={i} className="flex gap-4">
|
|
|
- <Skeleton className="h-10 flex-1" />
|
|
|
- <Skeleton className="h-10 flex-1" />
|
|
|
- <Skeleton className="h-10 flex-1" />
|
|
|
- <Skeleton className="h-10 w-20" />
|
|
|
- </div>
|
|
|
- ))}
|
|
|
+ // 表格加载状态
|
|
|
+ const tableContent = isLoading ? (
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle>收货地址列表</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 加载中...
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <div className="space-y-3">
|
|
|
+ {[...Array(5)].map((_, i) => (
|
|
|
+ <div key={i} className="flex gap-4">
|
|
|
+ <Skeleton className="h-10 flex-1" />
|
|
|
+ <Skeleton className="h-10 flex-1" />
|
|
|
+ <Skeleton className="h-10 flex-1" />
|
|
|
+ <Skeleton className="h-10 w-20" />
|
|
|
</div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ ) : (
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle>收货地址列表</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 共 {data?.data?.total || 0} 条收货地址记录
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent>
|
|
|
+ <Table>
|
|
|
+ <TableHeader>
|
|
|
+ <TableRow>
|
|
|
+ <TableHead>用户</TableHead>
|
|
|
+ <TableHead>收货人</TableHead>
|
|
|
+ <TableHead>手机号</TableHead>
|
|
|
+ <TableHead>地址</TableHead>
|
|
|
+ <TableHead>状态</TableHead>
|
|
|
+ <TableHead>默认地址</TableHead>
|
|
|
+ <TableHead>创建时间</TableHead>
|
|
|
+ <TableHead>操作</TableHead>
|
|
|
+ </TableRow>
|
|
|
+ </TableHeader>
|
|
|
+ <TableBody>
|
|
|
+ {data?.data?.list?.map((address: DeliveryAddress) => (
|
|
|
+ <TableRow key={address.id} data-testid={`address-row-${address.id}`}>
|
|
|
+ <TableCell data-testid={`address-user-${address.id}`}>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <MapPin className="h-4 w-4 text-muted-foreground" />
|
|
|
+ <span>{address.user?.name || '未知用户'}</span>
|
|
|
+ </div>
|
|
|
+ </TableCell>
|
|
|
+ <TableCell data-testid={`address-name-${address.id}`}>{address.name}</TableCell>
|
|
|
+ <TableCell data-testid={`address-phone-${address.id}`}>{address.phone}</TableCell>
|
|
|
+ <TableCell>
|
|
|
+ <div className="max-w-xs truncate">
|
|
|
+ {formatAddressDisplay(address)}
|
|
|
+ </div>
|
|
|
+ </TableCell>
|
|
|
+ <TableCell>{getStatusBadge(address.state)}</TableCell>
|
|
|
+ <TableCell>{getIsDefaultBadge(address.isDefault)}</TableCell>
|
|
|
+ <TableCell>
|
|
|
+ {address.createdAt ? format(new Date(address.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN }) : '-'}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell>
|
|
|
+ <div className="flex gap-2">
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleEditAddress(address)}
|
|
|
+ data-testid="edit-address-button"
|
|
|
+ >
|
|
|
+ <Edit className="h-4 w-4" />
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleDeleteAddress(address.id)}
|
|
|
+ data-testid="delete-address-button"
|
|
|
+ >
|
|
|
+ <Trash2 className="h-4 w-4" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </TableCell>
|
|
|
+ </TableRow>
|
|
|
+ ))}
|
|
|
+ </TableBody>
|
|
|
+ </Table>
|
|
|
+ {data?.data?.total && data.data.total > 0 && (
|
|
|
+ <div className="mt-4">
|
|
|
+ <DataTablePagination
|
|
|
+ currentPage={searchParams.page}
|
|
|
+ pageSize={searchParams.limit}
|
|
|
+ totalCount={data.data.total}
|
|
|
+ onPageChange={(page) => setSearchParams(prev => ({ ...prev, page }))}
|
|
|
+ onPageSizeChange={(limit) => setSearchParams(prev => ({ ...prev, limit, page: 1 }))}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ );
|
|
|
|
|
|
return (
|
|
|
<div className="space-y-4">
|
|
|
@@ -287,6 +364,7 @@ export const DeliveryAddressManagement: React.FC = () => {
|
|
|
value={searchParams.userId}
|
|
|
onChange={(value) => setSearchParams(prev => ({ ...prev, userId: value }))}
|
|
|
placeholder="选择用户"
|
|
|
+ data-testid="search-user-selector"
|
|
|
/>
|
|
|
</div>
|
|
|
<Button onClick={handleSearch}>搜索</Button>
|
|
|
@@ -295,83 +373,7 @@ export const DeliveryAddressManagement: React.FC = () => {
|
|
|
</Card>
|
|
|
|
|
|
{/* 数据表格 */}
|
|
|
- <Card>
|
|
|
- <CardHeader>
|
|
|
- <CardTitle>收货地址列表</CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 共 {data?.pagination.total || 0} 条记录
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent>
|
|
|
- <div className="rounded-md border">
|
|
|
- <Table>
|
|
|
- <TableHeader>
|
|
|
- <TableRow>
|
|
|
- <TableHead>ID</TableHead>
|
|
|
- <TableHead>用户</TableHead>
|
|
|
- <TableHead>收货人</TableHead>
|
|
|
- <TableHead>手机号</TableHead>
|
|
|
- <TableHead>地址</TableHead>
|
|
|
- <TableHead>状态</TableHead>
|
|
|
- <TableHead>默认</TableHead>
|
|
|
- <TableHead>创建时间</TableHead>
|
|
|
- <TableHead className="text-right">操作</TableHead>
|
|
|
- </TableRow>
|
|
|
- </TableHeader>
|
|
|
- <TableBody>
|
|
|
- {data?.data.map((address: DeliveryAddress) => (
|
|
|
- <TableRow key={address.id}>
|
|
|
- <TableCell>{address.id}</TableCell>
|
|
|
- <TableCell>{address.user?.username || '-'}</TableCell>
|
|
|
- <TableCell>{address.name}</TableCell>
|
|
|
- <TableCell>{address.phone}</TableCell>
|
|
|
- <TableCell className="max-w-xs truncate" title={formatAddressDisplay(address)}>
|
|
|
- {formatAddressDisplay(address)}
|
|
|
- </TableCell>
|
|
|
- <TableCell>{getStatusBadge(address.state)}</TableCell>
|
|
|
- <TableCell>{getIsDefaultBadge(address.isDefault)}</TableCell>
|
|
|
- <TableCell>
|
|
|
- {format(new Date(address.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN })}
|
|
|
- </TableCell>
|
|
|
- <TableCell className="text-right">
|
|
|
- <div className="flex justify-end gap-2">
|
|
|
- <Button
|
|
|
- variant="ghost"
|
|
|
- size="icon"
|
|
|
- onClick={() => handleEditAddress(address)}
|
|
|
- >
|
|
|
- <Edit className="h-4 w-4" />
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- variant="ghost"
|
|
|
- size="icon"
|
|
|
- onClick={() => handleDeleteAddress(address.id)}
|
|
|
- >
|
|
|
- <Trash2 className="h-4 w-4" />
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
- </TableCell>
|
|
|
- </TableRow>
|
|
|
- ))}
|
|
|
- </TableBody>
|
|
|
- </Table>
|
|
|
- </div>
|
|
|
-
|
|
|
- {data?.data.length === 0 && !isLoading && (
|
|
|
- <div className="text-center py-8">
|
|
|
- <MapPin className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
|
|
|
- <p className="text-muted-foreground">暂无收货地址数据</p>
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- <DataTablePagination
|
|
|
- currentPage={searchParams.page}
|
|
|
- pageSize={searchParams.limit}
|
|
|
- totalCount={data?.pagination.total || 0}
|
|
|
- onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
|
|
|
- />
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
+ {tableContent}
|
|
|
|
|
|
{/* 创建/编辑模态框 */}
|
|
|
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
|
|
@@ -399,6 +401,7 @@ export const DeliveryAddressManagement: React.FC = () => {
|
|
|
value={field.value}
|
|
|
onChange={field.onChange}
|
|
|
placeholder="选择用户"
|
|
|
+ data-testid="create-user-selector"
|
|
|
/>
|
|
|
</FormControl>
|
|
|
<FormMessage />
|