ボタンクリックイベント対応

This commit is contained in:
2024-03-01 22:47:37 +09:00
parent 72608a8ffd
commit 03904a4e35
19 changed files with 676 additions and 1419 deletions

1
backend/.gitignore vendored
View File

@@ -56,6 +56,7 @@ coverage.xml
# Django stuff:
*.log
*.log.*
local_settings.py
db.sqlite3
db.sqlite3-journal

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,8 @@ API_V1_AUTH_KEY = "X-Cybozu-Authorization"
DEPLOY_MODE = "DEV" #DEV,PROD
DEPLOY_JS_URL = "https://ka-addin.azurewebsites.net/alc_runtime.js"
#DEPLOY_JS_URL = "https://ka-addin.azurewebsites.net/alc_runtime.js"
DEPLOY_JS_URL = "https://b9c9-133-139-70-187.ngrok-free.app/alc_runtime.js"
KINTONE_FIELD_TYPE=["GROUP","GROUP_SELECT","CHECK_BOX","SUBTABLE","DROP_DOWN","USER_SELECT","RADIO_BUTTON","RICH_TEXT","LINK","REFERENCE_TABLE","CALC","TIME","NUMBER","ORGANIZATION_SELECT","FILE","DATETIME","DATE","MULTI_SELECT","SINGLE_LINE_TEXT","MULTI_LINE_TEXT"]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
<mxfile host="app.diagrams.net" modified="2024-02-28T04:38:57.047Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" etag="OcG1GMfhziVI7M4sfXsS" version="23.1.5" type="device">
<diagram name="Page-1" id="12e1b939-464a-85fe-373e-61e167be1490">
<mxGraphModel dx="1434" dy="820" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1.5" pageWidth="1169" pageHeight="826" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="60da8b9f42644d3a-2" value="" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;strokeWidth=8;fontSize=20;align=center;fillColor=#7EA6E0;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="316" y="405" width="280" height="430" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-3" value="AI-OCRフォーム認識" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=27;fontColor=#FFFFFF;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="316" y="425" width="280" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-4" value="AI-OCRのフォーム認識機能で&lt;br&gt;フォーマット情報を抽出する" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=14;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="316" y="725" width="280" height="90" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-6" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="336" y="485" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-7" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="336" y="705" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-8" value="PDFや&lt;br&gt;画像ファイル&lt;br&gt;レイアウト" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=6;fontSize=14;align=center;fillColor=#BFB4A9;strokeColor=#FFFFFF;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="259" y="545" width="115" height="115" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-9" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.stylisedArrow;dy=0.6;dx=40;notch=15;feather=0.4;rounded=0;strokeWidth=1;fontSize=27;strokeColor=none;fillColor=#fff2cc;" parent="1" vertex="1">
<mxGeometry x="406" y="573" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-17" value="" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;strokeWidth=8;fontSize=20;align=center;fillColor=#FFC001;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="596" y="405" width="280" height="430" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-18" value="WEBフォーマット&lt;br&gt;変換" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=27;fontColor=#FFFFFF;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="596" y="425" width="280" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-19" value="必要な注文情報項目、フォームレイアウト&lt;br&gt;データ種別等確認し、&lt;br&gt;入力フォームを自動変換する" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=14;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="596" y="725" width="280" height="90" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-20" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="616" y="485" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-21" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="616" y="705" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-22" value="フォーマット&lt;br&gt;確認" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=6;fontSize=14;align=center;fillColor=#397DAA;strokeColor=#FFFFFF;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="539" y="545" width="115" height="115" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-23" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.stylisedArrow;dy=0.6;dx=40;notch=15;feather=0.4;rounded=0;strokeWidth=1;fontSize=27;strokeColor=none;fillColor=#fff2cc;" parent="1" vertex="1">
<mxGeometry x="686" y="573" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-24" value="" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;strokeWidth=8;fontSize=20;align=center;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="876" y="405" width="280" height="430" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-25" value="ユーザーの注文" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=27;fontColor=#FFFFFF;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="876" y="425" width="280" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-26" value="ユーザーがWeb入力フォームから&lt;br&gt;注文データを入力する" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=14;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="876" y="725" width="280" height="90" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-27" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="896" y="485" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-28" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="896" y="705" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-29" value="注文フォーム&lt;br&gt;公開" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=6;fontSize=14;align=center;fillColor=#EF9353;strokeColor=#FFFFFF;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="819" y="545" width="115" height="115" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-30" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.stylisedArrow;dy=0.6;dx=40;notch=15;feather=0.4;rounded=0;strokeWidth=1;fontSize=27;strokeColor=none;fillColor=#fff2cc;" parent="1" vertex="1">
<mxGeometry x="966" y="573" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-31" value="" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;strokeWidth=8;fontSize=20;align=center;fillColor=#a20025;strokeColor=#6F0000;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="1156" y="405" width="280" height="430" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-32" value="データ管理" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=27;fontColor=#FFFFFF;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1156" y="425" width="280" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-33" value="注文データをDBに保管し、&lt;br&gt;注文データの 確認やCSV抽出など&lt;br&gt;管理する" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;fontSize=14;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="1156" y="725" width="280" height="90" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-34" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="1176" y="485" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-35" value="" style="line;strokeWidth=2;html=1;rounded=0;shadow=0;fontSize=27;align=center;fillColor=none;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="1176" y="705" width="240" height="10" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-36" value="メール自動送信" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=6;fontSize=14;align=center;fillColor=#68B85C;strokeColor=#FFFFFF;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="1099" y="545" width="115" height="115" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-37" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.stylisedArrow;dy=0.6;dx=40;notch=15;feather=0.4;rounded=0;strokeWidth=1;fontSize=27;strokeColor=none;fillColor=#fff2cc;" parent="1" vertex="1">
<mxGeometry x="1246" y="573" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="60da8b9f42644d3a-38" value="CSV出力&lt;br&gt;データ連携" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=6;fontSize=14;align=center;fillColor=#BF639A;strokeColor=#FFFFFF;fontColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="1379" y="545" width="115" height="115" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -2,13 +2,14 @@
<!-- <div class="q-pa-md q-gutter-sm"> -->
<q-tree
:nodes="store.eventTree.screens"
node-key="label"
node-key="eventId"
children-key="events"
no-connectors
v-model:expanded="store.expandedScreen"
:dense="true"
:ref="tree"
>
<template v-slot:default-header="prop">
<template v-slot:header-EVENT="prop">
<div class="row col items-start no-wrap event-node" @click="onSelected(prop.node)">
<q-icon v-if="prop.node.eventId"
name="play_circle"
@@ -16,27 +17,78 @@
size="16px" class="q-mr-sm">
</q-icon>
<div class="no-wrap" :class="selectedEvent && prop.node.eventId===selectedEvent.eventId?'selected-node':''">{{ prop.node.label }}</div>
<q-space></q-space>
<!-- <q-icon v-if="prop.node.hasFlow" name="delete" color="negative" size="16px" class="q-mr-sm"></q-icon> -->
</div>
</template>
<template v-slot:header-CHANGE="prop" >
<div class="row col items-start no-wrap event-node" >
<div class="no-wrap">{{ prop.node.label }}</div>
<q-space></q-space>
<q-icon name="add_circle" color="primary" size="16px" class="q-mr-sm" @click="addChangeEvent(prop.node)"></q-icon>
</div>
</template>
</q-tree>
<!-- </div> -->
<show-dialog v-model:visible="showDialog" name="フィールド選択" @close="closeDg" widht="400px">
<field-select ref="appDg" name="フィールド" type="single" :appId="store.appInfo?.appId"></field-select>
</show-dialog>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from 'vue';
import { IKintoneEvent } from '../../types/KintoneEvents';
import { IKintoneEvent ,IKintoneEventGroup, IKintoneEventNode, kintoneEvent} from '../../types/KintoneEvents';
import { storeToRefs } from 'pinia';
import { useFlowEditorStore } from 'stores/flowEditor';
import { ActionFlow, ActionNode, RootAction } from 'src/types/ActionTypes';
import ShowDialog from '../ShowDialog.vue';
import FieldSelect from '../FieldSelect.vue';
import { QTree } from 'quasar';
export default defineComponent({
name: 'EventTree',
components: {
ShowDialog,
FieldSelect,
},
setup(props, context) {
const appDg = ref();
const store = useFlowEditorStore();
const showDialog = ref(false);
const tree = ref<QTree>();
// const eventTree=ref(kintoneEvents);
// const selectedFlow = store.currentFlow;
// const expanded=ref();
const selectedEvent = ref<IKintoneEvent|null>(null);
const selectedChangeEvent=ref<IKintoneEventGroup|null>(null);
const isFieldChange = (node:IKintoneEventNode)=>{
return node.header=='EVENT' && node.eventId.indexOf(".change.")>-1;
}
//フィールド値変更イベント追加
const closeDg = (val:string) => {
if (val == 'OK') {
if(!selectedChangeEvent.value){return;}
const field = appDg.value.selected[0];
const eventid = `${selectedChangeEvent.value.eventId}.${field.code}`;
if(store.eventTree.findEventById(eventid)){
return;
}
selectedChangeEvent.value?.events.push(
new kintoneEvent(
field.label,
eventid,
selectedChangeEvent.value.eventId)
);
tree.value?.expanded?.push(selectedChangeEvent.value.eventId);
tree.value?.expandAll();
}
};
const addChangeEvent=(node:IKintoneEventGroup)=>{
if(store.appInfo===undefined){
return;
}
selectedChangeEvent.value=node;
showDialog.value=true;
}
const onSelected=(node:IKintoneEvent)=>{
if(!node.eventId){
return;
@@ -45,24 +97,35 @@ export default defineComponent({
if(store.appInfo===undefined){
return;
}
const screen = store.eventTree.findScreen(node.eventId);
const screen = store.eventTree.findEventById(node.parentId);
let flow =store.findFlowByEventId(node.eventId);
const screenName=screen!==null?screen.label:"";
let screenName=screen!==null?screen.label:"";
let nodeLabel = node.label;
// if(isFieldChange(node)){
// screenName=nodeLabel;
// nodeLabel=`${node.label}の値を変更したとき`;
// }
if(flow!==undefined && flow!==null ){
store.selectFlow(flow);
}else{
const root = new RootAction(node.eventId,screenName,node.label)
const root = new RootAction(node.eventId,screenName,nodeLabel)
const flow =new ActionFlow(root);
store.flows?.push(flow);
store.selectFlow(flow);
selectedEvent.value.flowData=flow;
}
}
};
return {
// eventTree,
// expanded,
appDg,
tree,
showDialog,
isFieldChange,
onSelected,
selectedEvent,
addChangeEvent,
closeDg,
store
}
}
@@ -83,4 +146,6 @@ export default defineComponent({
.event-node:hover{
background-color: $light-blue-1;
}
</style>

View File

@@ -1,25 +1,27 @@
<template>
<div>
<div v-for="(item, index) in componentData" :key="index">
<component :is="item.component" v-bind="item.props" v-model="item.props.modelValue"></component>
<component :is="item.component" v-bind="item.props" :connectProps="connectProps" v-model="item.props.modelValue"></component>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent,computed } 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 EventSetter from '../right/EventSetter.vue';
import { IActionProperty, IProp } from 'src/types/ActionTypes';
export default defineComponent({
name: 'ActionProperty',
components: {
InputText,
SelectBox,
DatePicker,
FieldInput
FieldInput,
EventSetter
},
props: {
jsonData: {
@@ -31,27 +33,43 @@ export default defineComponent({
required: false,
}
},
computed: {
componentData() {
return this.jsonData.elements.map((element: any) => {
if(this.jsonValue != undefined )
setup(props){
const componentData=computed<Array<IActionProperty>>(()=>{
return props.jsonData.elements.map((element: any) => {
if(props.jsonValue != undefined )
{
if(this.jsonValue.hasOwnProperty(element.props.name))
if(props.jsonValue.hasOwnProperty(element.props.name))
{
element.props.modelValue = this.jsonValue[element.props.name];
element.props.modelValue = props.jsonValue[element.props.name];
}
else
{
element.props.modelValue = '';
}
}
return {
component: element.component,
props: element.props,
};
});
},
},
});
const connectProps=(props:IProp)=>{
const connProps:any={};
if(props && "connectProps" in props && props.connectProps!=undefined){
for(let connProp of props.connectProps){
let targetProp = componentData.value.find((prop)=>prop.props.name===connProp.propName);
if(targetProp){
connProps[connProp.key]=targetProp;
}
}
}
return connProps;
}
return{
componentData,
connectProps
}
}
});
</script>

View File

@@ -0,0 +1,80 @@
<template>
<q-input :label="displayName" v-model="inputValue" label-color="primary" :placeholder="placeholder" stack-label>
<template v-slot:append>
<q-btn round dense flat icon="add" @click="addButtonEvent()" />
</template>
</q-input>
</template>
<script lang="ts">
import { defineComponent,ref,watchEffect } from 'vue';
import { useFlowEditorStore } from '../../stores/flowEditor';
import { IKintoneEventGroup,kintoneEvent } from 'src/types/KintoneEvents';
export default defineComponent({
name: 'EventSetter',
props: {
displayName:{
type: String,
default: '',
},
name:{
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
hint:{
type: String,
default: '',
},
modelValue: {
type: String,
default: '',
},
connectProps:{
type:Object,
default:undefined
}
},
setup(props , { emit }) {
const inputValue = ref(props.modelValue);
const store = useFlowEditorStore();
const addButtonEvent=()=>{
const eventId =store.currentFlow?.getRoot()?.name;
if(eventId===undefined){return;}
let displayName = inputValue.value;
if(props.connectProps!==undefined && "displayName" in props.connectProps){
displayName =props.connectProps["displayName"].props.modelValue;
}
const customButtonId=`${eventId}.customButtonClick`;
const findedEvent = store.eventTree.findEventById(customButtonId);
if(findedEvent && "events" in findedEvent){
const customEvents = findedEvent as IKintoneEventGroup;
const addEventId = customButtonId+"." + inputValue.value;
if(store.eventTree.findEventById(addEventId)){
return;
}
customEvents.events.push(
new kintoneEvent(
displayName,
addEventId,
customButtonId)
);
}
}
watchEffect(() => {
emit('update:modelValue', inputValue.value);
});
return {
inputValue,
addButtonEvent
};
},
});
</script>

View File

@@ -1,7 +1,7 @@
<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>
<component :is="item.component" v-bind="item.props" :connectProps="connectProps(item.props)" v-model="item.props.modelValue"></component>
</div>
</div>
</template>
@@ -17,7 +17,8 @@ import DatePicker from '../right/DatePicker.vue';
import FieldInput from '../right/FieldInput.vue';
import MuiltInputText from '../right/MuiltInputText.vue';
import ConditionInput from '../right/ConditionInput.vue';
import { IActionNode,IActionProperty } from 'src/types/ActionTypes';
import EventSetter from '../right/EventSetter.vue';
import { IActionNode,IActionProperty,IProp } from 'src/types/ActionTypes';
export default defineComponent({
name: 'PropertyList',
@@ -27,7 +28,8 @@ export default defineComponent({
DatePicker,
FieldInput,
MuiltInputText,
ConditionInput
ConditionInput,
EventSetter
},
props: {
nodeProps: {
@@ -40,9 +42,22 @@ export default defineComponent({
}
},
setup(props, context) {
const properties=ref(props.nodeProps)
const properties=ref(props.nodeProps);
const connectProps=(props:IProp)=>{
const connProps:any={};
if(props && "connectProps" in props && props.connectProps!=undefined){
for(let connProp of props.connectProps){
let targetProp = properties.value.find((prop)=>prop.props.name===connProp.propName);
if(targetProp){
connProps[connProp.key]=targetProp;
}
}
}
return connProps;
}
return {
properties
properties,
connectProps
}
}
});

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { AppInfo ,IActionFlow, IActionNode} from 'src/types/ActionTypes';
import { kintoneEvents,IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents';
import { IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents';
import {FlowCtrl } from '../control/flowctrl';
export interface FlowEditorState{
@@ -14,6 +14,7 @@ export interface FlowEditorState{
expandedScreen:any[];
}
const flowCtrl=new FlowCtrl();
const eventTree = new KintoneEventManager();
export const useFlowEditorStore = defineStore("flowEditor",{
state: ():FlowEditorState => ({
flowNames1: '',
@@ -21,7 +22,7 @@ export const useFlowEditorStore = defineStore("flowEditor",{
flows:[],
selectedFlow:undefined,
activeNode:undefined,
eventTree:kintoneEvents,
eventTree:eventTree,
selectedEvent:undefined,
expandedScreen:[]
}),

View File

@@ -26,6 +26,8 @@ export interface IProp{
placeholder: string;
//入力提示・説明
hint:string;
//関連属性リスト
connectProps:[{key:string,propName:string}]|undefined;
//プロパティ設定値
modelValue: any;
}
@@ -109,6 +111,7 @@ class ActionProperty implements IActionProperty {
displayName: displayName,
placeholder: placeholder,
hint:hint,
connectProps:undefined,
modelValue: modelValue
};
}

View File

@@ -1,112 +1,197 @@
import { publicDecrypt } from 'crypto';
import {IActionFlow} from './ActionTypes';
export interface TreeNode {
export interface IKintoneEventNode {
label: string;
header:string;
eventId:string;
parentId:string;
}
export interface IKintoneEvent extends TreeNode {
eventId: string;
export interface IKintoneEvent extends IKintoneEventNode {
hasFlow: boolean;
flowData?: IActionFlow;
}
export interface IKintoneScreen extends TreeNode {
label: string;
events: IKintoneEvent[];
export interface IKintoneEventGroup extends IKintoneEventNode {
events: IKintoneEventNode[];
}
export class kintoneEvent implements IKintoneEvent{
eventId: string;
get hasFlow(): boolean{
parentId:string;
get hasFlow(): boolean{
return this.flowData!==undefined && this.flowData.actionNodes.length>1
};
flowData?: IActionFlow | undefined;
label: string;
constructor({eventId,label}:{eventId:string,label:string}){
get header():string{
return "EVENT";
}
constructor(label:string,eventId:string,parentId:string){
this.eventId=eventId;
this.label=label;
this.parentId=parentId;
}
}
export class KintoneEventManager {
public screens: IKintoneScreen[];
export class kintoneEventGroup implements IKintoneEventGroup{
eventId: string;
parentId:string;
label: string;
events: IKintoneEventNode[];
get header():string{
return "EVENTGROUP";
}
constructor(eventId:string,label:string,events:IKintoneEventNode[],parentId:string){
this.eventId=eventId;
this.label=label;
this.events=events;
this.parentId=parentId;
}
}
constructor(screens: IKintoneScreen[]) {
this.screens = screens;
export class kintoneEventForChange implements IKintoneEventGroup{
eventId: string;
parentId:string;
label: string;
events: IKintoneEventNode[];
get header():string{
return "CHANGE";
}
constructor(eventId:string,label:string,events:IKintoneEventNode[],parentId:string){
this.eventId=eventId;
this.label=label;
this.events=events;
this.parentId=parentId;
}
}
export class KintoneEventManager {
public screens: IKintoneEventGroup[];
constructor() {
this.screens = this.getKintoneEvents();
}
public bindFlows(flows:IActionFlow[]){
for (const screen of this.screens) {
screen.events.forEach((ev)=>ev.flowData=undefined);
}
this.screens=this.getKintoneEvents();
for (const flow of flows){
const eventId =flow.getRoot()?.name;
if(eventId!==undefined){
const event = this.findEventById(eventId);
if(event!==null){
const eventNode = this.findEventById(eventId);
if(eventNode!==null && eventNode.header==="EVENT"){
const event =eventNode as kintoneEvent;
event.flowData=flow;
}else{
//EventGroupのIDを取得
const lastIndex = eventId.lastIndexOf(".");
const groupId=eventId.substring(0,lastIndex);
const eventNode = this.findEventById(groupId);
if(eventNode && (eventNode.header==="EVENTGROUP" || eventNode.header==="CHANGE")){
const groupEvent=eventNode as kintoneEventGroup;
const newEvent =new kintoneEvent(
flow.getRoot()?.subTitle || "",
eventId,
groupEvent.parentId
);
newEvent.flowData=flow;
groupEvent.events.push(newEvent);
}
}
}
}
}
public findEventById(eventId: string): IKintoneEvent | null {
public findEventById(eventId: string): IKintoneEventNode | null {
const screen=this.findScreen(eventId);
if(screen) {return screen;}
for (const screen of this.screens) {
for (const event of screen.events) {
if (event.eventId === eventId) {
return event;
}
if (event.eventId === eventId) {
return event;
}
if(event.header==="EVENTGROUP"||event.header==="CHANGE"){
const eventGroup = event as IKintoneEventGroup;
const targetEvent = eventGroup.events.find((ev)=>{
return ev.eventId===eventId;
})
if(targetEvent){
return targetEvent;
}
}
}
}
return null;
}
public findScreen(eventId:string):IKintoneScreen|null{
for (const screen of this.screens) {
if(screen.events.some((ev:IKintoneEvent)=>ev.eventId===eventId)){
return screen;
}
}
return null;
public findScreen(eventId:string):IKintoneEventGroup|undefined{
return this.screens.find(screen=>screen.eventId==eventId);
}
public getKintoneEvents():IKintoneEventGroup[]{
return [
new kintoneEventGroup("app.record.create","レコード追加画面",[
new kintoneEvent('レコード追加画面を表示した後','app.record.create.show',"app.record.create"),
new kintoneEvent('保存をクリックしたとき','app.record.create.submit',"app.record.create"),
new kintoneEvent('保存が成功したとき','app.record.create.submit.success',"app.record.create"),
new kintoneEventForChange('app.record.create.change','フィールドの値を変更したとき',[],"app.record.create"),
new kintoneEventGroup('app.record.create.show.customButtonClick','ボタンをクリックした時',[],"app.record.create")
],""),
new kintoneEventGroup("app.record.detail","レコード詳細画面",[
new kintoneEvent('レコード詳細画面を表示した後','app.record.detail.show',"app.record.detail"),
new kintoneEvent('レコードを削除するとき','app.record.detail.delete.submit',"app.record.detail"),
new kintoneEvent('プロセス管理のアクションを実行したとき','app.record.detail.process.proceed',"app.record.detail"),
new kintoneEventGroup('app.record.detail.show.customButtonClick','ボタンをクリックした時',[],"app.record.detail"),
],""),
new kintoneEventGroup("app.record.edit","レコード編集画面",[
new kintoneEvent('レコード編集画面を表示した後','app.record.edit.show',"app.record.edit"),
new kintoneEvent('保存をクリックしたとき','app.record.edit.submit',"app.record.edit"),
new kintoneEvent('保存が成功したとき','app.record.edit.submit.success',"app.record.edit"),
new kintoneEventForChange('app.record.edit.change','フィールドの値を変更したとき',[],"app.record.edit"),
new kintoneEventGroup('app.record.edit.show.customButtonClick','ボタンをクリックした時',[],"app.record.edit"),
],""),
new kintoneEventGroup("app.record.index","レコード一覧画面",[
new kintoneEvent('一覧画面を表示した後', 'app.record.index.show',"app.record.index"),
new kintoneEvent('インライン編集を開始したとき','app.record.index.edit.show',"app.record.index"),
new kintoneEvent('インライン編集の【保存】をクリックしたとき','app.record.index.edit.submit',"app.record.index"),
new kintoneEvent('インライン編集の保存が成功したとき', 'app.record.index.edit.submit.success',"app.record.index"),
new kintoneEventForChange('app.record.index.edit.change','インライン編集のフィールド値を変更したとき' ,[],"app.record.index"),
new kintoneEventGroup('app.record.detail.show.customButtonClick','ボタンをクリックした時',[],"app.record.index"),
],"")
];
}
}
export const kintoneEvents:KintoneEventManager = new KintoneEventManager([
{
label:'レコード追加画面',
events:[
new kintoneEvent({label:'レコード追加画面を表示した後',eventId:'app.record.create.show'}),
new kintoneEvent({label:'保存をクリックしたとき',eventId:'app.record.create.submit'}),
new kintoneEvent({label:'保存が成功したとき',eventId:'app.record.create.submit.success'}),
new kintoneEvent({label:'フィールドの値を変更したとき',eventId:'app.record.create.change'}),
]
},
{
label:'レコード詳細画面',
events:[
new kintoneEvent({label:'レコード詳細画面を表示した後',eventId:'app.record.detail.show'}),
new kintoneEvent({label:'レコードを削除するとき',eventId:'app.record.detail.delete.submit'}),
new kintoneEvent({label:'プロセス管理のアクションを実行したとき',eventId:'app.record.detail.process.proceed'}),
]
},
{
label:'レコード編集画面',
events:[new kintoneEvent({label:'レコード編集画面を表示した後',eventId:'app.record.edit.show'}),
new kintoneEvent({label:'保存をクリックしたとき',eventId:'app.record.edit.submit'}),
new kintoneEvent({label:'保存が成功したとき',eventId:'app.record.edit.submit.success'}),
new kintoneEvent({label:'フィールドの値を変更したとき',eventId:'app.record.edit.change'}),
]
},
{
label:'レコード一覧画面',
events:[
new kintoneEvent({label:'一覧画面を表示した後', eventId:'app.record.index.show'}),
new kintoneEvent({label:'インライン編集を開始したとき',eventId:'app.record.index.edit.show'}),
new kintoneEvent({label:'インライン編集のフィールド値を変更したとき', eventId:'app.record.index.edit.change'}),
new kintoneEvent({label:'インライン編集の【保存】をクリックしたとき',eventId:'app.record.index.edit.submit'}),
new kintoneEvent({label:'インライン編集の保存が成功したとき', eventId:'app.record.index.edit.submit.success'}),
]
}
]);
// export const kintoneEvents:KintoneEventManager = new KintoneEventManager([
// new kintoneEventGroup("app.record.create","レコード追加画面",[
// new kintoneEvent('レコード追加画面を表示した後','app.record.create.show',"app.record.create"),
// new kintoneEvent('保存をクリックしたとき','app.record.create.submit',"app.record.create"),
// new kintoneEvent('保存が成功したとき','app.record.create.submit.success',"app.record.create"),
// new kintoneEventForChange('app.record.create.change','フィールドの値を変更したとき',[],"app.record.create"),
// new kintoneEventGroup('app.record.create.customButtonClick','ボタンをクリックした時',[],"app.record.create")
// ],""),
// new kintoneEventGroup("app.record.detail","レコード詳細画面",[
// new kintoneEvent('レコード詳細画面を表示した後','app.record.detail.show',"app.record.detail"),
// new kintoneEvent('レコードを削除するとき','app.record.detail.delete.submit',"app.record.detail"),
// new kintoneEvent('プロセス管理のアクションを実行したとき','app.record.detail.process.proceed',"app.record.detail"),
// new kintoneEventGroup('app.record.detail.customButtonClick','ボタンをクリックした時',[],"app.record.detail"),
// ],""),
// new kintoneEventGroup("app.record.edit","レコード編集画面",[
// new kintoneEvent('レコード編集画面を表示した後','app.record.edit.show',"app.record.edit"),
// new kintoneEvent('保存をクリックしたとき','app.record.edit.submit',"app.record.edit"),
// new kintoneEvent('保存が成功したとき','app.record.edit.submit.success',"app.record.edit"),
// new kintoneEventForChange('app.record.edit.change','フィールドの値を変更したとき',[],"app.record.edit"),
// new kintoneEventGroup('app.record.edit.customButtonClick','ボタンをクリックした時',[],"app.record.edit"),
// ],""),
// new kintoneEventGroup("app.record.index","レコード一覧画面",[
// new kintoneEvent('一覧画面を表示した後', 'app.record.index.show',"app.record.index"),
// new kintoneEvent('インライン編集を開始したとき','app.record.index.edit.show',"app.record.index"),
// new kintoneEvent('インライン編集の【保存】をクリックしたとき','app.record.index.edit.submit',"app.record.index"),
// new kintoneEvent('インライン編集の保存が成功したとき', 'app.record.index.edit.submit.success',"app.record.index"),
// new kintoneEventForChange('app.record.index.edit.change','インライン編集のフィールド値を変更したとき' ,[],"app.record.index"),
// new kintoneEventGroup('app.record.detail.customButtonClick','ボタンをクリックした時',[],"app.record.index"),
// ],"")
// ]);

View File

@@ -0,0 +1,107 @@
import { actionAddins } from ".";
import $ from 'jquery';
import { IAction, IActionProperty, IActionNode, IActionResult } from "../types/ActionTypes";
/**
* ボタン配置属性定義
*/
interface IButtonAddProps {
//ボタン表示名
buttonName: string;
//配置位置
position: string;
//イベント名
eventName:string
}
export class ButtonAddAction implements IAction {
name: string;
actionProps: IActionProperty[];
props: IButtonAddProps;
constructor() {
this.name = "ボタンの配置";
this.actionProps = [];
this.props = {
buttonName: '',
position: '',
eventName:''
}
this.register();
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode: IActionNode, event: any): Promise<IActionResult> {
let result = {
canNext: true,
result: false
};
try {
this.actionProps = actionNode.actionProps;
if (!('buttonName' in actionNode.ActionValue) && !('position' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as IButtonAddProps;
//ボタンを配置する
const menuSpace = kintone.app.record.getHeaderMenuSpaceElement();
if(!menuSpace) return result;
if($("style#alc-button-add").length===0){
const css=`
.alc-button-normal {
display: inline-block;
box-sizing: border-box;
padding: 0 16px;
margin-left: 16px;
margin-top: 8px;
min-width: 100px;
outline: none;
border: 1px solid #e3e7e8;
background-color: #f7f9fa;
box-shadow: 1px 1px 1px #fff inset;
color: #3498db;
text-align: center;
line-height: 32px;
}
.alc-button-normal:hover {
background-color: #c8d6dd;
box-shadow: none;
cursor: pointer;
}
.alc-button-normal:active {
color: #f7f9fa;
background-color: #54b8eb;
}`;
const style = $("<style id='alc-button-add'>/<style>");
style.text(css);
$("head").append(style);
}
const button =$(`<button id='${this.props.eventName}' class='alc-button-normal' >${this.props.buttonName}</button>`);
if(this.props.position==="一番左に追加する"){
$(menuSpace).prepend(button);
}else{
$(menuSpace).append(button);
}
const clickEventName = `${event.type}.customButtonClick.${this.props.eventName}`;
button.on("click",()=>{
$(document).trigger(clickEventName,event);
});
return result;
} catch (error) {
event.error = error;
console.error(error);
result.canNext = false;
return result;
}
}
register(): void {
actionAddins[this.name] = this;
}
}
new ButtonAddAction();

View File

@@ -31,7 +31,7 @@ export class ErrorShowAction implements IAction {
* @returns
*/
async process(actionNode: IActionNode, event: any, context: IContext): Promise<IActionResult> { //异步处理某函数下的xx属性
let result = { //定义一个能否继续往下实行的开关
let result = {
canNext: true,
result: false
};
@@ -40,10 +40,10 @@ export class ErrorShowAction implements IAction {
if (!('message' in actionNode.ActionValue) && !('condition' in actionNode.ActionValue)) { //如果message以及condition两者都不存在的情况下return
return result
}
this.props = actionNode.ActionValue as IErrorShowProps; //将TS的action.actionvalue as转换为interface需要且定义的js部分
this.props = actionNode.ActionValue as IErrorShowProps;
const conditionResult = this.getConditionResult(context);
if (conditionResult) {
event.error = this.props.message
event.error = this.props.message;
} else {
result = {
canNext: false,

View File

@@ -14,14 +14,58 @@ declare const alcflow : {
};
$(function (){
const getChangeEvents=(events:string[])=>{
return events.filter((event)=>event.includes(".change."));
}
const getClickEvents=(events:string[])=>{
return events.filter((event)=>event.includes(".customButtonClick."));
}
const getKintoneEvents=(events:string[])=>{
return events.filter((event)=>{
return !event.includes(".customButtonClick.") && !event.includes(".change.")
});
}
const events=Object.keys(alcflow);
kintone.events.on(events,async (event:any)=>{
const flowinfo = alcflow[event.type];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(event.type,flow,event);
await process.exec();
}
return event;
});
const changeEvents = getChangeEvents(events);
const clickEvents = getClickEvents(events);
const kintoneEvents = getKintoneEvents(events);
if(kintoneEvents.length>0 ){
//通常Kintoneイベントをバンド
kintone.events.on(kintoneEvents,async (event:any)=>{
const flowinfo = alcflow[event.type];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(event.type,flow,event);
await process.exec();
}
return event;
});
}
if(changeEvents.length>0){
//値変更イベントをバンドする
kintone.events.on(changeEvents,(event:any)=>{
const flowinfo = alcflow[event.type];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(event.type,flow,event);
process.exec();
}
return event;
});
}
if(clickEvents.length>0){
clickEvents.forEach((eventName:string)=>{
$(document).on(eventName,async ()=>{
const event=kintone.app.record.get();
const flowinfo = alcflow[eventName];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(eventName,flow,event);
await process.exec();
}
const record = event.record;
kintone.app.record.set({record})
});
});
}
});

View File

@@ -257,7 +257,7 @@ export class ActionFlow implements IActionFlow {
const actionNodes = parsedObject.actionNodes.map((node: any) => {
const nodeClass = !node.isRoot ? new ActionNode(node)
: new RootAction(node);
nodeClass.nextNodeIds = new Map(node.nextNodeIds);
nodeClass.nextNodeIds = new Map<string,string>(Object.entries(node.nextNodeIds));
nodeClass.prevNodeId = node.prevNodeId;
nodeClass.id = node.id;
return nodeClass;

View File

@@ -324,7 +324,7 @@ export class ConditionTree {
*/
getObjectValue(object:any,context:IContext){
if(!object || typeof object!=="object" || !("objectType" in object)){
return object;
return undefined;
}
if(object.objectType==='field'){
const fieldValue = context.record[object.code];
@@ -402,6 +402,12 @@ export class ConditionTree {
*/
compare(operator: Operator, targetValue: any, value: any): boolean {
// targetValue は日期时value も日期に変換して比較する
if(targetValue===undefined || targetValue===null||targetValue===''){
if(value===undefined || value===null||value===''){
targetValue='';
value='';
}
}
if (targetValue instanceof Date) {
const dateValue = new Date(value);
if (!isNaN(dateValue.getTime())) {

View File

@@ -4,6 +4,7 @@ import '../actions/must-input';
import '../actions/auto-numbering';
import '../actions/field-shown';
import '../actions/error-show';
import '../actions/button-add';
import { ActionFlow,IActionFlow, IActionResult,IContext } from "./ActionTypes";
export class ActionProcess{

View File

@@ -1,167 +1,37 @@
{
"id": "e4929dfe-32bb-448a-bd66-83c19dad9c79",
"actionNodes": [
{
"id": "06388b74-ef56-4ba2-9fba-2dbde999c34d",
"name": "app.record.create.submit",
"title": "レコード追加画面",
"subTitle": "保存をクリックしたとき",
"inputPoint": "",
"outputPoints": [],
"isRoot": true,
"actionProps": [],
"ActionValue": {},
"nextNodeIds": {
"": "5963b63a-bfea-49d0-9a8e-b57abe99e2a7"
}
},
{
"id": "5963b63a-bfea-49d0-9a8e-b57abe99e2a7",
"name": "自動採番する",
"inputPoint": "",
"outputPoints": [],
"actionProps": [
{
"component": "InputText",
"props": {
"name": "displayName",
"displayName": "表示名",
"placeholder": "表示を入力してください",
"hint": "",
"modelValue": "文書番号を自動採番する"
}
},
{
"component": "FieldInput",
"props": {
"displayName": "採番項目",
"modelValue": {},
"name": "field",
"placeholder": "採番項目を選択してください"
}
},
{
"component": "InputText",
"props": {
"displayName": "フォーマット",
"modelValue": "000000",
"name": "format",
"placeholder": "数値書式文字列を指定します"
}
},
{
"component": "InputText",
"props": {
"displayName": "前につける文字列",
"modelValue": "",
"name": "prefix",
"placeholder": "前につける文字列を入力してください"
}
},
{
"component": "InputText",
"props": {
"displayName": "後ろにつける文字列",
"modelValue": "",
"name": "suffix",
"placeholder": "後ろにつける文字列を入力してください"
}
},
{
"component": "InputText",
"props": {
"displayName": "結果(戻り値)",
"modelValue": "DocumentNo",
"name": "verName",
"placeholder": "変数名を入力してください"
}
}
],
"prevNodeId": "06388b74-ef56-4ba2-9fba-2dbde999c34d",
"nextNodeIds": {
"": "f1fb6e7c-540c-4fcc-8d30-bd22aed7f923"
}
},
{
"id": "f1fb6e7c-540c-4fcc-8d30-bd22aed7f923",
"name": "条件式",
"inputPoint": "",
"outputPoints": [
"はい",
"いいえ"
],
"actionProps": [
{
"component": "InputText",
"props": {
"name": "displayName",
"displayName": "表示名",
"placeholder": "表示を入力してください",
"hint": "",
"modelValue": "条件式を設定する"
}
},
{
"component": "ConditionInput",
"props": {
"displayName": "条件",
"modelValue": "{\"index\":0,\"type\":\"root\",\"children\":[{\"index\":1,\"type\":\"condition\",\"parent\":\"root\",\"object\":{\"name\":\"ステータス\",\"objectType\":\"field\",\"type\":\"STATUS\",\"code\":\"ステータス\",\"label\":\"ステータス\",\"enabled\":true},\"operator\":\"=\",\"value\":\"作成中\"},{\"index\":2,\"type\":\"condition\",\"parent\":\"root\",\"object\":{\"name\":\"部署\",\"objectType\":\"field\",\"type\":\"DROP_DOWN\",\"code\":\"ドロップダウン\",\"label\":\"部署\",\"noLabel\":false,\"required\":false,\"options\":{\"総務\":{\"label\":\"総務\",\"index\":\"2\"},\"サポート\":{\"label\":\"サポート\",\"index\":\"3\"},\"マーケティング\":{\"label\":\"マーケティング\",\"index\":\"1\"},\"営業\":{\"label\":\"営業\",\"index\":\"0\"},\"開発\":{\"label\":\"開発\",\"index\":\"4\"}},\"defaultValue\":\"\"},\"operator\":\"!=\",\"value\":\"\"},{\"index\":4,\"type\":\"condition\",\"parent\":\"root\",\"object\":{\"objectType\":\"variable\",\"actionName\":\"自動採番する\",\"displayName\":\"結果(戻り値)\",\"name\":\"DocumentNo\"},\"operator\":\"=\",\"value\":\"\"}],\"parent\":null,\"logicalOperator\":\"AND\"}",
"name": "condition",
"placeholder": "条件式を設定してください"
}
},
{
"component": "InputText",
"props": {
"displayName": "結果(戻り値)",
"modelValue": "conditionValue",
"name": "verName",
"placeholder": "変数名を入力してください"
}
}
],
"prevNodeId": "5963b63a-bfea-49d0-9a8e-b57abe99e2a7",
"nextNodeIds": {
"いいえ": "81987177-746f-44f2-8cd5-acf52ebb83bb"
}
},
{
"id": "81987177-746f-44f2-8cd5-acf52ebb83bb",
"name": "エラー表示",
"inputPoint": "いいえ",
"outputPoints": [],
"actionProps": [
{
"component": "InputText",
"props": {
"name": "displayName",
"displayName": "表示名",
"placeholder": "表示を入力してください",
"hint": "",
"modelValue": "入力した内容が不適切な場合"
}
},
{
"component": "ConditionInput",
"props": {
"displayName": "条件",
"modelValue": "{\"index\":0,\"type\":\"root\",\"children\":[{\"index\":1,\"type\":\"condition\",\"parent\":\"root\",\"object\":{\"objectType\":\"variable\",\"actionName\":\"条件式\",\"displayName\":\"結果(戻り値)\",\"name\":\"conditionValue\"},\"operator\":\"=\",\"value\":\"いいえ\"}],\"parent\":null,\"logicalOperator\":\"AND\"}",
"name": "condition",
"placeholder": "条件を選択または入力してください"
}
},
{
"component": "MuiltInputText",
"props": {
"displayName": "エラーメッセージ",
"modelValue": "",
"name": "message",
"placeholder": "エラーメッセージを入力してください"
}
}
],
"prevNodeId": "f1fb6e7c-540c-4fcc-8d30-bd22aed7f923",
"nextNodeIds": {}
[
{
"component": "InputText",
"props": {
"displayName": "ボタン名",
"modelValue": "",
"name": "buttonName",
"placeholder": "ボタンのラベルを入力してください"
}
]
}
},
{
"component": "SelectBox",
"props": {
"displayName": "追加位置",
"modelValue": "",
"name": "position",
"options":[
"一番右に追加する",
"一番左に追加する"
],
"placeholder": "追加位置を選択してください"
}
},
{
"component": "EventSetter",
"props": {
"displayName": "イベント名",
"modelValue": "",
"name": "eventName",
"connectProps":[{
"key":"displayName",
"propName":"buttonName"
}],
"placeholder": "イベント名を入力してください"
}
}
]