Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef6cc44d50 | |||
| 1914a8b1d6 | |||
| 2353f414b3 | |||
| e1a6af7e57 | |||
| be05c0879f | |||
| 66d91f01b7 |
@@ -0,0 +1,10 @@
|
|||||||
|
<mxfile host="65bd71144e">
|
||||||
|
<diagram id="UMFNb7zEleFpzTH4EAp9" name="ページ1">
|
||||||
|
<mxGraphModel dx="1202" dy="612" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
BIN
document/Kintone自動化ツール画面設計.xlsx
Normal file
BIN
document/Kintone自動化ツール画面設計.xlsx
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
1479
document/Kintone自動化ツール設計図2.drawio
Normal file
1479
document/Kintone自動化ツール設計図2.drawio
Normal file
File diff suppressed because one or more lines are too long
38
frontend/src/components/ActionNode.vue
Normal file
38
frontend/src/components/ActionNode.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-pa-md" style="max-width: 350px">
|
||||||
|
<q-expansion-item
|
||||||
|
class="shadow-1 overflow-hidden"
|
||||||
|
style="border-radius: 30px"
|
||||||
|
icon="explore"
|
||||||
|
label="Counter"
|
||||||
|
@show="startCounting"
|
||||||
|
@hide="stopCounting"
|
||||||
|
header-class="bg-primary text-white"
|
||||||
|
expand-icon-class="text-white"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
Counting: <q-badge color="secondary">{{ counter }}</q-badge>.
|
||||||
|
Will only count when opened, using the show/hide events to control count timer.
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent} from "vue"
|
||||||
|
export default defineComponent({
|
||||||
|
props:{
|
||||||
|
icon:String,
|
||||||
|
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="">
|
||||||
|
|
||||||
|
</style>
|
||||||
30
frontend/src/components/ActionPanel.vue
Normal file
30
frontend/src/components/ActionPanel.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent,reactive} from 'vue';
|
||||||
|
import {KintoneEvent} from "../models/kintone";
|
||||||
|
export default defineComponent({
|
||||||
|
setup(props, ctx) {
|
||||||
|
const events = reactive<KintoneEvent[]>(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
screen:"レコード追加画面",
|
||||||
|
type:"app.record.create.show",
|
||||||
|
name:"レコード追加画面を表示した後"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
screen:"レコード追加画面",
|
||||||
|
type:"app.record.create.show",
|
||||||
|
name:"レコード追加画面を表示した後"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
13
frontend/src/components/PropertyList.vue
Normal file
13
frontend/src/components/PropertyList.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="">
|
||||||
|
|
||||||
|
</style>
|
||||||
0
frontend/src/components/main/ActionNode.vue
Normal file
0
frontend/src/components/main/ActionNode.vue
Normal file
@@ -1,107 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="row justify-center" :style="{marginLeft:node.inputPoint!==''?'240px':''}">
|
|
||||||
<div class="row">
|
|
||||||
<q-card class="action-node" :class="{'root-node':node.isRoot,'text-white':node.isRoot}" :square="false">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">{{ node.title }}</div>
|
|
||||||
<div class="text-subtitle2">{{ node.subTitle }}</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">
|
|
||||||
<div class="row justify-center" :style="{marginLeft:node.inputPoint!==''?'240px':''}">
|
|
||||||
<div v-for="(point,index) in node.outputPoints" :key="index">
|
|
||||||
<node-line :action-node="node" :mode="getMode(point)" @addNode="addNode" :input-point="point" ></node-line>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="!hasBranch">
|
|
||||||
<div class="row justify-center" :style="{marginLeft:node.inputPoint!==''?'240px':''}">
|
|
||||||
<node-line :action-node="node" :mode="getMode('')" @addNode="addNode" input-point=""></node-line>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import {defineComponent,computed} from 'vue';
|
|
||||||
import {IActionNode } from '../../types/ActionTypes';
|
|
||||||
import NodeLine,{Direction} from '../main/NodeLine.vue';
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'NodeItem',
|
|
||||||
components: {
|
|
||||||
NodeLine
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
actionNode:{
|
|
||||||
type:Object as ()=>IActionNode,
|
|
||||||
required:true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
emits: [
|
|
||||||
'addNode'
|
|
||||||
],
|
|
||||||
setup(props,context){
|
|
||||||
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
|
|
||||||
const getMode =(point:string)=>{
|
|
||||||
if(point==='' || props.actionNode.outputPoints.length===0){
|
|
||||||
return Direction.Default;
|
|
||||||
}
|
|
||||||
if(point===props.actionNode.outputPoints[0]){
|
|
||||||
if(props.actionNode.nextNodes.get(point)){
|
|
||||||
return Direction.Left;
|
|
||||||
}else{
|
|
||||||
return Direction.LeftNotNext;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(props.actionNode.nextNodes.get(point)){
|
|
||||||
return Direction.Right;
|
|
||||||
}else{
|
|
||||||
return Direction.RightNotNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const addNode=(point:string)=>{
|
|
||||||
context.emit('addNode',props.actionNode,point);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
node:props.actionNode,
|
|
||||||
hasBranch,
|
|
||||||
isRoot:props.actionNode.isRoot,
|
|
||||||
getMode,
|
|
||||||
addNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.action-node{
|
|
||||||
min-width:250px !important;
|
|
||||||
}
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<svg class="node-line">
|
|
||||||
<polyline :points="points.linePoints" class="line" ></polyline>
|
|
||||||
<text class="add-icon" @click="addNode(node)" :x="points.iconPoint.x" :y="points.iconPoint.y" font-family="Arial" font-size="25" text-anchor="middle" dy=".3em" style="cursor: pointer;" >
|
|
||||||
⊕
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { ref, defineComponent, computed, PropType } from 'vue';
|
|
||||||
import { IActionNode, ActionNode, ActionFlow, RootAction } from '../../types/ActionTypes';
|
|
||||||
export enum Direction {
|
|
||||||
Default = "None",
|
|
||||||
Left = "LEFT",
|
|
||||||
Right = "RIGHT",
|
|
||||||
LeftNotNext = "LEFTNOTNEXT",
|
|
||||||
RightNotNext = "RIGHTNOTNEXT",
|
|
||||||
}
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'NodeLine',
|
|
||||||
props: {
|
|
||||||
actionNode: {
|
|
||||||
type: Object as PropType<IActionNode>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
mode: {
|
|
||||||
type: String as PropType<Direction>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
inputPoint:{
|
|
||||||
type:String
|
|
||||||
}
|
|
||||||
},
|
|
||||||
emits: ['addNode'],
|
|
||||||
setup(props,context) {
|
|
||||||
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
|
|
||||||
const points = computed(() => {
|
|
||||||
switch (props.mode) {
|
|
||||||
case Direction.Left:
|
|
||||||
return {
|
|
||||||
linePoints: '180, 0, 180, 40, 120, 40, 120, 60',
|
|
||||||
iconPoint: { x: 180, y: 20 }
|
|
||||||
};
|
|
||||||
case Direction.Right:
|
|
||||||
return {
|
|
||||||
linePoints: '60, 0, 60, 40, 120, 40, 120, 60',
|
|
||||||
iconPoint: { x: 60, y: 20 }
|
|
||||||
};
|
|
||||||
case Direction.LeftNotNext:
|
|
||||||
return {
|
|
||||||
linePoints: '180, 0, 180, 40',
|
|
||||||
iconPoint: { x: 180, y: 20 }
|
|
||||||
};
|
|
||||||
case Direction.RightNotNext:
|
|
||||||
return {
|
|
||||||
linePoints: '60, 0, 60, 40',
|
|
||||||
iconPoint: { x: 60, y: 30 }
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
linePoints: '120, 0, 120, 60',
|
|
||||||
iconPoint: { x: 120, y: 30 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const addNode=(prveNode:IActionNode)=>{
|
|
||||||
context.emit('addNode',props.inputPoint);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
node: props.actionNode,
|
|
||||||
hasBranch,
|
|
||||||
points,
|
|
||||||
addNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.node-line {
|
|
||||||
height: 60px;
|
|
||||||
width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
stroke: $blue-7;
|
|
||||||
fill: none;
|
|
||||||
stroke-width: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-icon {
|
|
||||||
stroke: $blue-7;
|
|
||||||
fill: $blue-7;
|
|
||||||
font-family: Arial;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -32,3 +32,4 @@ export interface AppInfo {
|
|||||||
creator?:User;
|
creator?:User;
|
||||||
modifier?:User;
|
modifier?:User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } 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';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'ActionProperty',
|
|
||||||
components: {
|
|
||||||
InputText,
|
|
||||||
SelectBox,
|
|
||||||
DatePicker,
|
|
||||||
FieldInput
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
jsonData: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
jsonValue:{
|
|
||||||
type: Object,
|
|
||||||
required: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
componentData() {
|
|
||||||
return this.jsonData.elements.map((element: any) => {
|
|
||||||
if(this.jsonValue != undefined )
|
|
||||||
{
|
|
||||||
if(this.jsonValue.hasOwnProperty(element.props.name))
|
|
||||||
{
|
|
||||||
element.props.modelValue = this.jsonValue[element.props.name];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
element.props.modelValue = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
component: element.component,
|
|
||||||
props: element.props,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<template>
|
|
||||||
|
|
||||||
<q-input v-model="selectedDate" :label="placeholder" mask="date" :rules="['date']">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="event" class="cursor-pointer">
|
|
||||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
|
||||||
<q-date v-model="selectedDate">
|
|
||||||
<div class="row items-center justify-end">
|
|
||||||
<q-btn v-close-popup label="Close" color="primary" flat />
|
|
||||||
</div>
|
|
||||||
</q-date>
|
|
||||||
</q-popup-proxy>
|
|
||||||
</q-icon>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, ref ,watchEffect} from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'DatePicker',
|
|
||||||
props: {
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const selectedDate = ref(props.modelValue);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit('update:modelValue', selectedDate.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
selectedDate
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-input v-model="selectedField" :label="placeholder">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" class="cursor-pointer" @click="showDg"/>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeDg">
|
|
||||||
<field-select ref="appDg" name="フィールド" type="single" :appId="1"></field-select>
|
|
||||||
</show-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, ref ,watchEffect} from 'vue';
|
|
||||||
import ShowDialog from '../ShowDialog.vue';
|
|
||||||
import FieldSelect from '../FieldSelect.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'FieldInput',
|
|
||||||
components: {
|
|
||||||
ShowDialog,
|
|
||||||
FieldSelect,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const appDg = ref();
|
|
||||||
const show = ref(false);
|
|
||||||
const selectedField = ref(props.modelValue);
|
|
||||||
|
|
||||||
const showDg = () => {
|
|
||||||
show.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDg = (val:string) => {
|
|
||||||
if (val == 'OK') {
|
|
||||||
selectedField.value = appDg.value.selected[0].name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit('update:modelValue', selectedField.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
appDg,
|
|
||||||
show,
|
|
||||||
showDg,
|
|
||||||
closeDg,
|
|
||||||
selectedField,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-input :label="placeholder" v-model="inputValue"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent,ref,watchEffect } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'InputText',
|
|
||||||
props: {
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const inputValue = ref(props.modelValue);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit('update:modelValue', inputValue.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
inputValue,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-select v-model="selectedValue" :label="placeholder" :options="options"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent,ref,watchEffect } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'SelectBox',
|
|
||||||
props: {
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const selectedValue = ref(props.modelValue);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit('update:modelValue', selectedValue.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
selectedValue
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
8
frontend/src/models/kintone.ts
Normal file
8
frontend/src/models/kintone.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export interface KintoneEvent{
|
||||||
|
screen:string,
|
||||||
|
type:string,
|
||||||
|
name:string
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-page>
|
|
||||||
<div class="flowchart">
|
|
||||||
<node-item v-for="(node,index) in refFlow.actionNodes" :key="index" :actionNode="node" @addNode="addNode"></node-item>
|
|
||||||
</div>
|
|
||||||
</q-page>
|
|
||||||
<show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg">
|
|
||||||
<action-select ref="appDg" name="アクション" type="single"></action-select>
|
|
||||||
</show-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {ref} from 'vue';
|
|
||||||
import {IActionNode, ActionNode, IActionFlow, ActionFlow,RootAction } from 'src/types/ActionTypes';
|
|
||||||
import NodeItem from 'src/components/main/NodeItem.vue';
|
|
||||||
import ShowDialog from 'components/ShowDialog.vue';
|
|
||||||
import ActionSelect from 'components/ActionSelect.vue';
|
|
||||||
|
|
||||||
const rootAction:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき");
|
|
||||||
const actionFlow: ActionFlow = new ActionFlow(rootAction);
|
|
||||||
actionFlow.addNode(new ActionNode('自動採番','文書番号を自動採番する',''));
|
|
||||||
actionFlow.addNode(new ActionNode('入力データ取得','電話番号を取得する',''));
|
|
||||||
const branchNode = actionFlow.addNode(new ActionNode('条件分岐','電話番号入力形式チャック','',['はい','いいえ'] ));
|
|
||||||
actionFlow.addNode(new ActionNode('エラー表示','エラー表示して保存しない',''),branchNode,'いいえ' );
|
|
||||||
|
|
||||||
// ref関数を使ってtemplateとバインド
|
|
||||||
const refFlow = ref(actionFlow);
|
|
||||||
const showAddAction=ref(false);
|
|
||||||
|
|
||||||
const addActionNode=(action:IActionNode)=>{
|
|
||||||
refFlow.value.actionNodes.push(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
const addNode=(node:IActionNode,inputPoint:string)=>{
|
|
||||||
showAddAction.value=true;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.flowchart{
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="min-height: 100vh;">
|
<div style="min-height: 100vh;">
|
||||||
<div class="q-pa-md">
|
<div class="q-pa-md">
|
||||||
<q-btn-dropdown split color="primary" label="ルール新規作成" size="lg">
|
<!-- <q-btn-dropdown split color="primary" label="ルール新規作成" size="lg">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item v-for="action in actions" clickable v-close-popup @click="onItemClick" :key="action">
|
<q-item v-for="action in actions" clickable v-close-popup @click="onItemClick" :key="action">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="q-pa-md">
|
<div class="q-pa-md">
|
||||||
<q-select v-model="model" :options="options" label="Standard"/>
|
<q-select v-model="model" :options="options" label="ダイアログ選択"/>
|
||||||
<q-btn :label="model+'選択'" color="primary" @click="showDg()" />
|
<q-btn :label="model+'選択'" color="primary" @click="showDg()" />
|
||||||
<show-dialog v-model:visible="show" :name="model" @close="closeDg">
|
<show-dialog v-model:visible="show" :name="model" @close="closeDg">
|
||||||
<template v-if="model=='アプリ'">
|
<template v-if="model=='アプリ'">
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="q-pa-md q-gutter-sm">
|
|
||||||
<q-btn label="プロパティ" icon="keyboard_arrow_right" color="primary" @click="open('right')" />
|
|
||||||
<!-- <q-btn label="Readプロパティ" icon="keyboard_arrow_right" color="primary" @click="write('right')" /> -->
|
|
||||||
|
|
||||||
<q-dialog v-model="dialog" :position="position">
|
|
||||||
<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">
|
|
||||||
<ActionProperty :jsonData="jsonData" :jsonValue="jsonValue"/>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-actions align="right" class="bg-white text-teal">
|
|
||||||
<q-btn flat label="Save" v-close-popup @click="save"/>
|
|
||||||
<q-btn flat label="Cancel" v-close-popup />
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref,onMounted } from 'vue'
|
|
||||||
import ActionProperty from 'components/right/ActionProperty.vue';
|
|
||||||
const dialog = ref(false)
|
|
||||||
const position = ref('top')
|
|
||||||
|
|
||||||
const jsonData = {
|
|
||||||
elements: [
|
|
||||||
{
|
|
||||||
component: 'InputText',
|
|
||||||
props: {
|
|
||||||
name:'1',
|
|
||||||
placeholder: 'Enter some text',
|
|
||||||
modelValue: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'SelectBox',
|
|
||||||
props: {
|
|
||||||
name:'2',
|
|
||||||
placeholder: 'Choose an option',
|
|
||||||
modelValue: '',
|
|
||||||
options: [
|
|
||||||
'option1',
|
|
||||||
'option2',
|
|
||||||
'option3'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'DatePicker',
|
|
||||||
props: {
|
|
||||||
name:'3',
|
|
||||||
placeholder: 'Choose a date',
|
|
||||||
modelValue: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'FieldInput',
|
|
||||||
props: {
|
|
||||||
name:'4',
|
|
||||||
placeholder: 'Choose a field',
|
|
||||||
modelValue: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
let jsonValue = {
|
|
||||||
1:'abc',
|
|
||||||
2:'option2',
|
|
||||||
3:'2023/09/04',
|
|
||||||
4:'6666'
|
|
||||||
};
|
|
||||||
|
|
||||||
const open = (pos:string) => {
|
|
||||||
position.value = pos
|
|
||||||
dialog.value = true
|
|
||||||
};
|
|
||||||
|
|
||||||
const save = async () =>{
|
|
||||||
|
|
||||||
jsonData.elements.forEach(property => {
|
|
||||||
if(jsonValue != undefined)
|
|
||||||
{
|
|
||||||
jsonValue[property.props.name] = property.props.modelValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(jsonValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -4,14 +4,19 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
children: [
|
children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
|
||||||
{ path: '', component: () => import('pages/IndexPage.vue') },
|
|
||||||
{ path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') },
|
|
||||||
{ path: 'test', component: () => import('pages/testQursar.vue') },
|
|
||||||
{ path: 'flow', component: () => import('pages/testFlow.vue') },
|
|
||||||
{ path: 'flowchart', component: () => import('pages/FlowChartTest.vue') }
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/ruleEditor/',
|
||||||
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
|
children: [{ path: '', component: () => import('pages/RuleEditor.vue') }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test/',
|
||||||
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
|
children: [{ path: '', component: () => import('pages/testQursar.vue') }],
|
||||||
|
},
|
||||||
|
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,251 +0,0 @@
|
|||||||
/**
|
|
||||||
* アクションのプロパティ定義
|
|
||||||
*/
|
|
||||||
interface IActionProperty {
|
|
||||||
component: string;
|
|
||||||
props: {
|
|
||||||
//プロパティ名
|
|
||||||
name: string;
|
|
||||||
//プロパティ表示名
|
|
||||||
displayName:string;
|
|
||||||
placeholder: string;
|
|
||||||
//プロパティ設定値
|
|
||||||
modelValue: any;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* アクションタイプ定義
|
|
||||||
*/
|
|
||||||
export interface IActionNode{
|
|
||||||
id:string;
|
|
||||||
//アクション名
|
|
||||||
name:string;
|
|
||||||
title:string;
|
|
||||||
subTitle:string;
|
|
||||||
inputPoint:string;
|
|
||||||
//出力ポイント(条件分岐以外未使用)
|
|
||||||
outputPoints:Array<string>;
|
|
||||||
//ルートアクション(Kintone event)
|
|
||||||
isRoot:boolean;
|
|
||||||
//アクションのプロパティ定義
|
|
||||||
actionProps:Array<IActionProperty>;
|
|
||||||
//アクションのプロパティ設定値抽出
|
|
||||||
ActionValue:object
|
|
||||||
prevNode?: IActionNode;
|
|
||||||
nextNodes:Map<string,IActionNode>;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* アクションフローの定義
|
|
||||||
*/
|
|
||||||
export interface IActionFlow {
|
|
||||||
actionNodes:Array<IActionNode>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションのプロパティ定義に基づいたクラス
|
|
||||||
*/
|
|
||||||
class ActionProperty implements IActionProperty {
|
|
||||||
component: string;
|
|
||||||
props: {
|
|
||||||
// プロパティ名
|
|
||||||
name: string;
|
|
||||||
// プロパティ表示名
|
|
||||||
displayName: string;
|
|
||||||
placeholder: string;
|
|
||||||
// プロパティ設定値
|
|
||||||
modelValue: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProperty():IActionProperty{
|
|
||||||
return new ActionProperty("InputText","displayName","表示名","表示を入力してください","");
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
component: string,
|
|
||||||
name: string,
|
|
||||||
displayName: string,
|
|
||||||
placeholder: string,
|
|
||||||
modelValue: any
|
|
||||||
) {
|
|
||||||
this.component = component;
|
|
||||||
this.props = {
|
|
||||||
name: name,
|
|
||||||
displayName: displayName,
|
|
||||||
placeholder: placeholder,
|
|
||||||
modelValue: modelValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IActionNodeの実装、RootActionNode以外のアクション定義
|
|
||||||
*/
|
|
||||||
export class ActionNode implements IActionNode {
|
|
||||||
id:string;
|
|
||||||
name: string;
|
|
||||||
title:string;
|
|
||||||
get subTitle():string{
|
|
||||||
return this.name;
|
|
||||||
};
|
|
||||||
inputPoint:string;
|
|
||||||
//出力ポイント(条件分岐以外未使用)
|
|
||||||
outputPoints:Array<string>;
|
|
||||||
actionProps: Array<IActionProperty>;
|
|
||||||
get isRoot(): boolean{
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
get ActionValue():object{
|
|
||||||
const propValue:any={};
|
|
||||||
this.actionProps.forEach((value)=>{
|
|
||||||
propValue[value.props.name]=value.props.modelValue
|
|
||||||
});
|
|
||||||
return propValue;
|
|
||||||
};
|
|
||||||
prevNode?: IActionNode;
|
|
||||||
nextNodes:Map<string,IActionNode>;
|
|
||||||
constructor(
|
|
||||||
name: string,
|
|
||||||
title:string,
|
|
||||||
inputPoint:string,
|
|
||||||
outputPoint: Array<string> = [],
|
|
||||||
actionProps: Array<IActionProperty> =[ActionProperty.defaultProperty()]
|
|
||||||
) {
|
|
||||||
this.id='';
|
|
||||||
this.name = name;
|
|
||||||
this.title=title;
|
|
||||||
this.inputPoint=inputPoint;
|
|
||||||
this.outputPoints = outputPoint;
|
|
||||||
const defProp =ActionProperty.defaultProperty();
|
|
||||||
defProp.props.displayName=title;
|
|
||||||
this.actionProps =actionProps;
|
|
||||||
const prop = this.actionProps.find((prop)=>prop.props.name===defProp.props.name);
|
|
||||||
if(prop===undefined){
|
|
||||||
this.actionProps.unshift(defProp);
|
|
||||||
}
|
|
||||||
this.nextNodes=new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* ルートアクション定義
|
|
||||||
*/
|
|
||||||
export class RootAction implements IActionNode {
|
|
||||||
id:string;
|
|
||||||
name: string;
|
|
||||||
title:string;
|
|
||||||
subTitle:string;
|
|
||||||
inputPoint:string;
|
|
||||||
//出力ポイント(条件分岐以外未使用)
|
|
||||||
outputPoints:Array<string>;
|
|
||||||
isRoot: boolean;
|
|
||||||
actionProps: Array<IActionProperty>;
|
|
||||||
ActionValue:object;
|
|
||||||
prevNode?: IActionNode=undefined;
|
|
||||||
nextNodes:Map<string,IActionNode>;
|
|
||||||
constructor(
|
|
||||||
name: string,
|
|
||||||
title:string,
|
|
||||||
subTitle:string,
|
|
||||||
) {
|
|
||||||
this.id='';
|
|
||||||
this.name = name;
|
|
||||||
this.title=title;
|
|
||||||
this.subTitle=subTitle;
|
|
||||||
this.inputPoint='';
|
|
||||||
this.outputPoints = [];
|
|
||||||
this.isRoot = true;
|
|
||||||
this.actionProps=[];
|
|
||||||
this.ActionValue={};
|
|
||||||
this.nextNodes=new Map();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションフローの定義
|
|
||||||
*/
|
|
||||||
export class ActionFlow implements IActionFlow {
|
|
||||||
nextId:number;
|
|
||||||
actionNodes:Array<IActionNode>;
|
|
||||||
constructor(actionNodes:Array<IActionNode>|RootAction){
|
|
||||||
this.nextId=0;
|
|
||||||
|
|
||||||
if(actionNodes instanceof Array){
|
|
||||||
this.actionNodes=actionNodes;
|
|
||||||
}else{
|
|
||||||
this.actionNodes=[actionNodes];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* ノードを追加する
|
|
||||||
* 1.ID採番する
|
|
||||||
* 2.前のノードを関連付け
|
|
||||||
* @param newNode 新規追加するノード
|
|
||||||
* @param prevNode 前のノード
|
|
||||||
* @param inputPoint 入力ポイント
|
|
||||||
* @returns 追加されたノード
|
|
||||||
*/
|
|
||||||
addNode(
|
|
||||||
newNode:IActionNode,
|
|
||||||
prevNode?:IActionNode,
|
|
||||||
inputPoint?:string):IActionNode
|
|
||||||
{
|
|
||||||
newNode.id = this.generateId();
|
|
||||||
if(inputPoint!==undefined){
|
|
||||||
newNode.inputPoint=inputPoint;
|
|
||||||
}
|
|
||||||
if(prevNode!==undefined){
|
|
||||||
newNode.prevNode=prevNode;
|
|
||||||
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{
|
|
||||||
prevNode=this.actionNodes[this.actionNodes.length-1];
|
|
||||||
prevNode.nextNodes.set(inputPoint?inputPoint:'',newNode);
|
|
||||||
newNode.prevNode=prevNode;
|
|
||||||
}
|
|
||||||
this.actionNodes.push(newNode);
|
|
||||||
return newNode;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* ノードを削除する
|
|
||||||
* @param delNode
|
|
||||||
*/
|
|
||||||
removeNode(delNode:IActionNode){
|
|
||||||
const prevNode = delNode;
|
|
||||||
const nextNode = this.findNextNode(delNode);
|
|
||||||
if(nextNode!==undefined){
|
|
||||||
nextNode.prevNode=prevNode;
|
|
||||||
nextNode.inputPoint=delNode.inputPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 次のノードを探す
|
|
||||||
* @param delNode
|
|
||||||
*/
|
|
||||||
findNextNode(targetNode: IActionNode):ActionNode|undefined {
|
|
||||||
return this.actionNodes.find((node)=>node.prevNode===targetNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateId():string{
|
|
||||||
const no = this.nextId++;
|
|
||||||
return no.toString().padStart(10,'0');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
{
|
{
|
||||||
"extends": "@quasar/app-vite/tsconfig-preset",
|
"extends": "@quasar/app-vite/tsconfig-preset",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "."
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"src/*": ["src/*"],
|
||||||
|
"app/*": ["*"],
|
||||||
|
"components/*": ["src/components/*"],
|
||||||
|
"layouts/*": ["src/layouts/*"],
|
||||||
|
"pages/*": ["src/pages/*"],
|
||||||
|
"assets/*": ["src/assets/*"],
|
||||||
|
"boot/*": ["src/boot/*"],
|
||||||
|
"stores/*": ["src/stores/*"],
|
||||||
|
"models/*":["scr/models/*"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user