Merge branch 'main' of http://it.fairylandtech.com:8666/zhaowei.huang/Migu2.0
This commit is contained in:
@@ -75,8 +75,9 @@ function onDragStart(e: DragEvent, type: string) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.node-palette {
|
.node-palette {
|
||||||
height: 100%;
|
flex: 1;
|
||||||
overflow: auto;
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
@delete="onDeleteWorkflow"
|
@delete="onDeleteWorkflow"
|
||||||
/>
|
/>
|
||||||
<div class="wf-panel-title">控件库</div>
|
<div class="wf-panel-title">控件库</div>
|
||||||
<NodePalette class="wf-palette-body" />
|
<div class="wf-palette-body">
|
||||||
|
<NodePalette />
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="wf-panel wf-panel-center">
|
<main class="wf-panel wf-panel-center">
|
||||||
@@ -362,7 +364,8 @@ async function onImportJson() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const warnCount = prepared.reduce(
|
const warnCount = prepared.reduce(
|
||||||
(sum, w) => sum + validateWorkflow(w).filter((i) => i.level === 'warning').length,
|
(sum, w) =>
|
||||||
|
sum + validateWorkflow(w).filter((i) => i.level === 'warning').length,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
if (warnCount > 0) {
|
if (warnCount > 0) {
|
||||||
@@ -657,6 +660,8 @@ async function onClearCanvas() {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.wf-panel-center {
|
.wf-panel-center {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mission-page">
|
||||||
|
<ReflectionManagerPanel
|
||||||
|
kind="process"
|
||||||
|
kind-label="任务"
|
||||||
|
title="任务编排(Mission / 进程,含插件 MissionType 实例化)"
|
||||||
|
empty-text="当前没有任务;点击右上角「新建任务」从已加载的 MissionType 中选一个实例化。"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* /admin/missions 路由的页面入口。
|
||||||
|
*
|
||||||
|
* 由于「任务」与「进程」在后端 ReflectionApiController 共用 kind=process / mission
|
||||||
|
* (UiDiscoveryCache.MissionCreatableTypes 同源),这里直接用 ReflectionManagerPanel
|
||||||
|
* 接入,按下「新建任务」会列出所有 MissionType 子类(含 taskflow / trigger)。
|
||||||
|
*
|
||||||
|
* 旧版本的 DataTablePro + listMissions mock 已经下线,所有数据走 SimpleLite 反射 API。
|
||||||
|
*/
|
||||||
|
import ReflectionManagerPanel from '@/components/reflection/ReflectionManagerPanel.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mission-page {
|
||||||
|
height: calc(100vh - 56px);
|
||||||
|
padding: 12px 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="orchestration-page">
|
||||||
|
<el-tabs v-model="tab" class="orch-tabs" type="border-card" @tab-change="onTabChange">
|
||||||
|
<el-tab-pane label="车辆" name="cars">
|
||||||
|
<CarPanelView v-if="tab === 'cars'" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="进程" name="process">
|
||||||
|
<MissionEditorView v-if="tab === 'process'" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="脚本" name="script">
|
||||||
|
<ReflectionKindTable
|
||||||
|
v-if="tab === 'script'"
|
||||||
|
kind="script"
|
||||||
|
empty-text="脚本(继承 CarProgram 的子类型)未发现可暴露条目"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="任务流" name="taskflow">
|
||||||
|
<ReflectionKindTable
|
||||||
|
v-if="tab === 'taskflow'"
|
||||||
|
kind="mission"
|
||||||
|
extra-filter="taskflow"
|
||||||
|
empty-text="未发现任务流条目"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="触发器" name="trigger">
|
||||||
|
<ReflectionKindTable
|
||||||
|
v-if="tab === 'trigger'"
|
||||||
|
kind="mission"
|
||||||
|
extra-filter="trigger"
|
||||||
|
empty-text="未发现触发器条目"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import CarPanelView from '@/views/admin/CarPanelView.vue'
|
||||||
|
import MissionEditorView from '@/views/admin/MissionEditorView.vue'
|
||||||
|
import ReflectionKindTable from '@/components/orchestration/ReflectionKindTable.vue'
|
||||||
|
|
||||||
|
const tab = ref<'cars' | 'process' | 'script' | 'taskflow' | 'trigger'>('cars')
|
||||||
|
|
||||||
|
function onTabChange(name: string | number) {
|
||||||
|
tab.value = name as typeof tab.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const cached = sessionStorage.getItem('orchestration.tab')
|
||||||
|
if (cached) tab.value = cached as typeof tab.value
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.orchestration-page {
|
||||||
|
height: calc(100vh - 56px);
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.orch-tabs {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.orch-tabs :deep(.el-tabs__content) {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { ParamFieldDef } from '@/types/workflow'
|
import type { ParamFieldDef } from '@/types/workflow'
|
||||||
|
|
||||||
export const ASSIGN_STRATEGY_OPTIONS = [
|
export const ASSIGN_STRATEGY_OPTIONS = [
|
||||||
{ label: '指定车辆', value: 'fixed' },
|
{ label: '指定车辆', value: 'carId' },
|
||||||
{ label: '指定类型', value: 'byType' }
|
{ label: '指定类型', value: 'carType' }
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const FIXED = { key: 'strategy', equals: 'fixed' as const }
|
const BY_CAR_ID = { key: 'strategy', equals: 'carId' as const }
|
||||||
const BY_TYPE = { key: 'strategy', equals: 'byType' as const }
|
const BY_CAR_TYPE = { key: 'strategy', equals: 'carType' as const }
|
||||||
|
|
||||||
/** 分配车辆:指定车辆 / 指定类型 */
|
/** 分配车辆:指定车辆 / 指定类型 */
|
||||||
export function assignCarParams(): ParamFieldDef[] {
|
export function assignCarParams(): ParamFieldDef[] {
|
||||||
@@ -16,7 +16,7 @@ export function assignCarParams(): ParamFieldDef[] {
|
|||||||
label: '分配策略',
|
label: '分配策略',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
required: true,
|
required: true,
|
||||||
defaultValue: 'fixed',
|
defaultValue: 'carId',
|
||||||
options: [...ASSIGN_STRATEGY_OPTIONS]
|
options: [...ASSIGN_STRATEGY_OPTIONS]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,7 @@ export function assignCarParams(): ParamFieldDef[] {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
placeholder: '如 C01',
|
placeholder: '如 C01',
|
||||||
when: FIXED
|
when: BY_CAR_ID
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'carType',
|
key: 'carType',
|
||||||
@@ -33,7 +33,7 @@ export function assignCarParams(): ParamFieldDef[] {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
placeholder: '如 Forklift / SimpleLite.RCS.Cars.AgvCar',
|
placeholder: '如 Forklift / SimpleLite.RCS.Cars.AgvCar',
|
||||||
when: BY_TYPE
|
when: BY_CAR_TYPE
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export const DEMO_WORKFLOW: WorkflowDefinition = {
|
|||||||
viewport: { x: 0, y: 0, zoom: 1 },
|
viewport: { x: 0, y: 0, zoom: 1 },
|
||||||
nodes: [
|
nodes: [
|
||||||
{ id: 'n1', type: 'start', label: '开始', position: { x: 60, y: 200 }, params: {} },
|
{ id: 'n1', type: 'start', label: '开始', position: { x: 60, y: 200 }, params: {} },
|
||||||
{ id: 'n2', type: 'assignCar', label: '分配车辆', position: { x: 240, y: 180 }, params: { strategy: 'fixed', carId: 'C01' } },
|
{ id: 'n2', type: 'assignCar', label: '分配车辆', position: { x: 240, y: 180 }, params: { strategy: 'carId', carId: 'C01' } },
|
||||||
{ id: 'n3', type: 'gotoSite', label: '前往入库点', position: { x: 460, y: 180 }, params: { siteId: 'S001' } },
|
{ id: 'n3', type: 'gotoSite', label: '前往入库点', position: { x: 460, y: 180 }, params: { siteId: 'S001' } },
|
||||||
{
|
{
|
||||||
id: 'n4',
|
id: 'n4',
|
||||||
@@ -140,7 +140,7 @@ export const DEMO_WORKFLOW: WorkflowDefinition = {
|
|||||||
label: '取货',
|
label: '取货',
|
||||||
position: { x: 680, y: 180 },
|
position: { x: 680, y: 180 },
|
||||||
params: {
|
params: {
|
||||||
actionType: 'pick',
|
actionType: 'fetch',
|
||||||
actionParameters: '[{"key":"loadType","value":"pallet"}]'
|
actionParameters: '[{"key":"loadType","value":"pallet"}]'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import type { ParamFieldDef } from '@/types/workflow'
|
|||||||
|
|
||||||
/** 动作类型选项 */
|
/** 动作类型选项 */
|
||||||
export const ACTION_TYPE_OPTIONS = [
|
export const ACTION_TYPE_OPTIONS = [
|
||||||
{ label: '取货', value: 'pick' },
|
{ label: '取货', value: 'fetch' },
|
||||||
{ label: '放货', value: 'drop' },
|
{ label: '放货', value: 'put' }
|
||||||
{ label: '充电', value: 'startCharging' }
|
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
/** 执行动作:对齐 VDA5050 Order.actions[] 单条 Action 结构(不含 actionId,下发时由运行时生成) */
|
/** 执行动作:对齐 VDA5050 Order.actions[] 单条 Action 结构(不含 actionId,下发时由运行时生成) */
|
||||||
@@ -15,7 +14,7 @@ export function executeActionParams(): ParamFieldDef[] {
|
|||||||
label: '动作类型',
|
label: '动作类型',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
required: true,
|
required: true,
|
||||||
defaultValue: 'pick',
|
defaultValue: 'fetch',
|
||||||
options: [...ACTION_TYPE_OPTIONS]
|
options: [...ACTION_TYPE_OPTIONS]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
import type { ParamFieldDef, WorkflowDefinition, WorkflowEdgeDef, WorkflowNodeDef } from '@/types/workflow'
|
import type { ParamFieldDef, WorkflowDefinition, WorkflowEdgeDef, WorkflowNodeDef } from '@/types/workflow'
|
||||||
import type { WorkflowCatalogExport } from '@/workflow/workflowLibrary'
|
import type { WorkflowCatalogExport } from '@/workflow/workflowLibrary'
|
||||||
import { NODE_CATALOG_MAP, defaultParamsForType } from '@/workflow/nodeCatalog'
|
import { defaultParamsForType, NODE_CATALOG_MAP } from '@/workflow/nodeCatalog'
|
||||||
import { WAIT_EVENT_HANDLES, WAIT_EVENT_HANDLE_LABELS } from '@/workflow/waitEventParams'
|
import { WAIT_EVENT_HANDLES, WAIT_EVENT_HANDLE_LABELS } from '@/workflow/waitEventParams'
|
||||||
|
|
||||||
/** 深拷贝流程定义(workflow 为纯 JSON 结构,避免 structuredClone 对 Proxy/不可克隆对象报错) */
|
/** 深拷贝流程定义(workflow 为纯 JSON 结构,避免 structuredClone 对 Proxy/不可克隆对象报错) */
|
||||||
@@ -72,8 +72,10 @@ export const WORKFLOW_CATALOG_VERSION = 3
|
|||||||
export const WORKFLOW_EDITOR_STORAGE_KEY = 'workflowEditor.draft'
|
export const WORKFLOW_EDITOR_STORAGE_KEY = 'workflowEditor.draft'
|
||||||
|
|
||||||
const DEPRECATED_ASSIGN_STRATEGY: Record<string, string> = {
|
const DEPRECATED_ASSIGN_STRATEGY: Record<string, string> = {
|
||||||
nearest: 'fixed',
|
nearest: 'carId',
|
||||||
idle: 'byType'
|
idle: 'carType',
|
||||||
|
fixed: 'carId',
|
||||||
|
byType: 'carType'
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFieldVisible(field: ParamFieldDef, params: Record<string, unknown>): boolean {
|
function isFieldVisible(field: ParamFieldDef, params: Record<string, unknown>): boolean {
|
||||||
@@ -268,11 +270,12 @@ export function attachNextIds(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出用:附带 nextId 的流程快照 */
|
/** 导出用:附带 nextId 的流程快照(params 按当前控件 schema 规范化) */
|
||||||
export function workflowForExport(def: WorkflowDefinition): WorkflowDefinition {
|
export function workflowForExport(def: WorkflowDefinition): WorkflowDefinition {
|
||||||
|
const normalized = normalizeWorkflow(def)
|
||||||
return {
|
return {
|
||||||
...def,
|
...normalized,
|
||||||
nodes: attachNextIds(def.nodes, def.edges)
|
nodes: attachNextIds(normalized.nodes, normalized.edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user