Преглед на файлове

feat(order): 实现更新工作状态API并修复相关类型问题

- 在订单服务中添加updatePersonWorkStatus方法
- 在订单模块schema中添加UpdatePersonWorkStatusSchema
- 在订单模块路由中添加更新工作状态的API端点
- 更新OrderDetailModal中的updateWorkStatusMutation调用真实API
- 修复OrderPerson实体缺少actualStartDate字段的问题
- 修复薪资查询API调用,使用totalSalary字段
- 修复多个类型错误和未使用变量

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname преди 2 дни
родител
ревизия
04c37a4924

+ 579 - 0
allin-packages/order-management-ui/src/components/OrderAssetModal.tsx

@@ -0,0 +1,579 @@
+import React, { useState, useEffect } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+import { Button } from '@d8d/shared-ui-components/components/ui/button';
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from '@d8d/shared-ui-components/components/ui/dialog';
+import {
+  Table,
+  TableBody,
+  TableCell,
+  TableHead,
+  TableHeader,
+  TableRow,
+} from '@d8d/shared-ui-components/components/ui/table';
+import { Badge } from '@d8d/shared-ui-components/components/ui/badge';
+import { Separator } from '@d8d/shared-ui-components/components/ui/separator';
+import { Label } from '@d8d/shared-ui-components/components/ui/label';
+import { RadioGroup, RadioGroupItem } from '@d8d/shared-ui-components/components/ui/radio-group';
+import { Calendar } from '@d8d/shared-ui-components/components/ui/calendar';
+import { Popover, PopoverContent, PopoverTrigger } from '@d8d/shared-ui-components/components/ui/popover';
+import { format } from 'date-fns';
+import { CalendarIcon, Eye, Upload, X } from 'lucide-react';
+import { toast } from 'sonner';
+import { FileSelector } from '@d8d/file-management-ui/components';
+import { AssetType, AssetFileType } from '@d8d/allin-order-module/schemas';
+import { orderClientManager } from '../api/orderClient';
+
+// 资产类型映射
+const ASSET_TYPE_LABELS: Record<AssetType, string> = {
+  [AssetType.DISABILITY_CERT]: '残疾证明',
+  [AssetType.TAX]: '税务文件',
+  [AssetType.SALARY]: '薪资单',
+  [AssetType.JOB_RESULT]: '工作成果',
+  [AssetType.CONTRACT_SIGN]: '合同签署',
+  [AssetType.OTHER]: '其他',
+};
+
+interface OrderAssetModalProps {
+  orderId: number;
+  orderPersons: Array<{
+    personId: number;
+    personName: string;
+    disabilityId?: string;
+    disabilityType?: string;
+  }>;
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+}
+
+interface AssetItem {
+  id: number;
+  assetType: AssetType;
+  assetFileType: AssetFileType;
+  assetUrl: string;
+  relatedTime: string;
+  remark?: string;
+}
+
+interface PersonAssetData {
+  personId: number;
+  personName: string;
+  [key: string]: AssetItem | any; // 动态属性存储各种资产类型的数据
+}
+
+interface CurrentAsset {
+  personId: number;
+  personName: string;
+  assetType: AssetType;
+  assetItem?: AssetItem;
+}
+
+export const OrderAssetModal: React.FC<OrderAssetModalProps> = ({
+  orderId,
+  orderPersons,
+  open,
+  onOpenChange,
+}) => {
+  const queryClient = useQueryClient();
+  const [selectedDate, setSelectedDate] = useState<Date>(new Date());
+  const [assetData, setAssetData] = useState<PersonAssetData[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [uploadModalOpen, setUploadModalOpen] = useState(false);
+  const [previewModalOpen, setPreviewModalOpen] = useState(false);
+  const [currentAsset, setCurrentAsset] = useState<CurrentAsset | null>(null);
+  const [selectedFileId, setSelectedFileId] = useState<number | null>(null);
+  const [uploadFileType, setUploadFileType] = useState<AssetFileType>(AssetFileType.IMAGE);
+  const [uploading, setUploading] = useState(false);
+
+  // 获取资产数据
+  const fetchAssetData = async () => {
+    if (!orderId || !orderPersons.length) return;
+
+    setLoading(true);
+    try {
+      const personIds = orderPersons.map(person => person.personId);
+      const relatedTime = format(selectedDate, 'yyyy-MM-dd');
+
+      // 查询每个人员的资产
+      const assetsPromises = personIds.map(personId =>
+        orderClientManager.get().assets.query.$get({
+          query: {
+            orderId,
+            personId,
+            relatedTime,
+            page: 1,
+            limit: 100,
+          },
+        })
+      );
+
+      const responses = await Promise.all(assetsPromises);
+      const assets = [];
+      for (const response of responses) {
+        if (response.ok) {
+          const data = await response.json();
+          assets.push(...data.data);
+        }
+      }
+
+
+      // 按人员分组资产数据
+      const groupedData: Record<number, PersonAssetData> = {};
+
+      // 初始化人员数据
+      orderPersons.forEach(person => {
+        groupedData[person.personId] = {
+          personId: person.personId,
+          personName: person.personName,
+        };
+      });
+
+      // 添加资产数据
+      assets.forEach((asset: any) => {
+        const { personId, assetType, id, assetFileType, assetUrl } = asset;
+        if (groupedData[personId]) {
+          groupedData[personId][assetType] = {
+            id,
+            assetType,
+            assetFileType,
+            assetUrl,
+            relatedTime: asset.relatedTime,
+            remark: asset.remark,
+          };
+        }
+      });
+
+      setAssetData(Object.values(groupedData));
+    } catch (error) {
+      toast.error('获取资产数据失败');
+      console.error('获取资产数据失败:', error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 打开上传弹窗
+  const handleOpenUploadModal = (personId: number, personName: string, assetType: AssetType) => {
+    setCurrentAsset({
+      personId,
+      personName,
+      assetType,
+    });
+    setSelectedFileId(null);
+    setUploadFileType(AssetFileType.IMAGE);
+    setUploadModalOpen(true);
+  };
+
+  // 打开预览弹窗
+  const handleOpenPreviewModal = (personId: number, personName: string, assetType: AssetType, assetItem: AssetItem) => {
+    setCurrentAsset({
+      personId,
+      personName,
+      assetType,
+      assetItem,
+    });
+    setPreviewModalOpen(true);
+  };
+
+  // 处理上传
+  const handleUpload = async () => {
+    if (!currentAsset || !orderId || !selectedFileId) {
+      toast.error('请选择文件');
+      return;
+    }
+
+    setUploading(true);
+    try {
+      const assetData = {
+        orderId,
+        personId: currentAsset.personId,
+        assetType: currentAsset.assetType,
+        assetFileType: uploadFileType,
+        fileId: selectedFileId,
+        relatedTime: format(selectedDate, 'yyyy-MM-dd'),
+      };
+
+      // 如果有现有资产,先删除再创建(模拟更新)
+      if (currentAsset.assetItem && currentAsset.assetItem.id) {
+        // 先删除旧资产
+        const deleteResponse = await orderClientManager.get().assets.delete[':id'].$delete({
+          param: { id: currentAsset.assetItem.id },
+        });
+
+        if (!deleteResponse.ok) {
+          const error = await deleteResponse.json();
+          throw new Error(error.message || '删除旧资产失败');
+        }
+      }
+
+      // 创建新资产
+      const response = await orderClientManager.get().assets.create.$post({
+        json: assetData,
+      });
+
+      if (!response.ok) {
+        const error = await response.json();
+        throw new Error(error.message || '操作失败');
+      }
+
+      toast.success(currentAsset.assetItem ? '更新资产成功' : '上传资产成功');
+      setUploadModalOpen(false);
+      setPreviewModalOpen(false);
+      fetchAssetData();
+      queryClient.invalidateQueries({ queryKey: ['order-assets'] });
+    } catch (error: any) {
+      toast.error(`操作失败: ${error.message}`);
+      console.error('操作失败:', error);
+    } finally {
+      setUploading(false);
+    }
+  };
+
+  // 处理删除
+  const handleDelete = async () => {
+    if (!currentAsset?.assetItem?.id) return;
+
+    try {
+      const response = await orderClientManager.get().assets.delete[':id'].$delete({
+        param: { id: currentAsset.assetItem.id },
+      });
+
+      if (!response.ok) {
+        const error = await response.json();
+        throw new Error(error.message || '删除资产失败');
+      }
+
+      toast.success('删除资产成功');
+      setPreviewModalOpen(false);
+      fetchAssetData();
+      queryClient.invalidateQueries({ queryKey: ['order-assets'] });
+    } catch (error: any) {
+      toast.error(`删除资产失败: ${error.message}`);
+      console.error('删除资产失败:', error);
+    }
+  };
+
+  // 处理文件选择
+  const handleFileChange = (fileId: number | null | number[]) => {
+    if (fileId !== null && !Array.isArray(fileId)) {
+      setSelectedFileId(fileId);
+    } else {
+      setSelectedFileId(null);
+    }
+  };
+
+  // 获取资产类型列
+  const assetTypeColumns = Object.values(AssetType).map(assetType => ({
+    key: assetType,
+    title: ASSET_TYPE_LABELS[assetType],
+    dataIndex: assetType,
+  }));
+
+  // 表格列配置
+  const columns = [
+    {
+      title: '残疾人ID',
+      dataIndex: 'personId',
+      key: 'personId',
+      width: 100,
+    },
+    {
+      title: '姓名',
+      dataIndex: 'personName',
+      key: 'personName',
+      width: 120,
+    },
+    ...assetTypeColumns.map(col => ({
+      title: col.title,
+      dataIndex: col.key,
+      key: col.key,
+      width: 120,
+    })),
+  ];
+
+  // 当日期或订单人员变化时重新获取数据
+  useEffect(() => {
+    if (open && orderId && orderPersons.length > 0) {
+      fetchAssetData();
+    }
+  }, [open, selectedDate, orderId, orderPersons]);
+
+  // 关闭弹窗时重置状态
+  useEffect(() => {
+    if (!open) {
+      setSelectedDate(new Date());
+      setAssetData([]);
+      setCurrentAsset(null);
+      setUploadModalOpen(false);
+      setPreviewModalOpen(false);
+    }
+  }, [open]);
+
+  return (
+    <>
+      {/* 主弹窗 */}
+      <Dialog open={open} onOpenChange={onOpenChange}>
+        <DialogContent className="sm:max-w-[1200px] max-h-[90vh] overflow-y-auto">
+          <DialogHeader>
+            <DialogTitle data-testid="order-asset-modal-title">订单资源上传</DialogTitle>
+            <DialogDescription>
+              为订单中的残疾人管理资产文件(残疾证明、税务文件、薪资单等)
+            </DialogDescription>
+          </DialogHeader>
+
+          <div className="space-y-4">
+            {/* 月份筛选 */}
+            <div className="flex items-center space-x-2">
+              <Label htmlFor="month-select">选择月份:</Label>
+              <Popover>
+                <PopoverTrigger asChild>
+                  <Button
+                    variant="outline"
+                    className={`w-[240px] justify-start text-left font-normal ${!selectedDate ? "text-muted-foreground" : ""}`}
+                  >
+                    <CalendarIcon className="mr-2 h-4 w-4" />
+                    {selectedDate ? format(selectedDate, "yyyy年MM月") : "选择月份"}
+                  </Button>
+                </PopoverTrigger>
+                <PopoverContent className="w-auto p-0" align="start">
+                  <Calendar
+                    mode="single"
+                    selected={selectedDate}
+                    onSelect={(date) => date && setSelectedDate(date)}
+                    initialFocus
+                    className="p-3"
+                      fromYear={2020}
+                    toYear={2030}
+                  />
+                </PopoverContent>
+              </Popover>
+            </div>
+
+            <Separator />
+
+            {/* 资产表格 */}
+            {loading ? (
+              <div className="text-center py-8">加载资产数据...</div>
+            ) : assetData.length > 0 ? (
+              <div className="border rounded-md overflow-auto">
+                <Table>
+                  <TableHeader>
+                    <TableRow>
+                      {columns.map(col => (
+                        <TableHead key={col.key} style={{ width: col.width, minWidth: col.width }}>
+                          {col.title}
+                        </TableHead>
+                      ))}
+                    </TableRow>
+                  </TableHeader>
+                  <TableBody>
+                    {assetData.map((record) => (
+                      <TableRow key={record.personId}>
+                        {columns.map(col => {
+                          const value = record[col.dataIndex as keyof PersonAssetData];
+                          if (col.key === 'personId' || col.key === 'personName') {
+                            return (
+                              <TableCell key={col.key}>
+                                {value as React.ReactNode}
+                              </TableCell>
+                            );
+                          } else {
+                            // 资产类型列
+                            const assetItem = value as AssetItem | undefined;
+                            if (assetItem && assetItem.id) {
+                              // 已上传,显示查看按钮
+                              return (
+                                <TableCell key={col.key}>
+                                  <Button
+                                    variant="ghost"
+                                    size="sm"
+                                    onClick={() => handleOpenPreviewModal(record.personId, record.personName, col.key as AssetType, assetItem)}
+                                  >
+                                    <Eye className="h-4 w-4 mr-1" />
+                                    查看文件
+                                  </Button>
+                                </TableCell>
+                              );
+                            } else {
+                              // 未上传,显示上传按钮
+                              return (
+                                <TableCell key={col.key}>
+                                  <Button
+                                    variant="outline"
+                                    size="sm"
+                                    onClick={() => handleOpenUploadModal(record.personId, record.personName, col.key as AssetType)}
+                                  >
+                                    <Upload className="h-4 w-4 mr-1" />
+                                    上传文件
+                                  </Button>
+                                </TableCell>
+                              );
+                            }
+                          }
+                        })}
+                      </TableRow>
+                    ))}
+                  </TableBody>
+                </Table>
+              </div>
+            ) : (
+              <div className="text-center py-8 border rounded-md">
+                <div className="text-muted-foreground">暂无资产数据</div>
+                <div className="text-sm text-muted-foreground mt-1">
+                  点击"上传文件"按钮为残疾人上传资产文件
+                </div>
+              </div>
+            )}
+          </div>
+
+          <DialogFooter>
+            <Button variant="outline" onClick={() => onOpenChange(false)}>
+              关闭
+            </Button>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
+
+      {/* 上传弹窗 */}
+      <Dialog open={uploadModalOpen} onOpenChange={setUploadModalOpen}>
+        <DialogContent className="sm:max-w-[500px]">
+          <DialogHeader>
+            <DialogTitle>
+              上传{currentAsset ? ASSET_TYPE_LABELS[currentAsset.assetType] : ''} - {currentAsset?.personName}
+            </DialogTitle>
+          </DialogHeader>
+
+          <div className="space-y-4">
+            <div className="space-y-2">
+              <Label>文件类型</Label>
+              <RadioGroup
+                value={uploadFileType}
+                onValueChange={(value) => setUploadFileType(value as AssetFileType)}
+                className="flex space-x-2"
+              >
+                <div className="flex items-center space-x-2">
+                  <RadioGroupItem value={AssetFileType.IMAGE} id="image" />
+                  <Label htmlFor="image">图片</Label>
+                </div>
+                <div className="flex items-center space-x-2">
+                  <RadioGroupItem value={AssetFileType.VIDEO} id="video" />
+                  <Label htmlFor="video">视频</Label>
+                </div>
+              </RadioGroup>
+            </div>
+
+            <div className="space-y-2">
+              <Label>选择文件</Label>
+              <FileSelector
+                value={selectedFileId}
+                onChange={handleFileChange}
+                accept={uploadFileType === AssetFileType.IMAGE ? "image/*" : "video/*"}
+                filterType={uploadFileType === AssetFileType.IMAGE ? "image" : "video"}
+                placeholder="选择或上传文件"
+                title="选择资产文件"
+                description={uploadFileType === AssetFileType.IMAGE
+                  ? "支持上传图片文件,文件大小不超过2MB"
+                  : "支持上传视频文件,文件大小不超过50MB"}
+              />
+            </div>
+          </div>
+
+          <DialogFooter>
+            <Button variant="outline" onClick={() => setUploadModalOpen(false)}>
+              取消
+            </Button>
+            <Button
+              onClick={handleUpload}
+              disabled={!selectedFileId || uploading}
+            >
+              {uploading ? '提交中...' : '提交'}
+            </Button>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
+
+      {/* 预览弹窗 */}
+      <Dialog open={previewModalOpen} onOpenChange={setPreviewModalOpen}>
+        <DialogContent className="sm:max-w-[500px]">
+          <DialogHeader>
+            <DialogTitle>
+              {currentAsset ? ASSET_TYPE_LABELS[currentAsset.assetType] : ''} - {currentAsset?.personName}
+            </DialogTitle>
+          </DialogHeader>
+
+          {currentAsset?.assetItem && (
+            <div className="space-y-4">
+              <div className="space-y-2">
+                <Label>文件类型</Label>
+                <Badge variant="outline">
+                  {currentAsset.assetItem.assetFileType === AssetFileType.IMAGE ? '图片' : '视频'}
+                </Badge>
+              </div>
+
+              <div className="space-y-2">
+                <Label>关联时间</Label>
+                <div>{new Date(currentAsset.assetItem.relatedTime).toLocaleDateString()}</div>
+              </div>
+
+              {currentAsset.assetItem.remark && (
+                <div className="space-y-2">
+                  <Label>备注</Label>
+                  <div className="text-sm text-muted-foreground">{currentAsset.assetItem.remark}</div>
+                </div>
+              )}
+
+              <div className="space-y-2">
+                <Label>文件预览</Label>
+                <div className="border rounded-md p-4 text-center">
+                  {currentAsset.assetItem.assetFileType === AssetFileType.IMAGE ? (
+                    <img
+                      src={currentAsset.assetItem.assetUrl}
+                      alt="资产文件"
+                      className="max-w-full h-auto max-h-[200px] mx-auto"
+                    />
+                  ) : (
+                    <video
+                      src={currentAsset.assetItem.assetUrl}
+                      controls
+                      className="max-w-full h-auto max-h-[200px] mx-auto"
+                    />
+                  )}
+                </div>
+              </div>
+            </div>
+          )}
+
+          <DialogFooter className="flex justify-between">
+            <Button
+              variant="destructive"
+              onClick={handleDelete}
+              disabled={!currentAsset?.assetItem}
+            >
+              <X className="h-4 w-4 mr-1" />
+              删除
+            </Button>
+            <div className="flex space-x-2">
+              <Button variant="outline" onClick={() => setPreviewModalOpen(false)}>
+                关闭
+              </Button>
+              <Button onClick={() => {
+                setPreviewModalOpen(false);
+                setUploadModalOpen(true);
+              }}>
+                更新文件
+              </Button>
+            </div>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
+    </>
+  );
+};
+
+export default OrderAssetModal;

+ 28 - 41
allin-packages/order-management-ui/src/components/OrderDetailModal.tsx

@@ -44,7 +44,7 @@ import {
 import { orderClientManager } from "../api/orderClient";
 import { salaryClientManager } from "@d8d/allin-salary-management-ui";
 import { DisabledPersonSelector } from "@d8d/allin-disability-person-management-ui";
-import OrderPersonAssetAssociation from "./OrderPersonAssetAssociation";
+import OrderAssetModal from "./OrderAssetModal";
 import type { DisabledPersonData } from "@d8d/allin-disability-person-management-ui";
 
 interface OrderDetailModalProps {
@@ -65,22 +65,6 @@ interface PendingPerson {
   city?: string; // 原系统使用字符串
 }
 
-interface OrderPerson {
-  id: number; // 注意:从opId改为id,与schema保持一致
-  orderId: number;
-  personId: number;
-  joinDate?: string;
-  leaveDate?: string;
-  workStatus?: number;
-  salaryDetail?: number;
-  person?: {
-    id: number;
-    name: string;
-    gender: string;
-    disabilityType: string;
-    phone: string;
-  };
-}
 
 const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
   open,
@@ -90,7 +74,6 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
 }) => {
   const [isPersonSelectorOpen, setIsPersonSelectorOpen] = useState(false);
   const [isAssetAssociationOpen, setIsAssetAssociationOpen] = useState(false);
-  const [selectedPersonId, setSelectedPersonId] = useState<number | null>(null);
   const [isActionLoading, setIsActionLoading] = useState(false);
   const [pendingPersons, setPendingPersons] = useState<PendingPerson[]>([]);
 
@@ -166,18 +149,25 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
   // 更新人员工作状态
   const updateWorkStatusMutation = useMutation({
     mutationFn: async ({
-      orderId: _orderId,
-      personId: _personId,
-      workStatus: _workStatus,
+      orderId,
+      personId,
+      workStatus,
     }: {
       orderId: number;
       personId: number;
       workStatus: WorkStatus;
     }) => {
-      // 注意:这里需要根据实际API调整
-      // 原系统使用updatePersonWorkStatus,当前系统可能需要调用不同的API
-      // 暂时使用toast提示,实际实现需要根据后端API调整
-      throw new Error("更新工作状态API未实现");
+      const orderClient = orderClientManager.get();
+      const response = await orderClient.persons['work-status'].$put({
+        json: { orderId, personId, workStatus }
+      });
+
+      if (!response.ok) {
+        const error = await response.json();
+        throw new Error(error.message || "更新工作状态失败");
+      }
+
+      return await response.json();
     },
     onSuccess: () => {
       toast.success("工作状态更新成功");
@@ -266,7 +256,7 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
 
         if (response.ok) {
           const salaryData = await response.json();
-          return salaryData.salary || 5000; // 返回查询到的薪资
+          return salaryData?.totalSalary || 5000; // 返回查询到的总薪资
         }
       }
 
@@ -294,8 +284,6 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
     // 获取待添加人员的ID列表
     const pendingPersonIds = pendingPersons.map(p => p.personId);
 
-    const newPendingPersons: PendingPerson[] = [];
-
     // 使用Promise.all并行查询所有人员的薪资
     const salaryPromises = personsArray.map(async person => {
       // 检查是否已在订单中或已在待添加列表中
@@ -324,7 +312,7 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
     const pendingPersonResults = await Promise.all(salaryPromises);
 
     // 过滤掉null(已存在的人员)
-    const validPendingPersons = pendingPersonResults.filter((p): p is PendingPerson => p !== null);
+    const validPendingPersons = pendingPersonResults.filter((p): p is NonNullable<typeof p> => p !== null);
 
     if (validPendingPersons.length > 0) {
       console.log('OrderDetailModal: 设置pendingPersons,新人员:', validPendingPersons);
@@ -583,7 +571,7 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
               <div data-testid="pending-persons-debug" style={{ display: 'none' }}>
                 {JSON.stringify({ length: pendingPersons.length, persons: pendingPersons })}
               </div>
-              {console.log('OrderDetailModal: 条件渲染前,pendingPersons.length:', pendingPersons.length) || true}
+              {(() => { console.log('OrderDetailModal: 条件渲染前,pendingPersons.length:', pendingPersons.length); return null; })()}
               {pendingPersons.length > 0 ? (
                 <Card data-testid="pending-persons-card">
                   <CardHeader>
@@ -714,7 +702,7 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
                                 {formatDate(person.joinDate)}
                               </TableCell>
                               <TableCell>
-                                {formatDate(person.leaveDate)}
+                                {formatDate(person.leaveDate ? person.leaveDate.toString() : undefined)}
                               </TableCell>
                               <TableCell>
                                 <Select
@@ -849,19 +837,18 @@ const OrderDetailModal: React.FC<OrderDetailModalProps> = ({
         disabledIds={(order?.orderPersons || []).map(p => p.personId)}
       />
 
-      {/* 资产关联模态框 */}
-      {orderId && (
-        <OrderPersonAssetAssociation
+      {/* 资产上传模态框 */}
+      {orderId && order?.orderPersons && (
+        <OrderAssetModal
           orderId={orderId}
-          personId={selectedPersonId || undefined}
+          orderPersons={order.orderPersons.map((op) => ({
+            personId: op.personId,
+            personName: op.person?.name || '未知',
+            disabilityId: op.person?.disabilityId,
+            disabilityType: op.person?.disabilityType,
+          }))}
           open={isAssetAssociationOpen}
           onOpenChange={setIsAssetAssociationOpen}
-          onSuccess={() => {
-            setIsAssetAssociationOpen(false);
-            setSelectedPersonId(null);
-            refetch();
-            onSuccess?.();
-          }}
         />
       )}
 

+ 28 - 10
allin-packages/order-management-ui/src/components/OrderManagement.tsx

@@ -60,7 +60,7 @@ import { OrderStatus, WorkStatus, getOrderStatusLabel, getWorkStatusLabel } from
 import { orderClient, orderClientManager } from '../api/orderClient';
 import OrderForm from './OrderForm';
 import PersonSelector from './PersonSelector';
-import OrderPersonAssetAssociation from './OrderPersonAssetAssociation';
+import OrderAssetModal from './OrderAssetModal';
 import OrderDetailModal from './OrderDetailModal';
 import type { OrderListItem, OrderSearchParams, OrderDetail } from '../api/types';
 
@@ -108,6 +108,24 @@ export const OrderManagement: React.FC = () => {
     },
   });
 
+  // 查询订单详情(用于资产上传)
+  const { data: orderDetailData } = useQuery({
+    queryKey: ['order-detail-for-assets', selectedOrderId],
+    queryFn: async () => {
+      if (!selectedOrderId) return null;
+
+      const response = await orderClientManager.get().detail[':id'].$get({
+        param: { id: selectedOrderId },
+      });
+      if (!response.ok) {
+        const errorData = await response.json();
+        throw new Error(errorData.message || '获取订单详情失败');
+      }
+      return response.json();
+    },
+    enabled: !!selectedOrderId && isAssetAssociationOpen,
+  });
+
   // 删除订单Mutation
   const deleteMutation = useMutation({
     mutationFn: async (id: number) => {
@@ -577,18 +595,18 @@ export const OrderManagement: React.FC = () => {
         />
       )}
 
-      {/* 资产关联模态框 */}
-      {selectedOrderId && (
-        <OrderPersonAssetAssociation
+      {/* 资产上传模态框 */}
+      {selectedOrderId && orderDetailData?.orderPersons && (
+        <OrderAssetModal
           orderId={selectedOrderId}
-          personId={selectedPersonId || undefined}
+          orderPersons={orderDetailData.orderPersons.map((op: any) => ({
+            personId: op.personId,
+            personName: op.person?.name || op.personName || '未知',
+            disabilityId: op.person?.disabilityId,
+            disabilityType: op.person?.disabilityType,
+          }))}
           open={isAssetAssociationOpen}
           onOpenChange={setIsAssetAssociationOpen}
-          onSuccess={() => {
-            setIsAssetAssociationOpen(false);
-            setSelectedOrderId(null);
-            setSelectedPersonId(null);
-          }}
         />
       )}
 

+ 1 - 0
allin-packages/order-management-ui/src/components/index.ts

@@ -2,4 +2,5 @@
 export { default as OrderManagement } from './OrderManagement';
 export { default as OrderForm } from './OrderForm';
 export { default as PersonSelector } from './PersonSelector';
+export { default as OrderAssetModal } from './OrderAssetModal';
 export { default as OrderPersonAssetAssociation } from './OrderPersonAssetAssociation';