プロパティ属性設定連動実装

This commit is contained in:
2023-09-10 01:15:40 +09:00
parent fc2669dabf
commit 142cdcda38
9 changed files with 407 additions and 82 deletions

View File

@@ -11,12 +11,14 @@
"@quasar/extras": "^1.16.4", "@quasar/extras": "^1.16.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"quasar": "^2.6.0", "quasar": "^2.6.0",
"uuid": "^9.0.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-router": "^4.0.0" "vue-router": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app-vite": "^1.3.0", "@quasar/app-vite": "^1.3.0",
"@types/node": "^12.20.21", "@types/node": "^12.20.21",
"@types/uuid": "^9.0.3",
"@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0", "@typescript-eslint/parser": "^5.10.0",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
@@ -28,8 +30,9 @@
"typescript": "^4.5.4" "typescript": "^4.5.4"
}, },
"engines": { "engines": {
"node": "^18 || ^16 || ^14.19", "node": "^20 ||^18 || ^16 || ^14.19",
"npm": ">= 6.13.4", "npm": ">= 6.13.4",
"pnpm": ">=8.6.0",
"yarn": ">= 1.21.1" "yarn": ">= 1.21.1"
} }
}, },
@@ -544,6 +547,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/uuid": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.3.tgz",
"integrity": "sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.61.0", "version": "5.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz",
@@ -5045,6 +5054,14 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@@ -16,12 +16,14 @@
"@quasar/extras": "^1.16.4", "@quasar/extras": "^1.16.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"quasar": "^2.6.0", "quasar": "^2.6.0",
"uuid": "^9.0.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-router": "^4.0.0" "vue-router": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app-vite": "^1.3.0", "@quasar/app-vite": "^1.3.0",
"@types/node": "^12.20.21", "@types/node": "^12.20.21",
"@types/uuid": "^9.0.3",
"@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0", "@typescript-eslint/parser": "^5.10.0",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",

View File

@@ -1,9 +0,0 @@
export interface Rule{
id:number;
name:string;
condtion:CondtionTree
}
export interface CondtionTree{
}

View File

@@ -1,18 +1,24 @@
<template> <template>
<div class="row justify-center" :style="{ marginLeft: node.inputPoint !== '' ? '240px' : '' }" @click="onNodeClick"> <div class="row justify-center" :style="{ marginLeft: node.inputPoint !== '' ? '240px' : '' }" >
<div class="row"> <div class="row">
<q-card class="action-node" :class="nodeStyle" :square="false" > <q-card class="action-node" :class="nodeStyle" :square="false" @click="onNodeClick" >
<q-toolbar class="col" > <q-toolbar class="col" >
<div class="text-subtitle2">{{ node.subTitle }}</div> <div class="text-subtitle2">{{ node.subTitle }}</div>
<q-space></q-space> <q-space></q-space>
<q-btn flat round dense icon="more_horiz" > <q-btn flat round dense icon="more_horiz" size="sm" >
<q-menu auto-close anchor="top right"> <q-menu auto-close anchor="top right">
<q-list> <q-list>
<q-item clickable v-if="!isRoot"> <q-item clickable v-if="!isRoot" @click="onEditNode">
<q-item-section avatar><q-icon name="edit" ></q-icon></q-item-section>
<q-item-section >編集する</q-item-section>
</q-item>
<q-item clickable v-if="!isRoot" @click="onDeleteNode">
<q-item-section avatar><q-icon name="delete" ></q-icon></q-item-section>
<q-item-section>削除する</q-item-section> <q-item-section>削除する</q-item-section>
</q-item> </q-item>
<q-item clickable> <q-item clickable @click="onDeleteAllNode">
<q-item-section>以下すべて削除する</q-item-section> <q-item-section avatar><q-icon name="delete_sweep" ></q-icon></q-item-section>
<q-item-section >以下すべて削除する</q-item-section>
</q-item> </q-item>
</q-list> </q-list>
</q-menu> </q-menu>
@@ -67,7 +73,10 @@ export default defineComponent({
}, },
emits: [ emits: [
'addNode', 'addNode',
"nodeSelected" "nodeSelected",
"nodeEdit",
"deleteNode",
"deleteAllNextNodes",
], ],
setup(props, context) { setup(props, context) {
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0); const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
@@ -83,13 +92,13 @@ export default defineComponent({
return Direction.Default; return Direction.Default;
} }
if (point === props.actionNode.outputPoints[0]) { if (point === props.actionNode.outputPoints[0]) {
if (props.actionNode.nextNodes.get(point)) { if (props.actionNode.nextNodeIds.get(point)) {
return Direction.Left; return Direction.Left;
} else { } else {
return Direction.LeftNotNext; return Direction.LeftNotNext;
} }
} else { } else {
if (props.actionNode.nextNodes.get(point)) { if (props.actionNode.nextNodeIds.get(point)) {
return Direction.Right; return Direction.Right;
} else { } else {
return Direction.RightNotNext; return Direction.RightNotNext;
@@ -109,6 +118,23 @@ export default defineComponent({
const onNodeClick = () => { const onNodeClick = () => {
context.emit('nodeSelected', props.actionNode); context.emit('nodeSelected', props.actionNode);
} }
const onEditNode=()=>{
context.emit('nodeEdit', props.actionNode);
}
/**
* ノードを削除する
*/
const onDeleteNode=()=>{
context.emit('deleteNode', props.actionNode);
}
/**
* ノードの以下すべて削除する
*/
const onDeleteAllNode=()=>{
context.emit('deleteAllNextNodes', props.actionNode);
}
return { return {
node: props.actionNode, node: props.actionNode,
isRoot: props.actionNode.isRoot, isRoot: props.actionNode.isRoot,
@@ -116,7 +142,10 @@ export default defineComponent({
nodeStyle, nodeStyle,
getMode, getMode,
addNode, addNode,
onNodeClick onNodeClick,
onEditNode,
onDeleteNode,
onDeleteAllNode
} }
} }
}); });
@@ -152,6 +181,6 @@ export default defineComponent({
} }
.selected{ .selected{
background-color: $light-blue-1; background-color: $yellow-1;
} }
</style> </style>

View File

@@ -0,0 +1,45 @@
<template>
<div>
<div v-for="(item, index) in properties" :key="index">
<component :is="item.component" v-bind="item.props" v-model="item.props.modelValue"></component>
</div>
</div>
</template>
<script lang="ts">
/**
* プロパティ属性設定生成する
*/
import { PropType, defineComponent,ref } from 'vue';
import InputText from '../right/InputText.vue';
import SelectBox from '../right/SelectBox.vue';
import DatePicker from '../right/DatePicker.vue';
import FieldInput from '../right/FieldInput.vue';
import { IActionNode,IActionProperty } from 'src/types/ActionTypes';
export default defineComponent({
name: 'PropertyList',
components: {
InputText,
SelectBox,
DatePicker,
FieldInput
},
props: {
nodeProps: {
type: Object as PropType<Array<IActionProperty>>,
required: true,
},
jsonValue:{
type: Object,
required: false,
}
},
setup(props, context) {
const properties=ref(props.nodeProps)
return {
properties
}
}
});
</script>

View File

@@ -0,0 +1,80 @@
<template>
<div class="q-pa-md q-gutter-sm">
<q-drawer
side="right"
:show-if-above="false"
bordered
:width="301"
:breakpoint="500"
class="bg-grey-3"
:model-value="showPanel"
elevated
overlay
>
<q-card class="column full-height" style="width: 300px">
<q-card-section>
<div class="text-h6">プロパティ</div>
</q-card-section>
<q-card-section class="col q-pt-none">
<property-list :node-props="actionProps" v-if="showPanel" ></property-list>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn flat label="Save" @click="save"/>
<q-btn flat label="Cancel" @click="cancel" />
</q-card-actions>
</q-card>
</q-drawer>
</div>
</template>
<script lang="ts">
import { reactive, ref,defineComponent, defineProps,PropType ,watchEffect} from 'vue'
import PropertyList from 'components/right/PropertyList.vue';
import { IActionNode } from 'src/types/ActionTypes';
export default defineComponent({
name: 'PropertyPanel',
components: {
PropertyList
},
props: {
actionNode:{
type:Object as PropType<IActionNode>,
required:true
},
drawerRight:{
type:Boolean,
required:true
}
},
emits: [
"update:drawerRight"
],
setup(props,{emit}) {
const showPanel =ref(props.drawerRight);
const actionProps =ref(props.actionNode.actionProps);
watchEffect(() => {
showPanel.value = props.drawerRight;
actionProps.value= props.actionNode.actionProps;
});
const cancel = async() =>{
showPanel.value = false;
emit("update:drawerRight",false )
}
const save = async () =>{
showPanel.value=false;
emit("update:drawerRight",false )
}
return {
cancel,
save,
actionProps,
showPanel
}
}
});
</script>
<style lang="scss">
</style>

View File

@@ -1,34 +1,66 @@
<template> <template>
<q-page> <q-page>
<div class="flowchart"> <div class="flowchart">
<node-item v-for="(node,index) in refFlow.actionNodes" :key="index" <node-item v-for="(node,) in refFlow.actionNodes" :key="node.id"
:isSelected="node===activeNode" :actionNode="node" :isSelected="node===state.activeNode" :actionNode="node"
@addNode="addNode" @nodeSelected="onNodeSelected"></node-item> @addNode="addNode"
@nodeSelected="onNodeSelected"
@nodeEdit="onNodeEdit"
@deleteNode="onDeleteNode"
@deleteAllNextNodes="onDeleteAllNextNodes"
></node-item>
</div> </div>
</q-page> </q-page>
<PropertyPanel :actionNode="state.activeNode" v-model:drawerRight="drawerRight"></PropertyPanel>
<show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg"> <show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg">
<action-select ref="appDg" name="アクション" type="single"></action-select> <action-select ref="appDg" name="アクション" type="single"></action-select>
</show-dialog> </show-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref} from 'vue'; import {ref,reactive,computed} from 'vue';
import {IActionNode, ActionNode, IActionFlow, ActionFlow,RootAction } from 'src/types/ActionTypes'; import {IActionNode, ActionNode, IActionFlow, ActionFlow,RootAction, IActionProperty } from 'src/types/ActionTypes';
import NodeItem from 'src/components/main/NodeItem.vue'; import NodeItem from 'src/components/main/NodeItem.vue';
import ShowDialog from 'components/ShowDialog.vue'; import ShowDialog from 'components/ShowDialog.vue';
import ActionSelect from 'components/ActionSelect.vue'; import ActionSelect from 'components/ActionSelect.vue';
import PropertyPanel from 'components/right/PropertyPanel.vue';
const rootNode:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき"); const rootNode:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき");
const actionFlow: ActionFlow = new ActionFlow(rootNode); const actionFlow: ActionFlow = new ActionFlow(rootNode);
actionFlow.addNode(new ActionNode('自動採番','文書番号を自動採番する','')); const saibanProps:[IActionProperty]=[{
component:"InputText",
props:{
displayName:"フォーマット",
modelValue:"",
name:"format",
placeholder:"フォーマットを入力してください",
}
},{
component:"FieldInput",
props:{
displayName:"採番項目",
modelValue:"",
name:"filed",
placeholder:"採番項目を選択してください",
}
}];
actionFlow.addNode(new ActionNode('自動採番','文書番号を自動採番する','',[],saibanProps));
actionFlow.addNode(new ActionNode('入力データ取得','電話番号を取得する','')); actionFlow.addNode(new ActionNode('入力データ取得','電話番号を取得する',''));
const branchNode = actionFlow.addNode(new ActionNode('条件分岐','電話番号入力形式チャック','',['はい','いいえ'] )); const branchNode = actionFlow.addNode(new ActionNode('条件分岐','電話番号入力形式チャック','',['はい','いいえ'] ));
actionFlow.addNode(new ActionNode('入力データ取得','住所を取得する',''),branchNode,'はい');
actionFlow.addNode(new ActionNode('エラー表示','エラー表示して保存しない',''),branchNode,'いいえ' ); actionFlow.addNode(new ActionNode('エラー表示','エラー表示して保存しない',''),branchNode,'いいえ' );
// ref関数を使ってtemplateとバインド // ref関数を使ってtemplateとバインド
const state=reactive({
activeNode:rootNode,
})
const refFlow = ref(actionFlow); const refFlow = ref(actionFlow);
const showAddAction=ref(false); const showAddAction=ref(false);
const activeNode =ref(rootNode); const drawerRight=ref(false);
const addActionNode=(action:IActionNode)=>{ const addActionNode=(action:IActionNode)=>{
refFlow.value.actionNodes.push(action); refFlow.value.actionNodes.push(action);
@@ -39,9 +71,21 @@ const addNode=(node:IActionNode,inputPoint:string)=>{
} }
const onNodeSelected=(node:IActionNode)=>{ const onNodeSelected=(node:IActionNode)=>{
activeNode.value=node; state.activeNode = node;
} }
const onNodeEdit=(node:IActionNode)=>{
state.activeNode = node;
drawerRight.value=true;
}
const onDeleteNode=(node:IActionNode)=>{
refFlow.value.removeNode(node);
}
const onDeleteAllNextNodes=(node:IActionNode)=>{
refFlow.value.removeNode(node);
}
const closeDg=(val :any)=>{ const closeDg=(val :any)=>{
console.log("Dialog closed->",val); console.log("Dialog closed->",val);
} }

View File

@@ -1,7 +1,10 @@
import { v4 as uuidv4 } from 'uuid';
/** /**
* アクションのプロパティ定義 * アクションのプロパティ定義
*/ */
interface IActionProperty { export interface IActionProperty {
component: string; component: string;
props: { props: {
//プロパティ名 //プロパティ名
@@ -31,8 +34,8 @@ export interface IActionNode{
actionProps:Array<IActionProperty>; actionProps:Array<IActionProperty>;
//アクションのプロパティ設定値抽出 //アクションのプロパティ設定値抽出
ActionValue:object ActionValue:object
prevNode?: IActionNode; prevNodeId?: string;
nextNodes:Map<string,IActionNode>; nextNodeIds: Map<string, string>;
} }
/** /**
* アクションフローの定義 * アクションフローの定義
@@ -57,7 +60,7 @@ class ActionProperty implements IActionProperty {
}; };
static defaultProperty():IActionProperty{ static defaultProperty():IActionProperty{
return new ActionProperty("InputText","displayName","表示名","表示を入力してください",""); return new ActionProperty('InputText','displayName','表示名','表示を入力してください','');
}; };
constructor( constructor(
@@ -104,8 +107,8 @@ export class ActionNode implements IActionNode {
}); });
return propValue; return propValue;
}; };
prevNode?: IActionNode; prevNodeId?: string;
nextNodes:Map<string,IActionNode>; nextNodeIds: Map<string, string>;
constructor( constructor(
name: string, name: string,
title:string, title:string,
@@ -113,7 +116,7 @@ export class ActionNode implements IActionNode {
outputPoint: Array<string> = [], outputPoint: Array<string> = [],
actionProps: Array<IActionProperty> =[ActionProperty.defaultProperty()] actionProps: Array<IActionProperty> =[ActionProperty.defaultProperty()]
) { ) {
this.id=''; this.id=uuidv4();
this.name = name; this.name = name;
this.title=title; this.title=title;
this.inputPoint=inputPoint; this.inputPoint=inputPoint;
@@ -125,7 +128,7 @@ export class ActionNode implements IActionNode {
if(prop===undefined){ if(prop===undefined){
this.actionProps.unshift(defProp); this.actionProps.unshift(defProp);
} }
this.nextNodes=new Map(); this.nextNodeIds=new Map<string,string>();
} }
} }
@@ -143,14 +146,14 @@ export class RootAction implements IActionNode {
isRoot: boolean; isRoot: boolean;
actionProps: Array<IActionProperty>; actionProps: Array<IActionProperty>;
ActionValue:object; ActionValue:object;
prevNode?: IActionNode=undefined; prevNodeId?: string = undefined;
nextNodes:Map<string,IActionNode>; nextNodeIds: Map<string, string>;
constructor( constructor(
name: string, name: string,
title:string, title:string,
subTitle:string, subTitle:string,
) { ) {
this.id=''; this.id=uuidv4();
this.name = name; this.name = name;
this.title=title; this.title=title;
this.subTitle=subTitle; this.subTitle=subTitle;
@@ -159,7 +162,7 @@ export class RootAction implements IActionNode {
this.isRoot = true; this.isRoot = true;
this.actionProps=[]; this.actionProps=[];
this.ActionValue={}; this.ActionValue={};
this.nextNodes=new Map(); this.nextNodeIds=new Map<string,string>();
} }
} }
@@ -167,11 +170,8 @@ export class RootAction implements IActionNode {
* アクションフローの定義 * アクションフローの定義
*/ */
export class ActionFlow implements IActionFlow { export class ActionFlow implements IActionFlow {
nextId:number; actionNodes:Array<IActionNode>;
actionNodes:Array<IActionNode>;
constructor(actionNodes:Array<IActionNode>|RootAction){ constructor(actionNodes:Array<IActionNode>|RootAction){
this.nextId=0;
if(actionNodes instanceof Array){ if(actionNodes instanceof Array){
this.actionNodes=actionNodes; this.actionNodes=actionNodes;
}else{ }else{
@@ -192,32 +192,14 @@ export class ActionFlow implements IActionFlow {
prevNode?:IActionNode, prevNode?:IActionNode,
inputPoint?:string):IActionNode inputPoint?:string):IActionNode
{ {
newNode.id = this.generateId();
if(inputPoint!==undefined){ if(inputPoint!==undefined){
newNode.inputPoint=inputPoint; newNode.inputPoint=inputPoint;
} }
if(prevNode!==undefined){ if(prevNode){
newNode.prevNode=prevNode; this.resetNodeRelation(prevNode,newNode,inputPoint);
const nextNodes = prevNode.nextNodes;
if(nextNodes!==undefined && nextNodes.size>0){
const nextNode=inputPoint!==undefined?nextNodes.get(inputPoint):nextNodes.get('');
if(nextNode ){
nextNode.prevNode=newNode;
if(newNode.outputPoints.length>0){
if(!newNode.outputPoints.some((point)=>point==nextNode.inputPoint)){
nextNode.inputPoint=newNode.outputPoints[0];
}
}else{
nextNode.inputPoint='';
}
newNode.nextNodes.set(nextNode.inputPoint,nextNode);
}
}
prevNode.nextNodes.set(inputPoint?inputPoint:'',newNode);
}else{ }else{
prevNode=this.actionNodes[this.actionNodes.length-1]; prevNode=this.actionNodes[this.actionNodes.length-1];
prevNode.nextNodes.set(inputPoint?inputPoint:'',newNode); this.resetNodeRelation(prevNode,newNode,inputPoint);
newNode.prevNode=prevNode;
} }
this.actionNodes.push(newNode); this.actionNodes.push(newNode);
return newNode; return newNode;
@@ -226,25 +208,150 @@ export class ActionFlow implements IActionFlow {
* ノードを削除する * ノードを削除する
* @param delNode * @param delNode
*/ */
removeNode(delNode:IActionNode){ removeNode(targetNode :IActionNode):boolean{
const prevNode = delNode; if (!targetNode ) {
const nextNode = this.findNextNode(delNode); return false;
if(nextNode!==undefined){ }
nextNode.prevNode=prevNode; if(targetNode.isRoot){
nextNode.inputPoint=delNode.inputPoint; return false;
}
this.disconnectFromPrevNode(targetNode);
this.reconnectOrRemoveNextNodes(targetNode);
this.removeFromActionNodes(targetNode.id);
return true;
}
/***
* 目標ノードの次のノードを全部削除する
*/
removeAllNext(targetNodeId :string){
if (!targetNodeId || targetNodeId==='') {
return false;
}
const targetNode=this.findNodeById(targetNodeId);
if(!targetNode){
return false;
}
if(targetNode.nextNodeIds.size==0){
return false;
}
for (const [, id] of targetNode.nextNodeIds) {
this.removeAllNext(id);
this.removeFromActionNodes(id);
}
}
// 断开与前一个节点的连接
disconnectFromPrevNode(targetNode: IActionNode): void {
const prevNodeId = targetNode.prevNodeId;
if (prevNodeId) {
const prevNode = this.findNodeById(prevNodeId);
if (prevNode) {
for (const [key, value] of prevNode.nextNodeIds) {
if (value === targetNode.id) {
prevNode.nextNodeIds.delete(key);
}
}
} }
} }
/** }
* 次のノードを探す
* @param delNode // 从 actionNodes 数组中移除节点
*/ private removeFromActionNodes(targetNodeId: string): void {
findNextNode(targetNode: IActionNode):ActionNode|undefined { const index = this.actionNodes.findIndex(node => node.id === targetNodeId);
return this.actionNodes.find((node)=>node.prevNode===targetNode); if (index > -1) {
this.actionNodes.splice(index, 1);
}
}
/**
* ノード削除時、前のノードと次のノードを接続する
* @param targetNode
*/
reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
if(!targetNode || !targetNode.prevNodeId ){
return;
}
//前のノードを取得
const prevNode = this.findNodeById(targetNode.prevNodeId);
if(!prevNode) return;
//次のノード取得
const nextNodeIds = targetNode.nextNodeIds;
if(nextNodeIds.size==0){
return;
}
//次のノード一つの場合
if(nextNodeIds.size==1){
const nextNodeId = nextNodeIds.get('');
if(!nextNodeId) return;
const nextNode = this.findNodeById(nextNodeId) ;
if(!nextNode) return;
nextNode.prevNodeId=prevNode.id;
prevNode.nextNodeIds.set(targetNode.inputPoint||'',nextNodeId);
return;
}
//二つ以上の場合
for(const [point,nextid] of nextNodeIds){
if(!this.connectNodes(prevNode,nextid,point)){
this.removeAllNext(nextid);
this.removeFromActionNodes(nextid);
}
}
}
/**
* 二つノードを接続する
* @param prevNode
* @param nextNodeId
* @param point
* @returns
*/
connectNodes(prevNode:IActionNode,nextNodeId:string,point:string):boolean{
if(!prevNode || !nextNodeId){
return false;
}
const nextNode = this.findNodeById(nextNodeId);
if(!nextNode) return false;
prevNode.nextNodeIds.set(point,nextNodeId);
nextNode.prevNodeId=prevNode.id;
nextNode.inputPoint=point;
return true;
} }
generateId():string{ resetNodeRelation(prevNode: IActionNode, newNode: IActionNode, inputPoint?: string) {
const no = this.nextId++; // 设置新节点和前节点的关联
return no.toString().padStart(10,'0'); prevNode.nextNodeIds.set(inputPoint || '', newNode.id);
newNode.prevNodeId = prevNode.id;
// 保存前节点原有的后节点ID
const originalNextNodeId = prevNode.nextNodeIds.get(inputPoint || '');
this.setNewNodeNextId(newNode,originalNextNodeId,inputPoint);
}
/**
* 後ノードと新ノードの関連付け
* @param newNode
* @param originalNextNodeId
* @param inputPoint
*/
private setNewNodeNextId(newNode: IActionNode, originalNextNodeId: string | undefined, inputPoint?: string) {
// 如果原先的后节点存在
if (originalNextNodeId) {
// 检查新节点的 outputPoints 是否包含该 inputPoint
if (newNode.outputPoints.includes(inputPoint || '')) {
newNode.nextNodeIds.set(inputPoint || '', originalNextNodeId);
} else {
// 如果不包含,选择新节点的一个 outputPoint
const alternativeOutputPoint = newNode.outputPoints.length > 0 ? newNode.outputPoints[0] : '';
newNode.nextNodeIds.set(alternativeOutputPoint, originalNextNodeId);
}
}
}
/***
* IDでActionNodeを取得する
*/
findNodeById(id: string): IActionNode | undefined {
return this.actionNodes.find((node) => node.id === id);
} }
} }

View File

@@ -278,6 +278,11 @@
"@types/mime" "*" "@types/mime" "*"
"@types/node" "*" "@types/node" "*"
"@types/uuid@^9.0.3":
version "9.0.3"
resolved "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.3.tgz"
integrity sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug==
"@typescript-eslint/eslint-plugin@^5.10.0": "@typescript-eslint/eslint-plugin@^5.10.0":
version "5.61.0" version "5.61.0"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz"
@@ -2707,6 +2712,11 @@ utils-merge@1.0.1:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
vary@~1.1.2: vary@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"