272 lines
10 KiB
Vue
272 lines
10 KiB
Vue
<template>
|
|
<div>
|
|
<q-stepper v-model="step" ref="stepper" color="primary" animated flat>
|
|
<q-step :name="1" title="データソースの設定" icon="app_registration" :done="step > 1">
|
|
<div class="row justify-between items-center">
|
|
<div>アプリの選択 :</div>
|
|
<div>
|
|
<a v-if="data.sourceApp?.name" class="q-mr-xs"
|
|
:href="data.sourceApp ? `${authStore.currentDomain.kintoneUrl}/k/${data.sourceApp.id}` : ''"
|
|
target="_blank" title="Kiontoneへ">
|
|
{{ data.sourceApp?.name }}
|
|
</a>
|
|
<div v-else class="text-red">APPを選択してください</div>
|
|
<q-btn v-if="data.sourceApp?.name" flat color="grey" icon="clear" size="sm" padding="none"
|
|
@click="clearSelectedApp" />
|
|
</div>
|
|
<q-btn outline dense label="変更" padding="xs sm" color="primary" @click="showAppDialog" />
|
|
</div>
|
|
|
|
<!-- フィールド設定部分 -->
|
|
<template v-if="data.sourceApp?.name">
|
|
<q-separator class="q-mt-md" />
|
|
<div class="q-my-md row justify-between items-center">
|
|
データ階層を設定する :
|
|
<q-btn icon="add" size="sm" padding="xs" outline color="primary" @click="addRow" />
|
|
</div>
|
|
<q-virtual-scroll style="max-height: 13.5rem;" :items="data.fieldList" separator v-slot="{ item, index }">
|
|
<div class="row justify-between items-center q-my-md">
|
|
<div>{{ index + 1 }}階層 :</div>
|
|
<div>{{ item.source?.name }}</div>
|
|
<q-btn-group outline>
|
|
<q-btn outline dense label="変更" padding="xs sm" color="primary"
|
|
@click="() => showFieldDialog(item, 'source')" />
|
|
<q-btn outline dense label="削除" padding="xs sm" color="primary" @click="() => delRow(index)" />
|
|
</q-btn-group>
|
|
</div>
|
|
</q-virtual-scroll>
|
|
</template>
|
|
|
|
<!-- アプリ選択ダイアログ -->
|
|
<ShowDialog v-model:visible="data.sourceApp.showSelectApp" name="アプリ選択" @close="closeAppDialog" min-width="50vw"
|
|
min-height="50vh">
|
|
<template v-slot:toolbar>
|
|
<q-input dense debounce="300" v-model="data.sourceApp.appFilter" placeholder="検索" clearable>
|
|
<template v-slot:before>
|
|
<q-icon name="search" />
|
|
</template>
|
|
</q-input>
|
|
</template>
|
|
<AppSelectBox ref="appDg" name="アプリ" type="single" :filter="data.sourceApp.appFilter" />
|
|
</ShowDialog>
|
|
</q-step>
|
|
|
|
<q-step :name="2" title="ドロップダウンフィールドの設定" icon="multiple_stop" :done="step > 2">
|
|
<div class="row q-pa-sm q-col-gutter-x-sm flex-center">
|
|
<div class="col-grow row q-col-gutter-x-sm">
|
|
<div class="col-6">データソース</div>
|
|
<div class="col-6">ドロップダウンフィールド</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div style="width: 88px; height: 1px;"></div>
|
|
</div>
|
|
</div>
|
|
<div v-for="(item) in data.fieldList" :key="item.id" class="row q-pa-sm q-col-gutter-x-sm flex-center">
|
|
<div class="col-grow row q-col-gutter-x-sm">
|
|
|
|
<div class="col-6">{{ item.source.name }}</div>
|
|
<div class="col-6">{{ item.dropDown?.name }}</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="row justify-end">
|
|
<q-btn-group outline>
|
|
<q-btn outline dense label="設定" padding="xs sm" color="primary"
|
|
@click="() => showFieldDialog(item, 'dropDown')" />
|
|
<q-btn outline dense label="クリア" padding="xs sm" color="primary"
|
|
@click="() => item.dropDown = undefined" />
|
|
</q-btn-group>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-step>
|
|
|
|
<!-- ステップナビゲーション -->
|
|
<template v-slot:navigation>
|
|
<q-stepper-navigation>
|
|
<div class="row justify-end q-mt-md">
|
|
<q-btn v-if="step > 1" flat color="primary" @click="$refs.stepper.previous()" label="戻る" class="q-ml-sm" />
|
|
<q-btn @click="stepperNext" color="primary" :label="step === 2 ? '確定' : '次へ'"
|
|
:disable="nextBtnCheck()" />
|
|
</div>
|
|
</q-stepper-navigation>
|
|
</template>
|
|
</q-stepper>
|
|
|
|
<!-- フィールド選択ダイアログ -->
|
|
<template v-for="(item, index) in data.fieldList" :key="`dg${item.id}`">
|
|
<show-dialog v-model:visible="item.sourceDg.show" name="フィールド一覧" min-width="400px">
|
|
<template v-slot:toolbar>
|
|
<q-input dense debounce="300" v-model="item.sourceDg.filter" placeholder="検索" clearable>
|
|
<template v-slot:before>
|
|
<q-icon name="search" />
|
|
</template>
|
|
</q-input>
|
|
</template>
|
|
<FieldSelect name="フィールド" :appId="data.sourceApp.id" :selectedFields="item.source"
|
|
:filter="item.sourceDg.filter" :updateSelectFields="(f) => updateSelectField(f, item, index, 'source')"
|
|
:blackListLabel="blackListLabel" />
|
|
</show-dialog>
|
|
|
|
<show-dialog v-model:visible="item.dropDownDg.show" name="フィールド一覧" min-width="400px">
|
|
<template v-slot:toolbar>
|
|
<q-input dense debounce="300" v-model="item.dropDownDg.filter" placeholder="検索" clearable>
|
|
<template v-slot:before>
|
|
<q-icon name="search" />
|
|
</template>
|
|
</q-input>
|
|
</template>
|
|
<FieldSelect name="フィールド" :appId="data.dropDownApp.id" :selectedFields="item.source"
|
|
:filter="item.dropDownDg.filter" :updateSelectFields="(f) => updateSelectField(f, item, index, 'dropDown')"
|
|
:blackListLabel="blackListLabel" />
|
|
</show-dialog>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, reactive, ref, watchEffect,watch } from 'vue';
|
|
import ShowDialog from './ShowDialog.vue';
|
|
import AppSelectBox from './AppSelectBox.vue';
|
|
import FieldSelect from './FieldSelect.vue';
|
|
import { useAuthStore } from 'src/stores/useAuthStore';
|
|
import { useFlowEditorStore } from 'src/stores/flowEditor';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { useQuasar } from 'quasar';
|
|
|
|
export default defineComponent({
|
|
name: 'CascadingDropDownBox',
|
|
inheritAttrs: false,
|
|
components: { ShowDialog, AppSelectBox, FieldSelect },
|
|
props: {
|
|
modelValue: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
finishDialogHandler: Function,
|
|
},
|
|
emits: ['update:modelValue'],
|
|
setup(props, { emit }) {
|
|
const authStore = useAuthStore();
|
|
const flowStore = useFlowEditorStore();
|
|
const $q = useQuasar();
|
|
const appDg = ref();
|
|
const stepper = ref();
|
|
const step = ref(1);
|
|
|
|
const data =ref(props.modelValue);
|
|
// const data = ref({
|
|
// sourceApp: props.modelValue.sourceApp ?? { appFilter: '', showSelectApp: false },
|
|
// dropDownApp: props.modelValue.dropDownApp,
|
|
// fieldList: props.modelValue.fieldList ?? [],
|
|
// });
|
|
|
|
// アプリ関連の関数
|
|
const showAppDialog = () => data.value.sourceApp.showSelectApp = true;
|
|
|
|
const clearSelectedApp = () => {
|
|
data.value.sourceApp = { appFilter: '', showSelectApp: false };
|
|
data.value.fieldList = [];
|
|
};
|
|
|
|
const closeAppDialog = (val: 'OK' | 'Cancel') => {
|
|
data.value.sourceApp.showSelectApp = false;
|
|
const selected = appDg.value?.selected[0];
|
|
if (val === 'OK' && selected) {
|
|
if (flowStore.appInfo?.appId === selected.id) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
caption: 'エラー',
|
|
message: 'データソースを現在のアプリにすることはできません。'
|
|
});
|
|
} else if (selected.id !== data.value.sourceApp.id) {
|
|
clearSelectedApp();
|
|
Object.assign(data.value.sourceApp, { id: selected.id, name: selected.name });
|
|
}
|
|
}
|
|
};
|
|
|
|
// フィールド関連の関数
|
|
const defaultRow = () => ({
|
|
id: uuidv4(),
|
|
source: undefined,
|
|
dropDown: undefined,
|
|
sourceDg: { show: false, filter: '' },
|
|
dropDownDg: { show: false, filter: '' },
|
|
});
|
|
|
|
const addRow = () => data.value.fieldList.push(defaultRow());
|
|
const delRow = (index: number) => data.value.fieldList.splice(index, 1);
|
|
|
|
const showFieldDialog = (item: any, keyName: string) => item[`${keyName}Dg`].show = true;
|
|
|
|
const updateSelectField = (f: any, item: any, index: number, keyName: 'source' | 'dropDown') => {
|
|
const [selected] = f.value;
|
|
const isDuplicate = data.value.fieldList.some((field, idx) =>
|
|
idx !== index && (field[keyName]?.code === selected.code || field[keyName]?.label === selected.label)
|
|
);
|
|
|
|
if (isDuplicate) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
caption: 'エラー',
|
|
message: '重複したフィールドは選択できません'
|
|
});
|
|
} else {
|
|
item[keyName] = selected;
|
|
}
|
|
};
|
|
|
|
// ステッパー関連の関数
|
|
const nextBtnCheck = () => {
|
|
const stepNo = step.value
|
|
if (stepNo === 1) {
|
|
return !(data.value.sourceApp?.id && data.value.fieldList?.length > 0 && data.value.fieldList?.every(f => f.source?.name));
|
|
} else if (stepNo === 2) {
|
|
return !data.value.fieldList?.every(f => f.dropDown?.name);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const stepperNext = () => {
|
|
if (step.value === 2) {
|
|
props.finishDialogHandler?.(data.value);
|
|
} else {
|
|
data.value.dropDownApp = { name: flowStore.appInfo?.name, id: flowStore.appInfo?.appId };
|
|
stepper.value?.next();
|
|
}
|
|
};
|
|
|
|
// // データ変更の監視
|
|
// watchEffect(() =>{
|
|
// emit('update:modelValue', data.value);
|
|
// });
|
|
|
|
return {
|
|
// 状態と参照
|
|
authStore,
|
|
step,
|
|
stepper,
|
|
appDg,
|
|
data,
|
|
// アプリ関連の関数
|
|
showAppDialog,
|
|
closeAppDialog,
|
|
clearSelectedApp,
|
|
|
|
// フィールド関連の関数
|
|
addRow,
|
|
delRow,
|
|
showFieldDialog,
|
|
updateSelectField,
|
|
|
|
// ステッパー関連の関数
|
|
nextBtnCheck,
|
|
stepperNext,
|
|
|
|
// 定数
|
|
blackListLabel: ['レコード番号', '作業者', '更新者', '更新日時', '作成日時', '作成者', 'カテゴリー', 'ステータス'],
|
|
};
|
|
}
|
|
});
|
|
</script>
|