階層化ドロップダウンの不具合修正、実装完了

This commit is contained in:
xiaozhe.ma
2024-09-24 01:01:35 +09:00
parent 843db5f10c
commit 58bf916810
13 changed files with 638 additions and 553 deletions

View File

@@ -1,17 +1,16 @@
<template>
<div>
<q-stepper v-model="step" ref="stepper" color="primary" animated flat>
<q-step :name="1" title="データソースの設定" icon="app_registration" active-icon="app_registration"
done-icon="app_registration" :done="step > 1">
<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.appId}` : ''"
:href="data.sourceApp ? `${authStore.currentDomain.kintoneUrl}/k/${data.sourceApp.id}` : ''"
target="_blank" title="Kiontoneへ">
{{ data.sourceApp?.name }}
</a>
<div v-else>APP選択されていな</div>
<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>
@@ -22,17 +21,17 @@
<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>{{ 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 outline dense label="削除" padding="xs sm" color="primary" @click="() => delRow(index)" />
</q-btn-group>
</div>
</q-virtual-scroll>
@@ -52,12 +51,11 @@
</ShowDialog>
</q-step>
<q-step :name="2" title="ドロップダウンコンポーネントの設定" icon="multiple_stop" active-icon="multiple_stop"
done-icon="multiple_stop" :done="step > 2">
<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 class="col-6">ドロップダウンフィールド</div>
</div>
<div class="col-auto">
<div style="width: 88px; height: 1px;"></div>
@@ -72,9 +70,9 @@
<div class="col-auto">
<div class="row justify-end">
<q-btn-group outline>
<q-btn outline dense label="変更" padding="xs sm" color="primary"
<q-btn outline dense label="設定" padding="xs sm" color="primary"
@click="() => showFieldDialog(item, 'dropDown')" />
<q-btn outline dense label="消去" padding="xs sm" color="primary"
<q-btn outline dense label="クリア" padding="xs sm" color="primary"
@click="() => item.dropDown = undefined" />
</q-btn-group>
</div>
@@ -87,7 +85,7 @@
<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 ? '保存' : '次のステップ'"
<q-btn @click="stepperNext" color="primary" :label="step === 2 ? '保存' : '次'"
:disable="nextBtnCheck()" />
</div>
</q-stepper-navigation>
@@ -126,7 +124,7 @@
</template>
<script lang="ts">
import { defineComponent, reactive, ref, watchEffect } from 'vue';
import { defineComponent, reactive, ref, watchEffect,watch } from 'vue';
import ShowDialog from './ShowDialog.vue';
import AppSelectBox from './AppSelectBox.vue';
import FieldSelect from './FieldSelect.vue';
@@ -155,22 +153,23 @@ export default defineComponent({
const stepper = ref();
const step = ref(1);
const data = reactive({
sourceApp: props.modelValue.sourceApp ?? { appFilter: '', showSelectApp: false },
dropDownApp: props.modelValue.dropDownApp,
fieldList: props.modelValue.fieldList ?? [],
});
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.sourceApp.showSelectApp = true;
const showAppDialog = () => data.value.sourceApp.showSelectApp = true;
const clearSelectedApp = () => {
data.sourceApp = { appFilter: '', showSelectApp: false };
data.fieldList = [];
data.value.sourceApp = { appFilter: '', showSelectApp: false };
data.value.fieldList = [];
};
const closeAppDialog = (val: 'OK' | 'Cancel') => {
data.sourceApp.showSelectApp = false;
data.value.sourceApp.showSelectApp = false;
const selected = appDg.value?.selected[0];
if (val === 'OK' && selected) {
if (flowStore.appInfo?.appId === selected.id) {
@@ -179,9 +178,9 @@ export default defineComponent({
caption: "エラー",
message: 'データソースを現在のアプリにすることはできません。'
});
} else if (selected.id !== data.sourceApp.id) {
} else if (selected.id !== data.value.sourceApp.id) {
clearSelectedApp();
Object.assign(data.sourceApp, { id: selected.id, name: selected.name });
Object.assign(data.value.sourceApp, { id: selected.id, name: selected.name });
}
}
};
@@ -195,14 +194,14 @@ export default defineComponent({
dropDownDg: { show: false, filter: '' },
});
const addRow = () => data.fieldList.push(defaultRow());
const delRow = (index: number) => data.fieldList.splice(index, 1);
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.fieldList.some((field, idx) =>
const isDuplicate = data.value.fieldList.some((field, idx) =>
idx !== index && (field[keyName]?.code === selected.code || field[keyName]?.label === selected.label)
);
@@ -221,33 +220,34 @@ export default defineComponent({
const nextBtnCheck = () => {
const s = step.value
if (s === 1) {
return !(data.sourceApp?.id && data.fieldList?.length > 0 && data.fieldList?.every(f => f.source?.name));
return !(data.value.sourceApp?.id && data.value.fieldList?.length > 0 && data.value.fieldList?.every(f => f.source?.name));
} else if (s === 2) {
return !data.fieldList?.every(f => f.dropDown?.name);
return !data.value.fieldList?.every(f => f.dropDown?.name);
}
return true;
};
const stepperNext = () => {
if (step.value === 2) {
props.finishDialogHandler?.(data);
props.finishDialogHandler?.(data.value);
} else {
data.dropDownApp = { name: flowStore.appInfo?.name, id: flowStore.appInfo?.appId };
data.value.dropDownApp = { name: flowStore.appInfo?.name, id: flowStore.appInfo?.appId };
stepper.value?.next();
}
};
// データ変更の監視
watchEffect(() => emit('update:modelValue', data));
// // データ変更の監視
// watchEffect(() =>{
// emit('update:modelValue', data.value);
// });
return {
// 状態と参照
authStore,
data,
step,
stepper,
appDg,
data,
// アプリ関連の関数
showAppDialog,
closeAppDialog,
@@ -268,4 +268,4 @@ export default defineComponent({
};
}
});
</script>
</script>

View File

@@ -4,7 +4,7 @@
<template v-slot:control>
<q-card flat class="full-width">
<q-card-actions vertical>
<q-btn color="grey-3" text-color="black" @click="() => { dgIsShow = true }">設定ドロップダウンメニュー</q-btn>
<q-btn color="grey-3" text-color="black" @click="() => { dgIsShow = true }">クリックで設定</q-btn>
</q-card-actions>
<q-card-section class="text-caption">
<div v-if="data.dropDownApp?.name">
@@ -17,7 +17,23 @@
</q-field>
</div>
<ShowDialog v-model:visible="dgIsShow" name="設定ドロップダウンメニュー" @close="closeDg" min-width="50vw" min-height="20vh" disableBtn>
<ShowDialog v-model:visible="dgIsShow" name="ドロップダウン階層化設定" @close="closeDg" min-width="50vw" min-height="20vh" disableBtn>
<template v-slot:toolbar>
<q-btn flat round dense icon="more_vert" >
<q-menu auto-close anchor="bottom start">
<q-list>
<q-item clickable @click="copySetting()">
<q-item-section avatar><q-icon name="content_copy" ></q-icon></q-item-section>
<q-item-section >コピー</q-item-section>
</q-item>
<q-item clickable @click="pasteSetting()">
<q-item-section avatar><q-icon name="content_paste" ></q-icon></q-item-section>
<q-item-section >貼り付け</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</template>
<div class="q-mb-md q-ml-md q-mr-md">
<CascadingDropDownBox v-model:model-value="data" :finishDialogHandler="finishDialogHandler" />
</div>
@@ -52,8 +68,12 @@ export default defineComponent({
},
setup(props, { emit }) {
const dgIsShow = ref(false);
const data = ref(props.modelValue);
// const data = ref(props.modelValue);
const data = ref({
sourceApp: props.modelValue.sourceApp ?? { appFilter: '', showSelectApp: false },
dropDownApp: props.modelValue.dropDownApp,
fieldList: props.modelValue.fieldList ?? [],
});
const closeDg = (state: string) => {
dgIsShow.value = false;
};
@@ -63,17 +83,51 @@ export default defineComponent({
dgIsShow.value = false
emit('update:modelValue', data.value);
}
watchEffect(() => {
emit('update:modelValue', data.value);
});
//設定をコピーする
const copySetting=()=>{
if (navigator.clipboard) {
const jsonData= JSON.stringify(data.value);
navigator.clipboard.writeText(jsonData).then(() => {
console.log('Text successfully copied to clipboard');
},
(err) => {
console.error('Error in copying text: ', err);
});
} else {
console.log('Clipboard API not available');
}
};
//設定を貼り付ける
const pasteSetting=async ()=>{
try {
const text = await navigator.clipboard.readText();
console.log('Text from clipboard:', text);
const jsonData=JSON.parse(text);
if('sourceApp' in jsonData && 'dropDownApp' in jsonData && 'fieldList' in jsonData){
const {sourceApp,dropDownApp, fieldList}=jsonData;
data.value.sourceApp=sourceApp;
data.value.dropDownApp=dropDownApp;
data.value.fieldList=fieldList;
}
} catch (err) {
console.error('Failed to read text from clipboard: ', err);
throw err;
}
}
// watchEffect(() => {
// emit('update:modelValue', data.value);
// });
return {
dgIsShow,
closeDg,
data,
finishDialogHandler,
copySetting,
pasteSetting
};
}
});
</script>
</script>