| @@ -0,0 +1,7 @@ | |||||
| # 005~008 内容制作全流程 | |||||
| 打开 [验收报告](index.html) 查看入口、演示步骤和截图。 | |||||
| 已建立 4 个模型、4 个场景,关联 4 个编排;38 步、98 个动作。模型/场景/编排发布不更新现有教学任务。005 采用新模型制作,原任务与独立入口保留。 | |||||
| 验证:25 项前端测试、440 项后端测试、四套发布链、98 个动作和局域网 1366×768 页面通过。详细字段与回退说明见 API Wiki《41-桥梁内容制作关联》。 | |||||
| @@ -0,0 +1,352 @@ | |||||
| [ | |||||
| { | |||||
| "id": "front-pump-step-01:1", | |||||
| "stepCode": "front-pump-step-01", | |||||
| "stepIndex": 0, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "根据故障现象确定是哪一个液压泵发生故障", | |||||
| "title": "点击并确认发生故障的液压泵", | |||||
| "targetId": "front-pump-hydraulic-pump", | |||||
| "operation": { | |||||
| "targetId": "front-pump-hydraulic-pump", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "toleranceDegrees": 6, | |||||
| "directionHint": "点击并确认发生故障的液压泵" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "state", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0.000013113021848809584, | |||||
| 1.5521701574325566, | |||||
| 2.9428284168243413 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "液压泵", | |||||
| "sourceNode": "1_24柱塞泵3_05前泵3_08新泵", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0.000013113021848809584, | |||||
| 1.5521701574325566, | |||||
| 2.9428284168243413 | |||||
| ], | |||||
| "center": [ | |||||
| 0.000013113021848809584, | |||||
| 1.5521701574325566, | |||||
| 2.9428284168243413 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "front-pump-step-02:1", | |||||
| "stepCode": "front-pump-step-02", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "断开架桥车取力器", | |||||
| "title": "向断开方向摇动", | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": -30, | |||||
| "toleranceDegrees": 6, | |||||
| "directionHint": "向断开方向摇动" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0.000015258789048289145, | |||||
| -4.159996032714844, | |||||
| 9.5367431640625e-7 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": -30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "取力器开关", | |||||
| "sourceNode": "1_25和1_27和2_03和2_08取力器开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0.000015258789048289145, | |||||
| -4.159996032714844, | |||||
| 9.5367431640625e-7 | |||||
| ], | |||||
| "center": [ | |||||
| 0.000015258789055394573, | |||||
| 0.0000209808349609375, | |||||
| 9.5367431640625e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "front-pump-step-03:1", | |||||
| "stepCode": "front-pump-step-03", | |||||
| "stepIndex": 2, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "将架桥车备胎斜上方链接两个红色滤油器的球阀打开", | |||||
| "title": "由垂直与官道方向旋转至平行于管道方向", | |||||
| "targetId": "front-pump-ball-valve-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-ball-valve-switch", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 90, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "由垂直与官道方向旋转至平行于管道方向" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| -13599.449218749998, | |||||
| 86314.99218750001, | |||||
| 7118.238037109375 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 90, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "球阀开关", | |||||
| "sourceNode": "1_26球阀开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| -13599.449218749998, | |||||
| 86314.99218750001, | |||||
| 7118.238037109375 | |||||
| ], | |||||
| "center": [ | |||||
| -12266.56787109375, | |||||
| 86314.99218750001, | |||||
| 7118.238037109375 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "front-pump-step-04:1", | |||||
| "stepCode": "front-pump-step-04", | |||||
| "stepIndex": 3, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "重新联通取力器", | |||||
| "title": "按步骤2的相反方向摇回去", | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 30, | |||||
| "toleranceDegrees": 6, | |||||
| "directionHint": "按步骤2的相反方向摇回去" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0.000015258789048289145, | |||||
| -4.159996032714844, | |||||
| 9.5367431640625e-7 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "取力器开关", | |||||
| "sourceNode": "1_25和1_27和2_03和2_08取力器开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0.000015258789048289145, | |||||
| -4.159996032714844, | |||||
| 9.5367431640625e-7 | |||||
| ], | |||||
| "center": [ | |||||
| 0.000015258789055394573, | |||||
| 0.0000209808349609375, | |||||
| 9.5367431640625e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "front-pump-step-05:1", | |||||
| "stepCode": "front-pump-step-05", | |||||
| "stepIndex": 4, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "打开辅助控制盒内的“互救”开关", | |||||
| "title": "沿控制盒盖边缘铰链打开", | |||||
| "targetId": "front-pump-aux-control-box-cover", | |||||
| "operation": { | |||||
| "targetId": "front-pump-aux-control-box-cover", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 100, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "沿控制盒盖边缘铰链打开", | |||||
| "pivotEdge": { | |||||
| "space": "world", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "edge": "nearest-vertical-edge" | |||||
| } | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| -2.568113739801058e-16, | |||||
| -1, | |||||
| -2.5291018817332963e-16 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 100, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "辅助控制盒盖", | |||||
| "sourceNode": "1_28辅助控制盒盖", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| -0.9138641357421875, | |||||
| -9.23793029785157, | |||||
| -9.5367431640625e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "front-pump-step-05:2", | |||||
| "stepCode": "front-pump-step-05", | |||||
| "stepIndex": 4, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "打开辅助控制盒内的“互救”开关", | |||||
| "title": "往上搬", | |||||
| "targetId": "front-pump-rescue-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-rescue-switch", | |||||
| "axis": [ | |||||
| 0, | |||||
| 0, | |||||
| 1 | |||||
| ], | |||||
| "deltaDegrees": 30, | |||||
| "toleranceDegrees": 6, | |||||
| "directionHint": "往上搬" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 6.098689649701348e-32, | |||||
| 2.5291018817332963e-16, | |||||
| -1 | |||||
| ], | |||||
| "pivot": [ | |||||
| -0.000002145767204808635, | |||||
| -0.000002145767204808635, | |||||
| 4.76837158203125e-7 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "互救开关", | |||||
| "sourceNode": "1_28和2_06互救开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| -0.000002145767204808635, | |||||
| -0.000002145767204808635, | |||||
| 4.76837158203125e-7 | |||||
| ], | |||||
| "center": [ | |||||
| -0.5494954586028982, | |||||
| -0.000002145767204808635, | |||||
| 4.76837158203125e-7 | |||||
| ] | |||||
| } | |||||
| } | |||||
| ] | |||||
| @@ -0,0 +1,901 @@ | |||||
| [ | |||||
| { | |||||
| "id": "BRIDGE-STEP-1:1", | |||||
| "stepCode": "BRIDGE-STEP-1", | |||||
| "stepIndex": 0, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "保持架桥车运行并断开取力器", | |||||
| "title": "保持架桥车发动机运行", | |||||
| "targetId": "bridge-engine", | |||||
| "operation": { | |||||
| "targetId": "bridge-engine", | |||||
| "kind": "identify", | |||||
| "directionHint": "保持架桥车发动机运行", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-engine", | |||||
| "color": 2209160 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "state", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 1.4901161221603232e-8, | |||||
| 0, | |||||
| -5.960464477539063e-8 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "架桥车发动机", | |||||
| "sourceNode": "发动机", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 1.4901161221603232e-8, | |||||
| 0, | |||||
| -5.960464477539063e-8 | |||||
| ], | |||||
| "center": [ | |||||
| 1.4901161221603232e-8, | |||||
| 0, | |||||
| -5.960464477539063e-8 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-1:2", | |||||
| "stepCode": "BRIDGE-STEP-1", | |||||
| "stepIndex": 0, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "保持架桥车运行并断开取力器", | |||||
| "title": "断开架桥车取力器", | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-pto-switch", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": -30, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "断开架桥车取力器" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 1, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0.0000152587890625, | |||||
| -4.159996032714844, | |||||
| 9.536743448279594e-7 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": -30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "取力器开关", | |||||
| "sourceNode": "1_25和1_27和2_03和2_08取力器开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0.0000152587890625, | |||||
| -4.159996032714844, | |||||
| 9.536743448279594e-7 | |||||
| ], | |||||
| "center": [ | |||||
| 0.0000152587890625, | |||||
| 0.0000209808349609375, | |||||
| 9.536743306171047e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:1", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "打开架桥车快速接口防尘盖", | |||||
| "targetId": "bridge-hose-a", | |||||
| "operation": { | |||||
| "targetId": "bridge-hose-a", | |||||
| "kind": "identify", | |||||
| "directionHint": "打开架桥车快速接口防尘盖", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-hose-a", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| -0.7 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 1.1175870895385742e-8, | |||||
| 3.7252904094842165e-9, | |||||
| -3.725290298461914e-9 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| -0.06, | |||||
| 0, | |||||
| 0.09 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "架桥车快速接口盖", | |||||
| "sourceNode": "2_04架桥车接口盖子", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 1.1175870895385742e-8, | |||||
| 3.7252904094842165e-9, | |||||
| -3.725290298461914e-9 | |||||
| ], | |||||
| "center": [ | |||||
| 1.1175870895385742e-8, | |||||
| 3.7252904094842165e-9, | |||||
| -3.725290298461914e-9 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:2", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "打开保障车快速接口防尘盖", | |||||
| "targetId": "bridge-hose-b", | |||||
| "operation": { | |||||
| "targetId": "bridge-hose-b", | |||||
| "kind": "identify", | |||||
| "directionHint": "打开保障车快速接口防尘盖", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-hose-b", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| -0.7 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| -1.1548399925231934e-7, | |||||
| 1.862645149230957e-8, | |||||
| -7.450580596923828e-9 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| -0.06, | |||||
| 0, | |||||
| 0.09 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "保障车快速接口盖", | |||||
| "sourceNode": "2_04保障车接口盖子", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| -1.1548399925231934e-7, | |||||
| 1.862645149230957e-8, | |||||
| -7.450580596923828e-9 | |||||
| ], | |||||
| "center": [ | |||||
| -1.1548399925231934e-7, | |||||
| 1.862645149230957e-8, | |||||
| -7.450580596923828e-9 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:3", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 2, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "连接供油软管架桥车端接头", | |||||
| "targetId": "bridge-hose-line-a", | |||||
| "operation": { | |||||
| "targetId": "bridge-hose-line-a", | |||||
| "kind": "identify", | |||||
| "directionHint": "连接供油软管架桥车端接头", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-hose-line-a", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "供油软管 · 架桥车端接头", | |||||
| "sourceNode": "2_04油罐连接线大", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:4", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 3, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "连接供油软管保障车端接头", | |||||
| "targetId": "bridge-hose-line-b", | |||||
| "operation": { | |||||
| "targetId": "bridge-hose-line-b", | |||||
| "kind": "identify", | |||||
| "directionHint": "连接供油软管保障车端接头", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-hose-line-b", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "供油软管 · 保障车端接头", | |||||
| "sourceNode": "2_04油罐连接线大", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:5", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 4, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "连接回油软管架桥车端接头", | |||||
| "targetId": "bridge-return-line-a", | |||||
| "operation": { | |||||
| "targetId": "bridge-return-line-a", | |||||
| "kind": "identify", | |||||
| "directionHint": "连接回油软管架桥车端接头", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-return-line-a", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "回油软管 · 架桥车端接头", | |||||
| "sourceNode": "2_04油罐连接线小", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-2:6", | |||||
| "stepCode": "BRIDGE-STEP-2", | |||||
| "stepIndex": 1, | |||||
| "operationIndex": 5, | |||||
| "stepTitle": "连接两车液压软管", | |||||
| "title": "连接回油软管保障车端接头", | |||||
| "targetId": "bridge-return-line-b", | |||||
| "operation": { | |||||
| "targetId": "bridge-return-line-b", | |||||
| "kind": "identify", | |||||
| "directionHint": "连接回油软管保障车端接头", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-return-line-b", | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "translate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "回油软管 · 保障车端接头", | |||||
| "sourceNode": "2_04油罐连接线小", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-3:1", | |||||
| "stepCode": "BRIDGE-STEP-3", | |||||
| "stepIndex": 2, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "打开支援车回油截止阀", | |||||
| "title": "打开保障车回油截止阀", | |||||
| "targetId": "bridge-return-valve", | |||||
| "operation": { | |||||
| "targetId": "bridge-return-valve", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 90, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "打开保障车回油截止阀" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 5.541369318962097e-8, | |||||
| -1.3969838619232178e-9, | |||||
| -0.031092071905732155 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 90, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "保障车回油截止阀", | |||||
| "sourceNode": "2_05回油截止阀", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 5.541369318962097e-8, | |||||
| -1.3969838619232178e-9, | |||||
| -0.031092071905732155 | |||||
| ], | |||||
| "center": [ | |||||
| 5.541369318962097e-8, | |||||
| -1.3969838619232178e-9, | |||||
| 5.587935447692871e-9 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-4:1", | |||||
| "stepCode": "BRIDGE-STEP-4", | |||||
| "stepIndex": 3, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "打开互救作业开关", | |||||
| "title": "打开辅助控制盒盖", | |||||
| "targetId": "front-pump-aux-control-box-cover", | |||||
| "operation": { | |||||
| "targetId": "front-pump-aux-control-box-cover", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivotEdge": { | |||||
| "space": "world", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "edge": "nearest-vertical-edge" | |||||
| }, | |||||
| "deltaDegrees": 100, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "打开辅助控制盒盖" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| -1.691878294677667e-18, | |||||
| -1, | |||||
| -4.505224149471667e-16 | |||||
| ], | |||||
| "pivot": [ | |||||
| -7.105427357601002e-15, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 100, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "辅助控制盒盖", | |||||
| "sourceNode": "1_28辅助控制盒盖", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| -7.105427357601002e-15, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| -0.9138641357421875, | |||||
| -9.237930297851566, | |||||
| -9.5367431640625e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-4:2", | |||||
| "stepCode": "BRIDGE-STEP-4", | |||||
| "stepIndex": 3, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "打开互救作业开关", | |||||
| "title": "打开互救作业开关", | |||||
| "targetId": "front-pump-rescue-switch", | |||||
| "operation": { | |||||
| "targetId": "front-pump-rescue-switch", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 0, | |||||
| 1 | |||||
| ], | |||||
| "deltaDegrees": 30, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "打开互救作业开关" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 5.689507713189075e-34, | |||||
| 4.505224149471667e-16, | |||||
| -1 | |||||
| ], | |||||
| "pivot": [ | |||||
| -0.0000021457672261249172, | |||||
| -0.00000214576721901949, | |||||
| 4.76837158203125e-7 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "互救开关", | |||||
| "sourceNode": "1_28和2_06互救开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| -0.0000021457672261249172, | |||||
| -0.00000214576721901949, | |||||
| 4.76837158203125e-7 | |||||
| ], | |||||
| "center": [ | |||||
| -0.5494954586029053, | |||||
| -0.0000021457672119140625, | |||||
| 4.76837158203125e-7 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-5:1", | |||||
| "stepCode": "BRIDGE-STEP-5", | |||||
| "stepIndex": 4, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "打开滤油器出口连通球阀", | |||||
| "title": "打开第 1 个滤油器出口球阀", | |||||
| "targetId": "bridge-filter-valve-1", | |||||
| "operation": { | |||||
| "targetId": "bridge-filter-valve-1", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 90, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "打开第 1 个滤油器出口球阀" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 2.7939677238464355e-9, | |||||
| -3.725290298461914e-9, | |||||
| -0.05009720101952553 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 90, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "滤油器出口球阀 1", | |||||
| "sourceNode": "2_07球阀开关01", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 2.7939677238464355e-9, | |||||
| -3.725290298461914e-9, | |||||
| -0.05009720101952553 | |||||
| ], | |||||
| "center": [ | |||||
| 2.7939677238464355e-9, | |||||
| -3.725290298461914e-9, | |||||
| 1.862645149230957e-9 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-5:2", | |||||
| "stepCode": "BRIDGE-STEP-5", | |||||
| "stepIndex": 4, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "打开滤油器出口连通球阀", | |||||
| "title": "打开第 2 个滤油器出口球阀", | |||||
| "targetId": "bridge-filter-valve-2", | |||||
| "operation": { | |||||
| "targetId": "bridge-filter-valve-2", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 90, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "打开第 2 个滤油器出口球阀" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 9.313225746154785e-9, | |||||
| 1.862645149230957e-8, | |||||
| -0.05009720101952553 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 90, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "滤油器出口球阀 2", | |||||
| "sourceNode": "2_07球阀开关02和3-02球阀开关", | |||||
| "origin": "model", | |||||
| "pivot": [ | |||||
| 9.313225746154785e-9, | |||||
| 1.862645149230957e-8, | |||||
| -0.05009720101952553 | |||||
| ], | |||||
| "center": [ | |||||
| 9.313225746154785e-9, | |||||
| 1.862645149230957e-8, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-6:1", | |||||
| "stepCode": "BRIDGE-STEP-6", | |||||
| "stepIndex": 5, | |||||
| "operationIndex": 0, | |||||
| "stepTitle": "接通支援车液压取力器", | |||||
| "title": "接通保障车取力器", | |||||
| "targetId": "bridge-support-pto", | |||||
| "operation": { | |||||
| "targetId": "bridge-support-pto", | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "deltaDegrees": 30, | |||||
| "toleranceDegrees": 8, | |||||
| "directionHint": "接通保障车取力器" | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "rotate", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 30, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "保障车取力器开关(操作示意)", | |||||
| "sourceNode": "保障车取力器开关(操作示意)", | |||||
| "origin": "supplement", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| -0.015000000189989815, | |||||
| 0.00474999985098834 | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "id": "BRIDGE-STEP-6:2", | |||||
| "stepCode": "BRIDGE-STEP-6", | |||||
| "stepIndex": 5, | |||||
| "operationIndex": 1, | |||||
| "stepTitle": "接通支援车液压取力器", | |||||
| "title": "观察供油示意", | |||||
| "targetId": "bridge-supply", | |||||
| "operation": { | |||||
| "targetId": "bridge-supply", | |||||
| "kind": "identify", | |||||
| "directionHint": "观察供油示意", | |||||
| "effects": [ | |||||
| { | |||||
| "targetId": "bridge-hose-line", | |||||
| "color": 15312947 | |||||
| }, | |||||
| { | |||||
| "targetId": "bridge-return-line", | |||||
| "color": 3325905 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "motion": { | |||||
| "kind": "state", | |||||
| "axis": [ | |||||
| 0, | |||||
| 1, | |||||
| 0 | |||||
| ], | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "pivotMode": "hinge", | |||||
| "angle": 0, | |||||
| "offset": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "durationMs": 900, | |||||
| "easing": "easeInOut" | |||||
| }, | |||||
| "binding": { | |||||
| "name": "供油回路观察点", | |||||
| "sourceNode": "供油回路观察点", | |||||
| "origin": "supplement", | |||||
| "pivot": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ], | |||||
| "center": [ | |||||
| 0, | |||||
| 0, | |||||
| 0 | |||||
| ] | |||||
| } | |||||
| } | |||||
| ] | |||||
| @@ -0,0 +1,322 @@ | |||||
| { | |||||
| "records": [ | |||||
| { | |||||
| "id": "416", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-DA3BF394D66E48A3", | |||||
| "name": "动画能力验收-组合动作与流向-20260912", | |||||
| "description": "轻量 GLB 实测:车辆多部件、节点编辑、车轮动画、保存重开和发布。保留供后续模型/场景测试使用。", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/12/e1169bfa34a99afa9b1e84ad9523efd1.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "99", | |||||
| "ownerName": "内部维护", | |||||
| "departmentId": "1", | |||||
| "currentVersionId": "504", | |||||
| "currentVersionNo": 2, | |||||
| "currentVersionCode": "V2.0.0", | |||||
| "publishedVersionId": "479", | |||||
| "publishedTime": 1789202700, | |||||
| "usageCount": 14, | |||||
| "version": 10, | |||||
| "addTime": 1789202691, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "/models/bridge-erector-vehicle.glb", | |||||
| "nodeCount": 186, | |||||
| "materialCount": 114, | |||||
| "animationCount": 12 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 3, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "407", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-84E00933755E4D75", | |||||
| "name": "ZDS-模型1", | |||||
| "description": "ZDS-模型1", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/06/df6453b974c51518a1f78ee1dca67c0d.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "68", | |||||
| "ownerName": "沈君瑶", | |||||
| "departmentId": "5", | |||||
| "currentVersionId": "462", | |||||
| "currentVersionNo": 2, | |||||
| "currentVersionCode": "V2.0.0", | |||||
| "publishedVersionId": "461", | |||||
| "publishedTime": 1788699921, | |||||
| "usageCount": 3, | |||||
| "version": 36, | |||||
| "addTime": 1788699642, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "fsvc://unreal-tran/model/2026/09/06/8f9816c2e66e7d64ff98abb35accb245.glb", | |||||
| "nodeCount": 3, | |||||
| "materialCount": 1, | |||||
| "animationCount": 1 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 12, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "157", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-70875AA932B94685", | |||||
| "name": "实测模型-狐狸动画-已验证-20260905", | |||||
| "description": "轻量 GLB 实测:骨骼蒙皮、Survey/Walk/Run 动画、节点编辑、保存重开和发布。保留供后续模型/场景测试使用。", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/10/5d4501f4d8a2f32a8105a697cf2d0cf1.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "2", | |||||
| "currentVersionId": "202", | |||||
| "currentVersionNo": 3, | |||||
| "currentVersionCode": "V3.0.0", | |||||
| "publishedVersionId": "201", | |||||
| "publishedTime": 1788601684, | |||||
| "usageCount": 2, | |||||
| "version": 26, | |||||
| "addTime": 1788600787, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "fsvc://unreal-tran/model/2026/09/05/3fc69ae0d435f869afca1fcd943486d1.glb", | |||||
| "nodeCount": 27, | |||||
| "materialCount": 1, | |||||
| "animationCount": 1 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 3, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "156", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-3FA24629D59E4509", | |||||
| "name": "实测模型-牛奶卡车-已验证-20260905", | |||||
| "description": "轻量 GLB 实测:车辆多部件、节点编辑、车轮动画、保存重开和发布。保留供后续模型/场景测试使用。", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/05/67b2a5d15f9c1fc47a0a760f28fd2e2e.jpg", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "2", | |||||
| "currentVersionId": "204", | |||||
| "currentVersionNo": 4, | |||||
| "currentVersionCode": "V4.0.0", | |||||
| "publishedVersionId": "204", | |||||
| "publishedTime": 1788603046, | |||||
| "usageCount": 2, | |||||
| "version": 20, | |||||
| "addTime": 1788600271, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "fsvc://unreal-tran/model/2026/09/05/a75de903d95b0590307632d7bed44111.glb", | |||||
| "nodeCount": 10, | |||||
| "materialCount": 5, | |||||
| "animationCount": 1 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 3, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "94", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-BRIDGE-ERECTOR", | |||||
| "name": "桥梁架设装备整机模型", | |||||
| "description": "现场正式桥梁架设装备,44 个命名部件与 12 组机构联动时间轴", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/11/e559b1e73e5e0fc7a3fabb96ef380c93.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "2", | |||||
| "ownerName": "周教员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "503", | |||||
| "currentVersionNo": 5, | |||||
| "currentVersionCode": "V5.0.0", | |||||
| "publishedVersionId": "474", | |||||
| "publishedTime": 1789139384, | |||||
| "usageCount": 4, | |||||
| "version": 270, | |||||
| "addTime": 1788074748, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "/models/bridge-erector-vehicle.glb", | |||||
| "nodeCount": 186, | |||||
| "materialCount": 114, | |||||
| "animationCount": 12 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 4, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "48", | |||||
| "type": "MODEL", | |||||
| "code": "UAT-CONTENT-MODEL-EQUIPMENT", | |||||
| "name": "UAT 挖掘机装备模型", | |||||
| "description": "教学实施验收专用模型元数据,不依赖或更新业务模型工程。", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/models/excavator-a-thumb.png", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "54", | |||||
| "currentVersionNo": 9002, | |||||
| "currentVersionCode": "V9002.0.0", | |||||
| "publishedVersionId": "48", | |||||
| "publishedTime": 1786413935, | |||||
| "usageCount": 3, | |||||
| "version": 556, | |||||
| "addTime": 1786413935, | |||||
| "updateTime": "2026-09-23T22:48:47", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "/models/excavator-a.glb", | |||||
| "nodeCount": 5, | |||||
| "materialCount": 4, | |||||
| "animationCount": 6 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 1, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "436", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-EB2011E5BFC94A52", | |||||
| "name": "CL-mode1", | |||||
| "description": "这是测试数据", | |||||
| "categoryCode": "PART", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/13/e17e3797f33ab4dd72dafbb2efbd94e6.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "68", | |||||
| "ownerName": "沈君瑶", | |||||
| "departmentId": "5", | |||||
| "currentVersionId": "505", | |||||
| "currentVersionNo": 1, | |||||
| "currentVersionCode": "V1.0.0", | |||||
| "publishedVersionId": null, | |||||
| "publishedTime": null, | |||||
| "usageCount": 0, | |||||
| "version": 36, | |||||
| "addTime": 1789304132, | |||||
| "updateTime": "2026-09-13T21:23:41", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "fsvc://unreal-tran/model/2026/09/13/ae06b79910fa31172ccf42ad0a963815.glb", | |||||
| "nodeCount": 3, | |||||
| "materialCount": 1, | |||||
| "animationCount": 2 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 4, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "155", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-07082542E60B482D", | |||||
| "name": "测试", | |||||
| "description": "", | |||||
| "categoryCode": "TOOL", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/11/c40d90e85bdede8e3f7bb00d6a7c1520.jpg", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "2", | |||||
| "currentVersionId": "197", | |||||
| "currentVersionNo": 1, | |||||
| "currentVersionCode": "V1.0.0", | |||||
| "publishedVersionId": null, | |||||
| "publishedTime": null, | |||||
| "usageCount": 0, | |||||
| "version": 57, | |||||
| "addTime": 1788596734, | |||||
| "updateTime": "2026-09-11T23:02:03", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "/models/bridge-erector-vehicle.glb", | |||||
| "nodeCount": 186, | |||||
| "materialCount": 114, | |||||
| "animationCount": 14 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 2, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "151", | |||||
| "type": "MODEL", | |||||
| "code": "MODEL-FIELD-TERRAIN-BRIDGE", | |||||
| "name": "野外山地场景与桥梁架设装备", | |||||
| "description": "车.glb 整机与真实地形.glb 外部环境组成的模型编辑工程", | |||||
| "categoryCode": "EQUIPMENT", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/models/scene-field-terrain-vehicle-thumb.png", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "2", | |||||
| "ownerName": "周教员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "193", | |||||
| "currentVersionNo": 1, | |||||
| "currentVersionCode": "V1.0.0", | |||||
| "publishedVersionId": "193", | |||||
| "publishedTime": 1788316127, | |||||
| "usageCount": 1, | |||||
| "version": 1, | |||||
| "addTime": 1788316127, | |||||
| "updateTime": "2026-09-02T11:02:50", | |||||
| "contentSummary": { | |||||
| "schema": "digital-twin-editor-project", | |||||
| "resourceUri": "/models/车.glb", | |||||
| "nodeCount": 88, | |||||
| "materialCount": 18, | |||||
| "animationCount": 12 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 2, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| } | |||||
| ], | |||||
| "total": 9, | |||||
| "page": 1, | |||||
| "size": 100 | |||||
| } | |||||
| @@ -0,0 +1,347 @@ | |||||
| { | |||||
| "records": [ | |||||
| { | |||||
| "id": "160", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-DE5C246982A34F8B", | |||||
| "name": "实测场景-狐狸野外训练区-20260905", | |||||
| "description": "使用狐狸模型的已发布版本,验证野外模板、模型引用、保存截图封面、刷新恢复和发布。", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/06/6c5a19adbfba0ee118353c8826c45bda.jpg", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "2", | |||||
| "currentVersionId": "463", | |||||
| "currentVersionNo": 4, | |||||
| "currentVersionCode": "V4.0.0", | |||||
| "publishedVersionId": "463", | |||||
| "publishedTime": 1788700649, | |||||
| "usageCount": 4, | |||||
| "version": 13, | |||||
| "addTime": 1788605625, | |||||
| "updateTime": "2026-09-19T18:04:51", | |||||
| "contentSummary": { | |||||
| "schema": "edit3dv4.scene", | |||||
| "sceneType": "WORKSHOP", | |||||
| "environmentName": "演示 GLB · 独立部件编辑", | |||||
| "objectCount": 75 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 1, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "159", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-7ADDD6B5C3344C5F", | |||||
| "name": "实测场景-牛奶卡车检验区-20260905", | |||||
| "description": "使用已发布牛奶卡车模型,验证场景资源引用、变换、保存恢复、场景截图封面及发布。", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/05/d29810b273d75e43dff07622424f333f.jpg", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "2", | |||||
| "currentVersionId": "209", | |||||
| "currentVersionNo": 2, | |||||
| "currentVersionCode": "V2.0.0", | |||||
| "publishedVersionId": "209", | |||||
| "publishedTime": 1788607948, | |||||
| "usageCount": 7, | |||||
| "version": 7, | |||||
| "addTime": 1788603808, | |||||
| "updateTime": "2026-09-19T18:04:51", | |||||
| "contentSummary": { | |||||
| "schema": "edit3dv4.scene", | |||||
| "sceneType": "WORKSHOP", | |||||
| "environmentName": "正式车库建筑体量 · 整机停放与进出场", | |||||
| "objectCount": 2 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 1, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "457", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-EBC79290EA954BBC", | |||||
| "name": "005 液压泵应急切换(编排验证)", | |||||
| "description": "独立验证工程;不替换 TASK-VIRTUAL-005 及其发布版本。", | |||||
| "categoryCode": "MAINTENANCE", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/17/8c59a21c01de3867f2cc4c343e25a8ec.jpg", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "99", | |||||
| "ownerName": "内部维护", | |||||
| "departmentId": "1", | |||||
| "currentVersionId": "551", | |||||
| "currentVersionNo": 7, | |||||
| "currentVersionCode": "V7.0.0", | |||||
| "publishedVersionId": "551", | |||||
| "publishedTime": 1789655621, | |||||
| "usageCount": 1, | |||||
| "version": 20, | |||||
| "addTime": 1789566525, | |||||
| "updateTime": "2026-09-18T00:54:09", | |||||
| "contentSummary": { | |||||
| "schema": "edit3dv4.scene", | |||||
| "sceneType": "OUTDOOR", | |||||
| "environmentName": "正式起伏地形 · 机动架设与野外协同", | |||||
| "objectCount": 2 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 5, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "418", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-CE481C28E3494AC6", | |||||
| "name": "动画能力验收场景-20260912", | |||||
| "description": "", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "99", | |||||
| "ownerName": "内部维护", | |||||
| "departmentId": "1", | |||||
| "currentVersionId": "481", | |||||
| "currentVersionNo": 1, | |||||
| "currentVersionCode": "V1.0.0", | |||||
| "publishedVersionId": "481", | |||||
| "publishedTime": 1789204589, | |||||
| "usageCount": 12, | |||||
| "version": 3, | |||||
| "addTime": 1789204560, | |||||
| "updateTime": "2026-09-13T09:22:11", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "workshop", | |||||
| "environmentName": "动画能力验收场景-20260912", | |||||
| "objectCount": 1 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 0, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "408", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-60BB81B779274E01", | |||||
| "name": "ZDS-场景1", | |||||
| "description": "ZDS-场景1ZDS-场景1", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "fsvc://unreal-tran/image/2026/09/12/7a7c3070a4b3e0f6edbae142e7afb417.jpg", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "68", | |||||
| "ownerName": "沈君瑶", | |||||
| "departmentId": "5", | |||||
| "currentVersionId": "477", | |||||
| "currentVersionNo": 3, | |||||
| "currentVersionCode": "V3.0.0", | |||||
| "publishedVersionId": "477", | |||||
| "publishedTime": 1789183050, | |||||
| "usageCount": 1, | |||||
| "version": 8, | |||||
| "addTime": 1788700908, | |||||
| "updateTime": "2026-09-12T11:19:09", | |||||
| "contentSummary": { | |||||
| "schema": "edit3dv4.scene", | |||||
| "sceneType": "WORKSHOP", | |||||
| "environmentName": "正式车库建筑体量 · 整机停放与进出场", | |||||
| "objectCount": 2 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 1, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "131", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-VEHICLE-DEPOT-INSPECTION", | |||||
| "name": "真实装备车库整机进场点检场景", | |||||
| "description": "真实车库与桥梁架设装备整机组合场景,包含 12 个可操作部件", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/models/scene-depot-vehicle-thumb.png", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "2", | |||||
| "ownerName": "周教员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "174", | |||||
| "currentVersionNo": 2, | |||||
| "currentVersionCode": "V2.0.0", | |||||
| "publishedVersionId": "165", | |||||
| "publishedTime": 1788139208, | |||||
| "usageCount": 1, | |||||
| "version": 4, | |||||
| "addTime": 1788139208, | |||||
| "updateTime": "2026-09-02T14:48:53", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "workshop", | |||||
| "environmentName": "演示维修车间", | |||||
| "objectCount": 445 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 2, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "152", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-FIELD-TERRAIN-BRIDGE-ERECTOR", | |||||
| "name": "真实野外山地桥梁架设训练场", | |||||
| "description": "真实野外山地环境、同一桥梁架设装备整机与 12 个可操作部件", | |||||
| "categoryCode": "OUTDOOR", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/models/scene-field-terrain-vehicle-thumb.png", | |||||
| "status": "PUBLISHED", | |||||
| "ownerUserId": "2", | |||||
| "ownerName": "周教员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "194", | |||||
| "currentVersionNo": 1, | |||||
| "currentVersionCode": "V1.0.0", | |||||
| "publishedVersionId": "194", | |||||
| "publishedTime": 1788316127, | |||||
| "usageCount": 2, | |||||
| "version": 1, | |||||
| "addTime": 1788316127, | |||||
| "updateTime": "2026-09-02T11:02:50", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "outdoor", | |||||
| "environmentName": "真实野外山地桥梁架设训练场", | |||||
| "objectCount": 14 | |||||
| }, | |||||
| "currentVersionStatus": "PUBLISHED", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 0, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "49", | |||||
| "type": "SCENE", | |||||
| "code": "UAT-CONTENT-SCENE-WORKSHOP", | |||||
| "name": "UAT 综合检验车间", | |||||
| "description": "教学实施验收专用车间场景,不覆盖业务场景。", | |||||
| "categoryCode": "WORKSHOP", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/images/outdoor-training-yard.png", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "67", | |||||
| "currentVersionNo": 9002, | |||||
| "currentVersionCode": "V9002.0.0", | |||||
| "publishedVersionId": "49", | |||||
| "publishedTime": 1786413935, | |||||
| "usageCount": 2, | |||||
| "version": 3, | |||||
| "addTime": 1786413935, | |||||
| "updateTime": "2026-08-31T10:42:12", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "workshop", | |||||
| "environmentName": "UAT 综合检验车间", | |||||
| "objectCount": 3 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 0, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "95", | |||||
| "type": "SCENE", | |||||
| "code": "SCENE-BRIDGE-ERECTOR-YARD", | |||||
| "name": "桥梁架设装备架桥作业训练场", | |||||
| "description": "整机停放位、变幅油缸拆检架与编号零件盘", | |||||
| "categoryCode": "OUTDOOR", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/images/outdoor-training-yard.png", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "2", | |||||
| "ownerName": "周教员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "119", | |||||
| "currentVersionNo": 2, | |||||
| "currentVersionCode": "V2.0.0", | |||||
| "publishedVersionId": "103", | |||||
| "publishedTime": 1788074748, | |||||
| "usageCount": 2, | |||||
| "version": 1, | |||||
| "addTime": 1788074748, | |||||
| "updateTime": "2026-08-31T10:34:44", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "workshop", | |||||
| "environmentName": "标准维修车间", | |||||
| "objectCount": 4 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 0, | |||||
| "assetCount": 1, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| }, | |||||
| { | |||||
| "id": "50", | |||||
| "type": "SCENE", | |||||
| "code": "UAT-CONTENT-SCENE-OUTDOOR", | |||||
| "name": "UAT 室外对抗训练场", | |||||
| "description": "教学实施验收专用红蓝对抗场景,不覆盖业务场景。", | |||||
| "categoryCode": "OUTDOOR", | |||||
| "trainingMode": "", | |||||
| "coverUri": "/images/outdoor-training-yard.png", | |||||
| "status": "DRAFT", | |||||
| "ownerUserId": "1", | |||||
| "ownerName": "系统管理员", | |||||
| "departmentId": "3", | |||||
| "currentVersionId": "56", | |||||
| "currentVersionNo": 9002, | |||||
| "currentVersionCode": "V9002.0.0", | |||||
| "publishedVersionId": "50", | |||||
| "publishedTime": 1786413935, | |||||
| "usageCount": 1, | |||||
| "version": 5, | |||||
| "addTime": 1786413935, | |||||
| "updateTime": "2026-08-11T02:05:35", | |||||
| "contentSummary": { | |||||
| "schema": "unreal-tran.scene", | |||||
| "sceneType": "workshop", | |||||
| "environmentName": "综合训练车间", | |||||
| "objectCount": 12 | |||||
| }, | |||||
| "currentVersionStatus": "DRAFT", | |||||
| "dependencyCount": 1, | |||||
| "assetCount": 0, | |||||
| "readCount": 0, | |||||
| "downloadCount": 0 | |||||
| } | |||||
| ], | |||||
| "total": 10, | |||||
| "page": 1, | |||||
| "size": 100 | |||||
| } | |||||
| @@ -0,0 +1,92 @@ | |||||
| { | |||||
| "pass": false, | |||||
| "message": "expect(page).toHaveURL(expected) failed\n\nExpected pattern: /\\/training-projects\\/438\\/production/\nReceived string: \"http://127.0.0.1:6180/content/scenes/467/production\"\nTimeout: 5000ms\n\nCall log:\n\u001b[2m - Expect \"to.have.url\" with timeout 5000ms\u001b[22m\n\u001b[2m 9 × unexpected value \"http://127.0.0.1:6180/content/scenes/467/production\"\u001b[22m\n", | |||||
| "checks": [ | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "model", | |||||
| "actions": 6, | |||||
| "played": [ | |||||
| 2, | |||||
| 3, | |||||
| 5, | |||||
| 6 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "model", | |||||
| "actions": 15, | |||||
| "played": [ | |||||
| 3, | |||||
| 5, | |||||
| 9 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "model", | |||||
| "actions": 27, | |||||
| "played": [ | |||||
| 4, | |||||
| 8, | |||||
| 12 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "model", | |||||
| "actions": 50, | |||||
| "played": [ | |||||
| 3, | |||||
| 6, | |||||
| 36 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| } | |||||
| ], | |||||
| "errors": [] | |||||
| } | |||||
| @@ -0,0 +1,56 @@ | |||||
| { | |||||
| "items": [ | |||||
| { | |||||
| "taskId": "86", | |||||
| "taskCode": "TASK-VIRTUAL-005", | |||||
| "trainingProjectId": "438", | |||||
| "modelProjectId": "466", | |||||
| "sceneProjectId": "467", | |||||
| "modelVersionId": "575", | |||||
| "sceneVersionId": "576", | |||||
| "trainingVersionId": "577", | |||||
| "steps": 5, | |||||
| "actions": 6, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "87", | |||||
| "taskCode": "TASK-VIRTUAL-006", | |||||
| "trainingProjectId": "439", | |||||
| "modelProjectId": "468", | |||||
| "sceneProjectId": "469", | |||||
| "modelVersionId": "578", | |||||
| "sceneVersionId": "579", | |||||
| "trainingVersionId": "580", | |||||
| "steps": 6, | |||||
| "actions": 15, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "88", | |||||
| "taskCode": "TASK-VIRTUAL-007", | |||||
| "trainingProjectId": "440", | |||||
| "modelProjectId": "470", | |||||
| "sceneProjectId": "471", | |||||
| "modelVersionId": "581", | |||||
| "sceneVersionId": "582", | |||||
| "trainingVersionId": "583", | |||||
| "steps": 11, | |||||
| "actions": 27, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "89", | |||||
| "taskCode": "TASK-VIRTUAL-008", | |||||
| "trainingProjectId": "441", | |||||
| "modelProjectId": "472", | |||||
| "sceneProjectId": "473", | |||||
| "modelVersionId": "584", | |||||
| "sceneVersionId": "585", | |||||
| "trainingVersionId": "586", | |||||
| "steps": 16, | |||||
| "actions": 50, | |||||
| "complete": true | |||||
| } | |||||
| ] | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| [ | |||||
| { | |||||
| "endpoint": "auth/ping", | |||||
| "status": 200 | |||||
| }, | |||||
| { | |||||
| "endpoint": "tran/ping", | |||||
| "status": 200 | |||||
| }, | |||||
| { | |||||
| "url": "http://127.0.0.1:6100/actuator/health", | |||||
| "status": 200 | |||||
| }, | |||||
| { | |||||
| "url": "http://127.0.0.1:6180/", | |||||
| "status": 200 | |||||
| }, | |||||
| { | |||||
| "url": "http://192.168.31.168:6180/", | |||||
| "status": 200 | |||||
| } | |||||
| ] | |||||
| @@ -0,0 +1,10 @@ | |||||
| <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>005~008 内容制作全流程验收</title><style> | |||||
| *{box-sizing:border-box}body{margin:0;background:#f2f6f5;color:#163c38;font:15px/1.7 "Microsoft YaHei",sans-serif}main{max-width:1480px;margin:auto;padding:36px}header{background:#0c594e;color:white;padding:32px;border-radius:14px}h1{font-size:28px;margin:0}h2{font-size:21px}header p{margin:8px 0 0;color:#d4e9e2}.stats{display:flex;gap:12px;flex-wrap:wrap;margin:22px 0}.stats span{padding:13px 22px;background:white;border:1px solid #ccded8;border-radius:9px}.stats b{font-size:24px;color:#078c70;margin-right:8px}section{background:white;padding:24px;margin:20px 0;border:1px solid #d4e3de;border-radius:12px}table{width:100%;border-collapse:collapse}td,th{border-bottom:1px solid #e0eae7;padding:14px;text-align:left}th{background:#edf5f2}a{color:#087c67;text-decoration:none}a:hover{text-decoration:underline}.gallery{display:grid;grid-template-columns:1fr 1fr;gap:18px}figure{margin:0}figure img{display:block;width:100%;border:1px solid #d3e2dd;border-radius:7px}figcaption{padding:8px 0;color:#607f77}.note{border-left:4px solid #e7af51;padding-left:16px}.checks{columns:2}.checks p{margin:6px 0}.pass{color:#058565}code{background:#eff4f2;padding:2px 5px}li{margin:8px 0}@media(max-width:900px){main{padding:15px}.gallery{grid-template-columns:1fr}.table{overflow:auto}.checks{columns:1}} | |||||
| </style></head><body><main><header><h1>005~008 内容制作全流程验收</h1><p>2026-09-24 · 模型动作 → 场景组成 → 训练编排 → 现有虚拟仿真</p></header> | |||||
| <div class="stats"><span><b>4</b>机构模型</span><span><b>4</b>训练场景</span><span><b>38</b>操作步骤</span><span><b>98</b>部件子动作</span><span><b>5</b>原任务快照未变</span></div> | |||||
| <section><h2>演示入口</h2><p>登录数字车间后,从“内容制作 → 模型制作”进入对应机构模型,或打开下表链接。四套名称均采用实际训练名称。</p><div class="table"><table><thead><tr><th>训练内容</th><th>模型制作</th><th>场景制作</th><th>训练编排</th><th>规模</th><th>现有入口</th></tr></thead><tbody><tr><td><b>TASK-VIRTUAL-005</b><br>重型支援桥液压泵应急切换</td><td><a href="http://127.0.0.1:6180/content/models/466/production">机构模型 · V5.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/467/production">训练场景 · V3.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/438/production">训练编排 · V7.0.0</a></td><td>5 步 / 6 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/86">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-006</b><br>重型支援桥液压泵应急替代</td><td><a href="http://127.0.0.1:6180/content/models/468/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/469/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/439/production">训练编排 · V10.0.0</a></td><td>6 步 / 15 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/87">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-007</b><br>重型支援桥前泵换件修复</td><td><a href="http://127.0.0.1:6180/content/models/470/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/471/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/440/production">训练编排 · V8.0.0</a></td><td>11 步 / 27 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/88">进入仿真</a></td></tr><tr><td><b>TASK-VIRTUAL-008</b><br>重型支援桥平衡阀拆拼互换</td><td><a href="http://127.0.0.1:6180/content/models/472/production">机构模型 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/scenes/473/production">训练场景 · V2.0.0</a></td><td><a href="http://127.0.0.1:6180/content/training-projects/441/production">训练编排 · V8.0.0</a></td><td>16 步 / 50 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/89">进入仿真</a></td></tr></tbody></table></div></section> | |||||
| <section><h2>建议演示顺序</h2><ol><li>打开 005 机构模型,选择球阀或盒盖动作;调整旋转轴、方向、中心点、角度和时长,点击“播放动作”。</li><li>保存、刷新确认参数,再点击“发布并进入场景”。场景显示实际装备、环境和已绑定模型版本。</li><li>修改场景对象位置、旋转或缩放,应用预览;发布并进入编排。</li><li>查看对应步骤、模型动作和数字教员讲解,切到“背景与讲解”编辑图文或视频内容。</li><li>发布训练内容,再点击“虚拟仿真”查看原任务的背景、讲解和操作流程。</li></ol><p class="note">本次建立可保存、可发布的制作链。现有虚拟仿真继续使用固定静态脚本,新参数不会即时改写已发布任务。005 制作链使用已走通的新模型,原 005 与独立新模型入口都保留。</p></section> | |||||
| <section><h2>重型支援桥液压泵应急切换</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-005-model.png"><img loading="lazy" src="TASK-VIRTUAL-005-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-005-action-5.png"><img loading="lazy" src="TASK-VIRTUAL-005-action-5.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-005-scene.png"><img loading="lazy" src="TASK-VIRTUAL-005-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-005-training.png"><img loading="lazy" src="TASK-VIRTUAL-005-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥液压泵应急替代</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-006-model.png"><img loading="lazy" src="TASK-VIRTUAL-006-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-006-action-5.png"><img loading="lazy" src="TASK-VIRTUAL-006-action-5.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-006-scene.png"><img loading="lazy" src="TASK-VIRTUAL-006-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-006-training.png"><img loading="lazy" src="TASK-VIRTUAL-006-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥前泵换件修复</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-007-model.png"><img loading="lazy" src="TASK-VIRTUAL-007-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-007-action-8.png"><img loading="lazy" src="TASK-VIRTUAL-007-action-8.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-007-scene.png"><img loading="lazy" src="TASK-VIRTUAL-007-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-007-training.png"><img loading="lazy" src="TASK-VIRTUAL-007-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section><section><h2>重型支援桥平衡阀拆拼互换</h2><div class="gallery"><figure><a href="TASK-VIRTUAL-008-model.png"><img loading="lazy" src="TASK-VIRTUAL-008-model.png" alt="机构模型与动作库"></a><figcaption>机构模型与动作库</figcaption></figure><figure><a href="TASK-VIRTUAL-008-action-36.png"><img loading="lazy" src="TASK-VIRTUAL-008-action-36.png" alt="部件操作动画"></a><figcaption>部件操作动画</figcaption></figure><figure><a href="TASK-VIRTUAL-008-scene.png"><img loading="lazy" src="TASK-VIRTUAL-008-scene.png" alt="场景组成与位置配置"></a><figcaption>场景组成与位置配置</figcaption></figure><figure><a href="TASK-VIRTUAL-008-training.png"><img loading="lazy" src="TASK-VIRTUAL-008-training.png" alt="背景与讲解编排"></a><figcaption>背景与讲解编排</figcaption></figure></div></section> | |||||
| <section><h2>笔记本与局域网</h2><p>通过 192.168.31.168:6180 在 1366×768 验证,动作参数面板独立滚动,画布按可用宽度取景。</p><figure><a href="lan-TASK-VIRTUAL-005-action-3.png"><img src="lan-TASK-VIRTUAL-005-action-3.png" alt="小屏球阀动作参数"></a></figure></section> | |||||
| <section><h2>验证结果</h2><p class="pass">Web 构建及后处理通过;25 项相关前端测试通过;Java 17 下 ut-tran 与依赖模块 package 通过,440 项测试无失败。</p><div class="checks"><p>✓ TASK-VIRTUAL-005 · model</p><p>✓ TASK-VIRTUAL-005 · scene</p><p>✓ TASK-VIRTUAL-005 · training</p><p>✓ TASK-VIRTUAL-006 · model</p><p>✓ TASK-VIRTUAL-006 · scene</p><p>✓ TASK-VIRTUAL-006 · training</p><p>✓ TASK-VIRTUAL-007 · model</p><p>✓ TASK-VIRTUAL-007 · scene</p><p>✓ TASK-VIRTUAL-007 · training</p><p>✓ TASK-VIRTUAL-008 · model</p><p>✓ TASK-VIRTUAL-008 · scene</p><p>✓ TASK-VIRTUAL-008 · training</p><p>✓ 005 90→75 保存刷新→90 发布模型→场景→编排</p><p>✓ 制作链进入原005背景与讲解</p><p>✓ 原 005~008 及 005 独立入口固定定义完全未变</p><p>✓ 局域网 1366×768 · model</p><p>✓ 局域网 1366×768 · scene</p><p>✓ 局域网 1366×768 · training</p><p>✓ 006 场景位置修改保存刷新回读并恢复</p><p>✓ TASK-VIRTUAL-006 模型→场景→编排发布→原虚拟仿真</p><p>✓ TASK-VIRTUAL-007 模型→场景→编排发布→原虚拟仿真</p><p>✓ TASK-VIRTUAL-008 模型→场景→编排发布→原虚拟仿真</p><p>✓ 五个原任务完整快照未变</p></div><p>98 个动作逐一验证目标存在、聚焦居中、变换有限;旋转/移动动作确实改变姿态,并检查暂停、恢复、取消动画及场景位置增量。少量目标沿用原有补充示意,页面明确标注来源。</p><p>构建仍有现有的大包体积提示,不影响通过。验收使用本地管理员业务账号、Edge headless、真实 API;内置浏览器当前不可用。变更未部署测试环境,也未提交推送。</p><p><a href="ui-result.json">页面验收 JSON</a> · <a href="ui-lan-result.json">局域网结果</a> · <a href="actions-result.json">98 个动作记录</a> · <a href="integration-result.json">四套版本依赖</a> · <a href="health.json">服务健康检查</a></p></section> | |||||
| <section><h2>数据与回退</h2><p>保留 4 个新模型、4 个新场景和 4 个原编排的内容版本。测试角度、场景位置已恢复。原任务 86、87、88、89、116 完整快照未改;备份位于本目录的 before-task-*.json 与 before-project-*.json。历史失败截图保留供排查,以上最终结果均已重跑通过。</p><p>后续部署需要更新 Web、ut-tran,并在目标环境登记业务工程。当前工程 ID 为本环境专用,不应直接当作另一环境的固定 ID。</p></section></main></body></html> | |||||
| @@ -0,0 +1,207 @@ | |||||
| { | |||||
| "pass": true, | |||||
| "checks": [ | |||||
| { | |||||
| "name": "006 场景位置修改保存刷新回读并恢复", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "TASK-VIRTUAL-006 模型→场景→编排发布→原虚拟仿真", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "TASK-VIRTUAL-007 模型→场景→编排发布→原虚拟仿真", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "TASK-VIRTUAL-008 模型→场景→编排发布→原虚拟仿真", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "五个原任务完整快照未变", | |||||
| "pass": true | |||||
| } | |||||
| ], | |||||
| "current": [ | |||||
| { | |||||
| "taskCode": "TASK-VIRTUAL-005", | |||||
| "taskId": "86", | |||||
| "steps": 5, | |||||
| "actions": 6, | |||||
| "model": { | |||||
| "id": "466", | |||||
| "name": "重型支援桥液压泵应急切换机构模型", | |||||
| "versionId": "592", | |||||
| "versionCode": "V5.0.0", | |||||
| "dependencies": [] | |||||
| }, | |||||
| "scene": { | |||||
| "id": "467", | |||||
| "name": "重型支援桥液压泵应急切换训练场景", | |||||
| "versionId": "593", | |||||
| "versionCode": "V3.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "783", | |||||
| "targetProjectId": "466", | |||||
| "targetVersionId": "592", | |||||
| "relationType": "SCENE_MODEL", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "training": { | |||||
| "id": "438", | |||||
| "name": "重型支援桥液压泵应急切换", | |||||
| "versionId": "594", | |||||
| "versionCode": "V7.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "786", | |||||
| "targetProjectId": "467", | |||||
| "targetVersionId": "593", | |||||
| "relationType": "TRAINING_SCENE", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "taskCode": "TASK-VIRTUAL-006", | |||||
| "taskId": "87", | |||||
| "steps": 6, | |||||
| "actions": 15, | |||||
| "model": { | |||||
| "id": "468", | |||||
| "name": "重型支援桥液压泵应急替代机构模型", | |||||
| "versionId": "595", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [] | |||||
| }, | |||||
| "scene": { | |||||
| "id": "469", | |||||
| "name": "重型支援桥液压泵应急替代训练场景", | |||||
| "versionId": "596", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "790", | |||||
| "targetProjectId": "468", | |||||
| "targetVersionId": "595", | |||||
| "relationType": "SCENE_MODEL", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "training": { | |||||
| "id": "439", | |||||
| "name": "重型支援桥液压泵应急替代", | |||||
| "versionId": "597", | |||||
| "versionCode": "V10.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "793", | |||||
| "targetProjectId": "469", | |||||
| "targetVersionId": "596", | |||||
| "relationType": "TRAINING_SCENE", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "taskCode": "TASK-VIRTUAL-007", | |||||
| "taskId": "88", | |||||
| "steps": 11, | |||||
| "actions": 27, | |||||
| "model": { | |||||
| "id": "470", | |||||
| "name": "重型支援桥前泵换件修复机构模型", | |||||
| "versionId": "598", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [] | |||||
| }, | |||||
| "scene": { | |||||
| "id": "471", | |||||
| "name": "重型支援桥前泵换件修复训练场景", | |||||
| "versionId": "599", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "796", | |||||
| "targetProjectId": "470", | |||||
| "targetVersionId": "598", | |||||
| "relationType": "SCENE_MODEL", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "training": { | |||||
| "id": "440", | |||||
| "name": "重型支援桥前泵换件修复", | |||||
| "versionId": "600", | |||||
| "versionCode": "V8.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "799", | |||||
| "targetProjectId": "471", | |||||
| "targetVersionId": "599", | |||||
| "relationType": "TRAINING_SCENE", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| { | |||||
| "taskCode": "TASK-VIRTUAL-008", | |||||
| "taskId": "89", | |||||
| "steps": 16, | |||||
| "actions": 50, | |||||
| "model": { | |||||
| "id": "472", | |||||
| "name": "重型支援桥平衡阀拆拼互换机构模型", | |||||
| "versionId": "601", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [] | |||||
| }, | |||||
| "scene": { | |||||
| "id": "473", | |||||
| "name": "重型支援桥平衡阀拆拼互换训练场景", | |||||
| "versionId": "602", | |||||
| "versionCode": "V2.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "802", | |||||
| "targetProjectId": "472", | |||||
| "targetVersionId": "601", | |||||
| "relationType": "SCENE_MODEL", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "training": { | |||||
| "id": "441", | |||||
| "name": "重型支援桥平衡阀拆拼互换", | |||||
| "versionId": "603", | |||||
| "versionCode": "V8.0.0", | |||||
| "dependencies": [ | |||||
| { | |||||
| "id": "805", | |||||
| "targetProjectId": "473", | |||||
| "targetVersionId": "602", | |||||
| "relationType": "TRAINING_SCENE", | |||||
| "required": true, | |||||
| "sortOrder": 0 | |||||
| } | |||||
| ] | |||||
| } | |||||
| } | |||||
| ] | |||||
| } | |||||
| @@ -0,0 +1,65 @@ | |||||
| { | |||||
| "pass": true, | |||||
| "items": [ | |||||
| { | |||||
| "taskId": "86", | |||||
| "taskCode": "TASK-VIRTUAL-005", | |||||
| "trainingProjectId": "438", | |||||
| "modelProjectId": "466", | |||||
| "sceneProjectId": "467", | |||||
| "modelVersionId": "575", | |||||
| "sceneVersionId": "576", | |||||
| "trainingVersionId": "577", | |||||
| "steps": 5, | |||||
| "actions": 6, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "87", | |||||
| "taskCode": "TASK-VIRTUAL-006", | |||||
| "trainingProjectId": "439", | |||||
| "modelProjectId": "468", | |||||
| "sceneProjectId": "469", | |||||
| "modelVersionId": "578", | |||||
| "sceneVersionId": "579", | |||||
| "trainingVersionId": "580", | |||||
| "steps": 6, | |||||
| "actions": 15, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "88", | |||||
| "taskCode": "TASK-VIRTUAL-007", | |||||
| "trainingProjectId": "440", | |||||
| "modelProjectId": "470", | |||||
| "sceneProjectId": "471", | |||||
| "modelVersionId": "581", | |||||
| "sceneVersionId": "582", | |||||
| "trainingVersionId": "583", | |||||
| "steps": 11, | |||||
| "actions": 27, | |||||
| "complete": true | |||||
| }, | |||||
| { | |||||
| "taskId": "89", | |||||
| "taskCode": "TASK-VIRTUAL-008", | |||||
| "trainingProjectId": "441", | |||||
| "modelProjectId": "472", | |||||
| "sceneProjectId": "473", | |||||
| "modelVersionId": "584", | |||||
| "sceneVersionId": "585", | |||||
| "trainingVersionId": "586", | |||||
| "steps": 16, | |||||
| "actions": 50, | |||||
| "complete": true | |||||
| } | |||||
| ], | |||||
| "unchangedAssignments": [ | |||||
| 86, | |||||
| 87, | |||||
| 88, | |||||
| 89, | |||||
| 116 | |||||
| ], | |||||
| "checkedAt": "2026-09-24T08:11:46.156Z" | |||||
| } | |||||
| @@ -0,0 +1,29 @@ | |||||
| { | |||||
| "pass": true, | |||||
| "base": "http://192.168.31.168:6180", | |||||
| "checks": [ | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "model", | |||||
| "actions": 6, | |||||
| "played": [ | |||||
| 2, | |||||
| 3, | |||||
| 5, | |||||
| 6 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| } | |||||
| ], | |||||
| "errors": [] | |||||
| } | |||||
| @@ -0,0 +1,104 @@ | |||||
| { | |||||
| "pass": true, | |||||
| "base": "http://127.0.0.1:6180", | |||||
| "checks": [ | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "model", | |||||
| "actions": 6, | |||||
| "played": [ | |||||
| 2, | |||||
| 3, | |||||
| 5, | |||||
| 6 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-005", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "model", | |||||
| "actions": 15, | |||||
| "played": [ | |||||
| 3, | |||||
| 5, | |||||
| 9 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-006", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "model", | |||||
| "actions": 27, | |||||
| "played": [ | |||||
| 4, | |||||
| 8, | |||||
| 12 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-007", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "model", | |||||
| "actions": 50, | |||||
| "played": [ | |||||
| 3, | |||||
| 6, | |||||
| 36 | |||||
| ], | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "scene", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "task": "TASK-VIRTUAL-008", | |||||
| "stage": "training", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "005 90→75 保存刷新→90 发布模型→场景→编排", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "制作链进入原005背景与讲解", | |||||
| "pass": true | |||||
| }, | |||||
| { | |||||
| "name": "原 005~008 及 005 独立入口固定定义完全未变", | |||||
| "pass": true | |||||
| } | |||||
| ], | |||||
| "errors": [] | |||||
| } | |||||
| @@ -0,0 +1,49 @@ | |||||
| import fs from 'node:fs'; | |||||
| import assert from 'node:assert/strict'; | |||||
| import {chromium} from '@playwright/test'; | |||||
| import {login,call} from './role-permissions-live.mjs'; | |||||
| const dir='reports/bridge-content-authoring-20260924',fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8')); | |||||
| const signed=await login('root'),checks=[]; | |||||
| const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']}); | |||||
| try{const page=await browser.newPage({viewport:{width:1440,height:1000}}); | |||||
| await page.goto('http://127.0.0.1:6180/'); | |||||
| for(const item of fixture.items){ | |||||
| const response=await call('tran/v1/content/projects/'+item.modelProjectId,signed.accessToken);assert.equal(response.status,200); | |||||
| const result=await page.evaluate(async content=>{ | |||||
| const style=document.createElement('style');style.textContent='.authoring-test .training-shell,.authoring-test .training-main,.authoring-test .training-viewport{width:100%!important;height:100%!important;min-height:0!important;min-width:0!important;display:block!important;padding:0!important}.authoring-test .training-shell>:not(.training-main),.authoring-test .training-main>:not(.training-viewport),.authoring-test .training-viewport>:not(canvas){display:none!important}';document.head.append(style); | |||||
| const host=document.createElement('div');host.className='authoring-test';host.style.cssText='position:fixed;inset:0;width:700px;height:500px;z-index:999999';document.body.append(host); | |||||
| const {createProductionPreview}=await import('/src/features/content-production/preview.js'); | |||||
| const viewer=await createProductionPreview(host,content.productionDefinition),checks=[]; | |||||
| try{const actions=viewer.configure(content.productionActions);viewer.overview(); | |||||
| const overview=viewer.inspect(); | |||||
| for(let i=0;i<actions.length;i++){ | |||||
| viewer.select(i);const before=viewer.inspect().targets[i]; | |||||
| await viewer.play(i,{...actions[i],motion:{...actions[i].motion,durationMs:100}}); | |||||
| const after=viewer.inspect().targets[i]; | |||||
| checks.push({id:actions[i].id,target:actions[i].targetId,kind:actions[i].motion.kind,before,after}); | |||||
| } | |||||
| const pending=viewer.play(0,{...actions[0],motion:{...actions[0].motion,durationMs:900}}); | |||||
| viewer.pause();const cancelled=await pending,paused=viewer.inspect().paused; | |||||
| const changed=structuredClone(content.productionDefinition),items=changed.scenario.sceneResource.snapshot.objects; | |||||
| changed.authoringBaseObjects ||= structuredClone(items); | |||||
| const item=items.find(item=>item.assetRole!=='environment'&&!/terrain|地形/i.test(item.sourceName)); | |||||
| item.position[0]+=0.25; | |||||
| const secondHost=host.cloneNode(false);document.body.append(secondHost); | |||||
| const second=await createProductionPreview(secondHost,changed); | |||||
| let placement; | |||||
| try{const after=second.inspect().sceneObjects.find(o=>o.sceneId===item.sceneId),before=overview.sceneObjects.find(o=>o.sceneId===item.sceneId);placement={sceneId:item.sceneId,deltaX:after.position[0]-before.position[0]};} | |||||
| finally{second.destroy();secondHost.remove();} | |||||
| viewer.resume();return {overview,checks,paused,cancelled,resumed:!viewer.inspect().paused,placement}; | |||||
| }finally{viewer.destroy();host.remove();style.remove();} | |||||
| },response.data.currentVersion.content); | |||||
| assert.ok(result.paused&&result.resumed); | |||||
| assert.equal(result.cancelled,false); | |||||
| assert.ok(Math.abs(result.placement.deltaX-0.25)<1e-6,JSON.stringify(result.placement)); | |||||
| for(const action of result.checks){assert.ok([...action.after.position,...action.after.quaternion,...action.after.projected].every(Number.isFinite),action.id); | |||||
| assert.ok(Math.abs(action.before.projected[0])<0.01&&Math.abs(action.before.projected[1])<0.01,action.id+' focus'); | |||||
| if(action.kind!=='state')assert.notDeepEqual([action.after.position,action.after.quaternion],[action.before.position,action.before.quaternion],action.id+' must move'); | |||||
| } | |||||
| assert.equal(result.checks.length,item.actions);checks.push({task:item.taskCode,...result});console.log(item.taskCode+': '+result.checks.length+' actions'); | |||||
| } | |||||
| fs.writeFileSync(dir+'/actions-result.json',JSON.stringify({pass:true,checks},null,2)); | |||||
| }finally{await browser.close();} | |||||
| @@ -0,0 +1,80 @@ | |||||
| import fs from 'node:fs'; | |||||
| import path from 'node:path'; | |||||
| import assert from 'node:assert/strict'; | |||||
| import {chromium,expect} from '@playwright/test'; | |||||
| import {login,call} from './role-permissions-live.mjs'; | |||||
| const dir='reports/bridge-content-authoring-20260924'; | |||||
| const fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8')); | |||||
| const signed=await login('root'),checks=[],errors=[]; | |||||
| const api=async ep=>{const r=await call('tran/v1/'+ep,signed.accessToken);assert.equal(r.status,200,ep);return r.data;}; | |||||
| const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']}); | |||||
| const page=await browser.newPage({viewport:{width:Number(process.env.VIEW_WIDTH)||1440,height:Number(process.env.VIEW_HEIGHT)||1000}}); | |||||
| page.on('pageerror',e=>errors.push(e.message)); | |||||
| await page.addInitScript(({s,theme})=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);if(theme)localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:theme}));},{s:signed,theme:process.env.THEME}); | |||||
| const base=process.env.BASE_URL||'http://127.0.0.1:6180'; | |||||
| const screenshot=name=>page.screenshot({path:`${dir}/${process.env.IMAGE_PREFIX||''}${name}.png`}); | |||||
| const ready=async()=>{await expect(page.getByRole('button',{name:'播放动作',exact:true})).toBeEnabled({timeout:120000});}; | |||||
| try{ | |||||
| for(const item of process.env.FLOW_ONLY==='1'?[]:fixture.items){ | |||||
| await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready(); | |||||
| await expect(page.locator('.production-action-item')).toHaveCount(item.actions); | |||||
| const dimensions=await page.locator('.production-preview').evaluate(host=>({width:host.clientWidth,canvas:host.querySelector('canvas').clientWidth,height:host.clientHeight})); | |||||
| assert.ok(Math.abs(dimensions.width-dimensions.canvas)<5,JSON.stringify(dimensions)); | |||||
| await screenshot(`${item.taskCode}-model`); | |||||
| const candidates=item.taskCode.endsWith('005')?[1,2,4,5]:item.taskCode.endsWith('006')?[2,4,8]:item.taskCode.endsWith('007')?[3,7,11]:[2,5,35]; | |||||
| for(const index of candidates){ | |||||
| await page.locator('.production-action-item').nth(index).click(); | |||||
| await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready(); | |||||
| await screenshot(`${item.taskCode}-action-${index+1}`); | |||||
| } | |||||
| checks.push({task:item.taskCode,stage:'model',actions:item.actions,played:candidates.map(i=>i+1),pass:true}); | |||||
| await page.locator('.production-chain button').nth(1).click();await ready(); | |||||
| await expect(page.locator('.production-object')).toHaveCount(item.taskCode.endsWith('005')?2:item.taskCode.endsWith('008')?3:4); | |||||
| await screenshot(`${item.taskCode}-scene`); | |||||
| checks.push({task:item.taskCode,stage:'scene',pass:true}); | |||||
| await page.locator('.production-chain button').nth(2).click();await ready(); | |||||
| await page.getByRole('tab',{name:'背景与讲解'}).click(); | |||||
| await screenshot(`${item.taskCode}-training`); | |||||
| checks.push({task:item.taskCode,stage:'training',pass:true}); | |||||
| if(process.env.CAPTURE_ONLY==='1')break; | |||||
| } | |||||
| if(process.env.CAPTURE_ONLY!=='1'){ | |||||
| const item=fixture.items[0]; | |||||
| await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready(); | |||||
| await page.locator('.production-action-item').nth(2).click(); | |||||
| const angle=page.locator('.el-form-item').filter({hasText:'旋转角度(°)'}).getByRole('spinbutton'); | |||||
| await angle.fill('75');await angle.press('Tab'); | |||||
| await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready(); | |||||
| await page.getByRole('button',{name:'保存',exact:true}).click(); | |||||
| await expect(page.getByText('已保存,可刷新继续编辑')).toBeVisible(); | |||||
| await page.reload();await ready();await page.locator('.production-action-item').nth(2).click();await expect(angle).toHaveValue('75'); | |||||
| const changed=await api('content/projects/'+item.modelProjectId);assert.equal(changed.currentVersion.content.productionActions[2].motion.angle,75); | |||||
| await angle.fill('90');await angle.press('Tab'); | |||||
| await page.getByRole('button',{name:'发布并进入场景'}).click();await expect(page).toHaveURL(new RegExp('/scenes/'+item.sceneProjectId+'/production'));await ready(); | |||||
| await page.getByRole('button',{name:'发布并进入编排'}).click();await expect(page).toHaveURL(new RegExp('/training-projects/'+item.trainingProjectId+'/production'));await ready(); | |||||
| await page.getByRole('button',{name:'发布训练内容'}).click();await expect(page.getByText('训练内容已发布;现有仿真仍保持固定版本')).toBeVisible(); | |||||
| checks.push({name:'005 90→75 保存刷新→90 发布模型→场景→编排',pass:true}); | |||||
| const model=await api('content/projects/'+item.modelProjectId),scene=await api('content/projects/'+item.sceneProjectId),training=await api('content/projects/'+item.trainingProjectId); | |||||
| assert.equal(scene.currentVersion.dependencies[0].targetVersionId,model.project.publishedVersionId); | |||||
| assert.equal(training.currentVersion.dependencies[0].targetVersionId,scene.project.publishedVersionId); | |||||
| assert.equal(training.currentVersion.content.steps[2].modelActions[0].motion.angle,90); | |||||
| assert.equal(training.currentVersion.content.steps[2].modelActions[0].modelVersionId,model.project.publishedVersionId); | |||||
| await page.locator('.production-chain button').nth(3).click(); | |||||
| await expect(page).toHaveURL(new RegExp('/virtual-training/preview/'+item.taskId)); | |||||
| await expect(page.getByRole('button',{name:'跳过讲解',exact:true})).toBeVisible({timeout:120000}); | |||||
| await screenshot('existing-005-preview'); | |||||
| checks.push({name:'制作链进入原005背景与讲解',pass:true}); | |||||
| for(const list of ['models','scenes','training-projects']){ | |||||
| await page.goto(base+'/content/'+list);await expect(page.getByText(/重型支援桥液压泵应急切换/).first()).toBeVisible({timeout:30000}); | |||||
| await screenshot(`list-${list}`); | |||||
| const row=page.locator(list==='training-projects'?'.training-project-card':'.asset-row-list article').filter({hasText:'重型支援桥液压泵应急切换'}).first(); | |||||
| await row.getByRole('button',{name:list==='training-projects'?'编辑':'进入编辑',exact:true}).click();await ready(); | |||||
| await expect(page).toHaveURL(/\/production$/); | |||||
| } | |||||
| for(const id of [86,87,88,89,116]){const before=JSON.parse(fs.readFileSync(`${dir}/before-task-${id}.json`,'utf8')),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);} | |||||
| checks.push({name:'原 005~008 及 005 独立入口固定定义完全未变',pass:true}); | |||||
| } | |||||
| assert.deepEqual(errors,[]); | |||||
| fs.writeFileSync(dir+'/'+(process.env.RESULT_FILE||'ui-result.json'),JSON.stringify({pass:true,base,checks,errors},null,2)); | |||||
| console.log(JSON.stringify({pass:true,checks:checks.length})); | |||||
| }catch(e){await page.screenshot({path:dir+'/failure.png'}).catch(()=>{});fs.writeFileSync(dir+'/failure.json',JSON.stringify({pass:false,message:e.message,checks,errors},null,2));throw e;}finally{await browser.close();} | |||||
| @@ -0,0 +1,24 @@ | |||||
| import fs from 'node:fs'; | |||||
| import assert from 'node:assert/strict'; | |||||
| const dir='reports/bridge-content-authoring-20260924'; | |||||
| const read=file=>JSON.parse(fs.readFileSync(`${dir}/${file}.json`,'utf8')); | |||||
| const ui=read('ui-result'),lan=read('ui-lan-result'),actions=read('actions-result'),integration=read('integration-result'); | |||||
| assert.ok(ui.pass&&lan.pass&&actions.pass&&integration.pass); | |||||
| const escape=value=>String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); | |||||
| const url=(stage,id)=>`http://127.0.0.1:6180/content/${stage}/${id}/production`; | |||||
| const rows=integration.current.map(item=>`<tr><td><b>${escape(item.taskCode)}</b><br>${escape(item.training.name)}</td><td><a href="${url('models',item.model.id)}">机构模型 · ${escape(item.model.versionCode)}</a></td><td><a href="${url('scenes',item.scene.id)}">训练场景 · ${escape(item.scene.versionCode)}</a></td><td><a href="${url('training-projects',item.training.id)}">训练编排 · ${escape(item.training.versionCode)}</a></td><td>${item.steps} 步 / ${item.actions} 动作</td><td><a href="http://127.0.0.1:6180/teaching/virtual-training/preview/${item.taskId}">进入仿真</a></td></tr>`).join(''); | |||||
| const panels=integration.current.map(item=>`<section><h2>${escape(item.training.name)}</h2><div class="gallery">${[['model','机构模型与动作库'],['action-'+(item.taskCode.endsWith('005')?'5':item.taskCode.endsWith('006')?'5':item.taskCode.endsWith('007')?'8':'36'),'部件操作动画'],['scene','场景组成与位置配置'],['training','背景与讲解编排']].map(([suffix,title])=>`<figure><a href="${item.taskCode}-${suffix}.png"><img loading="lazy" src="${item.taskCode}-${suffix}.png" alt="${title}"></a><figcaption>${title}</figcaption></figure>`).join('')}</div></section>`).join(''); | |||||
| const checks=[...ui.checks,...lan.checks.map(x=>({...x,name:'局域网 1366×768 · '+x.stage})),...integration.checks]; | |||||
| const html=`<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>005~008 内容制作全流程验收</title><style> | |||||
| *{box-sizing:border-box}body{margin:0;background:#f2f6f5;color:#163c38;font:15px/1.7 "Microsoft YaHei",sans-serif}main{max-width:1480px;margin:auto;padding:36px}header{background:#0c594e;color:white;padding:32px;border-radius:14px}h1{font-size:28px;margin:0}h2{font-size:21px}header p{margin:8px 0 0;color:#d4e9e2}.stats{display:flex;gap:12px;flex-wrap:wrap;margin:22px 0}.stats span{padding:13px 22px;background:white;border:1px solid #ccded8;border-radius:9px}.stats b{font-size:24px;color:#078c70;margin-right:8px}section{background:white;padding:24px;margin:20px 0;border:1px solid #d4e3de;border-radius:12px}table{width:100%;border-collapse:collapse}td,th{border-bottom:1px solid #e0eae7;padding:14px;text-align:left}th{background:#edf5f2}a{color:#087c67;text-decoration:none}a:hover{text-decoration:underline}.gallery{display:grid;grid-template-columns:1fr 1fr;gap:18px}figure{margin:0}figure img{display:block;width:100%;border:1px solid #d3e2dd;border-radius:7px}figcaption{padding:8px 0;color:#607f77}.note{border-left:4px solid #e7af51;padding-left:16px}.checks{columns:2}.checks p{margin:6px 0}.pass{color:#058565}code{background:#eff4f2;padding:2px 5px}li{margin:8px 0}@media(max-width:900px){main{padding:15px}.gallery{grid-template-columns:1fr}.table{overflow:auto}.checks{columns:1}} | |||||
| </style></head><body><main><header><h1>005~008 内容制作全流程验收</h1><p>2026-09-24 · 模型动作 → 场景组成 → 训练编排 → 现有虚拟仿真</p></header> | |||||
| <div class="stats"><span><b>4</b>机构模型</span><span><b>4</b>训练场景</span><span><b>38</b>操作步骤</span><span><b>98</b>部件子动作</span><span><b>5</b>原任务快照未变</span></div> | |||||
| <section><h2>演示入口</h2><p>登录数字车间后,从“内容制作 → 模型制作”进入对应机构模型,或打开下表链接。四套名称均采用实际训练名称。</p><div class="table"><table><thead><tr><th>训练内容</th><th>模型制作</th><th>场景制作</th><th>训练编排</th><th>规模</th><th>现有入口</th></tr></thead><tbody>${rows}</tbody></table></div></section> | |||||
| <section><h2>建议演示顺序</h2><ol><li>打开 005 机构模型,选择球阀或盒盖动作;调整旋转轴、方向、中心点、角度和时长,点击“播放动作”。</li><li>保存、刷新确认参数,再点击“发布并进入场景”。场景显示实际装备、环境和已绑定模型版本。</li><li>修改场景对象位置、旋转或缩放,应用预览;发布并进入编排。</li><li>查看对应步骤、模型动作和数字教员讲解,切到“背景与讲解”编辑图文或视频内容。</li><li>发布训练内容,再点击“虚拟仿真”查看原任务的背景、讲解和操作流程。</li></ol><p class="note">本次建立可保存、可发布的制作链。现有虚拟仿真继续使用固定静态脚本,新参数不会即时改写已发布任务。005 制作链使用已走通的新模型,原 005 与独立新模型入口都保留。</p></section> | |||||
| ${panels} | |||||
| <section><h2>笔记本与局域网</h2><p>通过 192.168.31.168:6180 在 1366×768 验证,动作参数面板独立滚动,画布按可用宽度取景。</p><figure><a href="lan-TASK-VIRTUAL-005-action-3.png"><img src="lan-TASK-VIRTUAL-005-action-3.png" alt="小屏球阀动作参数"></a></figure></section> | |||||
| <section><h2>验证结果</h2><p class="pass">Web 构建及后处理通过;25 项相关前端测试通过;Java 17 下 ut-tran 与依赖模块 package 通过,440 项测试无失败。</p><div class="checks">${checks.map(x=>`<p>✓ ${escape(x.name||x.task+' · '+x.stage)}</p>`).join('')}</div><p>98 个动作逐一验证目标存在、聚焦居中、变换有限;旋转/移动动作确实改变姿态,并检查暂停、恢复、取消动画及场景位置增量。少量目标沿用原有补充示意,页面明确标注来源。</p><p>构建仍有现有的大包体积提示,不影响通过。验收使用本地管理员业务账号、Edge headless、真实 API;内置浏览器当前不可用。变更未部署测试环境,也未提交推送。</p><p><a href="ui-result.json">页面验收 JSON</a> · <a href="ui-lan-result.json">局域网结果</a> · <a href="actions-result.json">98 个动作记录</a> · <a href="integration-result.json">四套版本依赖</a> · <a href="health.json">服务健康检查</a></p></section> | |||||
| <section><h2>数据与回退</h2><p>保留 4 个新模型、4 个新场景和 4 个原编排的内容版本。测试角度、场景位置已恢复。原任务 86、87、88、89、116 完整快照未改;备份位于本目录的 before-task-*.json 与 before-project-*.json。历史失败截图保留供排查,以上最终结果均已重跑通过。</p><p>后续部署需要更新 Web、ut-tran,并在目标环境登记业务工程。当前工程 ID 为本环境专用,不应直接当作另一环境的固定 ID。</p></section></main></body></html>`; | |||||
| fs.writeFileSync(dir+'/index.html',html); | |||||
| fs.writeFileSync(dir+'/README.md',`# 005~008 内容制作全流程\n\n打开 [验收报告](index.html) 查看入口、演示步骤和截图。\n\n已建立 4 个模型、4 个场景,关联 4 个编排;38 步、98 个动作。模型/场景/编排发布不更新现有教学任务。005 采用新模型制作,原任务与独立入口保留。\n\n验证:25 项前端测试、440 项后端测试、四套发布链、98 个动作和局域网 1366×768 页面通过。详细字段与回退说明见 API Wiki《41-桥梁内容制作关联》。\n`); | |||||
| console.log(dir+'/index.html'); | |||||
| @@ -0,0 +1,91 @@ | |||||
| import fs from 'node:fs'; | |||||
| import path from 'node:path'; | |||||
| import crypto from 'node:crypto'; | |||||
| import assert from 'node:assert/strict'; | |||||
| import {chromium} from '@playwright/test'; | |||||
| import {login,call} from './role-permissions-live.mjs'; | |||||
| import {scriptTrainingDefinition} from '../../unreal_tran_web/src/features/virtual-training-scripts/registry.js'; | |||||
| import {collectActions,applyActionBindings} from '../../unreal_tran_web/src/features/content-production/document.js'; | |||||
| const dir='reports/bridge-content-authoring-20260924';fs.mkdirSync(dir,{recursive:true}); | |||||
| const session=await login('root'); | |||||
| const api=async(ep,body,method)=>{const r=await call('tran/v1/'+ep,session.accessToken,body,method);assert.equal(r.status,200,ep+': '+r.message);return r.data;}; | |||||
| const write=(name,value)=>fs.writeFileSync(`${dir}/${name}.json`,JSON.stringify(value,null,2)); | |||||
| const read=name=>JSON.parse(fs.readFileSync(`${dir}/${name}.json`,'utf8')); | |||||
| const backup=(name,value)=>{if(!fs.existsSync(`${dir}/${name}.json`))write(name,value);}; | |||||
| const fixture=fs.existsSync(`${dir}/fixture.json`)?read('fixture'):{items:[]}; | |||||
| const stripVolatile=t=>{const d=structuredClone(t);delete d.serverNow;return d;}; | |||||
| const bytesAsset=(object,index)=>{const bytes=fs.readFileSync(path.resolve('../unreal_tran_web/public',object.resourceUrl.slice(1)));return {code:`PRODUCTION-MODEL-${index+1}`,name:object.sourceName||object.name,type:'MODEL_FILE',storageUri:object.resourceUrl,mimeType:'model/gltf-binary',sizeBytes:bytes.length,sha256:crypto.createHash('sha256').update(bytes).digest('hex'),metadata:{builtin:true},status:'READY',sortOrder:index};}; | |||||
| const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']}); | |||||
| try{ | |||||
| for(const taskId of [86,87,88,89]){ | |||||
| const task=await api('teaching/assignments/'+taskId);backup('before-task-'+taskId,task); | |||||
| const original=await api('content/projects/'+task.contentProjectId);backup('before-project-'+task.contentProjectId,original); | |||||
| let item=fixture.items.find(i=>i.taskId===String(taskId)); | |||||
| if(item?.complete){console.log('Already prepared',item.taskCode);continue;} | |||||
| const definition=scriptTrainingDefinition(task); | |||||
| if(taskId===86){const pilot=await api('teaching/assignments/116');backup('before-task-116',pilot);definition.scenario.sceneResource=structuredClone(pilot.definitionSnapshot.scenario.sceneResource);delete definition.runtime.definitionSource;delete definition.runtime.scriptId;delete definition.runtime.scriptVersion;} | |||||
| definition.name=task.name;definition.task.title=task.name;definition.introduction=structuredClone(task.definitionSnapshot.introduction); | |||||
| for(const step of definition.steps)step.interaction||='rule-check'; | |||||
| let actions=collectActions(definition); | |||||
| const page=await browser.newPage({viewport:{width:1500,height:1000}}); | |||||
| await page.goto('http://127.0.0.1:6180/'); | |||||
| const captured=await page.evaluate(async({definition,actions})=>{ | |||||
| const host=document.createElement('div');host.style.cssText='position:fixed;inset:0;background:white;z-index:100000';document.body.append(host); | |||||
| const {createProductionPreview}=await import('/src/features/content-production/preview.js'); | |||||
| const viewer=await createProductionPreview(host,definition); | |||||
| try{return {actions:viewer.configure(actions)};}finally{viewer.destroy();host.remove();} | |||||
| },{definition,actions}); | |||||
| actions=captured.actions;await page.close(); | |||||
| assert.equal(actions.length,definition.steps.reduce((n,s)=>n+s.action.mechanism.steps.length,0)); | |||||
| assert.ok(actions.every(a=>a.binding?.sourceNode&&a.motion)); | |||||
| write('actions-'+task.code,actions); | |||||
| const snapshot=structuredClone(definition.scenario.sceneResource.snapshot); | |||||
| const assets=snapshot.objects.map(bytesAsset); | |||||
| const resource=snapshot.objects.find(o=>o.assetRole!=='environment'&&!/地形|terrain/i.test(o.sourceName));assert.ok(resource); | |||||
| const primaryAsset=assets[snapshot.objects.indexOf(resource)]; | |||||
| const production={schemaVersion:1,taskCode:task.code,taskId:String(taskId),trainingProjectId:task.contentProjectId, | |||||
| sourceContentVersionId:task.contentVersionId,scriptId:task.definitionSnapshot.runtime.scriptId,scriptVersion:task.definitionSnapshot.runtime.scriptVersion}; | |||||
| const modelName=task.name+'机构模型',sceneName=task.name+'训练场景'; | |||||
| if(!item){item={taskId:String(taskId),taskCode:task.code,trainingProjectId:task.contentProjectId};fixture.items.push(item);write('fixture',fixture);} | |||||
| const modelContent={schema:'digital-twin-editor-project',version:4,name:modelName,modelId:resource.modelId||resource.id, | |||||
| modelResource:{...structuredClone(resource),id:resource.modelId||resource.id,assetCode:primaryAsset.code,storageUri:primaryAsset.storageUri,kind:'builtin'}, | |||||
| camera:{position:[4,3,5],target:[0,1,0]},model:null,environment:null, | |||||
| timeline:{version:1,duration:Math.max(1,actions.length),currentTime:0,loop:false,zoom:1,tracks:[],clips:[]},blueprint:{version:1,nodes:[],edges:[]}, | |||||
| production:{...production},productionDefinition:definition,productionActions:actions,assemblyObjects:snapshot.objects}; | |||||
| const getOrCreate=async(type,name,content,projectId)=>{ | |||||
| if(projectId)return api('content/projects/'+projectId); | |||||
| const list=await api(`content/projects?type=${type}&keyword=${encodeURIComponent(name)}&size=100`); | |||||
| const found=list.records.find(p=>p.name===name);if(found)return api('content/projects/'+found.id); | |||||
| return api('content/projects',{type,name,description:'重型支援桥装备训练内容',categoryCode:type==='MODEL'?'EQUIPMENT':'OUTDOOR',coverUri:task.coverUri||'/models/step-model-front-pump-thumb.png',content,assets,dependencies:[]}); | |||||
| }; | |||||
| let model=await getOrCreate('MODEL',modelName,modelContent,item.modelProjectId);item.modelProjectId=model.project.id;write('fixture',fixture); | |||||
| let scene=await getOrCreate('SCENE',sceneName,{...snapshot,schema:'edit3dv4.scene',version:'1.5',name:sceneName},item.sceneProjectId);item.sceneProjectId=scene.project.id;write('fixture',fixture); | |||||
| Object.assign(production,{modelProjectId:model.project.id,sceneProjectId:scene.project.id}); | |||||
| modelContent.production={...production}; | |||||
| model=await api('content/projects/'+model.project.id,{...model.project,name:modelName,content:modelContent,assets,dependencies:[],version:model.project.version,changeSummary:'建立部件动作库、旋转轴心与场景制作关联'},'PUT'); | |||||
| model=await api('content/projects/'+model.project.id+'/publish',{version:model.project.version}); | |||||
| item.modelVersionId=model.project.publishedVersionId;production.modelVersionId=item.modelVersionId; | |||||
| const sceneObjects=snapshot.objects.map((o,i)=>({...o,assetCode:assets[i].code,targetProjectId:model.project.id,targetVersionId:item.modelVersionId})); | |||||
| const sceneContent={...snapshot,schema:'edit3dv4.scene',version:'1.5',name:sceneName,objects:sceneObjects, | |||||
| production:{...production},productionDefinition:structuredClone(definition),productionActions:actions}; | |||||
| sceneContent.productionDefinition.scenario.sceneResource.snapshot.objects=structuredClone(sceneObjects); | |||||
| scene=await api('content/projects/'+scene.project.id,{...scene.project,name:sceneName,content:sceneContent,assets, | |||||
| dependencies:[{targetProjectId:model.project.id,targetVersionId:item.modelVersionId,relationType:'SCENE_MODEL',required:true}],version:scene.project.version,changeSummary:'通过模型固定版本组成训练场景'},'PUT'); | |||||
| scene=await api('content/projects/'+scene.project.id+'/publish',{version:scene.project.version}); | |||||
| item.sceneVersionId=scene.project.publishedVersionId;production.sceneVersionId=item.sceneVersionId; | |||||
| const content=structuredClone(original.currentVersion.content); | |||||
| Object.assign(content,{production:{...production},productionDefinition:applyActionBindings(definition,actions,model.project.id,item.modelVersionId),productionActions:actions}); | |||||
| content.steps=content.steps.map((s,i)=>({...s,modelActions:content.productionDefinition.steps[i].modelActions})); | |||||
| content.scenario.sceneProjectId=scene.project.id;content.scenario.sceneVersionId=item.sceneVersionId; | |||||
| let training=await api('content/projects/'+original.project.id,{...original.project,description:'重型支援桥装备操作训练',content,assets:original.currentVersion.assets, | |||||
| dependencies:[{targetProjectId:scene.project.id,targetVersionId:item.sceneVersionId,relationType:'TRAINING_SCENE',required:true}],version:original.project.version,changeSummary:'关联模型动作和场景固定版本,保持现有教学任务不变'},'PUT'); | |||||
| training=await api('content/projects/'+training.project.id+'/publish',{version:training.project.version}); | |||||
| item.trainingVersionId=training.project.publishedVersionId;item.steps=definition.steps.length;item.actions=actions.length;item.complete=true;write('fixture',fixture); | |||||
| for(const [stage,data] of [['model',model],['scene',scene],['training',training]])write('after-'+stage+'-'+task.code,data); | |||||
| assert.deepEqual(stripVolatile(await api('teaching/assignments/'+taskId)),stripVolatile(read('before-task-'+taskId))); | |||||
| console.log(JSON.stringify(item)); | |||||
| } | |||||
| for(const id of [86,87,88,89,116])assert.deepEqual(stripVolatile(await api('teaching/assignments/'+id)),stripVolatile(read('before-task-'+id))); | |||||
| write('registration',{pass:true,items:fixture.items,unchangedAssignments:[86,87,88,89,116],checkedAt:new Date().toISOString()}); | |||||
| }finally{await browser.close();} | |||||
| @@ -0,0 +1,42 @@ | |||||
| import fs from 'node:fs'; | |||||
| import assert from 'node:assert/strict'; | |||||
| import {chromium,expect} from '@playwright/test'; | |||||
| import {login,call} from './role-permissions-live.mjs'; | |||||
| const dir='reports/bridge-content-authoring-20260924',fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8')); | |||||
| const signed=await login('root'),checks=[]; | |||||
| const api=async path=>{const r=await call('tran/v1/'+path,signed.accessToken);assert.equal(r.status,200,r.message);return r.data;}; | |||||
| const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']}); | |||||
| try{ | |||||
| const page=await browser.newPage({viewport:{width:1440,height:1000}}); | |||||
| await page.addInitScript(s=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:'military'}));},signed); | |||||
| const ready=()=>expect(page.getByRole('button',{name:'播放动作',exact:true})).toBeEnabled({timeout:120000}); | |||||
| for(const item of fixture.items.slice(1)){ | |||||
| await page.goto('http://127.0.0.1:6180/content/models/'+item.modelProjectId+'/production');await ready(); | |||||
| await page.getByRole('button',{name:'发布并进入场景'}).click();await expect(page).toHaveURL(new RegExp('/scenes/'+item.sceneProjectId+'/production'));await ready(); | |||||
| if(item.taskCode.endsWith('006')){ | |||||
| const x=page.locator('.production-object').nth(1).getByRole('spinbutton').first();const original=await x.inputValue(); | |||||
| await x.fill(String(Number(original)+0.25));await x.press('Tab');await page.getByRole('button',{name:'应用场景预览'}).click();await ready(); | |||||
| await page.getByRole('button',{name:'保存',exact:true}).click();await expect(page.getByText('已保存,可刷新继续编辑')).toBeVisible(); | |||||
| await page.reload();await ready();assert.ok(Math.abs(Number(await x.inputValue())-Number(original)-0.25)<0.001); | |||||
| await x.fill(original);await x.press('Tab');await page.getByRole('button',{name:'应用场景预览'}).click();await ready(); | |||||
| checks.push({name:'006 场景位置修改保存刷新回读并恢复',pass:true}); | |||||
| } | |||||
| await page.getByRole('button',{name:'发布并进入编排'}).click();await expect(page).toHaveURL(new RegExp('/training-projects/'+item.trainingProjectId+'/production'));await ready(); | |||||
| await page.getByRole('button',{name:'发布训练内容'}).click();await expect(page.getByText('训练内容已发布;现有仿真仍保持固定版本')).toBeVisible(); | |||||
| const model=await api('content/projects/'+item.modelProjectId),scene=await api('content/projects/'+item.sceneProjectId),training=await api('content/projects/'+item.trainingProjectId); | |||||
| assert.equal(scene.currentVersion.dependencies[0].targetVersionId,model.project.publishedVersionId); | |||||
| assert.equal(training.currentVersion.dependencies[0].targetVersionId,scene.project.publishedVersionId); | |||||
| assert.equal(training.currentVersion.content.steps.flatMap(s=>s.modelActions).length,item.actions); | |||||
| await page.locator('.production-chain button').nth(3).click();await expect(page.getByRole('button',{name:'跳过讲解',exact:true})).toBeVisible({timeout:120000}); | |||||
| await page.screenshot({path:dir+'/'+item.taskCode+'-simulation.png'}); | |||||
| checks.push({name:item.taskCode+' 模型→场景→编排发布→原虚拟仿真',pass:true}); | |||||
| } | |||||
| const current=[]; | |||||
| for(const item of fixture.items){ | |||||
| const stages={};for(const [stage,id] of [['model',item.modelProjectId],['scene',item.sceneProjectId],['training',item.trainingProjectId]]){const data=await api('content/projects/'+id);stages[stage]={id,name:data.project.name,versionId:data.project.publishedVersionId,versionCode:data.currentVersion.versionCode,dependencies:data.currentVersion.dependencies};fs.writeFileSync(dir+'/final-'+stage+'-'+item.taskCode+'.json',JSON.stringify(data,null,2));} | |||||
| current.push({taskCode:item.taskCode,taskId:item.taskId,steps:item.steps,actions:item.actions,...stages}); | |||||
| } | |||||
| for(const id of [86,87,88,89,116]){const before=JSON.parse(fs.readFileSync(dir+'/before-task-'+id+'.json')),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);} | |||||
| checks.push({name:'五个原任务完整快照未变',pass:true}); | |||||
| fs.writeFileSync(dir+'/integration-result.json',JSON.stringify({pass:true,checks,current},null,2));console.log(JSON.stringify({pass:true,checks:checks.length})); | |||||
| }catch(e){fs.writeFileSync(dir+'/integration-failure.json',JSON.stringify({pass:false,message:e.message,checks},null,2));throw e;}finally{await browser.close();} | |||||