新しい統合されたフィールド入力、すべての関連部品の一致修正. フィールドの値を取得する に新しいアクションを追加

This commit is contained in:
Mouriya
2024-06-10 06:10:32 +09:00
parent 63099eda8b
commit 04f28a3be5
15 changed files with 670 additions and 247 deletions

View File

@@ -1,20 +1,21 @@
<template>
<q-field v-model="selectedObject" labelColor="primary" class="condition-object"
:clearable="isSelected" stack-label :dense="true" :outlined="true" >
<template v-slot:control >
<q-chip color="primary" text-color="white" v-if="isSelected && selectedObject.objectType==='field'" :dense="true" class="selected-obj">
<q-field labelColor="primary" class="condition-object" :clearable="isSelected" stack-label :dense="true"
:outlined="true">
<template v-slot:control>
<!-- <q-chip color="primary" text-color="white" v-if="isSelected && selectedObject.objectType==='field'" :dense="true" class="selected-obj">
{{ selectedObject.name }}
</q-chip>
<q-chip color="info" text-color="white" v-if="isSelected && selectedObject.objectType==='variable'" :dense="true" class="selected-obj">
{{ selectedObject.name.name }}
</q-chip>
</q-chip> -->
{{ selectedObject?.sharedText }}
</template>
<template v-slot:append>
<q-icon name="search" class="cursor-pointer" @click="showDg"/>
<template v-slot:append>
<q-icon name="search" class="cursor-pointer" @click="showDg" />
</template>
</q-field>
<show-dialog v-model:visible="show" name="設定項目一覧" @close="closeDg" min-width="400px">
<template v-slot:toolbar>
<show-dialog v-model:visible="show" name="設定項目" @close="closeDg" min-width="400px">
<!-- <template v-slot:toolbar>
<q-input dense debounce="200" v-model="filter" placeholder="検索" clearable>
<template v-slot:before>
<q-icon name="search" />
@@ -22,75 +23,101 @@
</q-input>
</template>
<condition-objects ref="appDg" name="フィールド" type="single" :filter="filter" :appId="store.appInfo?.appId" :vars="vars"></condition-objects>
-->
<DynamicItemInput v-model:selectedObject="selectedObject" :canInput="config.canInput"
:buttonsConfig="config.buttonsConfig" :appId="store.appInfo?.appId" />
</show-dialog>
</template>
</template>
<script lang="ts">
import { defineComponent, reactive, ref ,watchEffect,computed} from 'vue';
import ShowDialog from '../ShowDialog.vue';
import ConditionObjects from '../ConditionObjects.vue';
import { useFlowEditorStore } from '../../stores/flowEditor';
import {IActionFlow,IActionNode,IActionVariable} from '../../types/ActionTypes';
export default defineComponent({
name: 'ConditionObject',
components: {
ShowDialog,
ConditionObjects
},
props: {
modelValue: {
type: Object,
default: null
},
},
setup(props, { emit }) {
const appDg = ref();
const show = ref(false);
const selectedObject = ref(props.modelValue);
const store = useFlowEditorStore();
const isSelected = computed(()=>{
return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value)
});
let vars:IActionVariable[] =[];
if(store.currentFlow!==undefined && store.activeNode!==undefined ){
vars =store.currentFlow.getVarNames(store.activeNode);
<script lang="ts">
import { defineComponent, reactive, ref, watchEffect, computed } from 'vue';
import ShowDialog from '../ShowDialog.vue';
// import ConditionObjects from '../ConditionObjects.vue';
import DynamicItemInput from '../DynamicItemInput/DynamicItemInput.vue';
import { useFlowEditorStore } from '../../stores/flowEditor';
import { IActionFlow, IActionNode, IActionVariable } from '../../types/ActionTypes';
export default defineComponent({
name: 'ConditionObject',
components: {
ShowDialog,
DynamicItemInput,
// ConditionObjects
},
props: {
config: {
type: Object,
default: () => {
return {
canInput: false,
buttonsConfig: [
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
{ label: '変数', color: 'green', type: 'VariableAdd' },
]
};
}
const filter=ref('');
const showDg = () => {
show.value = true;
};
const closeDg = (val:string) => {
if (val == 'OK') {
selectedObject.value = appDg.value.selected[0];
}
};
watchEffect(() => {
emit('update:modelValue', selectedObject.value);
});
return {
store,
appDg,
show,
showDg,
closeDg,
selectedObject,
vars:reactive(vars),
isSelected,
filter
};
},
modelValue: {
type: Object,
default: null
},
},
setup(props, { emit }) {
// const appDg = ref();
const show = ref(false);
const selectedObject = ref(props.modelValue);
const store = useFlowEditorStore();
// const sharedText = ref(''); // 共享的文本状态
const isSelected = computed(() => {
return selectedObject?.value?.sharedText !== '';
});
// const isSelected = computed(()=>{
// return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value)
// });
let vars: IActionVariable[] = [];
if (store.currentFlow !== undefined && store.activeNode !== undefined) {
vars = store.currentFlow.getVarNames(store.activeNode);
}
});
</script>
// const filter=ref('');
const showDg = () => {
show.value = true;
};
const closeDg = (val: string) => {
if (val == 'OK') {
// selectedObject.value = appDg.value.selected[0];
}
};
watchEffect(() => {
emit('update:modelValue', selectedObject.value);
});
return {
store,
// appDg,
show,
showDg,
closeDg,
selectedObject,
vars: reactive(vars),
isSelected,
buttonsConfig: [
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
{ label: '変数', color: 'green', type: 'VariableAdd' },
]
// filter
};
}
});
</script>
<style lang="scss">
.condition-object{
.condition-object {
min-width: 200px;
max-height: 40px;
margin: 0 2px;
}
.selected-obj{
.selected-obj {
margin: 0 2px;
}
</style>

View File

@@ -67,11 +67,13 @@
<!-- condition -->
<div @click.stop @keypress.stop v-else >
<div class="row no-wrap items-center q-my-xs">
<ConditionObject v-bind="prop.node" v-model="prop.node.object" class="col-4"></ConditionObject>
<ConditionObject v-bind="prop.node" v-model="prop.node.object" :config="leftDynamicItemConfig" class="col-4"/>
<q-select v-model="prop.node.operator" :options="operators" class="operator" :outlined="true" :dense="true"></q-select>
<q-input v-if="!prop.node.object || !('options' in prop.node.object)"
<ConditionObject v-bind="prop.node" v-model="prop.node.value" :config="rightDynamicItemConfig" class="col-4"/>
<!-- <ConditionObject v-bind="prop.node" v-model="prop.node.object" class="col-4"/> -->
<!-- <q-input v-if="!prop.node.object || !('options' in prop.node.object)"
v-model="prop.node.value"
class="condition-value" :outlined="true" :dense="true" ></q-input>
class="condition-value" :outlined="true" :dense="true" ></q-input> -->
<q-select v-if="prop.node.object && ('options' in prop.node.object)"
v-model="prop.node.value"
:options="objectValueOptions(prop.node.object.options)"
@@ -225,6 +227,8 @@ export default defineComponent( {
// addCondition(tree.root);
return {
leftDynamicItemConfig :inject('leftDynamicItemConfig'),
rightDynamicItemConfig:inject('rightDynamicItemConfig'),
showingCondition,
conditionString,
tree,

View File

@@ -0,0 +1,134 @@
<template>
<div class="q-mx-md" style="max-width: 600px;">
<!-- <q-card> -->
<div class="q-mb-md">
<q-input ref="inputRef" outlined dense debounce="200" @update:model-value="updateSharedText"
v-model="sharedText" :readonly="!canInput">
<template v-slot:append>
<q-btn flat round padding="none" icon="cancel" @click="clearSharedText" color="grey-6" />
</template>
</q-input>
</div>
<div class="row q-gutter-sm">
<q-btn v-for="button in buttonsConfig" :key="button.type" :color="button.color" @mousedown.prevent
@click="openDialog(button)" size="sm">
{{ button.label }}
</q-btn>
</div>
<show-dialog v-model:visible="dialogVisible" :name="currentDialogName" @close="closeDialog" min-width="400px">
<template v-slot:toolbar>
<q-input dense debounce="200" v-model="filter" clearable>
<template v-slot:before>
<q-icon name="search" />
</template>
</q-input>
</template>
<!-- asdf -->
<component :is="currentComponent" @select="handleSelect" :filter="filter" :appId="appId" />
</show-dialog>
<!-- </q-card> -->
</div>
</template>
<script lang="ts">
import { ref, inject, watchEffect, defineComponent } from 'vue';
import FieldAdd from './FieldAdd.vue';
import VariableAdd from './VariableAdd.vue';
// import FunctionAdd from './FunctionAdd.vue';
import ShowDialog from '../ShowDialog.vue';
type ButtonConfig = {
label: string;
color: string;
type: string;
};
export default defineComponent({
name: 'DynamicItemInput',
components: {
FieldAdd,
VariableAdd,
// FunctionAdd,
ShowDialog
},
props: {
canInput: {
type: Boolean,
default: false
},
appId: {
type: String,
},
selectedObject: {
default: {}
},
buttonsConfig: {
type: Array as () => ButtonConfig[],
default: () => [
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' }
]
}
},
setup(props, { emit }) {
const filter = ref('');
const dialogVisible = ref(false);
const currentDialogName = ref('');
const currentComponent = ref('FieldAdd');
const sharedText = ref(props.selectedObject?.sharedText ?? '');
const inputRef = ref();
const openDialog = (button: ButtonConfig) => {
currentDialogName.value = button.label;
currentComponent.value = button.type;
dialogVisible.value = true;
};
const closeDialog = () => {
dialogVisible.value = false;
};
const handleSelect = (value) => {
// 获取当前光标位置
// const cursorPosition = inputRef.value.getNativeElement().selectionStart;
// if (cursorPosition === undefined || cursorPosition === 0) {
sharedText.value = `${value._t}`;
// } else {
// const textBefore = sharedText.value.substring(0, cursorPosition);
// const textAfter = sharedText.value.substring(cursorPosition);
// sharedText.value = `${textBefore}${value._t}${textAfter}`;
// }
emit('update:selectedObject', { sharedText: sharedText.value, ...value });
dialogVisible.value = false;
};
const clearSharedText = () => {
sharedText.value = '';
emit('update:selectedObject', {});
}
const updateSharedText = (value) => {
sharedText.value = value;
emit('update:selectedObject', { ...props.selectedObject, sharedText: value });
console.log(props.selectedObject);
}
return {
filter,
dialogVisible,
currentDialogName,
currentComponent,
openDialog,
closeDialog,
handleSelect,
clearSharedText,
updateSharedText,
sharedText,
inputRef
};
}
});
</script>

View File

@@ -0,0 +1,41 @@
<template>
<field-list v-model="selected" type="single" :filter="filter" :appId="sourceApp ? sourceApp : appId"
:fields="sourceFields" @update:modelValue="handleSelect" />
</template>
<script lang="ts">
import { computed, inject, ref } from 'vue';
import FieldList from '../FieldList.vue';
export default {
name: 'FieldAdd',
components: {
FieldList,
},
props: {
appId: Number,
filter: String
},
setup(props, { emit }) {
const sourceFields = inject<Array<unknown>>('sourceFields')
const sourceApp = inject<number>('sourceApp')
const appId = computed(() => {
if (sourceFields || sourceApp) {
return sourceApp.value
} else {
return props.appId
}
});
return {
sourceFields,
sourceApp,
selected: ref([]),
handleSelect: (newSelection: any[]) => {
if (newSelection.length > 0) {
const v = newSelection[0]
emit('select', { _t: `field(${appId.value},${v.name})`, ...v }); // 假设您只需要选择的第一个字段的名称
}
}
}
},
}
</script>

View File

@@ -0,0 +1,42 @@
<template>
<variable-list v-model="selected" type="single" :vars="vars" :filter="filter" @update:modelValue="handleSelect" />
</template>
<script lang="ts">
import { ref } from 'vue';
import VariableList from '../VariableList.vue';
import { useFlowEditorStore } from 'src/stores/flowEditor';
import { IActionVariable } from 'src/types/ActionTypes';
export default {
name: 'VariableAdd',
components: {
VariableList,
},
props: {
appId: Number,
filter: String
},
setup(props, { emit }) {
const store = useFlowEditorStore();
let vars: IActionVariable[] = [];
console.log(store.currentFlow !== undefined && store.activeNode !== undefined);
if (store.currentFlow !== undefined && store.activeNode !== undefined) {
vars = store.currentFlow.getVarNames(store.activeNode);
}
return {
vars,
selected: ref([]),
handleSelect: (newSelection: any[]) => {
if (newSelection.length > 0) {
const v = newSelection[0];
let name = v.name
if (typeof name === 'object') {
name = name.name
}
emit('select', { _t: `var(${name})`, ...v }); // 假设您只需要选择的第一个字段的名称
}
}
}
},
}
</script>

View File

@@ -1,20 +1,17 @@
<template>
<!-- <div class="q-pa-md q-gutter-sm" > -->
<q-dialog :model-value="visible" persistent bordered >
<q-card style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
<q-card class="" style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
<q-toolbar class="bg-grey-4">
<q-toolbar-title>{{ name }}</q-toolbar-title>
<q-space></q-space>
<slot name="toolbar"></slot>
<q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" />
</q-toolbar>
<q-card-section>
<!-- <div class="text-h6">{{ name }}</div> -->
</q-card-section>
<q-card-section class="q-pt-none" :style="sectionStyle">
<q-card-section class="q-mt-md" :style="sectionStyle">
<slot></slot>
</q-card-section>
<q-card-actions align="right" class="text-primary q-mt-lg">
<q-card-actions align="right" class="text-primary">
<q-btn flat label="確定" v-close-popup @click="CloseDialogue('OK')" />
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
</q-card-actions>

View File

@@ -1,6 +1,6 @@
<template>
<div class="q-pa-md">
<q-table flat bordered row-key="id" :selection="type" :selected="modelValue"
<q-table flat bordered row-key="id" :selection="type" :selected="modelValue" :filter="filter"
@update:selected="$emit('update:modelValue', $event)" :columns="columns" :rows="rows" />
</div>
</template>
@@ -19,7 +19,8 @@ export default {
reqired: true,
default: () => []
},
modelValue: Array
modelValue: Array,
filter: String
},
emits: [
'update:modelValue'

View File

@@ -42,6 +42,15 @@ type Props = {
}
};
type InputConfg = {
canInput: boolean;
buttonsConfig: {
label: string;
color: string;
type: string;
}[]
};
export default defineComponent({
name: 'FieldInput',
@@ -84,6 +93,24 @@ export default defineComponent({
},
operatorList: {
type: Array,
},
inputConfig: {
type: Object,
default: () => ({
left: {
canInput: false,
buttonsConfig: [
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
{ label: '変数', color: 'green', type: 'VariableAdd' },
]
},
right: {
canInput: true,
buttonsConfig: [
{ label: '変数', color: 'green', type: 'VariableAdd' },
]
},
})
}
},
@@ -98,6 +125,8 @@ export default defineComponent({
}
}
provide('leftDynamicItemConfig', props.inputConfig.left);
provide('rightDynamicItemConfig', props.inputConfig.right);
provide('Operator', props.operatorList);
const btnDisable = computed(() => {

View File

@@ -21,12 +21,12 @@
<div class="q-mx-md">
<div class="row q-col-gutter-x-xs flex-center">
<div class="col-5">
<div class="q-mx-xs">From</div>
<div class="q-mx-xs">ソース</div>
</div>
<div class="col-1">
</div>
<div class="col-5">
<div class="q-mx-xs">To</div>
<div class="q-mx-xs">目標</div>
</div>
<div class="col-1"><q-btn flat round dense icon="add" size="sm" @click="addMappingObject" />
</div>
@@ -34,7 +34,7 @@
<div class="q-my-sm" v-for="(item, index) in mappingProps" :key="item.id">
<div class="row q-col-gutter-x-xs flex-center">
<div class="col-5">
<ConditionObject v-model="item.from" />
<ConditionObject :config="config" v-model="item.from" />
</div>
<div class="col-1">
</div>
@@ -99,7 +99,7 @@ type ValueType = {
};
}
const defaultMappingProp = () => ({ id: uuidv4(), to: { app: {}, fields: [], isDialogVisible: false } });
const defaultMappingProp = () => ({ id: uuidv4(), from: {}, to: { app: {}, fields: [], isDialogVisible: false } });
export default defineComponent({
name: 'DataMapping',
@@ -159,12 +159,9 @@ export default defineComponent({
const mappingObjectsInputDisplay = computed(() =>
mappingProps ?
mappingProps
.filter(item => item.from?.name && item.to.fields?.length > 0)
.filter(item => item.from?.sharedText && item.to.fields?.length > 0)
.map(item => {
const name = typeof item.from?.name === 'string'
? item.from.name
: item.from?.name.name;
return `[${name}] - (${item.to.app?.name} : ${item.to.fields[0].label})`;
return `field(${item.to.app?.id},${item.to.fields[0].label}) = ${item.from.sharedText} `;
})
: []
);
@@ -194,7 +191,13 @@ export default defineComponent({
mappingObjectsInputDisplay,
sourceApp,
sourceAppId,
btnDisable
btnDisable,
config: {
canInput: false,
buttonsConfig: [
{ label: '変数', color: 'green', type: 'VariableAdd' },
]
}
};
},
});

View File

@@ -139,6 +139,7 @@ export default defineComponent({
}
return null;
}));
provide('sourceApp', computed(() => source.props?.modelValue?.app?.id));
}
const actionName = props.context.find(element => element?.props?.name === 'displayName')
@@ -168,12 +169,7 @@ export default defineComponent({
processingObjects
.filter(item => item.field && item.logicalOperator && item.vName)
.map(item => {
const name = typeof item.field?.name === 'string'
? item.field.name
: item.field?.name.name;
return item.logicalOperator.operator!==''?
`${processingProps.name}.${item.vName} = ${item.logicalOperator.operator}(${name})`
:`${processingProps.name}.${item.vName} = ${name}`
return`var(${processingProps.name}.${item.vName}) = ${item.field.sharedText}`
})
: []
);

View File

@@ -201,11 +201,12 @@ export class ConditionTree {
} else {
const condNode=node as ConditionNode;
if (condNode.object && condNode.operator ) {
let value=condNode.value;
if(value && typeof value ==='object' && ('label' in value)){
value =condNode.value.label;
}
return `${typeof condNode.object.name === 'object' ? condNode.object.name.name : condNode.object.name} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} '${value}'`;
// let value=condNode.value;
// if(value && typeof value ==='object' && ('label' in value)){
// value =condNode.value.label;
// }
return `${condNode.object.sharedText} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} ${condNode.value.sharedText}`;
// return `${typeof condNode.object.name === 'object' ? condNode.object.name.name : condNode.object.name} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} '${value}'`;
} else {
return '';
}
@@ -237,11 +238,10 @@ export class ConditionTree {
} else {
const condNode=node as ConditionNode;
if (condNode.object && condNode.operator ) {
let value=condNode.value;
if(value && typeof value ==='object' && ('label' in value)){
value =condNode.value.label;
}
return `${condNode.object.code} ${typeof condNode.operator === 'object' ? condNode.operator.value : condNode.operator} "${value}"`;
if (!condNode.object.code || !condNode.value.sharedText){
return '';
}
return `${condNode.object.code} ${typeof condNode.operator === 'object' ? condNode.operator.value : condNode.operator} "${condNode.value.sharedText}"`;
} else {
return '';
}

View File

@@ -0,0 +1,74 @@
import {
IAction,
IActionResult,
IActionNode,
IActionProperty,
IContext,
} from "../types/ActionTypes";
import { actionAddins } from ".";
interface Props {
displayName: string;
field: Field;
verName: VerName;
}
interface VerName {
name: string;
}
interface Field {
name: string;
type: string;
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}
export class CurrentFieldGetAction implements IAction {
name: string;
actionProps: IActionProperty[];
currentFieldGetProps: Props;
constructor() {
this.name = "フィールドの値を取得する";
this.actionProps = [];
this.currentFieldGetProps = {} as Props;
this.register();
}
async process(
prop: IActionNode,
event: any,
context: IContext
): Promise<IActionResult> {
this.currentFieldGetProps = prop.ActionValue as Props;
this.actionProps = prop.actionProps;
let result = {
canNext: true,
result: "",
} as IActionResult;
try {
context.variables[this.currentFieldGetProps.verName.name] = context.record[this.currentFieldGetProps.field.code].value;
} catch (error) {
console.error("CurrentFieldGetAction error", error);
result.canNext = false;
}
return result;
}
register(): void {
actionAddins[this.name] = this;
}
}
new CurrentFieldGetAction();

View File

@@ -7,41 +7,70 @@ import {
} from "../types/ActionTypes";
import { actionAddins } from ".";
export type IApp = {
interface Props {
displayName: string;
sources: Sources;
dataMapping: DataMapping[];
}
interface DataMapping {
id: string;
from: From;
to: To;
}
interface To {
app: App;
fields: Field[];
isDialogVisible: boolean;
}
interface Field {
name: string;
};
export type IField = {
name: string;
code: string;
type: string;
};
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}
export type IAppFields = {
app?: IApp;
fields: IField[];
};
type ValueType = {
interface From {
sharedText: string;
_t: string;
id: string;
from: {
objectType: "variable" | "field";
name: { name: string };
code: string;
};
to: IAppFields & {
isDialogVisible: boolean;
};
};
objectType: string;
name: Name;
actionName: string;
displayName: string;
}
type Props = { app: IApp; field: ValueType[] };
interface Name {
name: string;
}
interface Sources {
app: App;
}
interface App {
id: string;
name: string;
description: string;
createdate: string;
}
export class DataMappingAction implements IAction {
name: string;
actionProps: IActionProperty[];
dataMappingProps: Props;
constructor() {
this.name = "DataMapping";
this.name = "データマッピング";
this.actionProps = [];
this.dataMappingProps = {} as Props;
this.register();
@@ -52,14 +81,17 @@ export class DataMappingAction implements IAction {
event: any,
context: IContext
): Promise<IActionResult> {
this.initActionProps(prop);
this.initTypedActionProps();
this.actionProps = prop.actionProps;
this.dataMappingProps = prop.ActionValue as Props;
console.log(prop.ActionValue);
// this.initTypedActionProps();
let result = {
canNext: true,
result: "",
} as IActionResult;
try {
for (const item of this.dataMappingProps.field) {
for (const item of this.dataMappingProps.dataMapping) {
if (item.from.objectType === "variable") {
if (
item.from.name.name &&
@@ -86,32 +118,6 @@ export class DataMappingAction implements IAction {
);
}
}
} else if (item.from.objectType === "field") {
if (
item.from.code &&
item.to.app &&
item.to.fields &&
item.to.fields.length > 0
) {
const value = await selectData(
item.to.app.id,
item.to.fields[0].code
);
if (value && value.type === context.record[item.from.code].type) {
await kintone.api(
kintone.api.url("/k/v1/records.json", true),
"POST",
{
app: item.to.app.id,
records: value.value.map((v) => ({
[item.to.fields[0].code]: {
value: v,
},
})),
}
);
}
}
}
}
} catch (error) {
@@ -123,19 +129,6 @@ export class DataMappingAction implements IAction {
return result;
}
private initActionProps(nodes: IActionNode) {
this.actionProps = nodes.actionProps;
}
private initTypedActionProps() {
for (const action of this.actionProps) {
if (action.component === "DataMapping") {
this.dataMappingProps.field = action.props.modelValue as ValueType[];
} else if (action.component === "AppSelect") {
this.dataMappingProps.app = action.props.modelValue.app as IApp;
}
}
}
register(): void {
actionAddins[this.name] = this;
}

View File

@@ -7,31 +7,135 @@ import {
} from "../types/ActionTypes";
import { actionAddins } from ".";
interface Props {
displayName: string;
sources: Sources;
condition: Condition;
verName: VerName;
}
type DataProcessingProps = {
app: {
id: string;
name: string;
};
conditionsQuery: string;
propcessing: {
varRootName: string;
fields: Field[];
};
};
type Field = {
name: string;
code: string;
interface Condition {
queryString: string;
index: number;
type: string;
varName: string;
children: Child[];
parent: null;
logicalOperator: string;
}
interface Child {
index: number;
type: string;
parent: string;
object: Object;
operator: ChildOperator;
value: Value;
}
interface Value {
sharedText: string;
_t: string;
objectType: string;
actionName: string;
displayName: string;
name: Name;
}
interface Name {
name: string;
}
interface ChildOperator {
label: string;
value: string;
}
interface Object {
sharedText: string;
_t: string;
name: string;
objectType: string;
type: string;
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}
interface VerName {
name: string;
actionName: string;
displayName: string;
vars: Var[];
}
interface Var {
id: string;
field: Field2;
logicalOperator: LogicalOperator;
vName: string;
}
interface LogicalOperator {
operator: string;
};
label: string;
}
interface Field2 {
sharedText: string;
_t: string;
name: string;
objectType: string;
type: string;
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}
interface Sources {
app: App;
fields: Field[];
}
interface Field {
name: string;
type: string;
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}
interface App {
id: string;
name: string;
description: string;
createdate: string;
}
export class DataProcessingAction implements IAction {
name: string;
actionProps: IActionProperty[];
dataProcessingProps: DataProcessingProps | null;
dataProcessingProps: Props | null;
constructor() {
this.name = "データ処理";
this.actionProps = [];
@@ -40,10 +144,13 @@ export class DataProcessingAction implements IAction {
}
async process(
nodes: IActionNode,event: any,context: IContext
nodes: IActionNode,
event: any,
context: IContext
): Promise<IActionResult> {
this.initActionProps(nodes);
this.initTypedActionProps();
this.actionProps = nodes.actionProps;
this.dataProcessingProps = nodes.ActionValue as Props;
let result = {
canNext: true,
result: "",
@@ -52,24 +159,24 @@ export class DataProcessingAction implements IAction {
if (!this.dataProcessingProps) {
return result;
}
const data = await selectData(this.dataProcessingProps.conditionsQuery);
const data = await selectData(varGet(this.dataProcessingProps.condition.queryString,context.variables));
console.log("data ", data);
context.variables[this.dataProcessingProps.propcessing.varRootName] =
this.dataProcessingProps.propcessing.fields.reduce((acc, f) => {
context.variables[this.dataProcessingProps.verName.name] =
this.dataProcessingProps.verName.vars.reduce((acc, f) => {
const v = calc(f, data);
if (v) {
acc[f.varName] = calc(f, data);
acc[f.vName] = calc(f, data);
}
return acc;
}, {} as Var);
}, {} as AnyObject);
console.log("context ", context);
return result;
} catch (error) {
console.error(error);
event.error=error;
event.error = error;
return result;
}
}
@@ -77,50 +184,24 @@ export class DataProcessingAction implements IAction {
register(): void {
actionAddins[this.name] = this;
}
private initActionProps(nodes: IActionNode) {
this.actionProps = nodes.actionProps;
}
private initTypedActionProps() {
this.dataProcessingProps = {
app: {
id: "",
name: "",
},
conditionsQuery: "",
propcessing: {
varRootName: "",
fields: [],
},
};
for (const action of this.actionProps) {
if (action.component === "AppFieldSelect") {
this.dataProcessingProps.app.id = action.props.modelValue.app.id;
this.dataProcessingProps.app.name = action.props.modelValue.app.name;
} else if (action.component === "DataProcessing") {
this.dataProcessingProps.propcessing.varRootName =
action.props.modelValue.name;
for (const f of action.props.modelValue.vars) {
this.dataProcessingProps.propcessing.fields.push({
name: f.field.name,
code: f.field.code,
type: f.field.type,
varName: f.vName,
operator: f.logicalOperator.operator,
});
}
} else if (action.component === "ConditionInput") {
this.dataProcessingProps.conditionsQuery = JSON.parse(
action.props.modelValue
).queryString;
}
}
}
}
new DataProcessingAction();
const varGet = (str: string, vars: any) => {
const regex = /var\((.*?)\)/g;
let match;
while ((match = regex.exec(str)) !== null) {
const varName = match[1];
if (varName in vars) {
str = str.replace(match[0], vars[varName]);
} else {
throw new Error(`変数${varName}が見つかりません`);
}
}
return str;
};
const selectData = async (query?: string) => {
return kintone
.api(kintone.api.url("/k/v1/records", true), "GET", {
@@ -157,24 +238,24 @@ type Result = {
};
};
type Var = {
type AnyObject = {
[key: string]: any;
};
const ERROR_TYPE = "ERROR_TYPE";
const calc = (field: Field, result: Result) => {
const type = typeCheck(field.type);
const calc = (f: Var, result: Result) => {
const type = typeCheck(f.field.type);
if (!type) {
return ERROR_TYPE;
}
const fun =
calcFunc[`${type}_${Operator[field.operator as keyof typeof Operator]}`];
calcFunc[`${type}_${Operator[f.logicalOperator.operator as keyof typeof Operator]}`];
if (!fun) {
return ERROR_TYPE;
}
const values = result[field.code].value;
const values = result[f.field.code].value;
if (!values) {
return null;
}
@@ -208,7 +289,7 @@ enum Operator {
MAX = "MAX",
MIN = "MIN",
COUNT = "COUNT",
FIRST = "FIRST"
FIRST = "FIRST",
}
enum CalcType {
@@ -274,7 +355,7 @@ const calcFunc: Record<string, (value: string[]) => string | null> = {
? minDateTime
: currentDateTime
),
[`${CalcType.STRING}_${Operator.FIRST}`]:(value: string[])=>{
[`${CalcType.STRING}_${Operator.FIRST}`]: (value: string[]) => {
return value[0];
}
},
};

View File

@@ -8,6 +8,7 @@ import '../actions/button-add';
import '../actions/condition-action';
import '../actions/data-processing';
import '../actions/data-mapping';
import '../actions/current-field-get';
import { ActionFlow,IActionFlow, IActionResult,IContext } from "./ActionTypes";
export class ActionProcess{