diff --git a/tests/routes/hicad-manufacturing-extension.test.ts b/tests/routes/hicad-manufacturing-extension.test.ts
index 184ea3bf5..43e8197b9 100644
--- a/tests/routes/hicad-manufacturing-extension.test.ts
+++ b/tests/routes/hicad-manufacturing-extension.test.ts
@@ -8,6 +8,7 @@ import {
createSnapshotDataUrl,
detectPanelHoles,
filterManufacturingBodies,
+ getOverallBounds,
groupBodiesIntoBom,
localizePartName,
type BodyMeasurement,
@@ -286,6 +287,10 @@ test.describe('HiCAD manufacturing extension', () => {
expect(html).toContain('data-generated-exploded-view="true"')
expect(html.match(/class="explode-part"/g)).toHaveLength(10)
expect(html).not.toContain('暂无爆炸图快照')
+ expect(html.match(/class="part-front-view"/g)).toHaveLength(4)
+ expect(html.match(/class="part-side-view"/g)).toHaveLength(4)
+ expect(html).toContain('正面图')
+ expect(html).toContain('侧面图')
await page.setViewportSize({ width: 1600, height: 900 })
await page.setContent(html)
@@ -307,6 +312,31 @@ test.describe('HiCAD manufacturing extension', () => {
})
})
+ test('renders every BOM part without limiting the number of process drawings', () => {
+ const manyParts: BodyMeasurement[] = Array.from(
+ { length: 7 },
+ (_, index) => ({
+ id: `custom-part-${index + 1}`,
+ name: `自定义零件${index + 1}`,
+ center: { x: index * 30, y: 0, z: 0 },
+ dimensions: { x: 20 + index, y: 12 + index, z: 4 + index },
+ })
+ )
+ const rows = groupBodiesIntoBom(manyParts)
+ const html = createEngineeringSheetHtml({
+ projectName: '多配件生产图',
+ generatedAt: '2026-07-27 23:00',
+ overallDimensions: getOverallBounds(manyParts).dimensions,
+ bodies: manyParts,
+ bomRows: rows,
+ })
+
+ expect(rows).toHaveLength(7)
+ expect(html.match(/class="part-detail"/g)).toHaveLength(7)
+ expect(html.match(/class="part-front-view"/g)).toHaveLength(7)
+ expect(html.match(/class="part-side-view"/g)).toHaveLength(7)
+ })
+
test('normalizes URL-safe engine snapshots for browser image rendering', () => {
expect(createSnapshotDataUrl('iVBORw0KGgo_8A')).toBe(
'data:image/png;base64,iVBORw0KGgo/8A=='
diff --git a/vendor/zoo-modeling-app/manufacturing.ts b/vendor/zoo-modeling-app/manufacturing.ts
index 7947f5a6a..bf7e4d068 100644
--- a/vendor/zoo-modeling-app/manufacturing.ts
+++ b/vendor/zoo-modeling-app/manufacturing.ts
@@ -881,6 +881,111 @@ function uniqueRounded(values: number[]) {
.sort((left, right) => left - right)
}
+function sideDimensionSvg(row: BomRow) {
+ const axes = panelAxes(row.dimensions)
+ const horizontal = Math.abs(row.dimensions[axes.depth])
+ const vertical = Math.abs(row.dimensions[axes.vertical])
+ const maxObjectWidth = 112
+ const maxObjectHeight = 145
+ const scale = Math.min(
+ maxObjectWidth / Math.max(horizontal, EPSILON),
+ maxObjectHeight / Math.max(vertical, EPSILON)
+ )
+ const objectWidth = Math.max(horizontal * scale, 1.5)
+ const objectHeight = Math.max(vertical * scale, 1.5)
+ const objectX = 68 + (maxObjectWidth - objectWidth) / 2
+ const objectY = 42 + (maxObjectHeight - objectHeight) / 2
+ const hiddenHoleRows = new Map<
+ string,
+ { center: number; diameter: number }
+ >()
+
+ row.holes
+ .filter((hole) => hole.axis === axes.depth)
+ .forEach((hole) => {
+ const center =
+ hole.center[axes.vertical] -
+ row.center[axes.vertical] +
+ vertical / 2
+ hiddenHoleRows.set(
+ `${roundDimension(center)}:${roundDimension(hole.diameter)}`,
+ { center, diameter: hole.diameter }
+ )
+ })
+
+ const hiddenHoleMarkup = [...hiddenHoleRows.values()]
+ .map(({ center, diameter }) => {
+ const centerY = objectY + objectHeight - center * scale
+ const radius = (diameter / 2) * scale
+ return `
+