cadeditfronttest/app.js

515 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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": "MASSkg", "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": "MASSkg", "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));