agentworkshop 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.AgentWorkShop/prompts/host-tools.json +631 -0
- package/.AgentWorkShop/prompts/lead-supervise.md +42 -0
- package/.AgentWorkShop/prompts/mode-goal.md +26 -0
- package/.AgentWorkShop/prompts/mode-loop.md +10 -0
- package/.AgentWorkShop/prompts/mode-pipeline.md +16 -0
- package/.AgentWorkShop/prompts/peer-lead-roster.md +3 -0
- package/.AgentWorkShop/prompts/peer-message.md +14 -0
- package/.AgentWorkShop/prompts/peer-reply-optional.md +2 -0
- package/.AgentWorkShop/prompts/peer-reply-required.md +8 -0
- package/.AgentWorkShop/prompts/scenario-default.md +11 -0
- package/.AgentWorkShop/prompts/system-manual.md +18 -0
- package/.AgentWorkShop/prompts/team-roster.md +9 -0
- package/.AgentWorkShop/prompts/worker-workflow.md +19 -0
- package/.env.example +34 -0
- package/README-zh.md +424 -0
- package/README.md +424 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.json +1 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/1459e6269990a5d229b9228c8b2f62ffaa7d350068380ae8b875fee0db45cca6.source +180 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/c2991e909c70c5c12b90a407c7adcf63384c51668a2b334e6e047fa675c268fe.source +305 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/e09129039d1e27e3fac24becc9bdafd2d1a7c8049bff24173df007be865f0165.source +264 -0
- package/app/.mimosa/hook-status/sess_05189059-e42d-4188-8911-4f7baabe009b-28ef7c75ea.json +14 -0
- package/app/app.vue +139 -0
- package/app/assets/css/main.css +1257 -0
- package/app/components/AppHeader.vue +675 -0
- package/app/components/AppSidebar.vue +286 -0
- package/app/components/AwChart.vue +44 -0
- package/app/components/AwLogo.vue +75 -0
- package/app/components/workshop/A2aRpcDebugger.vue +237 -0
- package/app/components/workshop/AgentInspectorDrawer.vue +424 -0
- package/app/components/workshop/AgentLanesView.vue +947 -0
- package/app/components/workshop/ArtifactCard.vue +120 -0
- package/app/components/workshop/AssetLibrary.vue +450 -0
- package/app/components/workshop/ChannelSessionList.vue +593 -0
- package/app/components/workshop/CommandPalette.vue +270 -0
- package/app/components/workshop/Composer.vue +731 -0
- package/app/components/workshop/DeviceTwinPanel.vue +607 -0
- package/app/components/workshop/EventBlock.vue +602 -0
- package/app/components/workshop/FileSelectorModal.vue +188 -0
- package/app/components/workshop/InspectorPanel.vue +265 -0
- package/app/components/workshop/LiveAgentBar.vue +362 -0
- package/app/components/workshop/MemoryPanel.vue +364 -0
- package/app/components/workshop/MentionHoverCard.vue +155 -0
- package/app/components/workshop/MultiChannelView.vue +189 -0
- package/app/components/workshop/PaneSplitter.vue +142 -0
- package/app/components/workshop/StatsPanel.vue +168 -0
- package/app/components/workshop/TaskBoardView.vue +636 -0
- package/app/components/workshop/TaskInspectorDrawer.vue +390 -0
- package/app/components/workshop/TranscriptTimeline.vue +545 -0
- package/app/components/workshop/blocks/ClusterArtifact.vue +77 -0
- package/app/components/workshop/blocks/ClusterError.vue +85 -0
- package/app/components/workshop/blocks/ClusterLife.vue +87 -0
- package/app/components/workshop/blocks/ClusterMember.vue +93 -0
- package/app/components/workshop/blocks/ClusterMemory.vue +84 -0
- package/app/components/workshop/blocks/ClusterOther.vue +67 -0
- package/app/components/workshop/blocks/ClusterRoute.vue +359 -0
- package/app/components/workshop/blocks/ClusterStatus.vue +147 -0
- package/app/components/workshop/blocks/ClusterStream.vue +325 -0
- package/app/components/workshop/blocks/ClusterTask.vue +151 -0
- package/app/components/workshop/blocks/ClusterTool.vue +158 -0
- package/app/components/workshop/lanes/LaneBlocks.vue +149 -0
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +1010 -0
- package/app/components/workshop/town/ModelPreview3D.vue +276 -0
- package/app/components/workshop/town/TownScene.ts +1355 -0
- package/app/components/workshop/town/TownScene3D.ts +4533 -0
- package/app/components/workshop/town/TownView.vue +7071 -0
- package/app/composables/useCodeCopy.ts +30 -0
- package/app/composables/useHttp.ts +8 -0
- package/app/composables/useRouteMeta.ts +50 -0
- package/app/composables/useSiteConfig.ts +21 -0
- package/app/composables/workshop/apiClient.ts +95 -0
- package/app/composables/workshop/useCharacterAssets.ts +292 -0
- package/app/composables/workshop/useClusteredBlocks.ts +150 -0
- package/app/composables/workshop/useComposerBus.ts +16 -0
- package/app/composables/workshop/useDaqStream.ts +379 -0
- package/app/composables/workshop/useDcwStream.ts +300 -0
- package/app/composables/workshop/useDeviceTwins.ts +147 -0
- package/app/composables/workshop/useEventBlocks.ts +662 -0
- package/app/composables/workshop/useLocalTime.ts +33 -0
- package/app/composables/workshop/useOpsLog.ts +164 -0
- package/app/composables/workshop/useSceneLayouts.ts +166 -0
- package/app/composables/workshop/useTownBus.ts +47 -0
- package/app/composables/workshop/useVisibleInterval.ts +42 -0
- package/app/composables/workshop/useWorkshopApi.ts +224 -0
- package/app/composables/workshop/useWorkshopWs.ts +113 -0
- package/app/config/index.ts +66 -0
- package/app/config/schema.ts +68 -0
- package/app/error.vue +76 -0
- package/app/layouts/default.vue +100 -0
- package/app/layouts/town.vue +16 -0
- package/app/pages/daq/[id].vue +796 -0
- package/app/pages/daq/index.vue +2577 -0
- package/app/pages/dcw/[id].vue +2259 -0
- package/app/pages/dcw/index.vue +1079 -0
- package/app/pages/index.vue +830 -0
- package/app/pages/logs/index.vue +621 -0
- package/app/pages/monitor.vue +665 -0
- package/app/pages/settings.vue +757 -0
- package/app/pages/tokens.vue +610 -0
- package/app/pages/town.vue +145 -0
- package/app/pages/users.vue +257 -0
- package/app/pages/workshop/agents.vue +382 -0
- package/app/pages/workshop/channel-templates.vue +456 -0
- package/app/pages/workshop/index.vue +604 -0
- package/app/pages/workshop/teams.vue +448 -0
- package/app/pages/workshop/w/[wsId].vue +516 -0
- package/app/plugins/http.ts +71 -0
- package/app/plugins/locale-restore.client.ts +13 -0
- package/app/plugins/runtime-config.client.ts +65 -0
- package/app/plugins/session-restore.client.ts +24 -0
- package/app/plugins/theme.client.ts +15 -0
- package/app/stores/app.ts +32 -0
- package/app/stores/route-trail.ts +45 -0
- package/app/stores/runtime-config.ts +212 -0
- package/app/stores/workshop/connection.ts +184 -0
- package/app/stores/workshop/entities.ts +350 -0
- package/app/stores/workshop/events.ts +292 -0
- package/app/stores/workshop/user.ts +176 -0
- package/app/stores/workshop/workspaces.ts +133 -0
- package/bin/aw.mjs +13 -0
- package/cli/aw.mjs +184 -0
- package/cli/commands/build.mjs +33 -0
- package/cli/commands/config.mjs +157 -0
- package/cli/commands/dev.mjs +74 -0
- package/cli/commands/doctor.mjs +120 -0
- package/cli/commands/home.mjs +75 -0
- package/cli/commands/init.mjs +150 -0
- package/cli/commands/register.mjs +123 -0
- package/cli/commands/start.mjs +106 -0
- package/cli/commands/status.mjs +67 -0
- package/cli/commands/update.mjs +116 -0
- package/cli/commands/version.mjs +32 -0
- package/cli/core/args.mjs +95 -0
- package/cli/core/context.mjs +264 -0
- package/cli/core/errors.mjs +18 -0
- package/cli/core/help.mjs +68 -0
- package/cli/core/logger.mjs +40 -0
- package/cli/core/registry.mjs +111 -0
- package/config.yml +70 -0
- package/docker-compose.yml +34 -0
- package/docs/cli.md +220 -0
- package/i18n/dicts/_en.json +1097 -0
- package/i18n/dicts/_en2.json +300 -0
- package/i18n/dicts/_en3.json +102 -0
- package/i18n/dicts/_en4.json +115 -0
- package/i18n/dicts/_en5.json +33 -0
- package/i18n/dicts/a2aRpcDebugger.json +10 -0
- package/i18n/dicts/agentInspectorDrawer.json +25 -0
- package/i18n/dicts/agentLanesView.json +57 -0
- package/i18n/dicts/agents.json +27 -0
- package/i18n/dicts/appHeader.json +7 -0
- package/i18n/dicts/appSidebar.json +1 -0
- package/i18n/dicts/artifactCard.json +6 -0
- package/i18n/dicts/assetLibrary.json +18 -0
- package/i18n/dicts/awChart.json +1 -0
- package/i18n/dicts/awLogo.json +1 -0
- package/i18n/dicts/channelSessionList.json +45 -0
- package/i18n/dicts/clusterArtifact.json +4 -0
- package/i18n/dicts/clusterError.json +3 -0
- package/i18n/dicts/clusterLife.json +5 -0
- package/i18n/dicts/clusterMember.json +3 -0
- package/i18n/dicts/clusterMemory.json +3 -0
- package/i18n/dicts/clusterOther.json +3 -0
- package/i18n/dicts/clusterRoute.json +15 -0
- package/i18n/dicts/clusterStatus.json +3 -0
- package/i18n/dicts/clusterStream.json +3 -0
- package/i18n/dicts/clusterTask.json +10 -0
- package/i18n/dicts/clusterTool.json +3 -0
- package/i18n/dicts/commandPalette.json +16 -0
- package/i18n/dicts/composer.json +38 -0
- package/i18n/dicts/ctpl.json +32 -0
- package/i18n/dicts/daq.json +157 -0
- package/i18n/dicts/daqDetail.json +55 -0
- package/i18n/dicts/dcw.json +68 -0
- package/i18n/dicts/dcwDetail.json +199 -0
- package/i18n/dicts/deviceTwinPanel.json +12 -0
- package/i18n/dicts/eventBlock.json +8 -0
- package/i18n/dicts/fileSelectorModal.json +6 -0
- package/i18n/dicts/inspectorPanel.json +12 -0
- package/i18n/dicts/laneBlocks.json +6 -0
- package/i18n/dicts/liveAgentBar.json +8 -0
- package/i18n/dicts/logs.json +39 -0
- package/i18n/dicts/memoryPanel.json +20 -0
- package/i18n/dicts/mentionHoverCard.json +4 -0
- package/i18n/dicts/modelPreview3D.json +1 -0
- package/i18n/dicts/monitor.json +5 -0
- package/i18n/dicts/multiChannelView.json +13 -0
- package/i18n/dicts/ompTerminalPanel.json +7 -0
- package/i18n/dicts/paneSplitter.json +3 -0
- package/i18n/dicts/settings.json +1 -0
- package/i18n/dicts/statsPanel.json +6 -0
- package/i18n/dicts/taskBoardView.json +22 -0
- package/i18n/dicts/taskInspectorDrawer.json +19 -0
- package/i18n/dicts/teams.json +33 -0
- package/i18n/dicts/tokens.json +30 -0
- package/i18n/dicts/town.json +7 -0
- package/i18n/dicts/townView.json +209 -0
- package/i18n/dicts/transcriptTimeline.json +31 -0
- package/i18n/dicts/users.json +15 -0
- package/i18n/dicts/wsHome.json +33 -0
- package/i18n/dicts/wsView.json +15 -0
- package/i18n/locales/en.ts +1737 -0
- package/i18n/locales/zh-CN.ts +1736 -0
- package/nuxt.config.ts +209 -0
- package/package.json +123 -0
- package/public/assets/game/character/bot.png +0 -0
- package/public/assets/game/character/device-3d.glb +0 -0
- package/public/assets/game/character/hero-3d.glb +0 -0
- package/public/assets/game/character/hero-anime-1.glb +0 -0
- package/public/assets/game/character/hero-anime-2.glb +0 -0
- package/public/assets/game/character/hero-anime-3.glb +0 -0
- package/public/assets/game/character/hero-anime-4.glb +0 -0
- package/public/assets/game/character/knight.png +0 -0
- package/public/assets/game/character/mage.png +0 -0
- package/public/assets/game/coin.png +0 -0
- package/public/assets/game/devices/agv.glb +0 -0
- package/public/assets/game/devices/caster.glb +0 -0
- package/public/assets/game/devices/device-console.glb +0 -0
- package/public/assets/game/devices/device-robot-arm.glb +0 -0
- package/public/assets/game/devices/device-scanner.glb +0 -0
- package/public/assets/game/devices/extruder.glb +0 -0
- package/public/assets/game/devices/mdo.glb +0 -0
- package/public/assets/game/devices/power-cabinet.glb +0 -0
- package/public/assets/game/devices/pump.glb +0 -0
- package/public/assets/game/devices/tdo.glb +0 -0
- package/public/assets/game/devices/thickness-scanner.glb +0 -0
- package/public/assets/game/devices/winder.glb +0 -0
- package/public/assets/game/npc/boltnu-sheet.png +0 -0
- package/public/assets/game/npc/fluttaflap-sheet.png +0 -0
- package/public/assets/game/npc/rockitten-sheet.png +0 -0
- package/public/assets/game/npc/tux-sheet.png +0 -0
- package/public/assets/game/player/misa-back-walk.000.png +0 -0
- package/public/assets/game/player/misa-back-walk.001.png +0 -0
- package/public/assets/game/player/misa-back-walk.002.png +0 -0
- package/public/assets/game/player/misa-back-walk.003.png +0 -0
- package/public/assets/game/player/misa-back.png +0 -0
- package/public/assets/game/player/misa-front-walk.000.png +0 -0
- package/public/assets/game/player/misa-front-walk.001.png +0 -0
- package/public/assets/game/player/misa-front-walk.002.png +0 -0
- package/public/assets/game/player/misa-front-walk.003.png +0 -0
- package/public/assets/game/player/misa-front.png +0 -0
- package/public/assets/game/player/misa-left-walk.000.png +0 -0
- package/public/assets/game/player/misa-left-walk.001.png +0 -0
- package/public/assets/game/player/misa-left-walk.002.png +0 -0
- package/public/assets/game/player/misa-left-walk.003.png +0 -0
- package/public/assets/game/player/misa-left.png +0 -0
- package/public/assets/game/player/misa-right-walk.000.png +0 -0
- package/public/assets/game/player/misa-right-walk.001.png +0 -0
- package/public/assets/game/player/misa-right-walk.002.png +0 -0
- package/public/assets/game/player/misa-right-walk.003.png +0 -0
- package/public/assets/game/player/misa-right.png +0 -0
- package/public/assets/game/player-sheet.png +0 -0
- package/public/assets/game/tuxemon-town.json +1 -0
- package/public/assets/game/tuxmon-sample-32px-extruded.png +0 -0
- package/public/assets/game/wuwa/scene-map.png +0 -0
- package/public/assets/game/wuwa/world-far.png +0 -0
- package/public/assets/game/wuwa/world-middle.png +0 -0
- package/public/assets/game/wuwa/world-near.png +0 -0
- package/public/assets/game/wuwa/wu-aura.png +0 -0
- package/public/assets/game/wuwa/wu-lead.png +0 -0
- package/public/assets/game/wuwa/wu-ring.png +0 -0
- package/public/assets/game/wuwa/wu-slash.png +0 -0
- package/public/assets/game/wuwa/wu-worker-0.png +0 -0
- package/public/assets/game/wuwa/wu-worker-1.png +0 -0
- package/public/assets/game/wuwa/wu-worker-2.png +0 -0
- package/public/favicon.svg +14 -0
- package/public/scene/background/cyber-town-background.svg +110 -0
- package/scripts/_daq-realtime-probe.mjs +68 -0
- package/scripts/_daq-warn-probe.mjs +50 -0
- package/scripts/_dbg-agent-tools-audit.mjs +154 -0
- package/scripts/_dbg-agent-workflow-audit.mjs +177 -0
- package/scripts/_dbg-anim-verify.mjs +81 -0
- package/scripts/_dbg-audit-all-pages.mjs +43 -0
- package/scripts/_dbg-autoframe.mjs +103 -0
- package/scripts/_dbg-beacon-final.mjs +181 -0
- package/scripts/_dbg-bind-bidi-e2e.mjs +239 -0
- package/scripts/_dbg-callout-near.mjs +108 -0
- package/scripts/_dbg-chan-focus.mjs +49 -0
- package/scripts/_dbg-cleanup.mjs +31 -0
- package/scripts/_dbg-closedloop-ui-shot.mjs +61 -0
- package/scripts/_dbg-console-audit.mjs +61 -0
- package/scripts/_dbg-console-probe.mjs +23 -0
- package/scripts/_dbg-context-inject-audit.mjs +231 -0
- package/scripts/_dbg-crash-recovery.mjs +143 -0
- package/scripts/_dbg-crop-shot.mjs +16 -0
- package/scripts/_dbg-daq-acc-probe.mjs +24 -0
- package/scripts/_dbg-daq-attrib.mjs +34 -0
- package/scripts/_dbg-daq-bind-audit.mjs +119 -0
- package/scripts/_dbg-daq-cadence.mjs +104 -0
- package/scripts/_dbg-daq-debug.mjs +22 -0
- package/scripts/_dbg-daq-diag.mjs +33 -0
- package/scripts/_dbg-daq-e2e.mjs +171 -0
- package/scripts/_dbg-daq-film.mjs +136 -0
- package/scripts/_dbg-daq-filter-e2e.mjs +109 -0
- package/scripts/_dbg-daq-filter-e2e2.mjs +62 -0
- package/scripts/_dbg-daq-final-regression.mjs +78 -0
- package/scripts/_dbg-daq-full-check.mjs +179 -0
- package/scripts/_dbg-daq-line-note-verify.mjs +61 -0
- package/scripts/_dbg-daq-linebanner-audit.mjs +107 -0
- package/scripts/_dbg-daq-pages-shot.mjs +37 -0
- package/scripts/_dbg-daq-perf.mjs +61 -0
- package/scripts/_dbg-daq-probe.mjs +27 -0
- package/scripts/_dbg-daq-recipe-e2e.mjs +30 -0
- package/scripts/_dbg-daq-redesign-shot.mjs +88 -0
- package/scripts/_dbg-daq-rows-probe.mjs +26 -0
- package/scripts/_dbg-daq-rowstate-e2e.mjs +152 -0
- package/scripts/_dbg-daq-rowstate-shot.mjs +45 -0
- package/scripts/_dbg-daq-rowstate-shot2.mjs +38 -0
- package/scripts/_dbg-daq-tpl.mjs +144 -0
- package/scripts/_dbg-dash-console-check.mjs +49 -0
- package/scripts/_dbg-dash-deep-probe.mjs +61 -0
- package/scripts/_dbg-dash-hitpoint.mjs +44 -0
- package/scripts/_dbg-dash-live-shot.mjs +36 -0
- package/scripts/_dbg-dash-shot.mjs +21 -0
- package/scripts/_dbg-dashboard-shot.mjs +35 -0
- package/scripts/_dbg-dcw-audit.mjs +126 -0
- package/scripts/_dbg-dcw-card-e2e.mjs +108 -0
- package/scripts/_dbg-dcw-channel-ui.mjs +144 -0
- package/scripts/_dbg-dcw-examples.mjs +81 -0
- package/scripts/_dbg-dcw-light-shot.mjs +19 -0
- package/scripts/_dbg-dcw-line-audit.mjs +107 -0
- package/scripts/_dbg-dcw-pause-tpl-audit.mjs +161 -0
- package/scripts/_dbg-dcw-pause-ui.mjs +175 -0
- package/scripts/_dbg-dcw-scene.mjs +72 -0
- package/scripts/_dbg-dcw-shot2.mjs +14 -0
- package/scripts/_dbg-dcw-twin-e2e.mjs +192 -0
- package/scripts/_dbg-dt-interact.mjs +193 -0
- package/scripts/_dbg-error-probe.mjs +54 -0
- package/scripts/_dbg-filter-light-verify.mjs +54 -0
- package/scripts/_dbg-final-acceptance-2.mjs +72 -0
- package/scripts/_dbg-final-acceptance.mjs +192 -0
- package/scripts/_dbg-final-agent-e2e.mjs +151 -0
- package/scripts/_dbg-final-full-e2e.mjs +218 -0
- package/scripts/_dbg-final-line-e2e.mjs +173 -0
- package/scripts/_dbg-final-t6-hitl.mjs +38 -0
- package/scripts/_dbg-focus-range.mjs +115 -0
- package/scripts/_dbg-fresh-scene-e2e.mjs +125 -0
- package/scripts/_dbg-full-feature-e2e.mjs +219 -0
- package/scripts/_dbg-fullchain-e2e.mjs +178 -0
- package/scripts/_dbg-gate-debug.mjs +37 -0
- package/scripts/_dbg-gif-encode.mjs +111 -0
- package/scripts/_dbg-gizmo.mjs +95 -0
- package/scripts/_dbg-glass-probe.mjs +57 -0
- package/scripts/_dbg-glass-shot.mjs +50 -0
- package/scripts/_dbg-glass-sweep.mjs +25 -0
- package/scripts/_dbg-glb-probe.mjs +43 -0
- package/scripts/_dbg-hardening-probe.mjs +90 -0
- package/scripts/_dbg-hero-btn-probe.mjs +21 -0
- package/scripts/_dbg-hidden-check.mjs +21 -0
- package/scripts/_dbg-hitl-timeout-e2e.mjs +93 -0
- package/scripts/_dbg-hmi-audit.mjs +164 -0
- package/scripts/_dbg-home-repro.mjs +24 -0
- package/scripts/_dbg-home-stack.mjs +13 -0
- package/scripts/_dbg-huashu-survey.mjs +31 -0
- package/scripts/_dbg-huashu-survey2.mjs +35 -0
- package/scripts/_dbg-huashu-verify.mjs +25 -0
- package/scripts/_dbg-huashu-verify2.mjs +26 -0
- package/scripts/_dbg-hydration-detail.mjs +18 -0
- package/scripts/_dbg-i18n-debug.mjs +27 -0
- package/scripts/_dbg-i18n-debug2.mjs +26 -0
- package/scripts/_dbg-i18n-final.mjs +42 -0
- package/scripts/_dbg-i18n-scan.mjs +38 -0
- package/scripts/_dbg-infra-lifecycle.mjs +44 -0
- package/scripts/_dbg-line-aura-shots.mjs +54 -0
- package/scripts/_dbg-line-control-e2e.mjs +120 -0
- package/scripts/_dbg-line-gate-audit.mjs +108 -0
- package/scripts/_dbg-line-live-e2e.mjs +144 -0
- package/scripts/_dbg-line-scenario-e2e.mjs +256 -0
- package/scripts/_dbg-minimal-omp.mjs +77 -0
- package/scripts/_dbg-mode-nav.mjs +173 -0
- package/scripts/_dbg-mqtt-chain-e2e.mjs +90 -0
- package/scripts/_dbg-nav-rpg.mjs +84 -0
- package/scripts/_dbg-nav-shot.mjs +28 -0
- package/scripts/_dbg-nav.mjs +27 -0
- package/scripts/_dbg-opslog-e2e.mjs +121 -0
- package/scripts/_dbg-opslog-shots.mjs +62 -0
- package/scripts/_dbg-opt-team-e2e.mjs +133 -0
- package/scripts/_dbg-optimization-e2e.mjs +94 -0
- package/scripts/_dbg-orbit.mjs +58 -0
- package/scripts/_dbg-panesplitter-fix-check.mjs +62 -0
- package/scripts/_dbg-phase-e3-monitor.mjs +25 -0
- package/scripts/_dbg-pop-clamp-verify.mjs +77 -0
- package/scripts/_dbg-preset-e2e.mjs +116 -0
- package/scripts/_dbg-preview-console.mjs +28 -0
- package/scripts/_dbg-preview-minrepro.mjs +156 -0
- package/scripts/_dbg-preview-pixels.mjs +93 -0
- package/scripts/_dbg-quality-pages-e2e.mjs +84 -0
- package/scripts/_dbg-query-cancel.ts +12 -0
- package/scripts/_dbg-rail-tree-e2e.mjs +183 -0
- package/scripts/_dbg-real-driver-e2e.mjs +55 -0
- package/scripts/_dbg-redesign-shots.mjs +34 -0
- package/scripts/_dbg-restart-persist.mjs +41 -0
- package/scripts/_dbg-rollback-mgr-audit.mjs +169 -0
- package/scripts/_dbg-rpg.mjs +71 -0
- package/scripts/_dbg-sc001-dispatch.mjs +63 -0
- package/scripts/_dbg-scenario-final-shot.mjs +24 -0
- package/scripts/_dbg-scenario-phase-e2.mjs +75 -0
- package/scripts/_dbg-scenario-phase-e3.mjs +96 -0
- package/scripts/_dbg-scenario-phase1.mjs +90 -0
- package/scripts/_dbg-scenario-phase2.mjs +105 -0
- package/scripts/_dbg-scenario-phase3-monitor.mjs +48 -0
- package/scripts/_dbg-scenario-phase3.mjs +111 -0
- package/scripts/_dbg-scene-polish-shot.mjs +118 -0
- package/scripts/_dbg-selring-probe.mjs +47 -0
- package/scripts/_dbg-sideinfo.mjs +101 -0
- package/scripts/_dbg-splitter.mjs +88 -0
- package/scripts/_dbg-team-repro.mjs +76 -0
- package/scripts/_dbg-town-alarm-e2e.mjs +40 -0
- package/scripts/_dbg-town-beauty-shots.mjs +54 -0
- package/scripts/_dbg-town-daq-shot.mjs +26 -0
- package/scripts/_dbg-town-full-e2e.mjs +104 -0
- package/scripts/_dbg-town-gate.mjs +44 -0
- package/scripts/_dbg-town-ins-probe.mjs +37 -0
- package/scripts/_dbg-town-perf.mjs +50 -0
- package/scripts/_dbg-town-polish-shots.mjs +82 -0
- package/scripts/_dbg-town-realtime.mjs +42 -0
- package/scripts/_dbg-town-shot3.mjs +17 -0
- package/scripts/_dbg-town-truth.mjs +37 -0
- package/scripts/_dbg-town-truth2.mjs +27 -0
- package/scripts/_dbg-transform-audit.mjs +98 -0
- package/scripts/_dbg-trend-expand-test.mjs +38 -0
- package/scripts/_dbg-twindaq-focus.mjs +81 -0
- package/scripts/_dbg-ui-polish-shot.mjs +43 -0
- package/scripts/_dbg-ui-polish-verify1.mjs +92 -0
- package/scripts/_dbg-ui-polish-verify2.mjs +50 -0
- package/scripts/_dbg-ui-polish-verify3.mjs +51 -0
- package/scripts/_dbg-ui-polish-verify4.mjs +50 -0
- package/scripts/_dbg-ui-shot.mjs +26 -0
- package/scripts/_dbg-unify-shot.mjs +43 -0
- package/scripts/_dbg-uplink-probe.mjs +36 -0
- package/scripts/_dbg-wizard-preview-shot.mjs +58 -0
- package/scripts/_dbg-wizard-ui-e2e.mjs +93 -0
- package/scripts/_dbg-ws-probe.mjs +86 -0
- package/scripts/_dbg-ws-shot2.mjs +23 -0
- package/scripts/_dbg-wsdot-sanity.mjs +25 -0
- package/scripts/_dev-stack.mjs +46 -0
- package/scripts/_http-driver-probe.mjs +30 -0
- package/scripts/_lib-dcw-line.mjs +24 -0
- package/scripts/_mqtt-driver-probe.mjs +30 -0
- package/scripts/_newdriver-rest-probe.mjs +100 -0
- package/scripts/_node-state-probe.mjs +5 -0
- package/scripts/_opcua-driver-probe.mjs +31 -0
- package/scripts/_rtu-debug.mjs +41 -0
- package/scripts/_rtu-direct.mjs +6 -0
- package/scripts/_rtu-driver-probe.mjs +53 -0
- package/scripts/_rtu-mini-slave.mjs +63 -0
- package/scripts/_townview_style.css +1185 -0
- package/scripts/_townview_template.html +1215 -0
- package/scripts/_wizard-shots.mjs +90 -0
- package/scripts/acceptance-suite.py +207 -0
- package/scripts/api-live-e2e.mjs +467 -0
- package/scripts/build-anime-models.mjs +339 -0
- package/scripts/build-character-models.mjs +175 -0
- package/scripts/build-film-devices.mjs +227 -0
- package/scripts/build-model-3d.mjs +173 -0
- package/scripts/build-player-sheet.mjs +58 -0
- package/scripts/build-world-map.mjs +188 -0
- package/scripts/build-wuwa-assets.mjs +345 -0
- package/scripts/collab-live-test.py +177 -0
- package/scripts/debug-mcp-http.ts +64 -0
- package/scripts/demo-lead-worker.ts +256 -0
- package/scripts/demo-modes.ts +415 -0
- package/scripts/demo-omp-channel.ts +135 -0
- package/scripts/design-preflight.mjs +223 -0
- package/scripts/design-verify-templates.mjs +110 -0
- package/scripts/design-verify.mjs +170 -0
- package/scripts/dev-guard.mjs +74 -0
- package/scripts/dev-modbus-simulator.mjs +55 -0
- package/scripts/dev-opcua-simulator.mjs +57 -0
- package/scripts/dev-protocol-simulators.mjs +145 -0
- package/scripts/dev-window.cmd +6 -0
- package/scripts/device-line-restore.mjs +34 -0
- package/scripts/diag-lead-status.ts +101 -0
- package/scripts/e2e-a2a-steer-live.mjs +193 -0
- package/scripts/e2e-agent-channel-task.ts +151 -0
- package/scripts/e2e-agent-team.mjs +168 -0
- package/scripts/e2e-auth-matrix.mjs +119 -0
- package/scripts/e2e-channel-isolation.ts +325 -0
- package/scripts/e2e-dual-drive-comm.ts +259 -0
- package/scripts/e2e-lead-team-mgmt.ts +217 -0
- package/scripts/e2e-memory-persistence.ts +320 -0
- package/scripts/e2e-memory-system.ts +332 -0
- package/scripts/e2e-omp-modes.ts +274 -0
- package/scripts/e2e-omp-realtime.ts +258 -0
- package/scripts/e2e-omp-workspace.ts +180 -0
- package/scripts/e2e-parallel-execution.mjs +151 -0
- package/scripts/e2e-rest-memory.mjs +251 -0
- package/scripts/e2e-rest-robustness.mjs +177 -0
- package/scripts/e2e-resume-crash.mjs +254 -0
- package/scripts/e2e-tab-close.mjs +136 -0
- package/scripts/e2e-task-queue.ts +277 -0
- package/scripts/e2e-template-isolation.mjs +267 -0
- package/scripts/e2e-token-ui.mjs +149 -0
- package/scripts/final-soup-run.py +192 -0
- package/scripts/final-soup-watch.py +83 -0
- package/scripts/home-bootstrap.mjs +117 -0
- package/scripts/i18n-codemod.mjs +173 -0
- package/scripts/i18n-fix-en.py +28 -0
- package/scripts/i18n-fix-en2.py +31 -0
- package/scripts/i18n-merge.mjs +67 -0
- package/scripts/omp-real-test.py +328 -0
- package/scripts/smoke-optimization-verify.ts +72 -0
- package/scripts/start.mjs +60 -0
- package/scripts/test-a2a-relay.mjs +222 -0
- package/scripts/test-a2a-types.ts +296 -0
- package/scripts/test-agent-brain.ts +135 -0
- package/scripts/test-agent-multi-channel.ts +160 -0
- package/scripts/test-agent-runtime.ts +348 -0
- package/scripts/test-api-client.ts +108 -0
- package/scripts/test-api-suite.mjs +271 -0
- package/scripts/test-block-consumption.mjs +194 -0
- package/scripts/test-channel-routing.ts +301 -0
- package/scripts/test-cmd-render.mjs +218 -0
- package/scripts/test-collab-e2e.mjs +136 -0
- package/scripts/test-collab-omp.mjs +192 -0
- package/scripts/test-dedup-logic.ts +132 -0
- package/scripts/test-dual-drive.ts +227 -0
- package/scripts/test-e2e-collaboration.ts +262 -0
- package/scripts/test-e2e-monitor.ts +192 -0
- package/scripts/test-event-history.ts +112 -0
- package/scripts/test-event-persistence.mjs +169 -0
- package/scripts/test-exec-modes.ts +316 -0
- package/scripts/test-fifo-realtime.mjs +193 -0
- package/scripts/test-final-e2e.mjs +290 -0
- package/scripts/test-full-system.ts +439 -0
- package/scripts/test-game-session.ts +160 -0
- package/scripts/test-goal-summary.ts +202 -0
- package/scripts/test-harness-liveness.ts +109 -0
- package/scripts/test-harness-terminal.ts +164 -0
- package/scripts/test-header-trail.mjs +95 -0
- package/scripts/test-hitl.ts +360 -0
- package/scripts/test-koda-restyle.mjs +105 -0
- package/scripts/test-lane-history-live.mjs +103 -0
- package/scripts/test-lane-raw.ts +67 -0
- package/scripts/test-lanes-history-render.mjs +138 -0
- package/scripts/test-loop-omp.mjs +154 -0
- package/scripts/test-mcp-tools.ts +285 -0
- package/scripts/test-mdlite.ts +53 -0
- package/scripts/test-memory-dynamic.ts +99 -0
- package/scripts/test-memory-maintenance.ts +117 -0
- package/scripts/test-memory-vector.ts +154 -0
- package/scripts/test-memory.ts +431 -0
- package/scripts/test-mock-collab.mjs +226 -0
- package/scripts/test-modern-render.mjs +174 -0
- package/scripts/test-omp-terminal.mjs +187 -0
- package/scripts/test-orchestration.ts +425 -0
- package/scripts/test-persistence-lazy.ts +323 -0
- package/scripts/test-prompt-system.mjs +181 -0
- package/scripts/test-prompts-loader.ts +56 -0
- package/scripts/test-protocol.ts +156 -0
- package/scripts/test-real-scenario.mjs +290 -0
- package/scripts/test-route-reason.mjs +111 -0
- package/scripts/test-scheduler-loop.ts +469 -0
- package/scripts/test-stream-render.ts +37 -0
- package/scripts/test-task-drawer.mjs +204 -0
- package/scripts/test-task-engine.ts +433 -0
- package/scripts/test-task-invariants.mjs +169 -0
- package/scripts/test-terminal-e2e.mjs +264 -0
- package/scripts/test-town-3d.mjs +121 -0
- package/scripts/test-town-drag.mjs +218 -0
- package/scripts/test-town-layout.mjs +200 -0
- package/scripts/test-town-page.mjs +136 -0
- package/scripts/test-town-scene-math.ts +169 -0
- package/scripts/test-town-twin.mjs +121 -0
- package/scripts/test-town-upload.mjs +89 -0
- package/scripts/test-town-user-flow.mjs +619 -0
- package/scripts/test-town-world.mjs +76 -0
- package/scripts/test-town-wuwa.mjs +198 -0
- package/scripts/test-user-isolation.mjs +193 -0
- package/scripts/test-workshop-db.ts +280 -0
- package/scripts/test-workshop-entries.ts +370 -0
- package/scripts/test-workspace-api.ts +329 -0
- package/scripts/test-ws-aep.mjs +187 -0
- package/scripts/test-ws-protocol.ts +134 -0
- package/scripts/test-ws-stress.mjs +208 -0
- package/scripts/town-demo-seed.mjs +75 -0
- package/scripts/town-scene-clear.mjs +56 -0
- package/scripts/ui-screenshot.mjs +128 -0
- package/scripts/verify-a2a-live.mjs +124 -0
- package/scripts/verify-exec-modes-report.ts +248 -0
- package/scripts/verify-game.mjs +477 -0
- package/scripts/verify-lead-mail.ts +188 -0
- package/scripts/verify-page-smoke.mjs +87 -0
- package/scripts/verify-ws-events.mjs +123 -0
- package/scripts/ws-test.mjs +176 -0
- package/scripts/ws-test.py +4 -0
- package/server/api/health.get.ts +32 -0
- package/server/api/mcp/workshop.ts +78 -0
- package/server/api/metrics.ts +75 -0
- package/server/api/system/config.get.ts +12 -0
- package/server/api/system/monitor/terminal/ws.ts +177 -0
- package/server/api/system/monitor/terminate.post.ts +36 -0
- package/server/api/system/monitor.get.ts +13 -0
- package/server/api/system/settings/events.get.ts +28 -0
- package/server/api/system/settings/index.get.ts +14 -0
- package/server/api/system/settings/index.patch.ts +22 -0
- package/server/api/system/settings/reset.post.ts +12 -0
- package/server/api/users/[id].delete.ts +11 -0
- package/server/api/users/[id].get.ts +11 -0
- package/server/api/users/[id].put.ts +14 -0
- package/server/api/users/index.get.ts +13 -0
- package/server/api/users/index.post.ts +13 -0
- package/server/api/users/login.post.ts +11 -0
- package/server/api/users/logout.post.ts +9 -0
- package/server/api/users/me.get.ts +9 -0
- package/server/api/users/register.post.ts +11 -0
- package/server/api/users/tokens/[id]/token.get.ts +11 -0
- package/server/api/users/tokens/[id].delete.ts +11 -0
- package/server/api/users/tokens/[id].patch.ts +14 -0
- package/server/api/users/tokens/index.get.ts +9 -0
- package/server/api/users/tokens/index.post.ts +13 -0
- package/server/api/workshop/[...path].ts +13 -0
- package/server/api/workshop/a2a/[agentId]/card/index.get.ts +75 -0
- package/server/api/workshop/a2a/[agentId]/rpc/index.post.ts +429 -0
- package/server/api/workshop/a2a/send.post.ts +28 -0
- package/server/api/workshop/agent-tools/approvals/[id]/decide.post.ts +21 -0
- package/server/api/workshop/agent-tools/approvals/index.get.ts +18 -0
- package/server/api/workshop/agent-tools/bindings/[id].delete.ts +21 -0
- package/server/api/workshop/agent-tools/bindings/[id].patch.ts +14 -0
- package/server/api/workshop/agent-tools/bindings/index.get.ts +14 -0
- package/server/api/workshop/agent-tools/bindings/index.post.ts +20 -0
- package/server/api/workshop/agent-tools/invoke.post.ts +34 -0
- package/server/api/workshop/agents/[id].delete.ts +20 -0
- package/server/api/workshop/agents/[id].get.ts +19 -0
- package/server/api/workshop/agents/[id].patch.ts +30 -0
- package/server/api/workshop/agents/index.get.ts +13 -0
- package/server/api/workshop/agents/index.post.ts +29 -0
- package/server/api/workshop/agents/subscribe.post.ts +21 -0
- package/server/api/workshop/approvals/[id]/decide.post.ts +30 -0
- package/server/api/workshop/approvals/index.get.ts +22 -0
- package/server/api/workshop/assets/character/[id].delete.ts +33 -0
- package/server/api/workshop/assets/character/index.get.ts +17 -0
- package/server/api/workshop/assets/character/index.post.ts +87 -0
- package/server/api/workshop/assets/devices/[id].delete.ts +43 -0
- package/server/api/workshop/assets/devices/index.get.ts +103 -0
- package/server/api/workshop/assets/devices/index.post.ts +53 -0
- package/server/api/workshop/audit.get.ts +43 -0
- package/server/api/workshop/caller.ts +141 -0
- package/server/api/workshop/channel-templates/[id]/instantiate.post.ts +31 -0
- package/server/api/workshop/channel-templates/[id].delete.ts +20 -0
- package/server/api/workshop/channel-templates/[id].get.ts +19 -0
- package/server/api/workshop/channel-templates/[id].patch.ts +48 -0
- package/server/api/workshop/channel-templates/from-channel.post.ts +31 -0
- package/server/api/workshop/channel-templates/index.get.ts +13 -0
- package/server/api/workshop/channel-templates/index.post.ts +61 -0
- package/server/api/workshop/channels/[id]/activate.post.ts +19 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/[memoryId].delete.ts +30 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/index.get.ts +29 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/index.post.ts +48 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/search.post.ts +43 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/model.patch.ts +39 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/position.patch.ts +33 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/range.patch.ts +54 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/stop.post.ts +25 -0
- package/server/api/workshop/channels/[id]/agents/[agentId].delete.ts +23 -0
- package/server/api/workshop/channels/[id]/agents/[agentId].patch.ts +28 -0
- package/server/api/workshop/channels/[id]/agents/index.get.ts +27 -0
- package/server/api/workshop/channels/[id]/agents/index.post.ts +61 -0
- package/server/api/workshop/channels/[id]/events.get.ts +55 -0
- package/server/api/workshop/channels/[id]/index.get.ts +18 -0
- package/server/api/workshop/channels/[id]/index.patch.ts +30 -0
- package/server/api/workshop/channels/[id]/memories/[memoryId].delete.ts +29 -0
- package/server/api/workshop/channels/[id]/memories/index.get.ts +28 -0
- package/server/api/workshop/channels/[id]/memories/index.post.ts +42 -0
- package/server/api/workshop/channels/[id]/messages/index.get.ts +56 -0
- package/server/api/workshop/channels/[id]/messages/index.post.ts +75 -0
- package/server/api/workshop/channels/[id]/queue.get.ts +25 -0
- package/server/api/workshop/channels/[id]/tasks/index.get.ts +24 -0
- package/server/api/workshop/channels/[id]/tasks/index.post.ts +79 -0
- package/server/api/workshop/channels/[id]/terminals.get.ts +21 -0
- package/server/api/workshop/channels/[id].delete.ts +18 -0
- package/server/api/workshop/channels.get.ts +11 -0
- package/server/api/workshop/channels.post.ts +48 -0
- package/server/api/workshop/daq/[id]/bind.post.ts +19 -0
- package/server/api/workshop/daq/[id]/samples.get.ts +22 -0
- package/server/api/workshop/daq/[id]/test.post.ts +17 -0
- package/server/api/workshop/daq/[id].delete.ts +30 -0
- package/server/api/workshop/daq/[id].patch.ts +31 -0
- package/server/api/workshop/daq/alarms/[id]/ack.post.ts +15 -0
- package/server/api/workshop/daq/alarms.get.ts +16 -0
- package/server/api/workshop/daq/controller.post.ts +52 -0
- package/server/api/workshop/daq/index.get.ts +40 -0
- package/server/api/workshop/daq/index.post.ts +33 -0
- package/server/api/workshop/daq/infra/reconnect.post.ts +17 -0
- package/server/api/workshop/daq/infra.get.ts +11 -0
- package/server/api/workshop/daq/templates/[key].delete.ts +17 -0
- package/server/api/workshop/daq/templates/[key].patch.ts +18 -0
- package/server/api/workshop/daq/templates/index.post.ts +17 -0
- package/server/api/workshop/daq/test-driver.post.ts +23 -0
- package/server/api/workshop/dcw/[id]/bind.post.ts +17 -0
- package/server/api/workshop/dcw/[id]/param-ledger.get.ts +15 -0
- package/server/api/workshop/dcw/[id]/test.post.ts +15 -0
- package/server/api/workshop/dcw/[id]/write.post.ts +19 -0
- package/server/api/workshop/dcw/[id].delete.ts +30 -0
- package/server/api/workshop/dcw/[id].patch.ts +18 -0
- package/server/api/workshop/dcw/controller.post.ts +43 -0
- package/server/api/workshop/dcw/index.get.ts +25 -0
- package/server/api/workshop/dcw/index.post.ts +20 -0
- package/server/api/workshop/dcw/journal/index.get.ts +21 -0
- package/server/api/workshop/dcw/journal/node/[nodeId]/rollback.post.ts +22 -0
- package/server/api/workshop/dcw/line/query.get.ts +31 -0
- package/server/api/workshop/dcw/lines/[id]/start.post.ts +35 -0
- package/server/api/workshop/dcw/lines/[id]/stop.post.ts +34 -0
- package/server/api/workshop/dcw/lines/[id].delete.ts +35 -0
- package/server/api/workshop/dcw/lines/[id].patch.ts +15 -0
- package/server/api/workshop/dcw/lines/index.get.ts +14 -0
- package/server/api/workshop/dcw/lines/index.post.ts +31 -0
- package/server/api/workshop/dcw/optimizations/[id]/judge.post.ts +24 -0
- package/server/api/workshop/dcw/optimizations/[id]/rollback.post.ts +29 -0
- package/server/api/workshop/dcw/optimizations/[id]/series.get.ts +18 -0
- package/server/api/workshop/dcw/optimizations/[id].get.ts +17 -0
- package/server/api/workshop/dcw/optimizations/index.get.ts +23 -0
- package/server/api/workshop/dcw/products/[id].delete.ts +16 -0
- package/server/api/workshop/dcw/products/[id].patch.ts +17 -0
- package/server/api/workshop/dcw/products/index.post.ts +16 -0
- package/server/api/workshop/dcw/recipes/[id]/apply.post.ts +40 -0
- package/server/api/workshop/dcw/recipes/[id]/mark-good.post.ts +21 -0
- package/server/api/workshop/dcw/recipes/[id]/rollback-good.post.ts +19 -0
- package/server/api/workshop/dcw/recipes/[id].delete.ts +14 -0
- package/server/api/workshop/dcw/recipes/[id].patch.ts +16 -0
- package/server/api/workshop/dcw/recipes/index.get.ts +12 -0
- package/server/api/workshop/dcw/recipes/index.post.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/close.post.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/data.get.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/rollback.post.ts +20 -0
- package/server/api/workshop/dcw/runs/index.get.ts +11 -0
- package/server/api/workshop/dcw/templates/[key].delete.ts +14 -0
- package/server/api/workshop/dcw/templates/[key].patch.ts +16 -0
- package/server/api/workshop/dcw/templates/index.post.ts +15 -0
- package/server/api/workshop/dcw/test-driver.post.ts +16 -0
- package/server/api/workshop/device-twins/[id]/control.post.ts +21 -0
- package/server/api/workshop/device-twins/[id]/telemetry.post.ts +21 -0
- package/server/api/workshop/device-twins/[id].delete.ts +25 -0
- package/server/api/workshop/device-twins/[id].patch.ts +29 -0
- package/server/api/workshop/device-twins/index.get.ts +16 -0
- package/server/api/workshop/device-twins/index.post.ts +32 -0
- package/server/api/workshop/fs/dirs.get.ts +40 -0
- package/server/api/workshop/mailbox/all.get.ts +19 -0
- package/server/api/workshop/mailbox.get.ts +14 -0
- package/server/api/workshop/memories/maintenance.post.ts +15 -0
- package/server/api/workshop/ops-logs/index.get.ts +30 -0
- package/server/api/workshop/ops-logs/index.post.ts +39 -0
- package/server/api/workshop/runtime.get.ts +11 -0
- package/server/api/workshop/scene/layouts/[channelId].delete.ts +23 -0
- package/server/api/workshop/scene/layouts/[channelId].put.ts +31 -0
- package/server/api/workshop/scene/layouts.get.ts +13 -0
- package/server/api/workshop/tasks/[id]/cancel/index.post.ts +34 -0
- package/server/api/workshop/tasks/[id]/complete.post.ts +32 -0
- package/server/api/workshop/tasks/[id]/dispatch.post.ts +30 -0
- package/server/api/workshop/tasks/[id]/report.post.ts +38 -0
- package/server/api/workshop/tasks/[id]/retry.post.ts +33 -0
- package/server/api/workshop/tasks/[id].get.ts +32 -0
- package/server/api/workshop/tasks.get.ts +13 -0
- package/server/api/workshop/teams/[id]/deploy.post.ts +40 -0
- package/server/api/workshop/teams/[id]/members/[agentId].delete.ts +19 -0
- package/server/api/workshop/teams/[id]/members/index.get.ts +19 -0
- package/server/api/workshop/teams/[id]/members/index.post.ts +32 -0
- package/server/api/workshop/teams/[id].delete.ts +20 -0
- package/server/api/workshop/teams/[id].get.ts +19 -0
- package/server/api/workshop/teams/[id].patch.ts +32 -0
- package/server/api/workshop/teams/index.get.ts +13 -0
- package/server/api/workshop/teams/index.post.ts +27 -0
- package/server/api/workshop/users/me.get.ts +23 -0
- package/server/api/workshop/users/register.post.ts +33 -0
- package/server/api/workshop/workspaces/[id]/channel-templates/[tplId].post.ts +31 -0
- package/server/api/workshop/workspaces/[id]/channels/[channelId].delete.ts +15 -0
- package/server/api/workshop/workspaces/[id]/channels/[channelId].post.ts +15 -0
- package/server/api/workshop/workspaces/[id]/index.delete.ts +14 -0
- package/server/api/workshop/workspaces/index.get.ts +11 -0
- package/server/api/workshop/workspaces/index.post.ts +17 -0
- package/server/api/workshop/ws.ts +694 -0
- package/server/data/agent-node-bindings.json +554 -0
- package/server/data/daq-templates.json +16 -0
- package/server/data/daqs.json +2680 -0
- package/server/data/dcw-lines.json +919 -0
- package/server/data/dcw-products.json +835 -0
- package/server/data/dcw-recipes.json +1650 -0
- package/server/data/dcw-rollback.json +9339 -0
- package/server/data/dcw-runs.json +4633 -0
- package/server/data/dcw-templates.json +26 -0
- package/server/data/dcw-writes.json +9770 -0
- package/server/data/dcws.json +1736 -0
- package/server/data/device-twins.json +133 -0
- package/server/data/line-runs.json +152 -0
- package/server/data/scene-layouts.json +200 -0
- package/server/mcp/workshop-server.ts +510 -0
- package/server/middleware/request-log.ts +15 -0
- package/server/plugins/backup.ts +85 -0
- package/server/plugins/daq.ts +65 -0
- package/server/plugins/dev-stability-guard.ts +42 -0
- package/server/plugins/security.ts +29 -0
- package/server/plugins/system-config.ts +38 -0
- package/server/plugins/workshop.ts +145 -0
- package/server/repositories/user.repository.ts +391 -0
- package/server/schemas/user.schema.ts +67 -0
- package/server/services/system-config.ts +356 -0
- package/server/services/user.service.ts +186 -0
- package/server/services/workshop/agents/adapters/omp-rpc-client.ts +539 -0
- package/server/services/workshop/agents/adapters/to-a2a.ts +40 -0
- package/server/services/workshop/agents/agent-interface.ts +219 -0
- package/server/services/workshop/agents/claude-agent.ts +47 -0
- package/server/services/workshop/agents/factory.ts +24 -0
- package/server/services/workshop/agents/harness-process.ts +158 -0
- package/server/services/workshop/agents/harness-terminal.ts +545 -0
- package/server/services/workshop/agents/industrial-context.ts +222 -0
- package/server/services/workshop/agents/industrial-tools.ts +362 -0
- package/server/services/workshop/agents/mock-agent.ts +396 -0
- package/server/services/workshop/agents/node-bindings.repo.ts +165 -0
- package/server/services/workshop/agents/omp-agent.ts +1598 -0
- package/server/services/workshop/agents/tool-approvals.ts +174 -0
- package/server/services/workshop/assets/character-asset.repo.ts +120 -0
- package/server/services/workshop/assets/device-twin.repo.ts +236 -0
- package/server/services/workshop/daq/alarm-notify.ts +100 -0
- package/server/services/workshop/daq/bus/index.ts +46 -0
- package/server/services/workshop/daq/bus/inproc.adapter.ts +62 -0
- package/server/services/workshop/daq/bus/mqtt.adapter.ts +112 -0
- package/server/services/workshop/daq/bus/queue-port.ts +36 -0
- package/server/services/workshop/daq/daq-controller.ts +778 -0
- package/server/services/workshop/daq/daq-node.repo.ts +96 -0
- package/server/services/workshop/daq/daq-node.ts +247 -0
- package/server/services/workshop/daq/daq-runtime.ts +168 -0
- package/server/services/workshop/daq/daq-templates.ts +157 -0
- package/server/services/workshop/daq/drivers.ts +771 -0
- package/server/services/workshop/daq/host-bindings.ts +71 -0
- package/server/services/workshop/daq/host-ports.ts +64 -0
- package/server/services/workshop/daq/infra.ts +202 -0
- package/server/services/workshop/daq/storage/index.ts +96 -0
- package/server/services/workshop/daq/storage/sqlite.adapter.ts +205 -0
- package/server/services/workshop/daq/storage/timescale.adapter.ts +185 -0
- package/server/services/workshop/daq/storage/tsdb-port.ts +74 -0
- package/server/services/workshop/db/agent.repo.ts +105 -0
- package/server/services/workshop/db/channel-agent.repo.ts +119 -0
- package/server/services/workshop/db/channel-event.repo.ts +154 -0
- package/server/services/workshop/db/channel-template.repo.ts +113 -0
- package/server/services/workshop/db/channel.repo.ts +101 -0
- package/server/services/workshop/db/database.ts +934 -0
- package/server/services/workshop/db/memory.repo.ts +249 -0
- package/server/services/workshop/db/message.repo.ts +149 -0
- package/server/services/workshop/db/ops.repo.ts +323 -0
- package/server/services/workshop/db/schema.sql +143 -0
- package/server/services/workshop/db/subscription.repo.ts +62 -0
- package/server/services/workshop/db/task.repo.ts +175 -0
- package/server/services/workshop/db/team-member.repo.ts +70 -0
- package/server/services/workshop/db/team.repo.ts +109 -0
- package/server/services/workshop/db/user.repo.ts +100 -0
- package/server/services/workshop/dcw/dcw-controller.ts +841 -0
- package/server/services/workshop/dcw/dcw-line.repo.ts +86 -0
- package/server/services/workshop/dcw/dcw-node.repo.ts +89 -0
- package/server/services/workshop/dcw/dcw-node.ts +200 -0
- package/server/services/workshop/dcw/dcw-product.repo.ts +95 -0
- package/server/services/workshop/dcw/dcw-recipe.repo.ts +349 -0
- package/server/services/workshop/dcw/dcw-runtime.ts +82 -0
- package/server/services/workshop/dcw/dcw-templates.ts +138 -0
- package/server/services/workshop/dcw/drivers.ts +523 -0
- package/server/services/workshop/dcw/line-run.ts +101 -0
- package/server/services/workshop/dcw/recipe-rollback-manager.ts +702 -0
- package/server/services/workshop/dcw/recipe-rollback.repo.ts +197 -0
- package/server/services/workshop/logger.ts +58 -0
- package/server/services/workshop/ops/ops.ts +78 -0
- package/server/services/workshop/prompts/loader.ts +112 -0
- package/server/services/workshop/runtime/agent-runtime.ts +698 -0
- package/server/services/workshop/runtime/channel-runtime.ts +202 -0
- package/server/services/workshop/runtime/embedding-provider.ts +79 -0
- package/server/services/workshop/runtime/execution-mode.ts +215 -0
- package/server/services/workshop/runtime/mailbox.ts +148 -0
- package/server/services/workshop/runtime/manager.ts +2543 -0
- package/server/services/workshop/runtime/memory.ts +439 -0
- package/server/services/workshop/runtime/monitor.ts +194 -0
- package/server/services/workshop/runtime/scheduler-loop.ts +773 -0
- package/server/services/workshop/runtime/task-engine.ts +574 -0
- package/server/services/workshop/scene/scene-layout.repo.ts +112 -0
- package/server/services/workshop/scene-events.ts +63 -0
- package/server/services/workshop/types/a2a.ts +61 -0
- package/server/services/workshop/types/task.ts +85 -0
- package/server/types/user.ts +53 -0
- package/server/utils/approval-gate.ts +67 -0
- package/server/utils/auth.ts +13 -0
- package/server/utils/config.ts +40 -0
- package/server/utils/errors.ts +24 -0
- package/server/utils/response.ts +49 -0
- package/server/utils/validate.ts +19 -0
- package/shared/config/engine.d.mts +41 -0
- package/shared/config/engine.mjs +291 -0
- package/shared/config/home.d.mts +48 -0
- package/shared/config/home.mjs +146 -0
- package/shared/config/schema.json +30 -0
- package/shared/daq-protocol.ts +361 -0
- package/shared/dcw-protocol.ts +576 -0
- package/shared/terminal-protocol.ts +70 -0
- package/shared/town-anim.ts +56 -0
- package/shared/town-behavior.ts +136 -0
- package/shared/town-protocol.ts +110 -0
- package/shared/town-scene-math.ts +283 -0
- package/shared/workshop-protocol.ts +199 -0
- package/tsconfig.json +10 -0
- package/uno.config.ts +37 -0
|
@@ -0,0 +1,2259 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onUnmounted, reactive, ref } from 'vue'
|
|
3
|
+
import { useRoute } from 'vue-router'
|
|
4
|
+
import { DCW_DRIVERS, type DcwParamLedger, type DcwTemplateIcon, type LineQueryResult, type RecipeRunData } from '#shared/dcw-protocol'
|
|
5
|
+
import type { DriverConfigField } from '#shared/daq-protocol'
|
|
6
|
+
import { useDcwStream } from '~/composables/workshop/useDcwStream'
|
|
7
|
+
import { useDaqStream } from '~/composables/workshop/useDaqStream'
|
|
8
|
+
import { useDeviceTwins } from '~/composables/workshop/useDeviceTwins'
|
|
9
|
+
|
|
10
|
+
const { t } = useI18n()
|
|
11
|
+
|
|
12
|
+
const route = useRoute()
|
|
13
|
+
const dcw = useDcwStream()
|
|
14
|
+
const daq = useDaqStream()
|
|
15
|
+
const deviceTwins = useDeviceTwins()
|
|
16
|
+
void daq.load()
|
|
17
|
+
|
|
18
|
+
/** 路由产线 id;总览页「产线管理」进入 */
|
|
19
|
+
const lineId = computed(() => String(route.params.id ?? ''))
|
|
20
|
+
const line = computed(() => dcw.lines.find(l => l.id === lineId.value))
|
|
21
|
+
const ls = computed(() => dcw.lineStateOf(lineId.value))
|
|
22
|
+
|
|
23
|
+
void dcw.load()
|
|
24
|
+
/** WS 喂帧:节点变更(dcw.node.changed)/写 ACK 实时收敛 —— 本页状态同步的通道 */
|
|
25
|
+
const unsubDcw = dcw.ensureWsFeed()
|
|
26
|
+
onUnmounted(() => unsubDcw())
|
|
27
|
+
|
|
28
|
+
const deviceName = (id: string | null): string =>
|
|
29
|
+
id ? (deviceTwins.twins.find(t => t.id === id)?.name ?? id) : t('dcwDetail.k3own4q121')
|
|
30
|
+
|
|
31
|
+
const stateLabel: Record<string, string> = {
|
|
32
|
+
idle: t('dcwDetail.k3zgkk122'), writing: t('dcwDetail.k3l3h80123'), ok: '已 ACK', error: t('dcwDetail.k40reu124'), offline: t('dcwDetail.k44c2n125'),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function dcwTemplateRefCh(templateRef?: string): string {
|
|
36
|
+
const ref = templateRef ?? ''
|
|
37
|
+
const tpl = dcw.templates.find(t => t.key === (ref.startsWith('dcw-') ? ref.slice(4) : ref))
|
|
38
|
+
return tpl?.ch ?? ref
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 配方参数窗口提示:目标节点的全局工艺量程 */
|
|
42
|
+
function nodeMin(nodeId: string): number | undefined {
|
|
43
|
+
return dcw.nodeById(nodeId)?.min
|
|
44
|
+
}
|
|
45
|
+
function nodeMax(nodeId: string): number | undefined {
|
|
46
|
+
return dcw.nodeById(nodeId)?.max
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 本产线数采节点(配方监控窗口的可选目标) */
|
|
50
|
+
const lineDaqNodes = computed(() => daq.nodes.filter(n => n.lineId === lineId.value))
|
|
51
|
+
|
|
52
|
+
// ---------- 参数台账(调控闭环:三值对照 + 设定历史 + 优化记录) ----------
|
|
53
|
+
const ledgerNodeId = ref('')
|
|
54
|
+
const ledger = ref<DcwParamLedger | null>(null)
|
|
55
|
+
const ledgerLoading = ref(false)
|
|
56
|
+
async function loadLedger(): Promise<void> {
|
|
57
|
+
if (!ledgerNodeId.value) return
|
|
58
|
+
ledgerLoading.value = true
|
|
59
|
+
try {
|
|
60
|
+
ledger.value = await dcw.fetchLedger(ledgerNodeId.value)
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
ledgerLoading.value = false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** 单步回退(经 write() 门控;回退后刷新台账) */
|
|
67
|
+
async function rollbackLedgerNode(): Promise<void> {
|
|
68
|
+
if (!ledgerNodeId.value) return
|
|
69
|
+
await dcw.rollbackNode(ledgerNodeId.value)
|
|
70
|
+
await loadLedger()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 监控窗口 chip 显示:数采节点参数语义 */
|
|
74
|
+
function daqNodeCh(nodeId: string): string {
|
|
75
|
+
return daq.nodeById(nodeId)?.name ?? nodeId
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------- 产线作用域数据(仅本产线 + 未分配收编) ----------
|
|
79
|
+
/** 本产线节点(直写表) */
|
|
80
|
+
const lineNodes = computed(() => dcw.nodes.filter(n => n.lineId === lineId.value))
|
|
81
|
+
/** 本产线产品 */
|
|
82
|
+
const lineProducts = computed(() => dcw.products.filter(p => p.lineId === lineId.value))
|
|
83
|
+
/** 本产线配方 */
|
|
84
|
+
const lineRecipesAll = computed(() => dcw.recipes.filter(r => r.lineId === lineId.value))
|
|
85
|
+
/** 本产线批次 */
|
|
86
|
+
const lineRuns = computed(() => dcw.runs.filter(r => r.lineId === lineId.value))
|
|
87
|
+
/** 本产线写历史(按本产线节点过滤) */
|
|
88
|
+
const lineHistory = computed(() => {
|
|
89
|
+
const ids = new Set(lineNodes.value.map(n => n.id))
|
|
90
|
+
return dcw.history.filter(h => ids.has(h.nodeId))
|
|
91
|
+
})
|
|
92
|
+
/** 未分配节点/产品(可收编进本产线) */
|
|
93
|
+
const unassignedNodes = computed(() => dcw.nodes.filter(n => !n.lineId))
|
|
94
|
+
const unassignedProducts = computed(() => dcw.products.filter(p => !p.lineId))
|
|
95
|
+
|
|
96
|
+
async function adoptNode(id: string): Promise<void> {
|
|
97
|
+
await dcw.patchNode(id, { lineId: lineId.value })
|
|
98
|
+
const n = dcw.nodeById(id)
|
|
99
|
+
if (n) n.lineId = lineId.value
|
|
100
|
+
}
|
|
101
|
+
async function adoptProduct(id: string): Promise<void> {
|
|
102
|
+
await dcw.updateProduct(id, { lineId: lineId.value })
|
|
103
|
+
await dcw.load()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------- 直写 ----------
|
|
107
|
+
const setInputs = reactive<Record<string, number | ''>>({})
|
|
108
|
+
const writingId = ref('')
|
|
109
|
+
const writeError = ref('')
|
|
110
|
+
const writeOk = ref('')
|
|
111
|
+
|
|
112
|
+
async function doWrite(nodeId: string, value: number): Promise<void> {
|
|
113
|
+
writingId.value = nodeId
|
|
114
|
+
writeError.value = ''
|
|
115
|
+
writeOk.value = ''
|
|
116
|
+
try {
|
|
117
|
+
const outcome = await dcw.write(nodeId, value)
|
|
118
|
+
if (outcome.ok) {
|
|
119
|
+
writeOk.value = t('dcwDetail.k1alcbyu182', { p0: dcw.nodeById(nodeId)?.name ?? nodeId, p1: outcome.message })
|
|
120
|
+
setInputs[nodeId] = ''
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
writeError.value = t('dcwDetail.kl1e9x9183', { p0: dcw.nodeById(nodeId)?.name ?? nodeId, p1: outcome.message })
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
writeError.value = err instanceof Error ? err.message : String(err)
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
writingId.value = ''
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---------- 逐节点 控制开启/暂停 ----------
|
|
135
|
+
const togglingId = ref('')
|
|
136
|
+
/** 开启/暂停单个节点的控制:暂停后服务端拒绝一切下发(409 当前节点暂停),本地即时收敛状态 */
|
|
137
|
+
async function toggleControl(nodeId: string, enabled: boolean): Promise<void> {
|
|
138
|
+
togglingId.value = nodeId
|
|
139
|
+
writeError.value = ''
|
|
140
|
+
try {
|
|
141
|
+
await dcw.patchNode(nodeId, { enabled })
|
|
142
|
+
const n = dcw.nodeById(nodeId)
|
|
143
|
+
if (n) {
|
|
144
|
+
n.enabled = enabled
|
|
145
|
+
if (!enabled) n.state = 'offline'
|
|
146
|
+
else if (n.state === 'offline') n.state = 'idle'
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
writeError.value = err instanceof Error ? err.message : String(err)
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
togglingId.value = ''
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------- 添加控制节点向导 ----------
|
|
158
|
+
const addOpen = ref(false)
|
|
159
|
+
const addScenario = ref<'mock' | 'real'>('mock')
|
|
160
|
+
const addTemplate = ref(dcw.templates[0]?.key ?? '')
|
|
161
|
+
const addDriver = ref<'mock' | 'modbus-tcp' | 'opcua'>('mock')
|
|
162
|
+
const addName = ref('')
|
|
163
|
+
const addHold = ref<number | null>(null)
|
|
164
|
+
const addCfg = ref<Record<string, string | number>>({})
|
|
165
|
+
const addTransform = reactive({ kind: 'none' as 'none' | 'linear', scale: 1, offset: 0 })
|
|
166
|
+
const addSemantics = ref('')
|
|
167
|
+
const addTesting = ref(false)
|
|
168
|
+
const addTest = ref<{ ok: boolean, message: string } | null>(null)
|
|
169
|
+
const addSaving = ref(false)
|
|
170
|
+
const addError = ref('')
|
|
171
|
+
|
|
172
|
+
const addDriverMeta = computed(() => DCW_DRIVERS.find(d => d.kind === addDriver.value))
|
|
173
|
+
const addFields = computed<DriverConfigField[]>(() => addDriverMeta.value?.configFields ?? [])
|
|
174
|
+
|
|
175
|
+
function resetAddCfg(): void {
|
|
176
|
+
const cfg: Record<string, string | number> = {}
|
|
177
|
+
for (const f of addFields.value) {
|
|
178
|
+
if (f.default !== undefined) cfg[f.key] = f.default
|
|
179
|
+
}
|
|
180
|
+
addCfg.value = cfg
|
|
181
|
+
addTest.value = null
|
|
182
|
+
}
|
|
183
|
+
void resetAddCfg()
|
|
184
|
+
|
|
185
|
+
async function doTestConnection(): Promise<void> {
|
|
186
|
+
addTesting.value = true
|
|
187
|
+
addTest.value = null
|
|
188
|
+
try {
|
|
189
|
+
addTest.value = await dcw.testDriver(addDriver.value, addCfg.value)
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
addTest.value = { ok: false, message: err instanceof Error ? err.message : String(err) }
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
addTesting.value = false
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function doAddNode(): Promise<void> {
|
|
200
|
+
addSaving.value = true
|
|
201
|
+
addError.value = ''
|
|
202
|
+
try {
|
|
203
|
+
for (const f of addFields.value) {
|
|
204
|
+
if (f.required && (addCfg.value[f.key] === undefined || addCfg.value[f.key] === '')) {
|
|
205
|
+
throw new Error(t('dcwDetail.k8x1un1184', { p0: f.label }))
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const transform = addTransform.kind === 'linear'
|
|
209
|
+
? { kind: 'linear' as const, scale: Number(addTransform.scale), offset: Number(addTransform.offset) }
|
|
210
|
+
: undefined
|
|
211
|
+
await dcw.createFromTemplate(`dcw-${addTemplate.value}`, {
|
|
212
|
+
name: addName.value.trim() || undefined,
|
|
213
|
+
driver: addDriver.value,
|
|
214
|
+
driverConfig: { ...addCfg.value },
|
|
215
|
+
transform,
|
|
216
|
+
holdIntervalMs: addHold.value,
|
|
217
|
+
lineId: lineId.value,
|
|
218
|
+
semantics: addSemantics.value.trim() || undefined,
|
|
219
|
+
})
|
|
220
|
+
addOpen.value = false
|
|
221
|
+
addName.value = ''
|
|
222
|
+
addSemantics.value = ''
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
addError.value = err instanceof Error ? err.message : String(err)
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
addSaving.value = false
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------- 添加控制节点模板(自定义创建) ----------
|
|
233
|
+
const tplOpen = ref(false)
|
|
234
|
+
const tplSaving = ref(false)
|
|
235
|
+
const tplError = ref('')
|
|
236
|
+
const tplOk = ref('')
|
|
237
|
+
|
|
238
|
+
function openTplModal(): void {
|
|
239
|
+
tplOpen.value = true
|
|
240
|
+
tplError.value = ''
|
|
241
|
+
tplOk.value = ''
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const tplForm = reactive({
|
|
245
|
+
name: '', ch: '', code: '', unit: '', min: '' as number | '', max: '' as number | '',
|
|
246
|
+
decimals: 1, icon: 'gateway' as DcwTemplateIcon, semantics: '',
|
|
247
|
+
})
|
|
248
|
+
const tplIcons: Array<{ key: DcwTemplateIcon, label: string }> = [
|
|
249
|
+
{ key: 'thermo', label: t('dcwDetail.k422b8126') },
|
|
250
|
+
{ key: 'pressure', label: t('dcwDetail.k3x6ff127') },
|
|
251
|
+
{ key: 'tension', label: t('dcwDetail.k3z9xc128') },
|
|
252
|
+
{ key: 'encoder', label: t('dcwDetail.kjb3vhs129') },
|
|
253
|
+
{ key: 'camera', label: t('dcwDetail.k47atw130') },
|
|
254
|
+
{ key: 'gateway', label: t('dcwDetail.k48c07131') },
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
async function doCreateTemplate(): Promise<void> {
|
|
258
|
+
tplSaving.value = true
|
|
259
|
+
tplError.value = ''
|
|
260
|
+
tplOk.value = ''
|
|
261
|
+
try {
|
|
262
|
+
if (!tplForm.name.trim()) throw new Error(t('dcwDetail.k6ugbw2132'))
|
|
263
|
+
if (tplForm.unit.trim() === '') throw new Error(t('dcwDetail.kx3pg59133'))
|
|
264
|
+
if (tplForm.min === '' || tplForm.max === '') throw new Error(t('dcwDetail.k13awowo134'))
|
|
265
|
+
const tpl = await dcw.createTemplate({
|
|
266
|
+
name: tplForm.name.trim(),
|
|
267
|
+
ch: tplForm.ch.trim() || tplForm.name.trim(),
|
|
268
|
+
code: tplForm.code.trim() || 'CUSTOM',
|
|
269
|
+
unit: tplForm.unit.trim(),
|
|
270
|
+
min: Number(tplForm.min),
|
|
271
|
+
max: Number(tplForm.max),
|
|
272
|
+
decimals: Number(tplForm.decimals) || 0,
|
|
273
|
+
icon: tplForm.icon,
|
|
274
|
+
semantics: tplForm.semantics.trim() || undefined,
|
|
275
|
+
})
|
|
276
|
+
// 新模板即刻可选:添加控制节点向导自动选中它,下拉随 store 响应式更新
|
|
277
|
+
addTemplate.value = tpl.key
|
|
278
|
+
tplOk.value = t('dcwDetail.ky4e1tr185', { p0: tpl.name })
|
|
279
|
+
tplForm.name = ''
|
|
280
|
+
tplForm.ch = ''
|
|
281
|
+
tplForm.code = ''
|
|
282
|
+
tplForm.unit = ''
|
|
283
|
+
tplForm.min = ''
|
|
284
|
+
tplForm.max = ''
|
|
285
|
+
tplForm.semantics = ''
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
tplError.value = err instanceof Error ? err.message : String(err)
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
tplSaving.value = false
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ---------- 产线开跑/停止(逐产线) ----------
|
|
296
|
+
const lineProductId = ref('')
|
|
297
|
+
const lineRecipeId = ref('')
|
|
298
|
+
const lineBusy = ref(false)
|
|
299
|
+
const lineMsg = ref('')
|
|
300
|
+
const lineErr = ref('')
|
|
301
|
+
|
|
302
|
+
const lineRecipes = computed(() => lineRecipesAll.value.filter(r => r.productId === lineProductId.value))
|
|
303
|
+
|
|
304
|
+
async function doLineStart(): Promise<void> {
|
|
305
|
+
lineBusy.value = true
|
|
306
|
+
lineMsg.value = ''
|
|
307
|
+
lineErr.value = ''
|
|
308
|
+
try {
|
|
309
|
+
if (!lineRecipeId.value) {
|
|
310
|
+
throw new Error(t('dcwDetail.k1b6qe0r135'))
|
|
311
|
+
}
|
|
312
|
+
await dcw.startLine(lineId.value, lineRecipeId.value)
|
|
313
|
+
lineMsg.value = t('dcwDetail.k17jteb9186', { p0: ls.value.productName, p1: ls.value.recipeName, p2: ls.value.runId })
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
lineErr.value = err instanceof Error ? err.message : String(err)
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
lineBusy.value = false
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function doLineStop(): Promise<void> {
|
|
324
|
+
lineBusy.value = true
|
|
325
|
+
lineMsg.value = ''
|
|
326
|
+
lineErr.value = ''
|
|
327
|
+
try {
|
|
328
|
+
const was = `${ls.value.productName ?? ''} · ${ls.value.recipeName ?? ''}`
|
|
329
|
+
await dcw.stopLine(lineId.value)
|
|
330
|
+
lineMsg.value = t('dcwDetail.kzl49pd187', { p0: was })
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
lineErr.value = err instanceof Error ? err.message : String(err)
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
lineBusy.value = false
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ---------- 产品管理 ----------
|
|
341
|
+
const productOpen = ref(false)
|
|
342
|
+
const productSaving = ref(false)
|
|
343
|
+
const productError = ref('')
|
|
344
|
+
const productForm = reactive({ name: '', description: '' })
|
|
345
|
+
const filterProductId = ref('')
|
|
346
|
+
|
|
347
|
+
async function doCreateProduct(): Promise<void> {
|
|
348
|
+
productSaving.value = true
|
|
349
|
+
productError.value = ''
|
|
350
|
+
try {
|
|
351
|
+
const p = await dcw.createProduct({ name: productForm.name.trim(), description: productForm.description.trim(), lineId: lineId.value })
|
|
352
|
+
lineProductId.value = p.id
|
|
353
|
+
filterProductId.value = p.id
|
|
354
|
+
productOpen.value = false
|
|
355
|
+
productForm.name = ''
|
|
356
|
+
productForm.description = ''
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
productError.value = err instanceof Error ? err.message : String(err)
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
productSaving.value = false
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const visibleRecipes = computed(() =>
|
|
367
|
+
filterProductId.value
|
|
368
|
+
? lineRecipesAll.value.filter(r => r.productId === filterProductId.value)
|
|
369
|
+
: lineRecipesAll.value,
|
|
370
|
+
)
|
|
371
|
+
const productName = (id: string): string => dcw.products.find(p => p.id === id)?.name ?? id
|
|
372
|
+
|
|
373
|
+
// ---------- 产线数据查询(产品/配方/参数/时间/间隔;限定本产线通道) ----------
|
|
374
|
+
const query = reactive({
|
|
375
|
+
productId: '',
|
|
376
|
+
recipeId: '',
|
|
377
|
+
paramKey: '',
|
|
378
|
+
nodeId: '',
|
|
379
|
+
lastMin: 30,
|
|
380
|
+
bucketMs: 0,
|
|
381
|
+
})
|
|
382
|
+
const queryResult = ref<LineQueryResult | null>(null)
|
|
383
|
+
const queryBusy = ref(false)
|
|
384
|
+
const queryError = ref('')
|
|
385
|
+
|
|
386
|
+
async function doQuery(): Promise<void> {
|
|
387
|
+
queryBusy.value = true
|
|
388
|
+
queryError.value = ''
|
|
389
|
+
queryResult.value = null
|
|
390
|
+
try {
|
|
391
|
+
queryResult.value = await dcw.queryLine({
|
|
392
|
+
lineId: lineId.value,
|
|
393
|
+
productId: query.productId || undefined,
|
|
394
|
+
recipeId: query.recipeId || undefined,
|
|
395
|
+
paramKey: query.paramKey || undefined,
|
|
396
|
+
nodeId: query.nodeId || undefined,
|
|
397
|
+
fromMs: Date.now() - query.lastMin * 60_000,
|
|
398
|
+
toMs: Date.now(),
|
|
399
|
+
bucketMs: query.bucketMs > 0 ? query.bucketMs : undefined,
|
|
400
|
+
limit: 2000,
|
|
401
|
+
})
|
|
402
|
+
if (queryResult.value.channels.length === 0) queryError.value = t('dcwDetail.kszv5sq136')
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
queryError.value = err instanceof Error ? err.message : String(err)
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
queryBusy.value = false
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** 查询参数选项(DAQ 模板 key,含自定义) */
|
|
413
|
+
const daqParamKeys = computed(() => dcw.templates.map(t => t.key))
|
|
414
|
+
|
|
415
|
+
// ---------- Recipe 配方管理(参数节点级绑定) ----------
|
|
416
|
+
const recipeOpen = ref(false)
|
|
417
|
+
const recipeEditing = ref<string | null>(null)
|
|
418
|
+
const recipeSaving = ref(false)
|
|
419
|
+
const recipeError = ref('')
|
|
420
|
+
const recipeForm = reactive({
|
|
421
|
+
productId: '',
|
|
422
|
+
name: '',
|
|
423
|
+
description: '',
|
|
424
|
+
params: [] as Array<{ nodeId: string, value: number | '', min: number | '', max: number | '' }>,
|
|
425
|
+
daqWindows: [] as Array<{ nodeId: string, min: number | '', max: number | '' }>,
|
|
426
|
+
})
|
|
427
|
+
const applyResult = ref<{ runId: string, ok: number, total: number } | null>(null)
|
|
428
|
+
const runDataView = ref<{ runId: string, data: RecipeRunData } | null>(null)
|
|
429
|
+
const runDataLoading = ref(false)
|
|
430
|
+
|
|
431
|
+
function openRecipeCreate(): void {
|
|
432
|
+
recipeEditing.value = null
|
|
433
|
+
recipeError.value = ''
|
|
434
|
+
recipeForm.productId = filterProductId.value || lineProductId.value || lineProducts.value[0]?.id || ''
|
|
435
|
+
recipeForm.name = ''
|
|
436
|
+
recipeForm.description = ''
|
|
437
|
+
recipeForm.params = [{ nodeId: lineNodes.value[0]?.id ?? '', value: '', min: '', max: '' }]
|
|
438
|
+
recipeForm.daqWindows = []
|
|
439
|
+
recipeOpen.value = true
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function openRecipeEdit(id: string): void {
|
|
443
|
+
const r = dcw.recipes.find(x => x.id === id)
|
|
444
|
+
if (!r) return
|
|
445
|
+
recipeEditing.value = id
|
|
446
|
+
recipeError.value = ''
|
|
447
|
+
recipeForm.productId = r.productId
|
|
448
|
+
recipeForm.name = r.name
|
|
449
|
+
recipeForm.description = r.description
|
|
450
|
+
recipeForm.params = r.params.map(p => ({
|
|
451
|
+
nodeId: p.nodeId,
|
|
452
|
+
value: p.value,
|
|
453
|
+
min: p.min ?? '',
|
|
454
|
+
max: p.max ?? '',
|
|
455
|
+
}))
|
|
456
|
+
recipeForm.daqWindows = (r.daqWindows ?? []).map(w => ({
|
|
457
|
+
nodeId: w.nodeId,
|
|
458
|
+
min: w.min ?? '',
|
|
459
|
+
max: w.max ?? '',
|
|
460
|
+
}))
|
|
461
|
+
recipeOpen.value = true
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function saveRecipe(): Promise<void> {
|
|
465
|
+
recipeSaving.value = true
|
|
466
|
+
recipeError.value = ''
|
|
467
|
+
try {
|
|
468
|
+
const input = {
|
|
469
|
+
productId: recipeForm.productId,
|
|
470
|
+
name: recipeForm.name.trim(),
|
|
471
|
+
description: recipeForm.description.trim(),
|
|
472
|
+
params: recipeForm.params
|
|
473
|
+
.filter(p => p.nodeId && p.value !== '' && Number.isFinite(Number(p.value)))
|
|
474
|
+
.map(p => ({
|
|
475
|
+
nodeId: p.nodeId,
|
|
476
|
+
value: Number(p.value),
|
|
477
|
+
...(p.min !== '' && Number.isFinite(Number(p.min)) ? { min: Number(p.min) } : {}),
|
|
478
|
+
...(p.max !== '' && Number.isFinite(Number(p.max)) ? { max: Number(p.max) } : {}),
|
|
479
|
+
})),
|
|
480
|
+
daqWindows: recipeForm.daqWindows
|
|
481
|
+
.filter(w => w.nodeId && (w.min !== '' || w.max !== ''))
|
|
482
|
+
.map(w => ({
|
|
483
|
+
nodeId: w.nodeId,
|
|
484
|
+
...(w.min !== '' && Number.isFinite(Number(w.min)) ? { min: Number(w.min) } : {}),
|
|
485
|
+
...(w.max !== '' && Number.isFinite(Number(w.max)) ? { max: Number(w.max) } : {}),
|
|
486
|
+
})),
|
|
487
|
+
}
|
|
488
|
+
if (recipeEditing.value) await dcw.updateRecipe(recipeEditing.value, input)
|
|
489
|
+
else await dcw.createRecipe(input)
|
|
490
|
+
recipeOpen.value = false
|
|
491
|
+
}
|
|
492
|
+
catch (err) {
|
|
493
|
+
recipeError.value = err instanceof Error ? err.message : String(err)
|
|
494
|
+
}
|
|
495
|
+
finally {
|
|
496
|
+
recipeSaving.value = false
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
async function doApplyRecipe(id: string): Promise<void> {
|
|
501
|
+
applyResult.value = null
|
|
502
|
+
writeError.value = ''
|
|
503
|
+
try {
|
|
504
|
+
const run = await dcw.applyRecipe(id)
|
|
505
|
+
const ok = run.results.filter(r => r.ok).length
|
|
506
|
+
applyResult.value = { runId: run.id, ok, total: run.results.length }
|
|
507
|
+
if (ok < run.results.length) writeError.value = t('dcwDetail.ks7szkt188', { p0: run.results.filter(r => !r.ok).map(r => r.message).join(';') })
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
writeError.value = err instanceof Error ? err.message : String(err)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async function doViewRun(id: string): Promise<void> {
|
|
515
|
+
runDataLoading.value = true
|
|
516
|
+
try {
|
|
517
|
+
const data = await dcw.runData(id)
|
|
518
|
+
runDataView.value = { runId: id, data }
|
|
519
|
+
}
|
|
520
|
+
catch (err) {
|
|
521
|
+
writeError.value = err instanceof Error ? err.message : String(err)
|
|
522
|
+
}
|
|
523
|
+
finally {
|
|
524
|
+
runDataLoading.value = false
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** 查询点位展示(原始值或桶均值) */
|
|
529
|
+
function fmtPoint(p: { value?: number, avg?: number } | undefined): string {
|
|
530
|
+
const v = p?.value ?? p?.avg
|
|
531
|
+
return v == null ? '--' : String(Math.round(v * 1000) / 1000)
|
|
532
|
+
}
|
|
533
|
+
</script>
|
|
534
|
+
|
|
535
|
+
<template>
|
|
536
|
+
<div
|
|
537
|
+
v-if="dcw.loaded && !line"
|
|
538
|
+
class="page"
|
|
539
|
+
>
|
|
540
|
+
<p class="banner bad">
|
|
541
|
+
{{ $t('dcwDetail.k19337yp018') }}
|
|
542
|
+
</p>
|
|
543
|
+
<NuxtLink
|
|
544
|
+
class="pill-btn"
|
|
545
|
+
to="/dcw"
|
|
546
|
+
>
|
|
547
|
+
{{ $t('dcwDetail.kllwu1c019') }}
|
|
548
|
+
</NuxtLink>
|
|
549
|
+
</div>
|
|
550
|
+
<div
|
|
551
|
+
v-else
|
|
552
|
+
class="page"
|
|
553
|
+
>
|
|
554
|
+
<div class="aw-page-head">
|
|
555
|
+
<div>
|
|
556
|
+
<p class="aw-kicker">
|
|
557
|
+
<span
|
|
558
|
+
class="line-chip"
|
|
559
|
+
:style="{ background: line?.color ?? '#3aa0ff' }"
|
|
560
|
+
/>AGENTWORKSHOP / LINE / {{ line?.name ?? lineId }}
|
|
561
|
+
</p>
|
|
562
|
+
<h1>{{ line?.name ?? $t('dcwDetail.k1b2snna151') }} · {{ $t('dcwDetail.k1l0iow1137') }}</h1>
|
|
563
|
+
<p class="sub">
|
|
564
|
+
{{ line?.description || $t('dcwDetail.kzcbfki152') }}
|
|
565
|
+
</p>
|
|
566
|
+
</div>
|
|
567
|
+
<div class="badges mono">
|
|
568
|
+
<span class="badge">{{ $t('dcwDetail.k45uio082') }} {{ lineNodes.length }}</span>
|
|
569
|
+
<span class="badge">{{ $t('dcwDetail.k3waz1025') }} {{ lineProducts.length }}</span>
|
|
570
|
+
<span class="badge">{{ $t('dcwDetail.k48grv027') }} {{ lineRecipesAll.length }}</span>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
|
|
574
|
+
<!-- 网关总控条 -->
|
|
575
|
+
<section class="aw-tile ctrl-card">
|
|
576
|
+
<div class="ctrl-left">
|
|
577
|
+
<button
|
|
578
|
+
class="aw-pill"
|
|
579
|
+
:class="{ running: dcw.controller.running }"
|
|
580
|
+
:title="$t('dcwDetail.k1snap154')"
|
|
581
|
+
@click="dcw.startStop(dcw.controller.running ? 'pause' : 'resume')"
|
|
582
|
+
>
|
|
583
|
+
<span :class="dcw.controller.running ? 'i-tabler-player-pause' : 'i-tabler-player-play'" />
|
|
584
|
+
{{ dcw.controller.running ? $t('dcwDetail.kplrsg153') : $t('dcwDetail.knxgni9169') }}
|
|
585
|
+
</button>
|
|
586
|
+
</div>
|
|
587
|
+
<div class="ctrl-right">
|
|
588
|
+
<span class="ctrl-metrics mono">
|
|
589
|
+
<span>{{ $t('dcwDetail.k45uio082') }} {{ dcw.controller.nodesOnline }}/{{ dcw.controller.nodesTotal }}</span>
|
|
590
|
+
<span class="sep">·</span>
|
|
591
|
+
<span :title="$t('dcwDetail.kd3ygmn001')">{{ $t('dcwDetail.k3wtib138') }} {{ dcw.controller.writesTotal }}</span>
|
|
592
|
+
<span class="sep">·</span>
|
|
593
|
+
<span
|
|
594
|
+
:class="{ warn: dcw.controller.writesFailed > 0 }"
|
|
595
|
+
:title="$t('dcwDetail.kd3znl0002')"
|
|
596
|
+
>{{ $t('dcwDetail.k3yit7139') }} {{ dcw.controller.writesFailed }}</span>
|
|
597
|
+
</span>
|
|
598
|
+
<button
|
|
599
|
+
class="aw-pill outline"
|
|
600
|
+
:title="$t('dcwDetail.kc4ixly003')"
|
|
601
|
+
@click="openTplModal"
|
|
602
|
+
>
|
|
603
|
+
<span class="i-tabler-template" />
|
|
604
|
+
{{ $t('dcwDetail.k1972dx9020') }}
|
|
605
|
+
</button>
|
|
606
|
+
<button
|
|
607
|
+
class="aw-pill add-btn"
|
|
608
|
+
@click="addOpen = true"
|
|
609
|
+
>
|
|
610
|
+
<span class="i-tabler-plus" />
|
|
611
|
+
{{ $t('dcwDetail.k1976uns021') }}
|
|
612
|
+
</button>
|
|
613
|
+
</div>
|
|
614
|
+
</section>
|
|
615
|
+
|
|
616
|
+
<!-- 写错误/成功横幅 -->
|
|
617
|
+
<p
|
|
618
|
+
v-if="writeError || writeOk"
|
|
619
|
+
class="banner"
|
|
620
|
+
:class="writeError ? 'bad' : 'good'"
|
|
621
|
+
>
|
|
622
|
+
{{ writeError || writeOk }}
|
|
623
|
+
</p>
|
|
624
|
+
|
|
625
|
+
<!-- 未分配资产收编(历史节点/产品迁移到本产线) -->
|
|
626
|
+
<section
|
|
627
|
+
v-if="unassignedNodes.length || unassignedProducts.length"
|
|
628
|
+
class="aw-tile adopt-card"
|
|
629
|
+
>
|
|
630
|
+
<b class="adopt-title">{{ $t('dcwDetail.kaebkky022') }}</b>
|
|
631
|
+
<div class="adopt-list">
|
|
632
|
+
<span
|
|
633
|
+
v-for="n in unassignedNodes"
|
|
634
|
+
:key="n.id"
|
|
635
|
+
class="adopt-chip"
|
|
636
|
+
>
|
|
637
|
+
{{ n.name }}
|
|
638
|
+
<button
|
|
639
|
+
class="mini-btn"
|
|
640
|
+
@click="adoptNode(n.id)"
|
|
641
|
+
>
|
|
642
|
+
{{ $t('dcwDetail.kyjp9fg023') }}
|
|
643
|
+
</button>
|
|
644
|
+
</span>
|
|
645
|
+
<span
|
|
646
|
+
v-for="p in unassignedProducts"
|
|
647
|
+
:key="p.id"
|
|
648
|
+
class="adopt-chip"
|
|
649
|
+
>
|
|
650
|
+
{{ p.name }}({{ $t('dcwDetail.k3km252140') }}<button
|
|
651
|
+
class="mini-btn"
|
|
652
|
+
@click="adoptProduct(p.id)"
|
|
653
|
+
>
|
|
654
|
+
{{ $t('dcwDetail.kyjp9fg023') }}
|
|
655
|
+
</button>
|
|
656
|
+
</span>
|
|
657
|
+
</div>
|
|
658
|
+
</section>
|
|
659
|
+
|
|
660
|
+
<!-- 产线运行控制:开跑必设配方;窗口内数采逐样本打标 -->
|
|
661
|
+
<section class="aw-tile line-card">
|
|
662
|
+
<div class="line-status">
|
|
663
|
+
<span
|
|
664
|
+
class="line-dot"
|
|
665
|
+
:class="{ on: ls.active }"
|
|
666
|
+
/>
|
|
667
|
+
<div class="line-info">
|
|
668
|
+
<b>{{ ls.active ? $t('dcwDetail.k1pxrhx0154') : $t('dcwDetail.k1b2hxmx170') }}</b>
|
|
669
|
+
<small
|
|
670
|
+
v-if="ls.active"
|
|
671
|
+
class="mono dim"
|
|
672
|
+
>{{ ls.productName }} · {{ ls.recipeName }} · {{ $t('dcwDetail.k400lb075') }} {{ ls.runId }} · {{ $t('dcwDetail.k3zkt2166') }} {{ ls.startedAt?.slice(11, 19) }} {{ $t('dcwDetail.k69vag8168') }} {{ ls.taggedSamples }} {{ $t('dcwDetail.k4118o085') }}</small>
|
|
673
|
+
<small
|
|
674
|
+
v-else
|
|
675
|
+
class="dim"
|
|
676
|
+
>{{ $t('dcwDetail.k19pof14024') }}</small>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
<div class="line-ctl">
|
|
680
|
+
<label class="ctl-sel">
|
|
681
|
+
<span>{{ $t('dcwDetail.k3waz1025') }}</span>
|
|
682
|
+
<select
|
|
683
|
+
v-model="lineProductId"
|
|
684
|
+
class="inp"
|
|
685
|
+
@change="lineRecipeId = ''"
|
|
686
|
+
>
|
|
687
|
+
<option value="">
|
|
688
|
+
{{ $t('dcwDetail.kuisodh026') }}
|
|
689
|
+
</option>
|
|
690
|
+
<option
|
|
691
|
+
v-for="p in lineProducts"
|
|
692
|
+
:key="p.id"
|
|
693
|
+
:value="p.id"
|
|
694
|
+
>
|
|
695
|
+
{{ p.name }}
|
|
696
|
+
</option>
|
|
697
|
+
</select>
|
|
698
|
+
</label>
|
|
699
|
+
<label class="ctl-sel">
|
|
700
|
+
<span>{{ $t('dcwDetail.k48grv027') }}</span>
|
|
701
|
+
<select
|
|
702
|
+
v-model="lineRecipeId"
|
|
703
|
+
class="inp"
|
|
704
|
+
:disabled="!lineProductId"
|
|
705
|
+
>
|
|
706
|
+
<option value="">
|
|
707
|
+
{{ $t('dcwDetail.kutxzsz028') }}
|
|
708
|
+
</option>
|
|
709
|
+
<option
|
|
710
|
+
v-for="r in lineRecipes"
|
|
711
|
+
:key="r.id"
|
|
712
|
+
:value="r.id"
|
|
713
|
+
:disabled="r.params.length === 0"
|
|
714
|
+
>
|
|
715
|
+
{{ r.name }}{{ r.params.length === 0 ? $t('dcwDetail.k1hicmch155') : $t('dcwDetail.k1lzfi4g180', { p0: r.params.length }) }}
|
|
716
|
+
</option>
|
|
717
|
+
</select>
|
|
718
|
+
</label>
|
|
719
|
+
<button
|
|
720
|
+
v-if="!ls.active"
|
|
721
|
+
class="pill-btn"
|
|
722
|
+
:disabled="lineBusy || !lineRecipeId"
|
|
723
|
+
:title="!lineRecipeId ? '开跑前必须先设定配方' : '下发配方参数并开始打标数据采集'"
|
|
724
|
+
@click="doLineStart"
|
|
725
|
+
>
|
|
726
|
+
{{ $t('dcwDetail.kfb8vml029') }}
|
|
727
|
+
</button>
|
|
728
|
+
<button
|
|
729
|
+
v-else
|
|
730
|
+
class="pill-btn stop"
|
|
731
|
+
:disabled="lineBusy"
|
|
732
|
+
@click="doLineStop"
|
|
733
|
+
>
|
|
734
|
+
{{ $t('dcwDetail.k1xyhd2y030') }}
|
|
735
|
+
</button>
|
|
736
|
+
</div>
|
|
737
|
+
<p
|
|
738
|
+
v-if="lineErr"
|
|
739
|
+
class="banner bad"
|
|
740
|
+
style="margin-top: 10px;"
|
|
741
|
+
>
|
|
742
|
+
{{ lineErr }}
|
|
743
|
+
</p>
|
|
744
|
+
<p
|
|
745
|
+
v-if="lineMsg"
|
|
746
|
+
class="banner good"
|
|
747
|
+
style="margin-top: 10px;"
|
|
748
|
+
>
|
|
749
|
+
{{ lineMsg }}
|
|
750
|
+
</p>
|
|
751
|
+
</section>
|
|
752
|
+
|
|
753
|
+
<!-- 添加控制节点模板(自定义创建) -->
|
|
754
|
+
<div
|
|
755
|
+
v-if="tplOpen"
|
|
756
|
+
class="modal-mask"
|
|
757
|
+
@click.self="tplOpen = false"
|
|
758
|
+
>
|
|
759
|
+
<div class="modal">
|
|
760
|
+
<h3 class="m-title">
|
|
761
|
+
{{ $t('dcwDetail.k1x4vam0031') }} <small class="dim mono">{{ $t('dcwDetail.k3x23c141') }} {{ dcw.templates.filter(t => t.builtin).length }} · {{ $t('dcwDetail.k3t616a142') }} {{ dcw.templates.filter(t => !t.builtin).length }}</small>
|
|
762
|
+
</h3>
|
|
763
|
+
<p class="dim tpl-hint">
|
|
764
|
+
{{ $t('dcwDetail.ke1iyud032') }}
|
|
765
|
+
</p>
|
|
766
|
+
|
|
767
|
+
<div class="tpl-chips">
|
|
768
|
+
<span
|
|
769
|
+
v-for="t in dcw.templates"
|
|
770
|
+
:key="t.key"
|
|
771
|
+
class="tpl-chip"
|
|
772
|
+
:title="`${t.code} · ${t.min}~${t.max} ${t.unit}${t.semantics ? ` · ${t.semantics}` : ''}`"
|
|
773
|
+
>
|
|
774
|
+
{{ t.name }} <small class="mono">{{ t.min }}~{{ t.max }}{{ t.unit }}</small>
|
|
775
|
+
<em
|
|
776
|
+
class="tpl-tag"
|
|
777
|
+
:class="{ builtin: t.builtin }"
|
|
778
|
+
>{{ t.builtin ? $t('dcwDetail.k3x23c141') : $t('dcwDetail.k3t616a142') }}</em>
|
|
779
|
+
</span>
|
|
780
|
+
</div>
|
|
781
|
+
|
|
782
|
+
<p class="sec-label">
|
|
783
|
+
{{ $t('dcwDetail.k169eb4s033') }}
|
|
784
|
+
</p>
|
|
785
|
+
<div class="f-grid tpl-form">
|
|
786
|
+
<label class="f">
|
|
787
|
+
<span>{{ $t('dcwDetail.k1f55q76034') }}<em>*</em></span>
|
|
788
|
+
<input
|
|
789
|
+
v-model="tplForm.name"
|
|
790
|
+
class="inp"
|
|
791
|
+
:placeholder="$t('dcwDetail.k1tooi3o004')"
|
|
792
|
+
>
|
|
793
|
+
</label>
|
|
794
|
+
<label class="f">
|
|
795
|
+
<span>{{ $t('dcwDetail.k1bqk219035') }}</span>
|
|
796
|
+
<input
|
|
797
|
+
v-model="tplForm.ch"
|
|
798
|
+
class="inp"
|
|
799
|
+
:placeholder="$t('dcwDetail.k698qz0005')"
|
|
800
|
+
>
|
|
801
|
+
</label>
|
|
802
|
+
<label class="f">
|
|
803
|
+
<span>{{ $t('dcwDetail.k1ayxrqb036') }}</span>
|
|
804
|
+
<input
|
|
805
|
+
v-model="tplForm.code"
|
|
806
|
+
class="inp"
|
|
807
|
+
placeholder="如 MOTOR · I"
|
|
808
|
+
>
|
|
809
|
+
</label>
|
|
810
|
+
<label class="f">
|
|
811
|
+
<span>{{ $t('dcwDetail.k3x4ef037') }}<em>*</em></span>
|
|
812
|
+
<input
|
|
813
|
+
v-model="tplForm.unit"
|
|
814
|
+
class="inp"
|
|
815
|
+
placeholder="如 A"
|
|
816
|
+
>
|
|
817
|
+
</label>
|
|
818
|
+
<label class="f">
|
|
819
|
+
<span>{{ $t('dcwDetail.k1l9jv5m038') }}<em>*</em></span>
|
|
820
|
+
<input
|
|
821
|
+
v-model.number="tplForm.min"
|
|
822
|
+
type="number"
|
|
823
|
+
class="inp"
|
|
824
|
+
:placeholder="$t('dcwDetail.kxz9174006')"
|
|
825
|
+
>
|
|
826
|
+
</label>
|
|
827
|
+
<label class="f">
|
|
828
|
+
<span>{{ $t('dcwDetail.k1l9jv4p039') }}<em>*</em></span>
|
|
829
|
+
<input
|
|
830
|
+
v-model.number="tplForm.max"
|
|
831
|
+
type="number"
|
|
832
|
+
class="inp"
|
|
833
|
+
:placeholder="$t('dcwDetail.kxz9167007')"
|
|
834
|
+
>
|
|
835
|
+
</label>
|
|
836
|
+
<label class="f">
|
|
837
|
+
<span>{{ $t('dcwDetail.k3mxmcx040') }}</span>
|
|
838
|
+
<input
|
|
839
|
+
v-model.number="tplForm.decimals"
|
|
840
|
+
type="number"
|
|
841
|
+
min="0"
|
|
842
|
+
max="4"
|
|
843
|
+
class="inp"
|
|
844
|
+
>
|
|
845
|
+
</label>
|
|
846
|
+
<label class="f">
|
|
847
|
+
<span>{{ $t('dcwDetail.k3xx56041') }}</span>
|
|
848
|
+
<select
|
|
849
|
+
v-model="tplForm.icon"
|
|
850
|
+
class="inp"
|
|
851
|
+
>
|
|
852
|
+
<option
|
|
853
|
+
v-for="ic in tplIcons"
|
|
854
|
+
:key="ic.key"
|
|
855
|
+
:value="ic.key"
|
|
856
|
+
>
|
|
857
|
+
{{ ic.label }}
|
|
858
|
+
</option>
|
|
859
|
+
</select>
|
|
860
|
+
</label>
|
|
861
|
+
</div>
|
|
862
|
+
<label class="f">
|
|
863
|
+
<span>{{ $t('dcwDetail.knjmyfr042') }}</span>
|
|
864
|
+
<textarea
|
|
865
|
+
v-model="tplForm.semantics"
|
|
866
|
+
class="inp"
|
|
867
|
+
rows="2"
|
|
868
|
+
:placeholder="$t('dcwDetail.k1qdnfzc008')"
|
|
869
|
+
/>
|
|
870
|
+
</label>
|
|
871
|
+
|
|
872
|
+
<p
|
|
873
|
+
v-if="tplOk"
|
|
874
|
+
class="banner good"
|
|
875
|
+
style="margin-top: 10px;"
|
|
876
|
+
>
|
|
877
|
+
{{ tplOk }}
|
|
878
|
+
</p>
|
|
879
|
+
<p
|
|
880
|
+
v-if="tplError"
|
|
881
|
+
class="m-err"
|
|
882
|
+
>
|
|
883
|
+
{{ tplError }}
|
|
884
|
+
</p>
|
|
885
|
+
<div class="m-actions">
|
|
886
|
+
<button
|
|
887
|
+
class="aw-pill outline"
|
|
888
|
+
@click="tplOpen = false"
|
|
889
|
+
>
|
|
890
|
+
{{ $t('dcwDetail.k3x62t043') }}
|
|
891
|
+
</button>
|
|
892
|
+
<button
|
|
893
|
+
class="pill-btn"
|
|
894
|
+
:disabled="tplSaving || !tplForm.name.trim() || tplForm.unit.trim() === '' || tplForm.min === '' || tplForm.max === ''"
|
|
895
|
+
@click="doCreateTemplate"
|
|
896
|
+
>
|
|
897
|
+
{{ tplSaving ? $t('dcwDetail.k1bg4759156') : $t('dcwDetail.k1bg9nga171') }}
|
|
898
|
+
</button>
|
|
899
|
+
</div>
|
|
900
|
+
</div>
|
|
901
|
+
</div>
|
|
902
|
+
|
|
903
|
+
<!-- 添加控制节点向导 -->
|
|
904
|
+
<div
|
|
905
|
+
v-if="addOpen"
|
|
906
|
+
class="modal-mask"
|
|
907
|
+
@click.self="addOpen = false"
|
|
908
|
+
>
|
|
909
|
+
<div class="modal">
|
|
910
|
+
<h3 class="m-title">
|
|
911
|
+
{{ $t('dcwDetail.k1976uns021') }}
|
|
912
|
+
</h3>
|
|
913
|
+
|
|
914
|
+
<div class="seg-row">
|
|
915
|
+
<button
|
|
916
|
+
class="seg"
|
|
917
|
+
:class="{ on: addScenario === 'mock' }"
|
|
918
|
+
@click="addScenario = 'mock'"
|
|
919
|
+
>
|
|
920
|
+
Mock 模拟 PLC
|
|
921
|
+
</button>
|
|
922
|
+
<button
|
|
923
|
+
class="seg"
|
|
924
|
+
:class="{ on: addScenario === 'real' }"
|
|
925
|
+
@click="addScenario = 'real'"
|
|
926
|
+
>
|
|
927
|
+
{{ $t('dcwDetail.kyj8fen044') }}
|
|
928
|
+
</button>
|
|
929
|
+
</div>
|
|
930
|
+
|
|
931
|
+
<div class="f-grid">
|
|
932
|
+
<label class="f">
|
|
933
|
+
<span>{{ $t('dcwDetail.k1ejzwqp045') }}</span>
|
|
934
|
+
<select
|
|
935
|
+
v-model="addTemplate"
|
|
936
|
+
class="inp"
|
|
937
|
+
>
|
|
938
|
+
<option
|
|
939
|
+
v-for="t in dcw.templates"
|
|
940
|
+
:key="t.key"
|
|
941
|
+
:value="t.key"
|
|
942
|
+
>
|
|
943
|
+
{{ t.name }} · {{ t.ch }}({{ t.min }}~{{ t.max }} {{ t.unit }}){{ t.builtin ? '' : $t('dcwDetail.kr45rk9157') }}
|
|
944
|
+
</option>
|
|
945
|
+
</select>
|
|
946
|
+
</label>
|
|
947
|
+
<label class="f">
|
|
948
|
+
<span>{{ $t('dcwDetail.k1ce2k1y046') }}</span>
|
|
949
|
+
<input
|
|
950
|
+
v-model="addName"
|
|
951
|
+
class="inp"
|
|
952
|
+
:placeholder="$t('dcwDetail.kgpxzy3009')"
|
|
953
|
+
>
|
|
954
|
+
</label>
|
|
955
|
+
<label class="f">
|
|
956
|
+
<span>{{ $t('dcwDetail.k1vyb8im047') }}</span>
|
|
957
|
+
<input
|
|
958
|
+
v-model.number="addHold"
|
|
959
|
+
type="number"
|
|
960
|
+
min="0"
|
|
961
|
+
max="3600000"
|
|
962
|
+
step="500"
|
|
963
|
+
class="inp"
|
|
964
|
+
:placeholder="$t('dcwDetail.kb1srz0010')"
|
|
965
|
+
>
|
|
966
|
+
</label>
|
|
967
|
+
</div>
|
|
968
|
+
|
|
969
|
+
<!-- 数据语义标定 encode(物理设定值 → PLC 设定值;mock/真实均可用) -->
|
|
970
|
+
<div class="f-grid cal-form">
|
|
971
|
+
<label class="f">
|
|
972
|
+
<span>{{ $t('dcwDetail.kiune1f048') }}</span>
|
|
973
|
+
<select
|
|
974
|
+
v-model="addTransform.kind"
|
|
975
|
+
class="inp"
|
|
976
|
+
>
|
|
977
|
+
<option value="none">
|
|
978
|
+
{{ $t('dcwDetail.kkzy0k049') }}
|
|
979
|
+
</option>
|
|
980
|
+
<option value="linear">
|
|
981
|
+
线性标定:PLC值 = (物理值 - offset) / scale
|
|
982
|
+
</option>
|
|
983
|
+
</select>
|
|
984
|
+
</label>
|
|
985
|
+
<label class="f">
|
|
986
|
+
<span>scale / offset</span>
|
|
987
|
+
<div style="display: flex; gap: 6px;">
|
|
988
|
+
<input
|
|
989
|
+
v-model.number="addTransform.scale"
|
|
990
|
+
type="number"
|
|
991
|
+
step="0.1"
|
|
992
|
+
class="inp"
|
|
993
|
+
:disabled="addTransform.kind !== 'linear'"
|
|
994
|
+
title="decoder 斜率(≠0):物理值 = scale × PLC值 + offset;下发时自动取逆"
|
|
995
|
+
>
|
|
996
|
+
<input
|
|
997
|
+
v-model.number="addTransform.offset"
|
|
998
|
+
type="number"
|
|
999
|
+
step="0.1"
|
|
1000
|
+
class="inp"
|
|
1001
|
+
:disabled="addTransform.kind !== 'linear'"
|
|
1002
|
+
title="decoder 截距"
|
|
1003
|
+
>
|
|
1004
|
+
</div>
|
|
1005
|
+
</label>
|
|
1006
|
+
</div>
|
|
1007
|
+
|
|
1008
|
+
<template v-if="addScenario === 'real'">
|
|
1009
|
+
<div class="f-grid">
|
|
1010
|
+
<label class="f">
|
|
1011
|
+
<span>{{ $t('dcwDetail.k1kt87rx050') }}</span>
|
|
1012
|
+
<select
|
|
1013
|
+
v-model="addDriver"
|
|
1014
|
+
class="inp"
|
|
1015
|
+
>
|
|
1016
|
+
<option
|
|
1017
|
+
v-for="d in DCW_DRIVERS.filter(x => x.status !== 'builtin')"
|
|
1018
|
+
:key="d.kind"
|
|
1019
|
+
:value="d.kind"
|
|
1020
|
+
>
|
|
1021
|
+
{{ d.label }}
|
|
1022
|
+
</option>
|
|
1023
|
+
</select>
|
|
1024
|
+
</label>
|
|
1025
|
+
</div>
|
|
1026
|
+
<div
|
|
1027
|
+
v-if="addFields.length"
|
|
1028
|
+
class="f-grid driver-form"
|
|
1029
|
+
>
|
|
1030
|
+
<label
|
|
1031
|
+
v-for="f in addFields"
|
|
1032
|
+
:key="f.key"
|
|
1033
|
+
class="f"
|
|
1034
|
+
>
|
|
1035
|
+
<span>{{ f.label }}<em
|
|
1036
|
+
v-if="f.required"
|
|
1037
|
+
style="color: var(--tone-danger-dot); font-style: normal;"
|
|
1038
|
+
>*</em></span>
|
|
1039
|
+
<select
|
|
1040
|
+
v-if="f.type === 'select'"
|
|
1041
|
+
v-model="addCfg[f.key]"
|
|
1042
|
+
class="inp"
|
|
1043
|
+
>
|
|
1044
|
+
<option
|
|
1045
|
+
v-for="o in f.options"
|
|
1046
|
+
:key="o.value"
|
|
1047
|
+
:value="o.value"
|
|
1048
|
+
>
|
|
1049
|
+
{{ o.label }}
|
|
1050
|
+
</option>
|
|
1051
|
+
</select>
|
|
1052
|
+
<input
|
|
1053
|
+
v-else
|
|
1054
|
+
v-model="addCfg[f.key]"
|
|
1055
|
+
:type="f.type === 'number' ? 'number' : 'text'"
|
|
1056
|
+
:placeholder="f.placeholder"
|
|
1057
|
+
class="inp"
|
|
1058
|
+
>
|
|
1059
|
+
<small
|
|
1060
|
+
v-if="f.hint"
|
|
1061
|
+
class="hint"
|
|
1062
|
+
>{{ f.hint }}</small>
|
|
1063
|
+
</label>
|
|
1064
|
+
</div>
|
|
1065
|
+
<div class="test-row">
|
|
1066
|
+
<button
|
|
1067
|
+
class="pill-btn outline"
|
|
1068
|
+
:disabled="addTesting"
|
|
1069
|
+
@click="doTestConnection"
|
|
1070
|
+
>
|
|
1071
|
+
{{ addTesting ? $t('dcwDetail.k1fsh720158') : $t('dcwDetail.k1fstglk172') }}
|
|
1072
|
+
</button>
|
|
1073
|
+
<span
|
|
1074
|
+
v-if="addTest"
|
|
1075
|
+
class="test-result"
|
|
1076
|
+
:class="addTest.ok ? 'good' : 'bad'"
|
|
1077
|
+
>{{ addTest.ok ? '✓' : '✗' }} {{ addTest.message }}</span>
|
|
1078
|
+
</div>
|
|
1079
|
+
</template>
|
|
1080
|
+
|
|
1081
|
+
<label class="f">
|
|
1082
|
+
<span>{{ $t('dcwDetail.k1hgizn7051') }}</span>
|
|
1083
|
+
<textarea
|
|
1084
|
+
v-model="addSemantics"
|
|
1085
|
+
class="inp"
|
|
1086
|
+
rows="2"
|
|
1087
|
+
:placeholder="$t('dcwDetail.k12bnyt9011')"
|
|
1088
|
+
/>
|
|
1089
|
+
</label>
|
|
1090
|
+
|
|
1091
|
+
<p
|
|
1092
|
+
v-if="addError"
|
|
1093
|
+
class="m-err"
|
|
1094
|
+
>
|
|
1095
|
+
{{ addError }}
|
|
1096
|
+
</p>
|
|
1097
|
+
|
|
1098
|
+
<div class="m-actions">
|
|
1099
|
+
<button
|
|
1100
|
+
class="aw-pill outline"
|
|
1101
|
+
@click="addOpen = false"
|
|
1102
|
+
>
|
|
1103
|
+
{{ $t('dcwDetail.k3xdnn052') }}
|
|
1104
|
+
</button>
|
|
1105
|
+
<button
|
|
1106
|
+
class="aw-pill"
|
|
1107
|
+
:disabled="addSaving"
|
|
1108
|
+
@click="doAddNode"
|
|
1109
|
+
>
|
|
1110
|
+
{{ addSaving ? $t('dcwDetail.k1bg4759156') : $t('dcwDetail.k1bge46t173') }}
|
|
1111
|
+
</button>
|
|
1112
|
+
</div>
|
|
1113
|
+
</div>
|
|
1114
|
+
</div>
|
|
1115
|
+
|
|
1116
|
+
<!-- 控制节点清单 -->
|
|
1117
|
+
<a-spin :spinning="!dcw.loaded && !dcw.error">
|
|
1118
|
+
<section class="aw-tile table-card">
|
|
1119
|
+
<table class="nodes-table">
|
|
1120
|
+
<thead>
|
|
1121
|
+
<tr>
|
|
1122
|
+
<th>{{ $t('dcwDetail.k1e2dtkt053') }}</th>
|
|
1123
|
+
<th>{{ $t('dcwDetail.k403cy054') }}</th>
|
|
1124
|
+
<th>{{ $t('dcwDetail.k42w8s055') }}</th>
|
|
1125
|
+
<th>{{ $t('dcwDetail.k1deqh0d056') }}</th>
|
|
1126
|
+
<th>{{ $t('dcwDetail.k1k79ec9057') }}</th>
|
|
1127
|
+
<th>{{ $t('dcwDetail.k1dexou6058') }}</th>
|
|
1128
|
+
<th>{{ $t('dcwDetail.k1b1nnaa059') }}</th>
|
|
1129
|
+
<th>{{ $t('dcwDetail.k1i8rtqt060') }}</th>
|
|
1130
|
+
<th class="right">
|
|
1131
|
+
{{ $t('dcwDetail.k40aa6061') }}
|
|
1132
|
+
</th>
|
|
1133
|
+
</tr>
|
|
1134
|
+
</thead>
|
|
1135
|
+
<tbody>
|
|
1136
|
+
<tr
|
|
1137
|
+
v-for="n in lineNodes"
|
|
1138
|
+
:key="n.id"
|
|
1139
|
+
:class="{ 'node-paused': !n.enabled }"
|
|
1140
|
+
>
|
|
1141
|
+
<td>
|
|
1142
|
+
<span class="mono dim">{{ n.id.slice(0, 8) }}</span>
|
|
1143
|
+
<b>{{ n.name }}</b>
|
|
1144
|
+
<small class="mono ch">{{ dcwTemplateRefCh(n.templateRef) }}</small>
|
|
1145
|
+
</td>
|
|
1146
|
+
<td>
|
|
1147
|
+
<button
|
|
1148
|
+
class="ctrl-toggle"
|
|
1149
|
+
:class="{ on: n.enabled }"
|
|
1150
|
+
:disabled="togglingId === n.id"
|
|
1151
|
+
:title="n.enabled ? '暂停该节点控制:下发将被拒绝' : '开启该节点控制:恢复可下发'"
|
|
1152
|
+
@click="toggleControl(n.id, !n.enabled)"
|
|
1153
|
+
>
|
|
1154
|
+
<span class="ct-dot" />
|
|
1155
|
+
{{ n.enabled ? $t('dcwDetail.k3o3ib3159') : $t('dcwDetail.k3n93ed062') }}
|
|
1156
|
+
</button>
|
|
1157
|
+
</td>
|
|
1158
|
+
<td>
|
|
1159
|
+
<span
|
|
1160
|
+
v-if="!n.enabled"
|
|
1161
|
+
class="st-pill paused"
|
|
1162
|
+
>{{ $t('dcwDetail.k3n93ed062') }}</span>
|
|
1163
|
+
<span
|
|
1164
|
+
v-else
|
|
1165
|
+
class="st-pill"
|
|
1166
|
+
:class="n.state"
|
|
1167
|
+
>{{ stateLabel[n.state] ?? n.state }}</span>
|
|
1168
|
+
</td>
|
|
1169
|
+
<td class="mono val">
|
|
1170
|
+
{{ n.value != null ? n.value.toFixed(n.decimals) : '--' }}
|
|
1171
|
+
<small>{{ n.unit }}</small>
|
|
1172
|
+
<small
|
|
1173
|
+
v-if="n.lastAckAt"
|
|
1174
|
+
class="dim"
|
|
1175
|
+
> · {{ n.lastAckAt.slice(11, 19) }}</small>
|
|
1176
|
+
</td>
|
|
1177
|
+
<td>
|
|
1178
|
+
<div class="write-row">
|
|
1179
|
+
<input
|
|
1180
|
+
v-model.number="setInputs[n.id]"
|
|
1181
|
+
type="number"
|
|
1182
|
+
class="inp write-inp mono"
|
|
1183
|
+
:placeholder="`${n.min}~${n.max}`"
|
|
1184
|
+
:min="n.min"
|
|
1185
|
+
:max="n.max"
|
|
1186
|
+
:step="10 ** -n.decimals"
|
|
1187
|
+
:disabled="!n.enabled"
|
|
1188
|
+
:title="!n.enabled ? '当前节点暂停:开启控制后方可设定' : ''"
|
|
1189
|
+
>
|
|
1190
|
+
<button
|
|
1191
|
+
class="mini-btn write-btn"
|
|
1192
|
+
:disabled="!n.enabled || writingId === n.id || setInputs[n.id] == null || setInputs[n.id] === ''"
|
|
1193
|
+
:title="!n.enabled ? '当前节点暂停:开启控制后方可设定' : '下发设定值'"
|
|
1194
|
+
@click="doWrite(n.id, Number(setInputs[n.id]))"
|
|
1195
|
+
>
|
|
1196
|
+
{{ writingId === n.id ? $t('dcwDetail.k3l3h80123') : $t('dcwDetail.k3w6td174') }}
|
|
1197
|
+
</button>
|
|
1198
|
+
</div>
|
|
1199
|
+
</td>
|
|
1200
|
+
<td class="mono dim">
|
|
1201
|
+
{{ n.min }} ~ {{ n.max }} {{ n.unit }}
|
|
1202
|
+
</td>
|
|
1203
|
+
<td class="mono">
|
|
1204
|
+
{{ n.holdIntervalMs == null ? $t('dcwDetail.k3zul4160') : `${n.holdIntervalMs}ms` }}
|
|
1205
|
+
</td>
|
|
1206
|
+
<td>{{ deviceName(n.deviceBindingId) }}</td>
|
|
1207
|
+
<td class="right">
|
|
1208
|
+
<button
|
|
1209
|
+
class="mini-btn danger"
|
|
1210
|
+
@click="dcw.removeNode(n.id)"
|
|
1211
|
+
>
|
|
1212
|
+
{{ $t('dcwDetail.k3xakp063') }}
|
|
1213
|
+
</button>
|
|
1214
|
+
</td>
|
|
1215
|
+
</tr>
|
|
1216
|
+
<tr v-if="dcw.loaded && lineNodes.length === 0">
|
|
1217
|
+
<td colspan="9">
|
|
1218
|
+
<div
|
|
1219
|
+
class="pane-empty"
|
|
1220
|
+
style="min-height: 120px;"
|
|
1221
|
+
>
|
|
1222
|
+
<p class="pe-sub">
|
|
1223
|
+
{{ $t('dcwDetail.k1wtecyw064') }}
|
|
1224
|
+
</p>
|
|
1225
|
+
</div>
|
|
1226
|
+
</td>
|
|
1227
|
+
</tr>
|
|
1228
|
+
</tbody>
|
|
1229
|
+
</table>
|
|
1230
|
+
</section>
|
|
1231
|
+
</a-spin>
|
|
1232
|
+
|
|
1233
|
+
<!-- 产品与配方管理 -->
|
|
1234
|
+
<section class="aw-tile recipe-card">
|
|
1235
|
+
<div class="recipe-hd">
|
|
1236
|
+
<h3>{{ $t('dcwDetail.k1j8w3hd065') }}</h3>
|
|
1237
|
+
<span class="panel-tag mono">{{ lineProducts.length }} {{ $t('dcwDetail.k1av3wgs143') }} {{ visibleRecipes.length }} {{ $t('dcwDetail.k48grv027') }}</span>
|
|
1238
|
+
<button
|
|
1239
|
+
class="pill-btn"
|
|
1240
|
+
style="margin-left: auto;"
|
|
1241
|
+
@click="openRecipeCreate"
|
|
1242
|
+
>
|
|
1243
|
+
{{ $t('dcwDetail.k1akm6dc066') }}
|
|
1244
|
+
</button>
|
|
1245
|
+
<button
|
|
1246
|
+
class="mini-btn"
|
|
1247
|
+
@click="productOpen = true"
|
|
1248
|
+
>
|
|
1249
|
+
{{ $t('dcwDetail.k1aka0ki067') }}
|
|
1250
|
+
</button>
|
|
1251
|
+
</div>
|
|
1252
|
+
<p class="recipe-sub">
|
|
1253
|
+
{{ $t('dcwDetail.k6bl2q8068') }}
|
|
1254
|
+
</p>
|
|
1255
|
+
|
|
1256
|
+
<!-- 产品管理行 -->
|
|
1257
|
+
<div
|
|
1258
|
+
v-if="lineProducts.length"
|
|
1259
|
+
class="product-row"
|
|
1260
|
+
>
|
|
1261
|
+
<button
|
|
1262
|
+
class="prod-chip"
|
|
1263
|
+
:class="{ on: filterProductId === '' }"
|
|
1264
|
+
@click="filterProductId = ''"
|
|
1265
|
+
>
|
|
1266
|
+
{{ $t('dcwDetail.k3x4t1069') }}
|
|
1267
|
+
</button>
|
|
1268
|
+
<button
|
|
1269
|
+
v-for="p in lineProducts"
|
|
1270
|
+
:key="p.id"
|
|
1271
|
+
class="prod-chip"
|
|
1272
|
+
:class="{ on: filterProductId === p.id }"
|
|
1273
|
+
:title="p.description"
|
|
1274
|
+
@click="filterProductId = p.id"
|
|
1275
|
+
>
|
|
1276
|
+
{{ p.name }}
|
|
1277
|
+
<span
|
|
1278
|
+
class="prod-del"
|
|
1279
|
+
:title="$t('dcwDetail.k1bpfjgx012')"
|
|
1280
|
+
@click.stop="dcw.removeProduct(p.id)"
|
|
1281
|
+
>×</span>
|
|
1282
|
+
</button>
|
|
1283
|
+
</div>
|
|
1284
|
+
<p
|
|
1285
|
+
v-else
|
|
1286
|
+
class="dim"
|
|
1287
|
+
style="margin: 6px 0 12px; font-size: 12px;"
|
|
1288
|
+
>
|
|
1289
|
+
{{ $t('dcwDetail.knov3zg070') }}
|
|
1290
|
+
</p>
|
|
1291
|
+
|
|
1292
|
+
<div class="recipe-grid">
|
|
1293
|
+
<div
|
|
1294
|
+
v-for="r in visibleRecipes"
|
|
1295
|
+
:key="r.id"
|
|
1296
|
+
class="recipe-item"
|
|
1297
|
+
>
|
|
1298
|
+
<div class="recipe-name">
|
|
1299
|
+
<b>{{ r.name }}</b>
|
|
1300
|
+
<small class="dim">{{ productName(r.productId) }}</small>
|
|
1301
|
+
<small class="mono dim">{{ r.id }}</small>
|
|
1302
|
+
</div>
|
|
1303
|
+
<p
|
|
1304
|
+
v-if="r.description"
|
|
1305
|
+
class="dim desc"
|
|
1306
|
+
>
|
|
1307
|
+
{{ r.description }}
|
|
1308
|
+
</p>
|
|
1309
|
+
<div class="recipe-params mono">
|
|
1310
|
+
<span
|
|
1311
|
+
v-for="(p, i) in r.params"
|
|
1312
|
+
:key="i"
|
|
1313
|
+
class="param-chip"
|
|
1314
|
+
>
|
|
1315
|
+
{{ (dcw.templates.find(t => t.key === (p.templateRef ?? '').replace('dcw-', ''))?.ch ?? p.templateRef) }} = {{ p.value }}
|
|
1316
|
+
</span>
|
|
1317
|
+
<span
|
|
1318
|
+
v-for="(w, i) in r.daqWindows"
|
|
1319
|
+
:key="`w-${i}`"
|
|
1320
|
+
class="param-chip daqwin"
|
|
1321
|
+
:title="$t('dcwDetail.k1l11api013')"
|
|
1322
|
+
>
|
|
1323
|
+
◎ {{ daqNodeCh(w.nodeId) }} ∈ [{{ w.min ?? '-∞' }}, {{ w.max ?? '+∞' }}]
|
|
1324
|
+
</span>
|
|
1325
|
+
</div>
|
|
1326
|
+
<div class="recipe-actions">
|
|
1327
|
+
<button
|
|
1328
|
+
class="pill-btn"
|
|
1329
|
+
@click="doApplyRecipe(r.id)"
|
|
1330
|
+
>
|
|
1331
|
+
{{ $t('dcwDetail.k1497qvr071') }}
|
|
1332
|
+
</button>
|
|
1333
|
+
<button
|
|
1334
|
+
class="mini-btn"
|
|
1335
|
+
@click="openRecipeEdit(r.id)"
|
|
1336
|
+
>
|
|
1337
|
+
{{ $t('dcwDetail.k45eb0072') }}
|
|
1338
|
+
</button>
|
|
1339
|
+
<button
|
|
1340
|
+
class="mini-btn danger"
|
|
1341
|
+
@click="dcw.removeRecipe(r.id)"
|
|
1342
|
+
>
|
|
1343
|
+
{{ $t('dcwDetail.k3xakp063') }}
|
|
1344
|
+
</button>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
<div
|
|
1348
|
+
v-if="visibleRecipes.length === 0"
|
|
1349
|
+
class="pane-empty"
|
|
1350
|
+
style="grid-column: 1 / -1;"
|
|
1351
|
+
>
|
|
1352
|
+
{{ $t('dcwDetail.keko1xy073') }}
|
|
1353
|
+
</div>
|
|
1354
|
+
</div>
|
|
1355
|
+
|
|
1356
|
+
<div
|
|
1357
|
+
v-if="applyResult"
|
|
1358
|
+
class="banner good"
|
|
1359
|
+
>
|
|
1360
|
+
{{ $t('dcwDetail.k1l3hrvp144') }} {{ applyResult.runId }} {{ $t('dcwDetail.k1g3lh2v167') }}{{ applyResult.ok }}/{{ applyResult.total }} {{ $t('dcwDetail.k1bqci06149') }}
|
|
1361
|
+
<button
|
|
1362
|
+
class="mini-btn"
|
|
1363
|
+
style="margin-left: 10px;"
|
|
1364
|
+
:disabled="runDataLoading"
|
|
1365
|
+
@click="doViewRun(applyResult.runId)"
|
|
1366
|
+
>
|
|
1367
|
+
{{ runDataLoading ? $t('dcwDetail.k1br0ij9161') : $t('dcwDetail.kxwei5175') }}
|
|
1368
|
+
</button>
|
|
1369
|
+
</div>
|
|
1370
|
+
|
|
1371
|
+
<!-- 批次列表 -->
|
|
1372
|
+
<div
|
|
1373
|
+
v-if="lineRuns.length"
|
|
1374
|
+
class="runs"
|
|
1375
|
+
>
|
|
1376
|
+
<p class="sec-label">
|
|
1377
|
+
{{ $t('dcwDetail.k1rjer8d074') }}
|
|
1378
|
+
</p>
|
|
1379
|
+
<table class="nodes-table">
|
|
1380
|
+
<thead>
|
|
1381
|
+
<tr>
|
|
1382
|
+
<th>{{ $t('dcwDetail.k400lb075') }}</th>
|
|
1383
|
+
<th>{{ $t('dcwDetail.k48grv027') }}</th>
|
|
1384
|
+
<th>{{ $t('dcwDetail.k4497j076') }}</th>
|
|
1385
|
+
<th>{{ $t('dcwDetail.k1bqhtmu077') }}</th>
|
|
1386
|
+
<th class="right">
|
|
1387
|
+
{{ $t('dcwDetail.k40aa6061') }}
|
|
1388
|
+
</th>
|
|
1389
|
+
</tr>
|
|
1390
|
+
</thead>
|
|
1391
|
+
<tbody>
|
|
1392
|
+
<tr
|
|
1393
|
+
v-for="run in [...lineRuns].reverse().slice(0, 8)"
|
|
1394
|
+
:key="run.id"
|
|
1395
|
+
>
|
|
1396
|
+
<td class="mono">
|
|
1397
|
+
{{ run.id }}
|
|
1398
|
+
</td>
|
|
1399
|
+
<td>{{ run.recipeName }}</td>
|
|
1400
|
+
<td class="mono dim">
|
|
1401
|
+
{{ run.startedAt.slice(5, 19) }} ~ {{ run.endedAt ? run.endedAt.slice(5, 19) : $t('dcwDetail.k3vpfg9162') }}
|
|
1402
|
+
</td>
|
|
1403
|
+
<td>
|
|
1404
|
+
<span
|
|
1405
|
+
class="st-pill"
|
|
1406
|
+
:class="run.results.every(r => r.ok) ? 'ok' : 'warn'"
|
|
1407
|
+
>{{ run.results.filter(r => r.ok).length }}/{{ run.results.length }}</span>
|
|
1408
|
+
</td>
|
|
1409
|
+
<td class="right">
|
|
1410
|
+
<button
|
|
1411
|
+
class="mini-btn"
|
|
1412
|
+
:disabled="runDataLoading"
|
|
1413
|
+
@click="doViewRun(run.id)"
|
|
1414
|
+
>
|
|
1415
|
+
{{ $t('dcwDetail.k1dzwrdp078') }}
|
|
1416
|
+
</button>
|
|
1417
|
+
<button
|
|
1418
|
+
v-if="!run.endedAt"
|
|
1419
|
+
class="mini-btn"
|
|
1420
|
+
@click="dcw.closeRun(run.id)"
|
|
1421
|
+
>
|
|
1422
|
+
{{ $t('dcwDetail.k1blr7y7079') }}
|
|
1423
|
+
</button>
|
|
1424
|
+
</td>
|
|
1425
|
+
</tr>
|
|
1426
|
+
</tbody>
|
|
1427
|
+
</table>
|
|
1428
|
+
</div>
|
|
1429
|
+
|
|
1430
|
+
<!-- 批次数据视图 -->
|
|
1431
|
+
<div
|
|
1432
|
+
v-if="runDataView"
|
|
1433
|
+
class="modal-mask"
|
|
1434
|
+
@click.self="runDataView = null"
|
|
1435
|
+
>
|
|
1436
|
+
<div class="modal">
|
|
1437
|
+
<h3 class="m-title">
|
|
1438
|
+
{{ $t('dcwDetail.k11lq94k145') }} {{ runDataView.data.run.recipeName }}({{ runDataView.runId }})
|
|
1439
|
+
</h3>
|
|
1440
|
+
<p class="sec-label">
|
|
1441
|
+
{{ $t('dcwDetail.k1me41w8080') }}
|
|
1442
|
+
</p>
|
|
1443
|
+
<table class="nodes-table">
|
|
1444
|
+
<thead>
|
|
1445
|
+
<tr>
|
|
1446
|
+
<th>{{ $t('dcwDetail.k48hde081') }}</th>
|
|
1447
|
+
<th>{{ $t('dcwDetail.k45uio082') }}</th>
|
|
1448
|
+
<th>{{ $t('dcwDetail.k40t11083') }}</th>
|
|
1449
|
+
<th>{{ $t('dcwDetail.k3xuaw084') }}</th>
|
|
1450
|
+
<th>min ~ max</th>
|
|
1451
|
+
<th>{{ $t('dcwDetail.k4118o085') }}</th>
|
|
1452
|
+
</tr>
|
|
1453
|
+
</thead>
|
|
1454
|
+
<tbody>
|
|
1455
|
+
<tr
|
|
1456
|
+
v-for="d in runDataView.data.daq"
|
|
1457
|
+
:key="d.nodeId"
|
|
1458
|
+
>
|
|
1459
|
+
<td>{{ d.ch }}<small class="mono dim"> {{ d.unit }}</small></td>
|
|
1460
|
+
<td>{{ d.nodeName }}</td>
|
|
1461
|
+
<td class="mono">
|
|
1462
|
+
{{ d.latest }}
|
|
1463
|
+
</td>
|
|
1464
|
+
<td class="mono">
|
|
1465
|
+
{{ d.avg }}
|
|
1466
|
+
</td>
|
|
1467
|
+
<td class="mono dim">
|
|
1468
|
+
{{ d.min }} ~ {{ d.max }}
|
|
1469
|
+
</td>
|
|
1470
|
+
<td class="mono dim">
|
|
1471
|
+
{{ d.cnt }}
|
|
1472
|
+
</td>
|
|
1473
|
+
</tr>
|
|
1474
|
+
<tr v-if="runDataView.data.daq.length === 0">
|
|
1475
|
+
<td
|
|
1476
|
+
colspan="6"
|
|
1477
|
+
class="dim"
|
|
1478
|
+
style="text-align: center; padding: 12px;"
|
|
1479
|
+
>
|
|
1480
|
+
{{ $t('dcwDetail.kd457ry086') }}
|
|
1481
|
+
</td>
|
|
1482
|
+
</tr>
|
|
1483
|
+
</tbody>
|
|
1484
|
+
</table>
|
|
1485
|
+
<p class="sec-label">
|
|
1486
|
+
{{ $t('dcwDetail.k12b7cxs087') }}
|
|
1487
|
+
</p>
|
|
1488
|
+
<table class="nodes-table">
|
|
1489
|
+
<thead>
|
|
1490
|
+
<tr>
|
|
1491
|
+
<th>{{ $t('dcwDetail.k3xbjr088') }}</th>
|
|
1492
|
+
<th>{{ $t('dcwDetail.k45uio082') }}</th>
|
|
1493
|
+
<th>{{ $t('dcwDetail.k3ncbsh089') }}</th>
|
|
1494
|
+
<th>{{ $t('dcwDetail.k3lh3mz090') }}</th>
|
|
1495
|
+
<th>{{ $t('dcwDetail.k454pg091') }}</th>
|
|
1496
|
+
<th>{{ $t('dcwDetail.k40ieu092') }}</th>
|
|
1497
|
+
</tr>
|
|
1498
|
+
</thead>
|
|
1499
|
+
<tbody>
|
|
1500
|
+
<tr
|
|
1501
|
+
v-for="(w, i) in runDataView.data.writes"
|
|
1502
|
+
:key="i"
|
|
1503
|
+
>
|
|
1504
|
+
<td>{{ w.param }}</td>
|
|
1505
|
+
<td>{{ w.nodeName }}</td>
|
|
1506
|
+
<td class="mono">
|
|
1507
|
+
{{ w.eng }}
|
|
1508
|
+
</td>
|
|
1509
|
+
<td class="mono dim">
|
|
1510
|
+
{{ w.raw }}
|
|
1511
|
+
</td>
|
|
1512
|
+
<td>
|
|
1513
|
+
<span
|
|
1514
|
+
class="st-pill"
|
|
1515
|
+
:class="w.ok ? 'ok' : 'alarm'"
|
|
1516
|
+
>{{ w.ok ? 'ACK' : $t('dcwDetail.k3yit7139') }}</span>
|
|
1517
|
+
</td>
|
|
1518
|
+
<td class="mono dim">
|
|
1519
|
+
{{ w.at.slice(5, 19) }}
|
|
1520
|
+
</td>
|
|
1521
|
+
</tr>
|
|
1522
|
+
<tr v-if="runDataView.data.writes.length === 0">
|
|
1523
|
+
<td
|
|
1524
|
+
colspan="6"
|
|
1525
|
+
class="dim"
|
|
1526
|
+
style="text-align: center; padding: 12px;"
|
|
1527
|
+
>
|
|
1528
|
+
{{ $t('dcwDetail.knzcp8i093') }}
|
|
1529
|
+
</td>
|
|
1530
|
+
</tr>
|
|
1531
|
+
</tbody>
|
|
1532
|
+
</table>
|
|
1533
|
+
<div class="m-actions">
|
|
1534
|
+
<button
|
|
1535
|
+
class="aw-pill outline"
|
|
1536
|
+
@click="runDataView = null"
|
|
1537
|
+
>
|
|
1538
|
+
{{ $t('dcwDetail.k3x62t043') }}
|
|
1539
|
+
</button>
|
|
1540
|
+
</div>
|
|
1541
|
+
</div>
|
|
1542
|
+
</div>
|
|
1543
|
+
</section>
|
|
1544
|
+
|
|
1545
|
+
<!-- 配方编辑弹窗 -->
|
|
1546
|
+
<div
|
|
1547
|
+
v-if="recipeOpen"
|
|
1548
|
+
class="modal-mask"
|
|
1549
|
+
@click.self="recipeOpen = false"
|
|
1550
|
+
>
|
|
1551
|
+
<div class="modal">
|
|
1552
|
+
<h3 class="m-title">
|
|
1553
|
+
{{ recipeEditing ? $t('dcwDetail.k1iiwk0i163') : $t('dcwDetail.k1efq0r9176') }}
|
|
1554
|
+
</h3>
|
|
1555
|
+
<div class="f-grid">
|
|
1556
|
+
<label class="f">
|
|
1557
|
+
<span>{{ $t('dcwDetail.k1dw4fzf094') }}<em>*</em></span>
|
|
1558
|
+
<select
|
|
1559
|
+
v-model="recipeForm.productId"
|
|
1560
|
+
class="inp"
|
|
1561
|
+
>
|
|
1562
|
+
<option
|
|
1563
|
+
v-for="p in lineProducts"
|
|
1564
|
+
:key="p.id"
|
|
1565
|
+
:value="p.id"
|
|
1566
|
+
>
|
|
1567
|
+
{{ p.name }}
|
|
1568
|
+
</option>
|
|
1569
|
+
</select>
|
|
1570
|
+
</label>
|
|
1571
|
+
<label class="f">
|
|
1572
|
+
<span>{{ $t('dcwDetail.k1l3f8so095') }}<em>*</em></span>
|
|
1573
|
+
<input
|
|
1574
|
+
v-model="recipeForm.name"
|
|
1575
|
+
class="inp"
|
|
1576
|
+
:placeholder="$t('dcwDetail.k12c11vm014')"
|
|
1577
|
+
>
|
|
1578
|
+
</label>
|
|
1579
|
+
<label class="f">
|
|
1580
|
+
<span>{{ $t('dcwDetail.k40gkk096') }}</span>
|
|
1581
|
+
<input
|
|
1582
|
+
v-model="recipeForm.description"
|
|
1583
|
+
class="inp"
|
|
1584
|
+
:placeholder="$t('dcwDetail.k1hxx8hy015')"
|
|
1585
|
+
>
|
|
1586
|
+
</label>
|
|
1587
|
+
</div>
|
|
1588
|
+
<p class="sec-label">
|
|
1589
|
+
{{ $t('dcwDetail.ka1kfgh097') }}
|
|
1590
|
+
</p>
|
|
1591
|
+
<div
|
|
1592
|
+
v-for="(p, i) in recipeForm.params"
|
|
1593
|
+
:key="i"
|
|
1594
|
+
class="param-row"
|
|
1595
|
+
>
|
|
1596
|
+
<label class="f">
|
|
1597
|
+
<span>{{ $t('dcwDetail.k1e2dtkt053') }}</span>
|
|
1598
|
+
<select
|
|
1599
|
+
v-model="p.nodeId"
|
|
1600
|
+
class="inp"
|
|
1601
|
+
>
|
|
1602
|
+
<option
|
|
1603
|
+
v-for="n in lineNodes"
|
|
1604
|
+
:key="n.id"
|
|
1605
|
+
:value="n.id"
|
|
1606
|
+
>
|
|
1607
|
+
{{ n.name }}({{ dcwTemplateRefCh(n.templateRef) }} · {{ n.min }}~{{ n.max }} {{ n.unit }})
|
|
1608
|
+
</option>
|
|
1609
|
+
</select>
|
|
1610
|
+
</label>
|
|
1611
|
+
<label class="f">
|
|
1612
|
+
<span>{{ $t('dcwDetail.k3renp2098') }}<em>*</em></span>
|
|
1613
|
+
<input
|
|
1614
|
+
v-model.number="p.value"
|
|
1615
|
+
type="number"
|
|
1616
|
+
class="inp"
|
|
1617
|
+
:step="0.1"
|
|
1618
|
+
>
|
|
1619
|
+
</label>
|
|
1620
|
+
<label class="f">
|
|
1621
|
+
<span>{{ $t('dcwDetail.k134rw3b146') }}{{ nodeMin(p.nodeId) ?? '-' }})</span>
|
|
1622
|
+
<input
|
|
1623
|
+
v-model.number="p.min"
|
|
1624
|
+
type="number"
|
|
1625
|
+
class="inp"
|
|
1626
|
+
:step="0.1"
|
|
1627
|
+
:placeholder="$t('dcwDetail.k5vew9z016')"
|
|
1628
|
+
>
|
|
1629
|
+
</label>
|
|
1630
|
+
<label class="f">
|
|
1631
|
+
<span>{{ $t('dcwDetail.khrv911147') }}{{ nodeMax(p.nodeId) ?? '-' }})</span>
|
|
1632
|
+
<input
|
|
1633
|
+
v-model.number="p.max"
|
|
1634
|
+
type="number"
|
|
1635
|
+
class="inp"
|
|
1636
|
+
:step="0.1"
|
|
1637
|
+
:placeholder="$t('dcwDetail.kzoh5pr017')"
|
|
1638
|
+
>
|
|
1639
|
+
</label>
|
|
1640
|
+
<button
|
|
1641
|
+
class="mini-btn danger param-del"
|
|
1642
|
+
@click="recipeForm.params.splice(i, 1)"
|
|
1643
|
+
>
|
|
1644
|
+
{{ $t('dcwDetail.k44idg099') }}
|
|
1645
|
+
</button>
|
|
1646
|
+
</div>
|
|
1647
|
+
<button
|
|
1648
|
+
class="mini-btn"
|
|
1649
|
+
@click="recipeForm.params.push({ nodeId: lineNodes[0]?.id ?? '', value: '', min: '', max: '' })"
|
|
1650
|
+
>
|
|
1651
|
+
{{ $t('dcwDetail.k1broh7h100') }}
|
|
1652
|
+
</button>
|
|
1653
|
+
<p class="sec-label">
|
|
1654
|
+
{{ $t('dcwDetail.k1h5gues101') }}
|
|
1655
|
+
</p>
|
|
1656
|
+
<div
|
|
1657
|
+
v-for="(w, i) in recipeForm.daqWindows"
|
|
1658
|
+
:key="`dw-${i}`"
|
|
1659
|
+
class="param-row"
|
|
1660
|
+
>
|
|
1661
|
+
<label class="f">
|
|
1662
|
+
<span>{{ $t('dcwDetail.k1empnnb102') }}</span>
|
|
1663
|
+
<select
|
|
1664
|
+
v-model="w.nodeId"
|
|
1665
|
+
class="inp"
|
|
1666
|
+
>
|
|
1667
|
+
<option
|
|
1668
|
+
v-for="n in lineDaqNodes"
|
|
1669
|
+
:key="n.id"
|
|
1670
|
+
:value="n.id"
|
|
1671
|
+
>
|
|
1672
|
+
{{ n.name }}({{ n.min }}~{{ n.max }} {{ n.unit }})
|
|
1673
|
+
</option>
|
|
1674
|
+
</select>
|
|
1675
|
+
</label>
|
|
1676
|
+
<label class="f">
|
|
1677
|
+
<span>{{ $t('dcwDetail.kqm08ap103') }}</span>
|
|
1678
|
+
<input
|
|
1679
|
+
v-model.number="w.min"
|
|
1680
|
+
type="number"
|
|
1681
|
+
class="inp"
|
|
1682
|
+
:step="0.1"
|
|
1683
|
+
>
|
|
1684
|
+
</label>
|
|
1685
|
+
<label class="f">
|
|
1686
|
+
<span>{{ $t('dcwDetail.kpypf8g104') }}</span>
|
|
1687
|
+
<input
|
|
1688
|
+
v-model.number="w.max"
|
|
1689
|
+
type="number"
|
|
1690
|
+
class="inp"
|
|
1691
|
+
:step="0.1"
|
|
1692
|
+
>
|
|
1693
|
+
</label>
|
|
1694
|
+
<button
|
|
1695
|
+
class="mini-btn danger param-del"
|
|
1696
|
+
@click="recipeForm.daqWindows.splice(i, 1)"
|
|
1697
|
+
>
|
|
1698
|
+
{{ $t('dcwDetail.k44idg099') }}
|
|
1699
|
+
</button>
|
|
1700
|
+
</div>
|
|
1701
|
+
<button
|
|
1702
|
+
class="mini-btn"
|
|
1703
|
+
:disabled="lineDaqNodes.length === 0"
|
|
1704
|
+
:title="lineDaqNodes.length === 0 ? '本产线暂无数采节点' : ''"
|
|
1705
|
+
@click="recipeForm.daqWindows.push({ nodeId: lineDaqNodes[0]?.id ?? '', min: '', max: '' })"
|
|
1706
|
+
>
|
|
1707
|
+
{{ $t('dcwDetail.kv1de1p105') }}
|
|
1708
|
+
</button>
|
|
1709
|
+
<p
|
|
1710
|
+
v-if="recipeError"
|
|
1711
|
+
class="m-err"
|
|
1712
|
+
>
|
|
1713
|
+
{{ recipeError }}
|
|
1714
|
+
</p>
|
|
1715
|
+
<div class="m-actions">
|
|
1716
|
+
<button
|
|
1717
|
+
class="aw-pill outline"
|
|
1718
|
+
@click="recipeOpen = false"
|
|
1719
|
+
>
|
|
1720
|
+
{{ $t('dcwDetail.k3xdnn052') }}
|
|
1721
|
+
</button>
|
|
1722
|
+
<button
|
|
1723
|
+
class="pill-btn"
|
|
1724
|
+
:disabled="recipeSaving"
|
|
1725
|
+
@click="saveRecipe"
|
|
1726
|
+
>
|
|
1727
|
+
{{ recipeSaving ? $t('dcwDetail.k1b38d59164') : $t('dcwDetail.k1b3kwg0177') }}
|
|
1728
|
+
</button>
|
|
1729
|
+
</div>
|
|
1730
|
+
</div>
|
|
1731
|
+
</div>
|
|
1732
|
+
|
|
1733
|
+
<!-- 产线数据查询(产品/配方/参数/时间/间隔) -->
|
|
1734
|
+
<section class="aw-tile query-card">
|
|
1735
|
+
<p class="sec-label">
|
|
1736
|
+
{{ $t('dcwDetail.k1us3jse106') }}
|
|
1737
|
+
</p>
|
|
1738
|
+
<div class="q-grid">
|
|
1739
|
+
<label class="f">
|
|
1740
|
+
<span>{{ $t('dcwDetail.k3waz1025') }}</span>
|
|
1741
|
+
<select
|
|
1742
|
+
v-model="query.productId"
|
|
1743
|
+
class="inp"
|
|
1744
|
+
@change="query.recipeId = ''"
|
|
1745
|
+
>
|
|
1746
|
+
<option value="">
|
|
1747
|
+
{{ $t('dcwDetail.k1bkl1jx107') }}
|
|
1748
|
+
</option>
|
|
1749
|
+
<option
|
|
1750
|
+
v-for="p in lineProducts"
|
|
1751
|
+
:key="p.id"
|
|
1752
|
+
:value="p.id"
|
|
1753
|
+
>
|
|
1754
|
+
{{ p.name }}
|
|
1755
|
+
</option>
|
|
1756
|
+
</select>
|
|
1757
|
+
</label>
|
|
1758
|
+
<label class="f">
|
|
1759
|
+
<span>{{ $t('dcwDetail.k48grv027') }}</span>
|
|
1760
|
+
<select
|
|
1761
|
+
v-model="query.recipeId"
|
|
1762
|
+
class="inp"
|
|
1763
|
+
:disabled="!query.productId"
|
|
1764
|
+
>
|
|
1765
|
+
<option value="">
|
|
1766
|
+
{{ $t('dcwDetail.k1bkx7cr108') }}
|
|
1767
|
+
</option>
|
|
1768
|
+
<option
|
|
1769
|
+
v-for="r in visibleRecipes"
|
|
1770
|
+
:key="r.id"
|
|
1771
|
+
:value="r.id"
|
|
1772
|
+
>
|
|
1773
|
+
{{ r.name }}
|
|
1774
|
+
</option>
|
|
1775
|
+
</select>
|
|
1776
|
+
</label>
|
|
1777
|
+
<label class="f">
|
|
1778
|
+
<span>{{ $t('dcwDetail.k1demcae109') }}</span>
|
|
1779
|
+
<select
|
|
1780
|
+
v-model="query.paramKey"
|
|
1781
|
+
class="inp"
|
|
1782
|
+
>
|
|
1783
|
+
<option value="">
|
|
1784
|
+
{{ $t('dcwDetail.k1bkx7ya110') }}
|
|
1785
|
+
</option>
|
|
1786
|
+
<option
|
|
1787
|
+
v-for="k in daqParamKeys"
|
|
1788
|
+
:key="k"
|
|
1789
|
+
:value="k"
|
|
1790
|
+
>
|
|
1791
|
+
{{ k }}
|
|
1792
|
+
</option>
|
|
1793
|
+
</select>
|
|
1794
|
+
</label>
|
|
1795
|
+
<label class="f">
|
|
1796
|
+
<span>{{ $t('dcwDetail.k45uio082') }}</span>
|
|
1797
|
+
<select
|
|
1798
|
+
v-model="query.nodeId"
|
|
1799
|
+
class="inp"
|
|
1800
|
+
>
|
|
1801
|
+
<option value="">
|
|
1802
|
+
{{ $t('dcwDetail.k1bkul3k111') }}
|
|
1803
|
+
</option>
|
|
1804
|
+
<option
|
|
1805
|
+
v-for="n in lineDaqNodes"
|
|
1806
|
+
:key="n.id"
|
|
1807
|
+
:value="n.id"
|
|
1808
|
+
>
|
|
1809
|
+
{{ n.name }}
|
|
1810
|
+
</option>
|
|
1811
|
+
</select>
|
|
1812
|
+
</label>
|
|
1813
|
+
<label class="f">
|
|
1814
|
+
<span>{{ $t('dcwDetail.kx2ojn0112') }}</span>
|
|
1815
|
+
<input
|
|
1816
|
+
v-model.number="query.lastMin"
|
|
1817
|
+
type="number"
|
|
1818
|
+
min="1"
|
|
1819
|
+
max="10080"
|
|
1820
|
+
class="inp"
|
|
1821
|
+
>
|
|
1822
|
+
</label>
|
|
1823
|
+
<label class="f">
|
|
1824
|
+
<span>{{ $t('dcwDetail.kjvnvn4113') }}</span>
|
|
1825
|
+
<input
|
|
1826
|
+
v-model.number="query.bucketMs"
|
|
1827
|
+
type="number"
|
|
1828
|
+
min="0"
|
|
1829
|
+
step="100"
|
|
1830
|
+
class="inp"
|
|
1831
|
+
>
|
|
1832
|
+
</label>
|
|
1833
|
+
<div class="f q-actions">
|
|
1834
|
+
<button
|
|
1835
|
+
class="pill-btn"
|
|
1836
|
+
:disabled="queryBusy"
|
|
1837
|
+
@click="doQuery"
|
|
1838
|
+
>
|
|
1839
|
+
{{ queryBusy ? $t('dcwDetail.k1eyx09r165') : $t('dcwDetail.k416ek178') }}
|
|
1840
|
+
</button>
|
|
1841
|
+
</div>
|
|
1842
|
+
</div>
|
|
1843
|
+
<p
|
|
1844
|
+
v-if="queryError"
|
|
1845
|
+
class="banner bad"
|
|
1846
|
+
style="margin-top: 8px;"
|
|
1847
|
+
>
|
|
1848
|
+
{{ queryError }}
|
|
1849
|
+
</p>
|
|
1850
|
+
<div
|
|
1851
|
+
v-if="queryResult"
|
|
1852
|
+
class="q-result"
|
|
1853
|
+
>
|
|
1854
|
+
<table class="nodes-table">
|
|
1855
|
+
<thead>
|
|
1856
|
+
<tr>
|
|
1857
|
+
<th>{{ $t('dcwDetail.k48hde081') }}</th>
|
|
1858
|
+
<th>{{ $t('dcwDetail.k45uio082') }}</th>
|
|
1859
|
+
<th>{{ $t('dcwDetail.k42kcu114') }}</th>
|
|
1860
|
+
<th>{{ $t('dcwDetail.k49ujb115') }}</th>
|
|
1861
|
+
<th>{{ $t('dcwDetail.k40pvw116') }}</th>
|
|
1862
|
+
<th>{{ $t('dcwDetail.k3xho3117') }}</th>
|
|
1863
|
+
</tr>
|
|
1864
|
+
</thead>
|
|
1865
|
+
<tbody>
|
|
1866
|
+
<tr
|
|
1867
|
+
v-for="c in queryResult.channels"
|
|
1868
|
+
:key="c.nodeId"
|
|
1869
|
+
>
|
|
1870
|
+
<td>{{ c.ch }}<small class="mono dim"> {{ c.unit }}</small></td>
|
|
1871
|
+
<td>{{ c.nodeName }}</td>
|
|
1872
|
+
<td class="mono">
|
|
1873
|
+
{{ c.points.length }}
|
|
1874
|
+
</td>
|
|
1875
|
+
<td class="mono">
|
|
1876
|
+
{{ fmtPoint(c.points[0]) }}
|
|
1877
|
+
</td>
|
|
1878
|
+
<td class="mono">
|
|
1879
|
+
{{ fmtPoint(c.points[c.points.length - 1]) }}
|
|
1880
|
+
</td>
|
|
1881
|
+
<td class="mono dim">
|
|
1882
|
+
{{ new Date(c.points[0]?.at ?? 0).toLocaleTimeString() }} ~ {{ new Date(c.points[c.points.length - 1]?.at ?? 0).toLocaleTimeString() }}
|
|
1883
|
+
</td>
|
|
1884
|
+
</tr>
|
|
1885
|
+
</tbody>
|
|
1886
|
+
</table>
|
|
1887
|
+
</div>
|
|
1888
|
+
</section>
|
|
1889
|
+
|
|
1890
|
+
<!-- 写历史 -->
|
|
1891
|
+
<section
|
|
1892
|
+
v-if="lineHistory.length"
|
|
1893
|
+
class="aw-tile table-card"
|
|
1894
|
+
>
|
|
1895
|
+
<p class="sec-label">
|
|
1896
|
+
{{ $t('dcwDetail.kw7ym9b148') }} {{ lineHistory.length }} {{ $t('dcwDetail.k40bfz150') }}
|
|
1897
|
+
</p>
|
|
1898
|
+
<table class="nodes-table">
|
|
1899
|
+
<tbody>
|
|
1900
|
+
<tr
|
|
1901
|
+
v-for="h in lineHistory.slice(0, 12)"
|
|
1902
|
+
:key="h.id"
|
|
1903
|
+
>
|
|
1904
|
+
<td class="mono dim">
|
|
1905
|
+
{{ h.at.slice(5, 19) }}
|
|
1906
|
+
</td>
|
|
1907
|
+
<td>{{ h.param }}</td>
|
|
1908
|
+
<td>{{ h.nodeName }}</td>
|
|
1909
|
+
<td class="mono">
|
|
1910
|
+
{{ h.eng }}
|
|
1911
|
+
</td>
|
|
1912
|
+
<td class="mono dim">
|
|
1913
|
+
raw {{ h.raw ?? '-' }}
|
|
1914
|
+
</td>
|
|
1915
|
+
<td>
|
|
1916
|
+
<span
|
|
1917
|
+
class="st-pill"
|
|
1918
|
+
:class="h.ok ? 'ok' : 'alarm'"
|
|
1919
|
+
>{{ h.ok ? 'ACK' : $t('dcwDetail.k3yit7139') }}</span>
|
|
1920
|
+
</td>
|
|
1921
|
+
<td class="dim">
|
|
1922
|
+
{{ h.recipeRunId ? $t('dcwDetail.k6vgks7181', { p0: h.recipeRunId }) : '手动' }}
|
|
1923
|
+
</td>
|
|
1924
|
+
</tr>
|
|
1925
|
+
</tbody>
|
|
1926
|
+
</table>
|
|
1927
|
+
</section>
|
|
1928
|
+
|
|
1929
|
+
<!-- 新建产品弹窗 -->
|
|
1930
|
+
<div
|
|
1931
|
+
v-if="productOpen"
|
|
1932
|
+
class="modal-mask"
|
|
1933
|
+
@click.self="productOpen = false"
|
|
1934
|
+
>
|
|
1935
|
+
<div class="modal">
|
|
1936
|
+
<h3 class="m-title">
|
|
1937
|
+
{{ $t('dcwDetail.k1efduyf118') }}
|
|
1938
|
+
</h3>
|
|
1939
|
+
<div class="f-grid">
|
|
1940
|
+
<label class="f">
|
|
1941
|
+
<span>{{ $t('dcwDetail.k1avjrl6119') }}<em>*</em></span>
|
|
1942
|
+
<input
|
|
1943
|
+
v-model="productForm.name"
|
|
1944
|
+
class="inp"
|
|
1945
|
+
:placeholder="$t('dcwDetail.k12c11vm014')"
|
|
1946
|
+
>
|
|
1947
|
+
</label>
|
|
1948
|
+
<label class="f">
|
|
1949
|
+
<span>{{ $t('dcwDetail.k40gkk096') }}</span>
|
|
1950
|
+
<input
|
|
1951
|
+
v-model="productForm.description"
|
|
1952
|
+
class="inp"
|
|
1953
|
+
:placeholder="$t('dcwDetail.k1hxx8hy015')"
|
|
1954
|
+
>
|
|
1955
|
+
</label>
|
|
1956
|
+
</div>
|
|
1957
|
+
<p
|
|
1958
|
+
v-if="productError"
|
|
1959
|
+
class="m-err"
|
|
1960
|
+
>
|
|
1961
|
+
{{ productError }}
|
|
1962
|
+
</p>
|
|
1963
|
+
<div class="m-actions">
|
|
1964
|
+
<button
|
|
1965
|
+
class="aw-pill outline"
|
|
1966
|
+
@click="productOpen = false"
|
|
1967
|
+
>
|
|
1968
|
+
{{ $t('dcwDetail.k3xdnn052') }}
|
|
1969
|
+
</button>
|
|
1970
|
+
<button
|
|
1971
|
+
class="pill-btn"
|
|
1972
|
+
:disabled="productSaving"
|
|
1973
|
+
@click="doCreateProduct"
|
|
1974
|
+
>
|
|
1975
|
+
{{ productSaving ? $t('dcwDetail.k1bg4759156') : $t('dcwDetail.k1bg4kn6179') }}
|
|
1976
|
+
</button>
|
|
1977
|
+
</div>
|
|
1978
|
+
</div>
|
|
1979
|
+
</div>
|
|
1980
|
+
|
|
1981
|
+
<p
|
|
1982
|
+
v-if="dcw.error"
|
|
1983
|
+
class="err"
|
|
1984
|
+
>
|
|
1985
|
+
{{ dcw.error }}(<NuxtLink to="/workshop">{{ $t('dcwDetail.k1bhhheq120') }}</NuxtLink>)
|
|
1986
|
+
</p>
|
|
1987
|
+
|
|
1988
|
+
<!-- 参数台账(调控闭环:当前值/配方目标/lastGood 三值对照 + 设定历史 + 优化记录) -->
|
|
1989
|
+
<section class="aw-tile ledger-card">
|
|
1990
|
+
<div class="ledger-head">
|
|
1991
|
+
<b>{{ $t('dcwDetail.k1ledgt001') }}</b>
|
|
1992
|
+
<select
|
|
1993
|
+
v-model="ledgerNodeId"
|
|
1994
|
+
class="inp"
|
|
1995
|
+
@change="loadLedger"
|
|
1996
|
+
>
|
|
1997
|
+
<option value="">
|
|
1998
|
+
{{ $t('dcwDetail.k1ledgs001') }}
|
|
1999
|
+
</option>
|
|
2000
|
+
<option
|
|
2001
|
+
v-for="n in lineNodes"
|
|
2002
|
+
:key="n.id"
|
|
2003
|
+
:value="n.id"
|
|
2004
|
+
>
|
|
2005
|
+
{{ n.name }}
|
|
2006
|
+
</option>
|
|
2007
|
+
</select>
|
|
2008
|
+
</div>
|
|
2009
|
+
<template v-if="ledger">
|
|
2010
|
+
<div class="ledger-tri">
|
|
2011
|
+
<div class="tri-cell">
|
|
2012
|
+
<small>{{ $t('dcwDetail.k1ledgv001') }}</small>
|
|
2013
|
+
<b class="mono">{{ ledger.current ?? '--' }}</b>
|
|
2014
|
+
</div>
|
|
2015
|
+
<div class="tri-cell">
|
|
2016
|
+
<small>{{ $t('dcwDetail.k1ledgv002') }}</small>
|
|
2017
|
+
<b class="mono">{{ ledger.recipeTarget ?? '--' }}</b>
|
|
2018
|
+
</div>
|
|
2019
|
+
<div class="tri-cell">
|
|
2020
|
+
<small>{{ $t('dcwDetail.k1ledgv003') }}</small>
|
|
2021
|
+
<b class="mono">{{ ledger.lastGood ?? '--' }}</b>
|
|
2022
|
+
</div>
|
|
2023
|
+
<button
|
|
2024
|
+
class="mini-btn"
|
|
2025
|
+
@click="loadLedger"
|
|
2026
|
+
>
|
|
2027
|
+
{{ $t('dcwDetail.k1ledga001') }}
|
|
2028
|
+
</button>
|
|
2029
|
+
<button
|
|
2030
|
+
class="mini-btn danger"
|
|
2031
|
+
@click="rollbackLedgerNode"
|
|
2032
|
+
>
|
|
2033
|
+
{{ $t('dcwDetail.k1ledga002') }}
|
|
2034
|
+
</button>
|
|
2035
|
+
</div>
|
|
2036
|
+
<div class="ledger-cols">
|
|
2037
|
+
<div class="ledger-col">
|
|
2038
|
+
<small class="sec-t">{{ $t('dcwDetail.k1ledgh001') }}</small>
|
|
2039
|
+
<ul class="ledger-list mono">
|
|
2040
|
+
<li
|
|
2041
|
+
v-for="a in ledger.journal"
|
|
2042
|
+
:key="a.id"
|
|
2043
|
+
>
|
|
2044
|
+
{{ a.at.slice(5, 16).replace('T', ' ') }} [{{ a.source }}] {{ a.prevValue ?? '?' }} → {{ a.newValue }}
|
|
2045
|
+
</li>
|
|
2046
|
+
</ul>
|
|
2047
|
+
</div>
|
|
2048
|
+
<div class="ledger-col">
|
|
2049
|
+
<small class="sec-t">{{ $t('dcwDetail.k1ledgr001') }}</small>
|
|
2050
|
+
<ul class="ledger-list">
|
|
2051
|
+
<li
|
|
2052
|
+
v-for="r in ledger.records"
|
|
2053
|
+
:key="r.id"
|
|
2054
|
+
class="ledger-rec"
|
|
2055
|
+
>
|
|
2056
|
+
<span
|
|
2057
|
+
class="rec-st"
|
|
2058
|
+
:class="r.status"
|
|
2059
|
+
>{{ r.status }}</span>
|
|
2060
|
+
<span class="mono">{{ r.params[0]?.from ?? '?' }} → {{ r.params[0]?.to }}</span>
|
|
2061
|
+
<small>{{ r.judge ? `${r.judge.verdict}(${r.judge.by})` : (r.status === 'open' ? 'open' : r.closedBy ?? '') }}</small>
|
|
2062
|
+
</li>
|
|
2063
|
+
</ul>
|
|
2064
|
+
</div>
|
|
2065
|
+
</div>
|
|
2066
|
+
</template>
|
|
2067
|
+
</section>
|
|
2068
|
+
</div>
|
|
2069
|
+
</template>
|
|
2070
|
+
|
|
2071
|
+
<style scoped>
|
|
2072
|
+
.line-chip {
|
|
2073
|
+
display: inline-block;
|
|
2074
|
+
width: 10px;
|
|
2075
|
+
height: 10px;
|
|
2076
|
+
margin-right: 8px;
|
|
2077
|
+
vertical-align: -1px;
|
|
2078
|
+
border-radius: 3px;
|
|
2079
|
+
box-shadow: 0 0 10px currentColor;
|
|
2080
|
+
}
|
|
2081
|
+
.param-chip.daqwin { color: #41c8f4; border-color: rgba(65, 200, 244, 0.4); }
|
|
2082
|
+
.adopt-card { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
|
|
2083
|
+
.adopt-title { font-size: 12px; color: var(--aw-dim, #8fa0b5); flex: none; padding-top: 4px; }
|
|
2084
|
+
.adopt-list { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
2085
|
+
.adopt-chip {
|
|
2086
|
+
display: inline-flex;
|
|
2087
|
+
gap: 6px;
|
|
2088
|
+
align-items: center;
|
|
2089
|
+
padding: 4px 8px;
|
|
2090
|
+
font-size: 11px;
|
|
2091
|
+
color: #e8eef8;
|
|
2092
|
+
background: rgba(13, 20, 32, 0.7);
|
|
2093
|
+
border: 1px solid rgba(60, 80, 110, 0.5);
|
|
2094
|
+
border-radius: 8px;
|
|
2095
|
+
}
|
|
2096
|
+
.page { padding: 4px; }
|
|
2097
|
+
h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.015em; }
|
|
2098
|
+
|
|
2099
|
+
/* ── 参数台账(调控闭环) ── */
|
|
2100
|
+
.ledger-card { padding: 14px 18px; margin-bottom: 14px; }
|
|
2101
|
+
.ledger-head { display: flex; gap: 12px; align-items: center; }
|
|
2102
|
+
.ledger-head b { font-size: 13.5px; }
|
|
2103
|
+
.ledger-tri { display: flex; gap: 12px; align-items: center; margin-top: 12px; }
|
|
2104
|
+
.tri-cell {
|
|
2105
|
+
display: flex;
|
|
2106
|
+
flex: 0 0 auto;
|
|
2107
|
+
flex-direction: column;
|
|
2108
|
+
gap: 2px;
|
|
2109
|
+
padding: 8px 16px;
|
|
2110
|
+
background: var(--paper-deep);
|
|
2111
|
+
border: 1px solid var(--line-strong);
|
|
2112
|
+
border-radius: var(--radius-chip);
|
|
2113
|
+
}
|
|
2114
|
+
.tri-cell small { font-size: 10px; color: var(--ink-faint); }
|
|
2115
|
+
.tri-cell b { font-size: 17px; color: var(--ink); }
|
|
2116
|
+
.ledger-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
|
|
2117
|
+
.sec-t { display: block; margin-bottom: 6px; font-size: 10px; font-weight: 700; letter-spacing: 0.14em; color: var(--ink-faint); }
|
|
2118
|
+
.ledger-list { display: flex; flex-direction: column; gap: 3px; max-height: 200px; margin: 0; padding: 0; overflow: auto; font-size: 11px; color: var(--ink-soft); list-style: none; }
|
|
2119
|
+
.ledger-rec { display: flex; gap: 8px; align-items: center; }
|
|
2120
|
+
.rec-st {
|
|
2121
|
+
flex: none;
|
|
2122
|
+
padding: 0 6px;
|
|
2123
|
+
font-family: var(--font-mono);
|
|
2124
|
+
font-size: 9px;
|
|
2125
|
+
color: var(--ink-faint);
|
|
2126
|
+
border: 1px solid var(--line-strong);
|
|
2127
|
+
border-radius: 4px;
|
|
2128
|
+
}
|
|
2129
|
+
.rec-st.open { color: var(--tone-warning-dot); border-color: var(--tone-warning-dot); }
|
|
2130
|
+
.rec-st.judged { color: var(--tone-warning-dot); border-color: var(--tone-warning-dot); opacity: 0.85; }
|
|
2131
|
+
.rec-st.rolled-back, .rec-st.judged-keep { color: var(--tone-success-dot); border-color: var(--tone-success-dot); }
|
|
2132
|
+
.sub { margin: 0; font-size: 12.5px; opacity: 0.6; }
|
|
2133
|
+
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
2134
|
+
.dim { opacity: 0.55; }
|
|
2135
|
+
.badges { display: flex; gap: 8px; }
|
|
2136
|
+
.badge { padding: 3px 10px; font-size: 11px; letter-spacing: 0.05em; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
|
|
2137
|
+
|
|
2138
|
+
.ctrl-card { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; padding: 14px 18px; margin-bottom: 14px; }
|
|
2139
|
+
.ctrl-left { display: flex; gap: 14px; align-items: center; }
|
|
2140
|
+
.aw-pill.running { background: var(--accent); }
|
|
2141
|
+
.ctrl-right { display: flex; gap: 12px; align-items: center; }
|
|
2142
|
+
.ctrl-metrics { display: flex; gap: 8px; align-items: center; font-size: 11.5px; color: var(--ink-soft); }
|
|
2143
|
+
.ctrl-metrics .sep { opacity: 0.4; }
|
|
2144
|
+
.ctrl-metrics .warn { color: var(--tone-warning-dot); }
|
|
2145
|
+
.add-btn { padding: 8px 16px; font-size: 13px; }
|
|
2146
|
+
|
|
2147
|
+
.banner { padding: 9px 14px; margin: 0 0 12px; font-size: 12.5px; border-radius: var(--radius-chip); }
|
|
2148
|
+
.banner.bad { color: var(--tone-danger-dot); background: var(--tone-danger-bg); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 40%, transparent); }
|
|
2149
|
+
.banner.good { color: var(--tone-success-dot); background: var(--tone-success-bg); border: 1px solid color-mix(in srgb, var(--tone-success-dot) 40%, transparent); }
|
|
2150
|
+
|
|
2151
|
+
.table-card { overflow-x: auto; margin-bottom: 14px; }
|
|
2152
|
+
.nodes-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
|
2153
|
+
.nodes-table th, .nodes-table td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--divider-hair); }
|
|
2154
|
+
.nodes-table th { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); border-bottom: 1px solid var(--line-strong); }
|
|
2155
|
+
.nodes-table td b { margin-left: 8px; }
|
|
2156
|
+
.ch { display: block; margin-top: 2px; font-size: 10px; color: var(--ink-faint); }
|
|
2157
|
+
.right { text-align: right; }
|
|
2158
|
+
.val { font-size: 13px; }
|
|
2159
|
+
.val small { margin-left: 3px; color: var(--ink-faint); }
|
|
2160
|
+
|
|
2161
|
+
.st-pill { display: inline-block; padding: 2px 9px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.04em; border-radius: var(--radius-pill); }
|
|
2162
|
+
.st-pill.ok { color: var(--tone-success-dot); background: var(--tone-success-bg); }
|
|
2163
|
+
.st-pill.writing { color: var(--ink); background: var(--hover-tint); }
|
|
2164
|
+
.st-pill.error, .st-pill.alarm { color: var(--tone-danger-dot); background: var(--tone-danger-bg); }
|
|
2165
|
+
.st-pill.warn { color: var(--tone-warning-dot); background: var(--tone-warning-bg); }
|
|
2166
|
+
.st-pill.offline, .st-pill.idle { color: var(--tone-neutral-dot); background: var(--tone-neutral-bg); }
|
|
2167
|
+
.st-pill.paused { color: var(--tone-warning-dot); background: var(--tone-warning-bg); }
|
|
2168
|
+
|
|
2169
|
+
/* 逐节点 控制开关 */
|
|
2170
|
+
.ctrl-toggle { display: inline-flex; gap: 6px; align-items: center; padding: 3px 10px; font-size: 11.5px; cursor: pointer; color: var(--ink-faint); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-pill); }
|
|
2171
|
+
.ctrl-toggle .ct-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--tone-neutral-dot); }
|
|
2172
|
+
.ctrl-toggle.on { color: var(--tone-success-dot); border-color: color-mix(in srgb, var(--tone-success-dot) 45%, transparent); }
|
|
2173
|
+
.ctrl-toggle.on .ct-dot { background: var(--tone-success-dot); box-shadow: 0 0 6px var(--tone-success-dot); }
|
|
2174
|
+
.ctrl-toggle:disabled { opacity: 0.55; cursor: wait; }
|
|
2175
|
+
.node-paused td { opacity: 0.6; }
|
|
2176
|
+
.node-paused .write-row { opacity: 0.5; }
|
|
2177
|
+
|
|
2178
|
+
/* 模板弹窗 */
|
|
2179
|
+
.tpl-hint { margin: 0 0 10px; font-size: 12px; }
|
|
2180
|
+
.tpl-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
|
|
2181
|
+
.tpl-chip { display: inline-flex; gap: 6px; align-items: center; padding: 3px 9px; font-size: 11.5px; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
|
|
2182
|
+
.tpl-chip small { color: var(--ink-faint); }
|
|
2183
|
+
.tpl-tag { padding: 1px 6px; font-size: 10px; font-style: normal; color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: var(--radius-pill); }
|
|
2184
|
+
.tpl-tag.builtin { color: var(--ink-faint); border-color: var(--line-strong); }
|
|
2185
|
+
|
|
2186
|
+
.write-row { display: flex; gap: 6px; align-items: center; }
|
|
2187
|
+
.write-inp { width: 92px; padding: 4px 8px; }
|
|
2188
|
+
.write-btn { padding: 5px 12px; }
|
|
2189
|
+
.mini-btn { padding: 4px 10px; font-size: 11.5px; cursor: pointer; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); margin-right: 4px; }
|
|
2190
|
+
.mini-btn.danger { color: var(--tone-danger-dot); }
|
|
2191
|
+
.mini-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
2192
|
+
.inp { padding: 5px 9px; font-size: 12.5px; color: var(--ink); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
|
|
2193
|
+
|
|
2194
|
+
.line-card { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; padding: 14px 18px; margin-bottom: 14px; }
|
|
2195
|
+
.line-status { display: flex; gap: 12px; align-items: center; min-width: 260px; }
|
|
2196
|
+
.line-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--tone-neutral-dot); box-shadow: 0 0 0 4px color-mix(in srgb, var(--tone-neutral-dot) 20%, transparent); }
|
|
2197
|
+
.line-dot.on { background: var(--tone-success-dot); box-shadow: 0 0 0 4px color-mix(in srgb, var(--tone-success-dot) 22%, transparent); animation: linePulse 1.6s infinite; }
|
|
2198
|
+
@keyframes linePulse { 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--tone-success-dot) 25%, transparent); } 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--tone-success-dot) 8%, transparent); } }
|
|
2199
|
+
.line-info b { font-size: 14px; }
|
|
2200
|
+
.line-info small { display: block; margin-top: 2px; font-size: 11px; }
|
|
2201
|
+
.line-ctl { display: flex; gap: 10px; align-items: flex-end; }
|
|
2202
|
+
.ctl-sel { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--ink-faint); }
|
|
2203
|
+
.ctl-sel .inp { min-width: 150px; }
|
|
2204
|
+
.pill-btn.stop { background: var(--tone-danger-dot); border-color: var(--tone-danger-dot); }
|
|
2205
|
+
.product-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
|
|
2206
|
+
.prod-chip { display: inline-flex; gap: 6px; align-items: center; padding: 4px 12px; font-size: 12px; cursor: pointer; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-pill); }
|
|
2207
|
+
.prod-chip.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
|
|
2208
|
+
.prod-del { color: var(--tone-danger-dot); font-weight: 700; }
|
|
2209
|
+
.query-card { padding: 14px 18px; margin-bottom: 14px; }
|
|
2210
|
+
.q-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; align-items: end; }
|
|
2211
|
+
.q-grid .f { font-size: 11px; color: var(--ink-faint); }
|
|
2212
|
+
.q-actions { justify-content: flex-end; }
|
|
2213
|
+
.q-result { margin-top: 12px; overflow-x: auto; }
|
|
2214
|
+
.cal-form { grid-template-columns: 2fr 1fr; align-items: end; }
|
|
2215
|
+
.recipe-card { padding: 16px 18px; margin-bottom: 14px; }
|
|
2216
|
+
.recipe-hd { display: flex; gap: 10px; align-items: center; margin-bottom: 6px; }
|
|
2217
|
+
.recipe-hd h3 { margin: 0; font-size: 16px; }
|
|
2218
|
+
.panel-tag { padding: 2px 8px; font-size: 11px; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
|
|
2219
|
+
.recipe-sub { margin: 0 0 14px; font-size: 12px; color: var(--ink-faint); }
|
|
2220
|
+
.recipe-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; }
|
|
2221
|
+
.recipe-item { padding: 12px 14px; background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
|
|
2222
|
+
.recipe-name b { font-size: 13.5px; }
|
|
2223
|
+
.recipe-name small { margin-left: 8px; }
|
|
2224
|
+
.desc { margin: 4px 0; font-size: 12px; }
|
|
2225
|
+
.recipe-params { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
|
|
2226
|
+
.param-chip { padding: 2px 8px; font-size: 11px; color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); border-radius: var(--radius-chip); }
|
|
2227
|
+
.recipe-actions { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
|
|
2228
|
+
.sec-label { margin: 14px 0 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
|
|
2229
|
+
.runs { margin-top: 6px; }
|
|
2230
|
+
.pill-btn { padding: 6px 14px; font-size: 12.5px; cursor: pointer; color: var(--paper-raised); background: var(--accent); border: 1px solid var(--accent); border-radius: var(--radius-pill); }
|
|
2231
|
+
.pill-btn.outline { color: var(--ink); background: var(--paper-raised); border-color: var(--line-strong); }
|
|
2232
|
+
.pill-btn:disabled { opacity: 0.5; cursor: default; }
|
|
2233
|
+
|
|
2234
|
+
.modal-mask { position: fixed; z-index: 50; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--scrim); backdrop-filter: blur(2px); }
|
|
2235
|
+
.modal { width: 680px; max-width: 94vw; max-height: 88vh; overflow-y: auto; padding: 22px 24px; background: var(--surface-glass-strong); backdrop-filter: var(--aurora-blur); -webkit-backdrop-filter: var(--aurora-blur); border: 1px solid var(--glass-line); border-radius: var(--radius-panel); box-shadow: var(--glass-edge), var(--shadow-float); }
|
|
2236
|
+
.m-title { margin: 0 0 14px; font-size: 17px; }
|
|
2237
|
+
.seg-row { display: flex; gap: 8px; margin-bottom: 14px; }
|
|
2238
|
+
.seg { flex: 1; padding: 8px 0; font-size: 13px; cursor: pointer; color: var(--ink-faint); background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
|
|
2239
|
+
.seg.on { font-weight: 600; color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
|
|
2240
|
+
.f-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
|
|
2241
|
+
.driver-form { grid-template-columns: repeat(3, 1fr); }
|
|
2242
|
+
.param-row { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr auto; gap: 8px; align-items: end; margin-bottom: 8px; }
|
|
2243
|
+
.param-row .f { flex: 1; }
|
|
2244
|
+
.param-del { margin-bottom: 2px; }
|
|
2245
|
+
.f { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--ink-faint); }
|
|
2246
|
+
.f em { margin-left: 3px; font-style: normal; }
|
|
2247
|
+
.hint { font-size: 10px; color: var(--ink-fainter); }
|
|
2248
|
+
.test-row { display: flex; gap: 10px; align-items: center; margin: 6px 0 4px; }
|
|
2249
|
+
.test-result { font-family: var(--font-mono); font-size: 11px; }
|
|
2250
|
+
.test-result.good { color: var(--tone-success-dot); }
|
|
2251
|
+
.test-result.bad { color: var(--tone-danger-dot); }
|
|
2252
|
+
.m-err { margin: 8px 0 0; font-size: 12px; color: var(--tone-danger-dot); }
|
|
2253
|
+
.m-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }
|
|
2254
|
+
.aw-pill.outline { color: var(--ink); background: var(--paper-raised); border-color: var(--line-strong); }
|
|
2255
|
+
.err { margin-top: 14px; font-size: 13px; color: var(--tone-danger-dot); }
|
|
2256
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2257
|
+
.nodes-table tbody tr:hover { background: var(--hover-tint); }
|
|
2258
|
+
}
|
|
2259
|
+
</style>
|