Files
KintoneAppBuilder/frontend/src/components/main/NodeItem.vue

277 lines
8.0 KiB
Vue

<template>
<div class="row justify-center no-wrap" >
<div class="row">
<q-card class="action-node" :class="nodeStyle" :square="false" @click="onNodeClick" >
<q-toolbar class="col" >
<div class="text-subtitle2">{{ node.subTitle }}</div>
<q-space></q-space>
<q-btn flat round dense icon="more_horiz" size="sm" >
<q-menu auto-close anchor="top right">
<q-list>
<q-item clickable v-if="isRoot" @click="copyFlow">
<q-item-section avatar><q-icon name="content_copy" ></q-icon></q-item-section>
<q-item-section >コピーする</q-item-section>
</q-item>
<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>
<q-item clickable @click="onDeleteAllNode">
<q-item-section avatar><q-icon name="delete_sweep" ></q-icon></q-item-section>
<q-item-section >以下すべて削除する</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-toolbar>
<q-separator />
<q-card-section class="action-title">
<div class="row">
<span class="text-h7">{{ node.title }}</span>
<q-space></q-space>
<q-chip color="info" text-color="white" size="0.70rem" v-if="varName(node)" clickable>{{ varName(node) }}</q-chip>
</div>
</q-card-section>
<template v-if="hasBranch">
<q-separator />
<q-card-actions align="around">
<q-btn flat v-for="(point, index) in node.outputPoints" :key="index">
{{ point }}
</q-btn>
</q-card-actions>
</template>
</q-card>
</div>
</div>
<template v-if="hasBranch">
<node-line :action-node="node" @addNode="addNode" :left-columns="leftColumns" :right-columns="rightColumns"></node-line>
<div class="row justify-center no-wrap" >
<div v-for="(point, index) in node.outputPoints" :key="index" class="column" style="min-width: 300px;">
<div class="justify-center" >
<node-item v-if="nextNode(point)!==undefined" :key="nextNode(point).id" :isSelected="nextNode(point) === store.activeNode"
:actionNode="nextNode(point)" @addNode="addNodeFromItem" @nodeSelected="onNodeSelected" @nodeEdit="onNodeEdit"
@deleteNode="onDeleteNodeFromItem" @deleteAllNextNodes="onDeleteAllNextNodes" ></node-item>
</div>
</div>
</div>
</template>
<template v-if="!hasBranch">
<div class="row justify-center no-wrap" >
<node-line :action-node="node" @addNode="addNode" ></node-line>
</div>
<div>
<node-item v-if="nextNode('')!==undefined" :key="nextNode('').id" :isSelected="nextNode('') === store.activeNode"
:actionNode="nextNode('')" @addNode="addNodeFromItem" @nodeSelected="onNodeSelected" @nodeEdit="onNodeEdit"
@deleteNode="onDeleteNodeFromItem" @deleteAllNextNodes="onDeleteAllNextNodes" ></node-item>
</div>
</template>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from 'vue';
import { IActionNode, IActionProperty } from '../../types/ActionTypes';
import NodeLine, { Direction } from '../main/NodeLine.vue';
import { useFlowEditorStore } from 'stores/flowEditor';
export default defineComponent({
name: 'NodeItem',
components: {
NodeLine
},
props: {
actionNode: {
type: Object as () => IActionNode,
required: true
},
isSelected: {
type: Boolean
}
},
emits: [
'addNode',
"nodeSelected",
"nodeEdit",
"deleteNode",
"deleteAllNextNodes",
"copyFlow"
],
setup(props, context) {
const store = useFlowEditorStore();
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
const nodeStyle = computed(() => {
return {
'root-node': props.actionNode.isRoot,
'text-white': props.actionNode.isRoot,
'selected': props.isSelected && !props.actionNode.isRoot
};
});
const nextNode=(point:string)=>{
const nextId= props.actionNode.nextNodeIds.get(point);
if(!nextId) return undefined;
return store.currentFlow?.findNodeById(nextId);
}
/**
* アクションノード追加イベントを
* @param point 入力ポイント
*/
const addNode = (point: string) => {
context.emit('addNode', props.actionNode, point);
}
/**
* アクションノード追加イベントを
* @param point 入力ポイント
*/
const addNodeFromItem = (node:IActionNode,point: string) => {
context.emit('addNode', node, point);
}
const leftColumns=computed(()=>{
if(!props.actionNode.outputPoints || props.actionNode.outputPoints.length<2){
return 1;
}
const leftNode = nextNode(props.actionNode.outputPoints[0]);
if(leftNode){
return store.currentFlow?.getColumns(leftNode);
}else{
return 1;
}
});
const rightColumns=computed(()=>{
if(!props.actionNode.outputPoints || props.actionNode.outputPoints.length<2){
return 1;
}
const rightNode = nextNode(props.actionNode.outputPoints[1]);
if(rightNode){
return store.currentFlow?.getColumns(rightNode);
}else{
return 1;
}
});
/**
* ノード選択状態
*/
const onNodeClick = () => {
context.emit('nodeSelected', props.actionNode);
}
const onNodeSelected = (node: IActionNode) => {
context.emit('nodeSelected', node);
}
const onEditNode=()=>{
context.emit('nodeEdit', props.actionNode);
}
const onNodeEdit=(node:IActionNode)=>{
context.emit('nodeEdit', node);
}
/**
* ノードを削除する
*/
const onDeleteNode=()=>{
context.emit('deleteNode', props.actionNode);
}
/**
* ノードを削除する
*/
const onDeleteNodeFromItem=(node:IActionNode)=>{
context.emit('deleteNode', node);
}
/**
* ノードの以下すべて削除する
*/
const onDeleteAllNode=()=>{
context.emit('deleteAllNextNodes', props.actionNode);
};
/**
* ノードの以下すべて削除する
*/
const onDeleteAllNextNodes=(node:IActionNode)=>{
context.emit('deleteAllNextNodes', node);
};
/**
* 変数名取得
*/
const varName =(node:IActionNode)=>{
const prop = node.actionProps.find((prop) => prop.props.name === "verName");
return prop?.props.modelValue;
};
const copyFlow=()=>{
context.emit('copyFlow', props.actionNode);
}
return {
store,
node: props.actionNode,
nextNode,
isRoot: props.actionNode.isRoot,
hasBranch,
nodeStyle,
// getMode,
addNode,
addNodeFromItem,
onNodeClick,
onNodeSelected,
onEditNode,
onNodeEdit,
onDeleteNode,
onDeleteNodeFromItem,
onDeleteAllNode,
onDeleteAllNextNodes,
copyFlow,
varName,
leftColumns,
rightColumns
}
}
});
</script>
<style lang="scss">
.action-node {
min-width: 280px !important;
}
.action-title{
max-width: 280px !important;
overflow-wrap: anywhere;
}
.line {
height: 20px;
}
.line:after {
content: '';
background-color: $blue-7;
display: block;
width: 3px;
}
.add-icon {
font-size: 2em;
color: $blue-7;
}
.root-node {
background-color: $blue-7;
border-radius: 20px;
}
.action-node:not(.root-node):hover{
background-color: $light-blue-1;
}
.selected{
background-color: $yellow-1;
}
</style>