データ追加&更新処理アクション修正完了

This commit is contained in:
xiaozhe.ma
2024-07-12 19:05:15 +09:00
parent 0fda3d143c
commit e726843189
8 changed files with 169 additions and 106 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="q-pa-md"> <div class="q-pa-sm">
<q-field labelColor="primary" class="condition-object" dense outlined :label="label" :disable="disabled" <q-field labelColor="primary" class="condition-object" dense outlined :label="label" :disable="disabled"
:clearable="isSelected"> :clearable="isSelected">
<template v-slot:control> <template v-slot:control>
@@ -10,7 +10,6 @@
{{ selectedObject.name.name }} {{ selectedObject.name.name }}
</q-chip> </q-chip>
<div v-if="isSelected && selectedObject.objectType==='text'">{{ selectedObject?.sharedText }}</div> <div v-if="isSelected && selectedObject.objectType==='text'">{{ selectedObject?.sharedText }}</div>
</template> </template>
<template v-slot:append> <template v-slot:append>
<q-icon name="search" class="cursor-pointer" @click="showDg" /> <q-icon name="search" class="cursor-pointer" @click="showDg" />
@@ -81,7 +80,7 @@ export default defineComponent({
const store = useFlowEditorStore(); const store = useFlowEditorStore();
// const sharedText = ref(''); // 共享的文本状态 // const sharedText = ref(''); // 共享的文本状态
const isSelected = computed(() => { const isSelected = computed(() => {
return selectedObject?.value?.sharedText !== ''; return selectedObject.value?.sharedText !== '';
}); });
// const isSelected = computed(()=>{ // const isSelected = computed(()=>{
// return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value) // return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value)

View File

@@ -263,7 +263,8 @@ export default defineComponent( {
.operator{ .operator{
min-width: 150px; min-width: 150px;
max-height: 40px; max-height: 40px;
margin: 0 2px; margin-left: 12px;
text-align: center; text-align: center;
font-size: 12pt; font-size: 12pt;
} }

View File

@@ -3,7 +3,7 @@
<!-- <q-card> --> <!-- <q-card> -->
<div class="q-mb-md"> <div class="q-mb-md">
<q-input ref="inputRef" outlined dense debounce="200" @update:model-value="updateSharedText" <q-input ref="inputRef" outlined dense debounce="200" @update:model-value="updateSharedText"
v-model="sharedText" :readonly="!canInput"> v-model="sharedText" :readonly="!canInput" autogrow>
<template v-slot:append> <template v-slot:append>
<q-btn flat round padding="none" icon="cancel" @click="clearSharedText" color="grey-6" /> <q-btn flat round padding="none" icon="cancel" @click="clearSharedText" color="grey-6" />
</template> </template>

View File

@@ -66,7 +66,8 @@ export interface IApp {
export interface IField { export interface IField {
name: string, name: string,
code: string, code: string,
type: string type: string,
label?:string
} }
export interface IAppFields { export interface IAppFields {

View File

@@ -18,7 +18,7 @@
</q-field> </q-field>
<show-dialog v-model:visible="dgIsShow" name="データマッピング" @close="closeDg" min-width="55vw" min-height="60vh"> <show-dialog v-model:visible="dgIsShow" name="データマッピング" @close="closeDg" min-width="55vw" min-height="60vh">
<div class="q-mx-none"> <div class="">
<div class="row q-col-gutter-x-xs flex-center"> <div class="row q-col-gutter-x-xs flex-center">
<div class="col-5"> <div class="col-5">
<div class="q-mx-xs">ソース</div> <div class="q-mx-xs">ソース</div>
@@ -27,7 +27,7 @@
</div> --> </div> -->
<div class="col-5"> <div class="col-5">
<div class="row justify-between q-mr-md"> <div class="row justify-between q-mr-md">
<div class="">目標</div> <div class="">{{ sourceApp?.name }}</div>
<q-btn outline color="primary" size="xs" label="最新のフィールドを取得する" <q-btn outline color="primary" size="xs" label="最新のフィールドを取得する"
@click="() => updateFields(sourceAppId!)" /> @click="() => updateFields(sourceAppId!)" />
</div> </div>
@@ -36,9 +36,9 @@
キー キー
</div> </div>
</div> </div>
<q-virtual-scroll style="max-height: 65vh;" :items="mappingProps" separator v-slot="{ item, index }"> <q-virtual-scroll style="max-height: 60vh;" :items="mappingProps" separator v-slot="{ item, index }">
<!-- <div class="q-my-sm" v-for="(item, index) in mappingProps" :key="item.id"> --> <!-- <div class="q-my-sm" v-for="(item, index) in mappingProps" :key="item.id"> -->
<div class="row q-my-md q-col-gutter-x-md flex-center"> <div class="row q-pa-sm q-col-gutter-x-md flex-center">
<div class="col-5"> <div class="col-5">
<ConditionObject :config="config" v-model="item.from" :disabled="item.disabled" <ConditionObject :config="config" v-model="item.from" :disabled="item.disabled"
:label="item.disabled ? '「Lookup」によってロックされる' : undefined" /> :label="item.disabled ? '「Lookup」によってロックされる' : undefined" />
@@ -55,12 +55,14 @@
<div class="self-center full-width no-outline" tabindex="0" <div class="self-center full-width no-outline" tabindex="0"
v-if="item.to.app?.name && item.to.fields?.length > 0 && item.to.fields[0].label"> v-if="item.to.app?.name && item.to.fields?.length > 0 && item.to.fields[0].label">
{{ `${item.to.fields[0].label}` }} {{ `${item.to.fields[0].label}` }}
<q-tooltip> <span class="text-red" v-if="item.to.fields[0].required">*</span>
<q-tooltip class="bg-yellow-2 text-black shadow-4" >
<div>アプリ : {{ item.to.app.name }}</div> <div>アプリ : {{ item.to.app.name }}</div>
<div>フィールドのコード : {{ item.to.fields[0].code }}</div> <div>フィールドのコード : {{ item.to.fields[0].code }}</div>
<div>フィールドのタイプ : {{ item.to.fields[0].type }}</div> <div>フィールドのタイプ : {{ item.to.fields[0].type }}</div>
<div>フィールド : {{ item.to.fields[0] }}</div> <div v-if="item.to.fields[0].required">必須項目</div>
<div>フィールド : {{ item.isKey }}</div> <!-- <div>フィールド : {{ item.to.fields[0] }}</div>
<div>フィールド : {{ item.isKey }}</div> -->
</q-tooltip> </q-tooltip>
</div> </div>
</template> </template>
@@ -84,7 +86,7 @@
</q-virtual-scroll> </q-virtual-scroll>
<div class="q-mt-lg q-ml-md row "> <div class="q-mt-lg q-ml-md row ">
<q-checkbox size="sm" v-model="createWithNull" label="目标が存在しない場合は新規作成、存在する場合は更新する。" /> <q-checkbox size="sm" v-model="createWithNull" label="キーが存在しない場合は新規作成され、存在する場合はデータが更新されます。" />
</div> </div>
</div> </div>
</show-dialog> </show-dialog>
@@ -123,7 +125,7 @@ type MappingValueType = {
from: { sharedText?: string }; from: { sharedText?: string };
to: { to: {
app?: IApp, app?: IApp,
fields: (IField & { label?: string })[] fields: IField[],
isDialogVisible: boolean; isDialogVisible: boolean;
}; };
isKey: boolean; isKey: boolean;
@@ -164,7 +166,11 @@ export default defineComponent({
onlySourceSelect: { onlySourceSelect: {
type: Boolean, type: Boolean,
default: false default: false
} },
fieldTypes:{
type:Array,
default:()=>[]
},
}, },
setup(props, { emit }) { setup(props, { emit }) {
@@ -266,6 +272,7 @@ export default defineComponent({
config: { config: {
canInput: false, canInput: false,
buttonsConfig: [ buttonsConfig: [
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
{ label: '変数', color: 'green', type: 'VariableAdd', editable: false }, { label: '変数', color: 'green', type: 'VariableAdd', editable: false },
] ]
} }

View File

@@ -40,7 +40,7 @@
<div class="col-5"> <div class="col-5">
<ConditionObject v-model="item.field" /> <ConditionObject v-model="item.field" />
</div> </div>
<div class="col-2"> <div class="col-2 q-pa-sm">
<q-select v-model="item.logicalOperator" :options="logicalOperators" outlined dense></q-select> <q-select v-model="item.logicalOperator" :options="logicalOperators" outlined dense></q-select>
</div> </div>
<div class="col-4"> <div class="col-4">
@@ -78,14 +78,8 @@ type Props = {
type ProcessingObjectType = { type ProcessingObjectType = {
field?: { field?: {
name: string | { sharedText: string;
name: string; objectType: 'field';
};
objectType: string;
type: string;
code: string;
label: string;
noLabel: boolean;
}; };
logicalOperator?: string; logicalOperator?: string;
vName?: string; vName?: string;
@@ -145,34 +139,54 @@ export default defineComponent({
const actionName = props.context.find(element => element?.props?.name === 'displayName') const actionName = props.context.find(element => element?.props?.name === 'displayName')
const processingProps: ValueType = props.modelValue && props.modelValue.vars const processingProps: ValueType = props.modelValue && props.modelValue.vars
? props.modelValue ? reactive(props.modelValue)
: reactive({ : reactive({
name: '', name: '',
actionName: actionName?.props?.modelValue as string, actionName: actionName?.props?.modelValue as string,
displayName: '結果(戻り値)', displayName: '結果(戻り値)',
vars: [{ id: uuidv4() }] vars: [
{
id: uuidv4(),
field:{
objectType:'field',
sharedText:''
}
}]
}); });
const closeDg = () => { const closeDg = () => {
emit('update:modelValue', processingProps emit('update:modelValue', processingProps);
);
} }
const processingObjects = processingProps.vars; const processingObjects = processingProps.vars;
const deleteProcessingObject = (index: number) => processingObjects.length === 1 const deleteProcessingObject = (index: number) => {
? processingObjects.splice(0, processingObjects.length, { id: uuidv4() }) if(processingObjects.length >0){
: processingObjects.splice(index, 1); processingObjects.splice(index, 1);
}
if(processingObjects.length===0){
addProcessingObject();
}
}
const processingObjectsInputDisplay = computed(() => const processingObjectsInputDisplay = computed(() =>
processingObjects ? processingObjects ?
processingObjects processingObjects
.filter(item => item.field && item.logicalOperator && item.vName) .filter(item => item.field && item.logicalOperator && item.vName)
.map(item => { .map(item => {
return`var(${processingProps.name}.${item.vName}) = ${item.field.sharedText}` return`var(${processingProps.name}.${item.vName}) = ${item.field?.sharedText}`
}) })
: [] : []
); );
const addProcessingObject=()=>{
processingObjects.push({
id: uuidv4(),
field:{
objectType:'field',
sharedText:''
}
});
}
//集計処理方法 //集計処理方法
const logicalOperators = ref([ const logicalOperators = ref([
{ {
@@ -214,7 +228,7 @@ export default defineComponent({
closeDg, closeDg,
processingObjects, processingObjects,
processingProps, processingProps,
addProcessingObject: () => processingObjects.push({ id: uuidv4() }), addProcessingObject,
deleteProcessingObject, deleteProcessingObject,
logicalOperators, logicalOperators,
processingObjectsInputDisplay, processingObjectsInputDisplay,

View File

@@ -8,7 +8,7 @@ import {
import { actionAddins } from "."; import { actionAddins } from ".";
import { KintoneRestAPIClient } from "@kintone/rest-api-client"; import { KintoneRestAPIClient } from "@kintone/rest-api-client";
import { Lookup } from "@kintone/rest-api-client/lib/src/KintoneFields/types/property"; import { Lookup } from "@kintone/rest-api-client/lib/src/KintoneFields/types/property";
import { FieldForm, FieldType } from "../types/FieldLayout";
interface Props { interface Props {
displayName: string; displayName: string;
sources: Sources; sources: Sources;
@@ -27,37 +27,30 @@ interface Mapping {
isKey: boolean; isKey: boolean;
} }
interface To { interface To {
app: App; app: App;
fields: { fields:FieldForm[];
name: string;
type: string;
code: string;
label: string;
noLabel: boolean;
required: boolean;
minLength: string;
maxLength: string;
expression: string;
hideExpression: boolean;
unique: boolean;
defaultValue: string;
}[];
isDialogVisible: boolean; isDialogVisible: boolean;
} }
interface From { interface From {
sharedText: string; sharedText: string;
_t: string;
id: string; id: string;
objectType: string; objectType: 'variable'|'field'|'text';
name: {
name: string;
};
actionName: string;
displayName: string;
} }
interface IVar extends From{
name:{
name:string;
}
}
interface IFromField extends From,FieldForm{
}
interface Sources { interface Sources {
app: App; app: App;
} }
@@ -98,7 +91,7 @@ export class DataUpdateAction implements IAction {
); );
// createWithNull が有効な場合は、4 番目のパラメーターを true にして doUpdate 関数ブランチを実行します。 // createWithNull が有効な場合は、4 番目のパラメーターを true にして doUpdate 関数ブランチを実行します。
if (this.dataMappingProps.dataMapping.createWithNull === true) { if (this.dataMappingProps.dataMapping.createWithNull) {
await doUpdate( await doUpdate(
this.dataMappingProps.dataMapping.data, this.dataMappingProps.dataMapping.data,
this.dataMappingProps.sources.app.id, this.dataMappingProps.sources.app.id,
@@ -158,10 +151,23 @@ interface UpdateRecord {
const client = new KintoneRestAPIClient(); const client = new KintoneRestAPIClient();
const getFromValue=(item:Mapping,context:IContext)=>{
if (item.from.objectType === "variable") {
const rfrom =item.from as IVar;
return getContextVarByPath(context.variables,rfrom.name.name);
}else if(item.from.objectType === "field"){
const field = item.from as IFromField;
return context.record[field.code].value;
}
else {
return item.from.sharedText;
}
}
const doUpdate = async ( const doUpdate = async (
mappingData: Mapping[], mappingData: Mapping[],
appId: string, appId: string,
context: any, context: IContext,
needCreate: boolean, needCreate: boolean,
lookupFixedFieldCodes: string[] lookupFixedFieldCodes: string[]
) => { ) => {
@@ -179,11 +185,19 @@ const doUpdate = async (
) )
.map((m) => { .map((m) => {
if (m.from.objectType === "variable") { if (m.from.objectType === "variable") {
const rfrom =m.from as IVar;
return { return {
value: getContextVarByPath(context.variables, m.from.name.name), value: getContextVarByPath(context.variables,rfrom.name.name),
code: m.to.fields[0].code, code: m.to.fields[0].code,
}; };
} else { }else if(m.from.objectType === "field"){
const field = m.from as IFromField;
return {
value: context.record[field.code].value,
code: m.to.fields[0].code,
}
}
else {
return { return {
value: m.from.sharedText, value: m.from.sharedText,
code: m.to.fields[0].code, code: m.to.fields[0].code,
@@ -211,6 +225,7 @@ const doUpdate = async (
} }
); );
console.log(updateRecords);
await client.record.updateRecords({ await client.record.updateRecords({
app: appId, app: appId,
records: updateRecords, records: updateRecords,
@@ -218,21 +233,34 @@ const doUpdate = async (
} }
}; };
const makeQuery=(field:FieldForm,key:any)=>{
if(field.type===FieldType.NUMBER || field.type===FieldType.RECORD_NUMBER){
return `${field.code} = ${Number(key)}`
}
if(typeof key==='string'){
return `${field.code} = "${key}"`
}
}
const findUpdateField = async ( const findUpdateField = async (
mappingData: Mapping[], mappingData: Mapping[],
appId: string, appId: string,
context: any context: IContext
) => { ) => {
const queryStr = mappingData const queryStr = mappingData
.filter((m) => m.to.app && m.to.fields && m.to.fields.length > 0 && m.isKey) .filter((m) => m.to.app && m.to.fields && m.to.fields.length > 0 && m.isKey)
.map((m) => { .map((m) => {
if (m.from.objectType === "variable") { if (m.from.objectType === "variable") {
return `${m.to.fields[0].code} = "${getContextVarByPath( const vfrom = m.from as IVar;
context.variables, return makeQuery(m.to.fields[0],getContextVarByPath(context.variables , vfrom.name.name));
m.from.name.name }
)}"`; else if(m.from.objectType === "field"){
} else { const field = m.from as IFromField;
return `${m.to.fields[0].code}=${m.from.sharedText}`; return makeQuery(m.to.fields[0],context.record[field.code].value);
}
else{
return makeQuery(m.to.fields[0],m.from.sharedText);
} }
}) })
.join("&"); .join("&");
@@ -253,38 +281,51 @@ const findUpdateField = async (
const doCreate = async ( const doCreate = async (
mappingData: Mapping[], mappingData: Mapping[],
appId: string, appId: string,
context: any, context: IContext,
lookupFixedFieldCodes: string[] lookupFixedFieldCodes: string[]
) => { ) => {
const filterHandler = (item:Mapping)=>{
if(!item.to.fields || item.to.fields.length===0){
return false;
}
if(item.from.objectType === "variable" && (item.from as IVar).name.name ){
return true;
}
if(item.from.objectType === "field" && (item.from as IFromField).code){
return true;
}
if(item.from.objectType === "text" && item.from.sharedText!==null){
return true;
}
return false;
}
const record = mappingData const record = mappingData
.filter( .filter(filterHandler)
(item) =>
item.from.objectType === "variable" &&
item.from.name.name &&
item.to.app &&
item.to.fields &&
item.to.fields.length > 0
)
.filter((item) => !lookupFixedFieldCodes.includes(item.to.fields[0].code)) .filter((item) => !lookupFixedFieldCodes.includes(item.to.fields[0].code))
.reduce((accumulator, item) => { .reduce((accumulator, item) => {
return { return {
...accumulator, ...accumulator,
[item.to.fields[0].code]: { [item.to.fields[0].code]: {
value: getContextVarByPath(context.variables, item.from.name.name), value: getFromValue(item,context),
}, },
}; };
}, {}); }, {});
if (record && Object.keys(record).length > 0) { if (record && Object.keys(record).length > 0) {
await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", { console.log(record);
await client.record.addRecord({
app:appId, app:appId,
record: record, record:record
}); });
// await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", {
// app: appId,
// record: record,
// });
} }
}; };
const getLookupFixedFieldCodes = async (appId: string) => { const getLookupFixedFieldCodes = async (appId: string) => {
return await client.app return await client.app
.getFormFields({ app: appId, lang: "ja" }) .getFormFields({ app: appId })
.then((resp) => .then((resp) =>
Object.values(resp.properties) Object.values(resp.properties)
.filter((f) => (f as Lookup).lookup !== undefined) .filter((f) => (f as Lookup).lookup !== undefined)