// Use React.* APIs without destructuring to avoid global redeclaration conflicts // 模拟的初始数据 const pictureInfo = [ { "图片名": "图1", "图片世界id": "A$C2EB80DB8", "图片图号": "NCY24-S008-00", "世界坐标": [ { "x": 559.314, "y": -2484.602 }, { "x": 1153.314, "y": -2484.602 }, { "x": 1153.314, "y": -1643.602 }, { "x": 559.314, "y": -1643.602 } ] }, { "图片名": "图2-1", "图片世界id": "A$C1CC9093B_1", "图片图号": "NCY24-S008-01", "世界坐标": [ { "x": 1288.8, "y": -2458.3 }, { "x": 1691.8, "y": -2458.3 }, { "x": 1691.8, "y": -1884.3 }, { "x": 1288.8, "y": -1884.3 } ] }, { "图片名": "图2-2", "图片世界id": "A$C1CC9093B_1", "图片图号": "NCY24-S008-01", "世界坐标": [ { "x": 1691.8, "y": -2171.3 }, { "x": 1893.3, "y": -2171.3 }, { "x": 1893.3, "y": -1884.3 }, { "x": 1691.8, "y": -1884.3 } ] }, { "图片名": "图2-3", "图片世界id": "A$C1CC9093B_1", "图片图号": "NCY24-S008-01", "世界坐标": [ { "x": 1893.3, "y": -2171.3 }, { "x": 2094.8, "y": -2171.3 }, { "x": 2094.8, "y": -1884.3 }, { "x": 1893.3, "y": -1884.3 } ] }, { "图片名": "图2-4", "图片世界id": "A$C1CC9093B_1", "图片图号": "NCY24-S008-01", "世界坐标": [ { "x": 1691.8, "y": -2458.3 }, { "x": 2094.8, "y": -2458.3 }, { "x": 2094.8, "y": -2171.3 }, { "x": 1691.8, "y": -2171.3 } ] }, { "图片名": "图3-1", "图片世界id": "A$C1CC9093B_2", "图片图号": "NCY24-S008-02", "世界坐标": [ { "x": 2237.5, "y": -2458.3 }, { "x": 2640.5, "y": -2458.3 }, { "x": 2640.5, "y": -1884.3 }, { "x": 2237.5, "y": -1884.3 } ] }, { "图片名": "图3-2", "图片世界id": "A$C1CC9093B_2", "图片图号": "NCY24-S008-02", "世界坐标": [ { "x": 2640.5, "y": -2458.3 }, { "x": 3043.5, "y": -2458.3 }, { "x": 3043.5, "y": -1884.3 }, { "x": 2640.5, "y": -1884.3 } ] } ]; // New logic: Treat each picture as a separate drawing const processedDrawings = {}; pictureInfo.forEach(info => { const imageName = info["图片名"]; const coords = info["世界坐标"]; const minX = Math.min(coords[0].x, coords[1].x, coords[2].x, coords[3].x); const minY = Math.min(coords[0].y, coords[1].y, coords[2].y, coords[3].y); const maxX = Math.max(coords[0].x, coords[1].x, coords[2].x, coords[3].x); const maxY = Math.max(coords[0].y, coords[1].y, coords[2].y, coords[3].y); processedDrawings[imageName] = { id: imageName, name: `图纸 ${imageName}`, drawingNumber: info["图片图号"], // Each drawing now has only one image images: [{ url: `图片/${imageName}_01.png`, name: imageName, coords: coords, width: Math.abs(coords[1].x - coords[0].x), height: Math.abs(coords[2].y - coords[1].y) }], minX, minY, maxX, maxY, }; }); const initialData = { drawings: processedDrawings, items: { '__ROOT__': { id: '__ROOT__', name: '总装图', quantity: 1, weight: 0, drawingId: '图1', parentId: null, children: [] }, }, // Positions are now per-drawing (which is per-image) tablePositions: { '图1': { x: 50, y: 50 }, // Example default }, templates: { 'zongzhuang': { "template_name": "标准物料清单-底部表头", "row_height": 8.0, "header_height": 14.0, "column_boundaries": [0.0, 15.0, 45.0, 100.0, 110.0, 140.0, 150.0, 160.0, 185.0], "header_definition": { "lines": [{ "start": [0.0, 14.0], "end": [185.0, 14.0] }, { "start": [160.0, 6.5], "end": [140.0, 6.5] }, { "start": [110.0, 0.0], "end": [110.0, 14.0] }, { "start": [15.0, 14.0], "end": [15.0, 0.0] }, { "start": [45.0, 0.0], "end": [45.0, 14.0] }, { "start": [100.0, 0.0], "end": [100.0, 14.0] }, { "start": [140.0, 0.0], "end": [140.0, 14.0] }, { "start": [160.0, 0.0], "end": [160.0, 14.0] }, { "start": [150.0, 6.5], "end": [150.0, 14.0] }, { "start": [0.0, 0.0], "end": [185.0, 0.0] }, { "start": [185.0, 0.0], "end": [185.0, 14.0] }], "texts": [{ "content": "总", "relative_pos": [153.62, 10.23], "height": 3.5 }, { "content": "单", "relative_pos": [143.79, 10.02], "height": 3.5 }, { "content": "TOTAL", "relative_pos": [152.64, 7.45], "height": 2.0 }, { "content": "SINGLE", "relative_pos": [141.89, 7.45], "height": 2.0 }, { "content": "备 注", "relative_pos": [169.33, 6.8], "alignment": "BOTTOM_CENTER", "height": 3.5 }, { "content": "材 料", "relative_pos": [121.56, 6.56], "height": 3.5 }, { "content": "数量", "relative_pos": [102.5, 6.56], "height": 3.5 }, { "content": "名 称", "relative_pos": [67.3, 6.56], "height": 3.5 }, { "content": "件 号", "relative_pos": [4.43, 6.56], "height": 3.5 }, { "content": "图号或标准号", "relative_pos": [22.43, 6.53], "height": 3.5 }, { "content": "MAT'L", "relative_pos": [122.94, 2.33], "height": 2.0 }, { "content": "QTY.", "relative_pos": [102.89, 2.33], "height": 2.0 }, { "content": "REMARKS", "relative_pos": [165.48, 2.3], "height": 2.0 }, { "content": "PARTS. NAME.", "relative_pos": [65.06, 1.76], "height": 2.0 }, { "content": "DWG NO. OR STD. NO.", "relative_pos": [19.43, 1.76], "height": 2.0 }, { "content": "PARTS .NO.", "relative_pos": [7.77, 1.76], "alignment": "BOTTOM_CENTER", "height": 2.0 }, { "content": "MASS(kg)", "relative_pos": [148.32, 1.7], "height": 2.0 }, { "content": "质量", "relative_pos": [142.49, 0.87], "height": 3.5 }] }, "column_definitions": [{ "name": "件号", "relative_x_start": 0.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.82, 1.46], "alignment": "BOTTOM_LEFT", "height": 3.5 }] }, { "name": "图号或标准号", "relative_x_start": 15.0, "text_definitions": [{ "data_key": "main", "relative_pos": [14.77, 1.15], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "名称", "relative_x_start": 45.0, "text_definitions": [{ "data_key": "chinese_name", "relative_pos": [1.76, 3.88], "height": 3.5 }, { "data_key": "english_name", "relative_pos": [1.79, 1.0], "height": 2.0 }, { "data_key": "specification", "relative_pos": [18.68, 3.9], "height": 3.0 }] }, { "name": "数量", "relative_x_start": 100.0, "text_definitions": [{ "data_key": "main", "relative_pos": [4.97, 1.37], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "材料", "relative_x_start": 110.0, "text_definitions": [{ "data_key": "main", "relative_pos": [15.16, 1.12], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "单", "relative_x_start": 140.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "总", "relative_x_start": 150.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "备注", "relative_x_start": 160.0, "text_definitions": [{ "data_key": "main", "relative_pos": [12.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }] }, 'lingjian': { "template_name": "标准物料清单-底部表头", "row_height": 12.0, "header_height": 12.0, "column_boundaries": [0.0, 20.0, 65.0, 95.0, 115.0, 130.0, 155.0, 180.0], "header_definition": { "lines": [{ "start": [0.0, 0.0], "end": [180.0, 0.0] }, { "start": [0.0, 0.0], "end": [0.0, 12.0] }, { "start": [20.0, 0.0], "end": [20.0, 12.0] }, { "start": [65.0, 0.0], "end": [65.0, 12.0] }, { "start": [95.0, 0.0], "end": [95.0, 12.0] }, { "start": [115.0, 0.0], "end": [115.0, 12.0] }, { "start": [130.0, 0.0], "end": [130.0, 12.0] }, { "start": [155.0, 0.0], "end": [155.0, 12.0] }, { "start": [180.0, 0.0], "end": [180.0, 12.0] }], "texts": [{ "content": "件 号", "relative_pos": [6.16, 6.25], "height": 3.5 }, { "content": "PART NO.", "relative_pos": [5.29, 1.89], "height": 2.0 }, { "content": "名 称", "relative_pos": [35.60, 6.25], "height": 3.5 }, { "content": "PARTS. NAME.", "relative_pos": [35.45, 1.89], "height": 2.0 }, { "content": "材 料", "relative_pos": [75.65, 6.25], "height": 3.5 }, { "content": "MAT'L", "relative_pos": [76.19, 1.89], "height": 2.0 }, { "content": "质量", "relative_pos": [98.70, 6.25], "height": 3.5 }, { "content": "(kg)", "relative_pos": [104.32, 6.25], "height": 3 }, { "content": "MASS", "relative_pos": [102.15, 1.89], "height": 2.0 }, { "content": "比 例", "relative_pos": [119.09, 6.25], "height": 3.5 }, { "content": "SCALE", "relative_pos": [119.04, 1.89], "height": 2.0 }, { "content": "所在图号", "relative_pos": [137.23, 6.25], "height": 3.5 }, { "content": "DWG. NO.", "relative_pos": [138.16, 1.89], "height": 2.0 }, { "content": "装配图号", "relative_pos": [162.59, 6.25], "height": 3.5 }, { "content": "ASSY. DWG. NO.", "relative_pos": [160.50, 1.89], "height": 2.0 }] }, "column_definitions": [{ "name": "件号", "relative_x_start": 0.0, "text_definitions": [{ "data_key": "main", "relative_pos": [7.5, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "名称", "relative_x_start": 20.0, "text_definitions": [{ "data_key": "chinese_name", "relative_pos": [2.0, 3.58], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "data_key": "english_name", "relative_pos": [2.0, 1.0], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "data_key": "specification", "relative_pos": [20.0, 4.1], "alignment": "BOTTOM_LEFT", "height": 3.0 }] }, { "name": "材料", "relative_x_start": 65.0, "text_definitions": [{ "data_key": "main", "relative_pos": [15.0, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }, { "data_key": "chinese_name", "relative_pos": [15.0, 3.58], "alignment": "BOTTOM_CENTER", "height": 3.5 }, { "data_key": "english_name", "relative_pos": [15.0, 1.0], "alignment": "BOTTOM_CENTER", "height": 2.0 }] }, { "name": "质量", "relative_x_start": 95.0, "text_definitions": [{ "data_key": "main", "relative_pos": [10.0, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "比例", "relative_x_start": 115.0, "text_definitions": [{ "data_key": "main", "relative_pos": [7.5, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "所在图号", "relative_x_start": 130.0, "text_definitions": [{ "data_key": "main", "relative_pos": [12.5, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "装配图号", "relative_x_start": 155.0, "text_definitions": [{ "data_key": "main", "relative_pos": [12.5, 2.17], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }] }, 'lingjian_sub': { "template_name": "标准物料清单-底部表头", "row_height": 8.0, "header_height": 14.0, "column_boundaries": [0.0, 15.0, 45.0, 100.0, 110.0, 140.0, 150.0, 160.0, 180.0], "header_definition": { "lines": [{ "start": [0.0, 14.0], "end": [185.0, 14.0] }, { "start": [160.0, 6.5], "end": [140.0, 6.5] }, { "start": [110.0, 0.0], "end": [110.0, 14.0] }, { "start": [15.0, 14.0], "end": [15.0, 0.0] }, { "start": [45.0, 0.0], "end": [45.0, 14.0] }, { "start": [100.0, 0.0], "end": [100.0, 14.0] }, { "start": [140.0, 0.0], "end": [140.0, 14.0] }, { "start": [160.0, 0.0], "end": [160.0, 14.0] }, { "start": [150.0, 6.5], "end": [150.0, 14.0] }, { "start": [0.0, 0.0], "end": [185.0, 0.0] }, { "start": [180.0, 0.0], "end": [180.0, 14.0] }], "texts": [{ "content": "总", "relative_pos": [153.62, 10.23], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "单", "relative_pos": [143.79, 10.02], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "TOTAL", "relative_pos": [152.64, 7.45], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "SINGLE", "relative_pos": [141.89, 7.45], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "备 注", "relative_pos": [169.33, 6.8], "alignment": "BOTTOM_CENTER", "height": 3.5 }, { "content": "材 料", "relative_pos": [121.56, 6.56], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "数量", "relative_pos": [102.5, 6.56], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "名 称", "relative_pos": [67.3, 6.56], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "件 号", "relative_pos": [4.43, 6.56], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "图号或标准号", "relative_pos": [22.43, 6.53], "alignment": "BOTTOM_LEFT", "height": 3.5 }, { "content": "MAT'L", "relative_pos": [122.94, 2.33], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "QTY.", "relative_pos": [102.89, 2.33], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "REMARKS", "relative_pos": [165.48, 2.3], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "PARTS. NAME.", "relative_pos": [65.06, 1.76], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "DWG NO. OR STD. NO.", "relative_pos": [19.43, 1.76], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "PARTS .NO.", "relative_pos": [7.77, 1.76], "alignment": "BOTTOM_CENTER", "height": 2.0 }, { "content": "MASS(kg)", "relative_pos": [148.32, 1.7], "alignment": "BOTTOM_LEFT", "height": 2.0 }, { "content": "质量", "relative_pos": [142.49, 0.87], "alignment": "BOTTOM_LEFT", "height": 3.5 }] }, "column_definitions": [{ "name": "件 号", "relative_x_start": 0.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.82, 1.46], "alignment": "BOTTOM_LEFT", "height": 3.5 }] }, { "name": "图号或标准号", "relative_x_start": 15.0, "text_definitions": [{ "data_key": "main", "relative_pos": [14.77, 1.15], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "名 称", "relative_x_start": 45.0, "text_definitions": [{ "data_key": "chinese_name", "relative_pos": [1.76, 3.88], "height": 3.5 }, { "data_key": "english_name", "relative_pos": [1.79, 1.0], "height": 2.0 }, { "data_key": "specification", "relative_pos": [18.68, 3.9], "height": 3.0 }] }, { "name": "数量", "relative_x_start": 100.0, "text_definitions": [{ "data_key": "main", "relative_pos": [4.97, 1.37], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "材 料", "relative_x_start": 110.0, "text_definitions": [{ "data_key": "main", "relative_pos": [15.16, 1.12], "alignment": "BOTTOM_CENTER", "height": 3.5 }] }, { "name": "单", "relative_x_start": 140.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "总", "relative_x_start": 150.0, "text_definitions": [{ "data_key": "main", "relative_pos": [5.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }, { "name": "备 注", "relative_x_start": 160.0, "text_definitions": [{ "data_key": "main", "relative_pos": [10.06, 1.42], "alignment": "BOTTOM_CENTER", "height": 3.0 }] }] } } }; function App() { const [drawings, setDrawings] = React.useState(initialData.drawings); const [items, setItems] = React.useState(initialData.items); const [tablePositions, setTablePositions] = React.useState(initialData.tablePositions); const [templates, setTemplates] = React.useState(initialData.templates); const [selectedItemId, setSelectedItemId] = React.useState('__ROOT__'); const [currentDrawingId, setCurrentDrawingId] = React.useState(null); // 根据item层级决定使用哪个模板 const getTemplateForItem = (itemId) => { if (!itemId) return null; if (itemId === '__ROOT__') { return templates.zongzhuang; } const depth = (itemId.match(/-/g) || []).length; if (depth === 0) { // e.g. '1', '2' -> children of root are parts. return templates.lingjian; } // e.g. '1-1', '1-2' return templates.lingjian_sub; }; // 自动计算重量的副作用钩子 React.useEffect(() => { const calculateWeight = (itemId) => { const item = items[itemId]; if (!item) return 0; if (item.children.length === 0) { return item.quantity * (item.weight || 0); } const childrenWeight = item.children.reduce((sum, childId) => { return sum + calculateWeight(childId); }, 0); // 更新当前项的重量(如果是父项)- 添加容差避免无限更新 if (Math.abs(items[itemId].weight - childrenWeight) > 0.001) { setItems(prevItems => ({ ...prevItems, [itemId]: { ...prevItems[itemId], weight: childrenWeight } })); } return childrenWeight; }; // 从根节点开始计算 const rootItems = Object.keys(items).filter(id => items[id].parentId === null); rootItems.forEach(calculateWeight); }, [items]); // 依赖于 items 状态 // 件号到图纸的映射关系 const getDrawingForItem = (itemId) => { const mapping = { '__ROOT__': '图1', // 总装图 -> 图1 '1': '图2-1', // 件号1 -> 图2-1 '1-1': '图2-1', // 件号1-1 -> 图2-1 '1-2': '图2-2', // 件号1-2 -> 图2-2 '2': '图2-3', // 件号2 -> 图2-3 '2-1': '图2-4', // 件号2-1 -> 图2-4 '3': '图3-1', // 件号3 -> 图3-1 '3-1': '图3-2' // 件号3-1 -> 图3-2 }; // 如果有直接映射,使用映射;否则尝试解析件号寻找对应图纸 if (mapping[itemId]) { return mapping[itemId]; } // 对于新创建的件号,尝试根据层级推导对应图纸 if (itemId.includes('-')) { const parts = itemId.split('-'); if (parts.length === 2) { // 二级件号,尝试找到对应的图2-x或图3-x系列 const base = parts[0]; if (base === '1') return '图2-1'; if (base === '2') return '图2-3'; if (base === '3') return '图3-1'; } } // 默认返回图1 return '图1'; }; // 根据选中的件号自动切换图纸显示 React.useEffect(() => { const targetDrawingId = getDrawingForItem(selectedItemId); if (currentDrawingId !== targetDrawingId) { setCurrentDrawingId(targetDrawingId); } }, [selectedItemId]); const handleItemRename = (oldId, newId) => { if (oldId === '__ROOT__') { alert('错误:根节点“总装图”的ID不可修改。'); return; } if (!newId || oldId === newId) return; if (items[newId]) { alert(`错误:件号 "${newId}" 已存在,请使用唯一的件号。`); return; } setItems(prevItems => { const newItems = { ...prevItems }; const itemToMove = { ...newItems[oldId] }; const oldParentId = itemToMove.parentId; // 1. 从旧的父节点移除 if (oldParentId && newItems[oldParentId]) { newItems[oldParentId].children = newItems[oldParentId].children.filter(id => id !== oldId); } // 2. 递归更新所有子孙 const descendantsToUpdate = {}; const updateDescendantsRecursively = (currentOldId, currentNewId) => { const item = newItems[currentOldId]; if (!item || !item.children) return; item.children.forEach(childOldId => { // e.g., child '1-1-1' of '1-1' becomes child '3-1' of '3' const newChildId = childOldId.replace(currentOldId, currentNewId); const childItem = { ...newItems[childOldId] }; childItem.id = newChildId; childItem.parentId = currentNewId; descendantsToUpdate[childOldId] = { newId: newChildId, newItem: childItem }; updateDescendantsRecursively(childOldId, newChildId); }); }; updateDescendantsRecursively(oldId, newId); // 3. 应用子孙更新 Object.values(descendantsToUpdate).forEach(({ newId, newItem }) => { newItem.children = newItem.children.map(cid => descendantsToUpdate[cid] ? descendantsToUpdate[cid].newId : cid); newItems[newId] = newItem; }); Object.keys(descendantsToUpdate).forEach(oldDescendantId => delete newItems[oldDescendantId]); // 4. 更新当前项本身 itemToMove.id = newId; itemToMove.parentId = newId.includes('-') ? newId.substring(0, newId.lastIndexOf('-')) : '__ROOT__'; itemToMove.children = itemToMove.children.map(cid => descendantsToUpdate[cid] ? descendantsToUpdate[cid].newId : cid); delete newItems[oldId]; newItems[newId] = itemToMove; // 5. 添加到新的父节点 const newParentId = itemToMove.parentId; if (newParentId) { if (!newItems[newParentId]) { // 如果新父项不存在,则创建它 (e.g. renaming 1-1 to 3-1-1, and 3-1 does not exist) newItems[newParentId] = { id: newParentId, name: `新建部件 ${newParentId}`, quantity: 1, weight: 0, drawingId: null, parentId: newParentId.includes('-') ? newParentId.substring(0, newParentId.lastIndexOf('-')) : '__ROOT__', children: [] }; } if (!newItems[newParentId].children.includes(newId)) { newItems[newParentId].children.push(newId); } } return newItems; }); setSelectedItemId(newId); }; const handleCycleDrawing = (direction) => { const ids = Object.keys(drawings); if (ids.length === 0) return; let idx = ids.indexOf(currentDrawingId || ''); if (idx === -1) idx = 0; let next = idx + direction; if (next >= ids.length) next = 0; if (next < 0) next = ids.length - 1; setCurrentDrawingId(ids[next]); }; const handleItemUpdate = (newItemData) => { const { id } = newItemData; const ensureAncestors = (itemsMap, targetId) => { const parentId = targetId.includes('-') ? targetId.substring(0, targetId.lastIndexOf('-')) : '__ROOT__'; if (parentId === '__ROOT__') { // 根一定存在 if (!itemsMap['__ROOT__']) { itemsMap['__ROOT__'] = { id: '__ROOT__', name: '总装图', quantity: 1, weight: 0, drawingId: Object.keys(drawings)[0] || null, parentId: null, children: [] }; } return '__ROOT__'; } // 确保父存在 if (!itemsMap[parentId]) { const grandParentId = parentId.includes('-') ? parentId.substring(0, parentId.lastIndexOf('-')) : '__ROOT__'; // 递归确保祖先 ensureAncestors(itemsMap, parentId); itemsMap[parentId] = { id: parentId, name: `新建部件 ${parentId}`, quantity: 1, weight: 0, drawingId: null, parentId: grandParentId, children: [] }; // 将父加入祖父的 children if (itemsMap[grandParentId] && !itemsMap[grandParentId].children.includes(parentId)) { itemsMap[grandParentId].children = [...itemsMap[grandParentId].children, parentId]; } } return parentId; }; setItems(prevItems => { const newItems = { ...prevItems }; // 确保祖先链存在 const parentId = ensureAncestors(newItems, id); // 创建/更新当前项(parentId 由我们统一设置) const merged = { ...newItems[id], ...newItemData, parentId, children: newItems[id]?.children || [] }; newItems[id] = merged; // 将当前项加入父的 children if (parentId && newItems[parentId] && !newItems[parentId].children.includes(id)) { newItems[parentId] = { ...newItems[parentId], children: [...newItems[parentId].children, id] }; } return newItems; }); setSelectedItemId(id); }; const bindDrawingToItem = (itemId, drawingId) => { setItems(prev => ({ ...prev, [itemId]: { ...prev[itemId], drawingId } })); }; const handleSave = () => { const emptyItems = Object.values(items).filter(item => { return !item.id || (item.weight === 0 && item.children.length === 0); }); if (emptyItems.length > 0) { const itemIds = emptyItems.map(item => item.id || '[空ID]').join(', '); alert(`校验失败!\n以下件号存在问题(ID为空或重量为0):\n${itemIds}\n\n请处理后再保存。`); return; } alert('校验通过,保存成功!\n(原型功能,数据未实际持久化)'); }; const handleExport = () => { // 模拟DXF文件内容 let dxfContent = "0\nSECTION\n2\nENTITIES\n"; Object.values(items).forEach(item => { dxfContent += "0\nTEXT\n"; dxfContent += `10\n${Math.random() * 100}\n`; // X coordinate dxfContent += `20\n${Math.random() * 100}\n`; // Y coordinate dxfContent += `40\n8.0\n`; // Text height dxfContent += `1\nID: ${item.id}, Name: ${item.name}, Qty: ${item.quantity}, W: ${item.weight}\n`; }); dxfContent += "0\nENDSEC\n0\nEOF\n"; // 创建并触发下载 const blob = new Blob([dxfContent], { type: 'application/dxf' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `export_${new Date().toISOString()}.dxf`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); alert('DXF文件已开始下载。'); }; const selectedItem = items[selectedItemId]; const currentDrawing = currentDrawingId ? drawings[currentDrawingId] : undefined; // 找到与当前图纸绑定的item,用于右侧编辑区域显示 const currentDrawingItem = React.useMemo(() => { if (!currentDrawingId) return selectedItem; // 查找绑定了当前图纸的item const boundItem = Object.values(items).find(item => item.drawingId === currentDrawingId); return boundItem || selectedItem; }, [currentDrawingId, items, selectedItem]); // 使用与当前图纸绑定的item来选择模板 const currentTemplate = getTemplateForItem(currentDrawingItem?.id || selectedItemId); const updateImagePosition = (drawingId, imageName, deltaX, deltaY) => { setDrawings(prevDrawings => { const newDrawings = { ...prevDrawings }; const drawing = newDrawings[drawingId]; if (!drawing) return prevDrawings; // 更新图片的世界坐标 const updatedImages = drawing.images.map(img => { if (img.name === imageName) { const newCoords = img.coords.map(coord => ({ x: coord.x + deltaX, y: coord.y + deltaY })); return { ...img, coords: newCoords }; } return img; }); // 重新计算drawing的边界 const allCoords = updatedImages.flatMap(img => img.coords); const minX = Math.min(...allCoords.map(c => c.x)); const minY = Math.min(...allCoords.map(c => c.y)); const maxX = Math.max(...allCoords.map(c => c.x)); const maxY = Math.max(...allCoords.map(c => c.y)); newDrawings[drawingId] = { ...drawing, images: updatedImages, minX, minY, maxX, maxY }; return newDrawings; }); }; return React.createElement('div', { className: 'flex h-screen bg-gray-100' }, React.createElement('aside', { className: 'w-[var(--sidebar-width)] bg-white p-4 border-r border-gray-200' }, React.createElement(StructureTree, { items, selectedItemId, setSelectedItemId }) ), React.createElement('main', { className: 'flex-1 flex flex-col' }, React.createElement(Header, { drawings, items, onSave: handleSave, onExport: handleExport }), React.createElement('div', { className: 'flex-1 p-4 grid grid-cols-3 gap-4' }, React.createElement('div', { className: 'col-span-2' }, React.createElement(DrawingViewer, { drawing: currentDrawing, item: currentDrawingItem, items, tablePositions, setTablePositions, drawings, template: currentTemplate, onItemUpdate: handleItemUpdate, onItemRename: handleItemRename, onBindDrawing: bindDrawingToItem, onCycleDrawing: handleCycleDrawing, onUpdateImagePosition: updateImagePosition }) ), React.createElement('div', { className: 'col-span-1' }, React.createElement(DataEditor, { item: currentDrawingItem, items, setItems }) ) ) ) ); } const container = document.getElementById('root'); const root = ReactDOM.createRoot(container); root.render(React.createElement(App));