Merged PR 82: feat:アクション選択UIの改善

以下の内容を改修しました。
1.TASK581:アクションのカテゴリおよび並び順を設定できるようにする
2.各アクションは対応イベント以外に設置できないようにする
3.DB構造を変更しました。
  * action: categoryidとnosort列追加
  * category: アクションのカテゴリマスタ追加
  * eventaction: アクションごと設置できないイベントIDを登録
4.デプロイの際、scripts\kintoneToolDB_20240820_update.sqlを実行してDBを更新してください。

Related work items: #524, #581
This commit is contained in:
Shohtetsu Ma
2024-08-21 01:30:01 +00:00
committed by Takuto Yoshida(タクト)
9 changed files with 839 additions and 28 deletions

View File

@@ -3,20 +3,46 @@
<div v-if="!isLoaded" class="spinner flex flex-center">
<q-spinner color="primary" size="3em" />
</div>
<q-table v-else row-key="index" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows"
class="action-table"
flat bordered
virtual-scroll
:pagination="pagination"
:rows-per-page-options="[0]"
:filter="filter"
<q-splitter
v-model="splitterModel"
style="height: 100%"
before-class="tab"
unit="px"
v-else
>
</q-table>
<template v-slot:before>
<q-tabs
v-model="tab"
vertical
active-color="white"
indicator-color="primary"
active-bg-color="primary"
class="bg-grey-2 text-grey-8"
dense
>
<q-tab :name="cate"
:label="cate"
v-for="(cate,) in categorys"
:key="cate"
></q-tab>
</q-tabs>
</template>
<template v-slot:after>
<q-table row-key="index" :selection="type" v-model:selected="selected" :columns="columns" :rows="actionForTab"
class="action-table"
flat bordered
virtual-scroll
:pagination="pagination"
:rows-per-page-options="[0]"
:filter="filter"></q-table>
</template>
</q-splitter>
</div>
</template>
<script>
import { ref,onMounted,reactive } from 'vue'
import { ref,onMounted,reactive,watchEffect,computed,watch } from 'vue'
import { api } from 'boot/axios';
import { useFlowEditorStore } from 'stores/flowEditor';
export default {
name: 'actionSelect',
@@ -25,30 +51,74 @@ export default {
type: String,
filter:String
},
setup(props) {
emits:[
"clearFilter"
],
setup(props,{emit}) {
const isLoaded=ref(false);
const columns = [
{ name: 'name', required: true,label: 'アクション名',align: 'left',field: 'name',sortable: true},
{ name: 'desc', align: 'left', label: '説明', field: 'desc', sortable: true },
// { name: 'content', label: '内容', field: 'content', sortable: true }
];
const rows = reactive([])
const store = useFlowEditorStore();
let actionData =reactive([]);
const categorys = ref('');
const tab=ref('');
const actionForTab=computed(()=>{
const rows=[];
const actions= props.filter? actionData:actionData.filter(x=>x.categoryname===tab.value);
actions.forEach((item,index) =>{
rows.push({index,
name:item.name,
desc:item.title,
outputPoints:item.outputpoints,
property:item.property});
});
return rows;
});
onMounted(async () => {
const res =await api.get('api/actions');
res.data.forEach((item,index) =>
{
rows.push({index,name:item.name,desc:item.title,outputPoints:item.outputpoints,property:item.property});
});
let eventId='';
if(store.selectedEvent ){
eventId=store.selectedEvent.header!=='DELETABLE'? store.selectedEvent.eventId : store.selectedEvent.parentId;
}
const res =await api.get(`api/eventactions/${store.selectedEvent.eventId}`);
actionData= res.data;
const categoryNames = Array.from(new Set(actionData.map(x=>x.categoryname)));
categorys.value=categoryNames;
tab.value = categoryNames.length>0? categoryNames[0]:'';
isLoaded.value=true;
});
// watch(props.filter,()=>{
// if(props.filter && props.filter!==''){
// tab.value='';
// }
// });
watch(tab,()=>{
if(tab.value!==''){
emit('clearFilter','');
}
});
// watchEffect(()=>{
// if(props.filter && props.filter!==''){
// tab.value='';
// }
// if(tab.value!==''){
// emit('update:filter','');
// }
// });
return {
columns,
rows,
selected: ref([]),
pagination:ref({
rowsPerPage:0
}),
isLoaded,
tab,
actionData,
categorys,
splitterModel: ref(150),
actionForTab
}
},
@@ -58,5 +128,6 @@ export default {
.action-table{
min-height: 10vh;
max-height: 68vh;
min-width: 550px;
}
</style>

View File

@@ -42,7 +42,7 @@
import { QTree, useQuasar } from 'quasar';
import { ActionFlow, RootAction } from 'src/types/ActionTypes';
import { useFlowEditorStore } from 'stores/flowEditor';
import { defineComponent, ref } from 'vue';
import { defineComponent, ref,watchEffect } from 'vue';
import { IKintoneEvent, IKintoneEventGroup, IKintoneEventNode } from '../../types/KintoneEvents';
import FieldSelect from '../FieldSelect.vue';
import ShowDialog from '../ShowDialog.vue';
@@ -75,8 +75,8 @@ export default defineComponent({
// const selectedFlow = store.currentFlow;
// const expanded=ref();
const selectedEvent = ref<IKintoneEvent | null>(null);
const selectedChangeEvent = ref<IKintoneEventGroup | null>(null);
const selectedEvent = ref<IKintoneEvent | undefined>(store.selectedEvent);
const selectedChangeEvent = ref<IKintoneEventGroup | undefined>(undefined);
const isFieldChange = (node: IKintoneEventNode) => {
return node.header == 'EVENT' && node.eventId.indexOf(".change.") > -1;
}
@@ -149,6 +149,9 @@ export default defineComponent({
selectedEvent.value.flowData = flow;
}
};
watchEffect(()=>{
store.setCurrentEvent(selectedEvent.value);
});
return {
// eventTree,
// expanded,

View File

@@ -41,7 +41,7 @@
</template>
</q-input>
</template>
<action-select ref="appDg" name="model" :filter="filter" type="single"></action-select>
<action-select ref="appDg" name="model" :filter="filter" type="single" @clearFilter="onClearFilter" ></action-select>
</ShowDialog>
</q-page>
@@ -198,7 +198,7 @@ const onSaveFlow = async () => {
if (targetFlow === undefined) {
$q.notify({
type: 'negative',
caption: "エラー",
caption: 'エラー',
message: `編集中のフローがありません。`
});
return;
@@ -241,6 +241,10 @@ const fetchData = async () => {
}
}
const onClearFilter=()=>{
filter.value='';
}
onMounted(() => {
authStore.toggleLeftMenu();
fetchData();

View File

@@ -66,6 +66,9 @@ export const useFlowEditorStore = defineStore('flowEditor', {
setActiveNode(node: IActionNode) {
this.activeNode = node;
},
setCurrentEvent(event:IKintoneEvent | undefined){
this.selectedEvent=event;
},
setApp(app: AppInfo) {
this.appInfo = app;
},