[feature] add new application
This commit is contained in:
@@ -17,28 +17,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, onMounted, reactive, watchEffect } from 'vue'
|
import { ref, onMounted, reactive, watchEffect, PropType } from 'vue'
|
||||||
import { api } from 'boot/axios';
|
import { api } from 'boot/axios';
|
||||||
|
|
||||||
|
interface IAppDisplay {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
createdate: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AppSelectBox',
|
name: 'AppSelectBox',
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
type: String,
|
type: String,
|
||||||
filter: String,
|
filter: String,
|
||||||
|
filterInitRowsFunc: {
|
||||||
|
type: Function as PropType<(app: IAppDisplay) => boolean>,
|
||||||
|
},
|
||||||
updateSelectApp: {
|
updateSelectApp: {
|
||||||
type: Function
|
type: Function
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'id', required: true, label: 'ID', align: 'left', field: 'id', sortable: true },
|
{ name: 'id', required: true, label: 'ID', align: 'left', field: 'id', sortable: true, sort: (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10) },
|
||||||
{ name: 'name', label: 'アプリ名', field: 'name', sortable: true, align: 'left' },
|
{ name: 'name', label: 'アプリ名', field: 'name', sortable: true, align: 'left' },
|
||||||
{ name: 'description', label: '概要', field: 'description', align: 'left', sortable: false },
|
{ name: 'description', label: '概要', field: 'description', align: 'left', sortable: false },
|
||||||
{ name: 'createdate', label: '作成日時', field: 'createdate', align: 'left' }
|
{ name: 'createdate', label: '作成日時', field: 'createdate', align: 'left' }
|
||||||
]
|
]
|
||||||
const isLoaded = ref(false);
|
const isLoaded = ref(false);
|
||||||
const rows: any[] = reactive([]);
|
const rows = reactive<IAppDisplay[]>([]);
|
||||||
const selected = ref([])
|
const selected = ref([])
|
||||||
|
|
||||||
watchEffect(()=>{
|
watchEffect(()=>{
|
||||||
@@ -49,12 +59,16 @@ export default {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
api.get('api/v1/allapps').then(res => {
|
api.get('api/v1/allapps').then(res => {
|
||||||
res.data.apps.forEach((item: any) => {
|
res.data.apps.forEach((item: any) => {
|
||||||
rows.push({
|
const row : IAppDisplay = {
|
||||||
id: item.appId,
|
id: item.appId,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
createdate: dateFormat(item.createdAt)
|
createdate: dateFormat(item.createdAt)
|
||||||
});
|
}
|
||||||
|
if (props.filterInitRowsFunc && !props.filterInitRowsFunc(row)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rows.push(row);
|
||||||
});
|
});
|
||||||
isLoaded.value = true;
|
isLoaded.value = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions v-if="!disableBtn" align="right" class="text-primary">
|
<q-card-actions v-if="!disableBtn" align="right" class="text-primary">
|
||||||
<q-btn flat label="確定" v-close-popup @click="CloseDialogue('OK')" />
|
<q-btn flat label="確定" :loading="okBtnLoading" :v-close-popup="okBtnAutoClose" @click="CloseDialogue('OK')" />
|
||||||
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
|
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
@@ -30,6 +30,11 @@ export default {
|
|||||||
height:String,
|
height:String,
|
||||||
minWidth:String,
|
minWidth:String,
|
||||||
minHeight:String,
|
minHeight:String,
|
||||||
|
okBtnLoading:Boolean,
|
||||||
|
okBtnAutoClose:{
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
disableBtn:{
|
disableBtn:{
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<q-table title="Treats" :rows="rows" :columns="columns" row-key="id" :filter="filter" :loading="loading" :pagination="pagination">
|
<q-table title="Treats" :rows="rows" :columns="columns" row-key="id" :filter="filter" :loading="loading" :pagination="pagination">
|
||||||
|
|
||||||
<template v-slot:top>
|
<template v-slot:top>
|
||||||
<q-btn disabled color="primary" :disable="loading" label="新規" @click="addRow" />
|
<q-btn color="primary" :disable="loading" label="新規" @click="showAddAppDialog" />
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-input borderless dense filled debounce="300" v-model="filter" placeholder="検索">
|
<q-input borderless dense filled debounce="300" v-model="filter" placeholder="検索">
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
@@ -32,9 +32,18 @@
|
|||||||
</q-btn-group>
|
</q-btn-group>
|
||||||
</q-td>
|
</q-td>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</q-table>
|
</q-table>
|
||||||
|
|
||||||
|
<show-dialog v-model:visible="showSelectApp" name="アプリ選択" @close="closeSelectAppDialog" min-width="50vw" min-height="50vh" :ok-btn-auto-close="false" :ok-btn-loading="isAdding">
|
||||||
|
<template v-slot:toolbar>
|
||||||
|
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
||||||
|
<template v-slot:before>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</template>
|
||||||
|
<app-select-box ref="appDialog" name="アプリ" type="single" :filter="filter" :filterInitRowsFunc="filterInitRows" />
|
||||||
|
</show-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -46,9 +55,12 @@ import { useFlowEditorStore } from 'stores/flowEditor';
|
|||||||
import { router } from 'src/router';
|
import { router } from 'src/router';
|
||||||
import { date } from 'quasar'
|
import { date } from 'quasar'
|
||||||
import { IManagedApp } from 'src/types/AppTypes';
|
import { IManagedApp } from 'src/types/AppTypes';
|
||||||
|
import ShowDialog from 'src/components/ShowDialog.vue';
|
||||||
|
import AppSelectBox from 'src/components/AppSelectBox.vue';
|
||||||
|
|
||||||
interface IAppDisplay{
|
interface IAppDisplay{
|
||||||
id:string;
|
id:string;
|
||||||
|
sortId: number;
|
||||||
name:string;
|
name:string;
|
||||||
url:string;
|
url:string;
|
||||||
user:string;
|
user:string;
|
||||||
@@ -58,36 +70,38 @@ interface IAppDisplay{
|
|||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const numberStringSorting = (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10);
|
const numberStringSorting = (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10);
|
||||||
|
const addedRowHighlightClass = (row: IAppDisplay) => row.id === addedRowId.value ? 'highlight-row' : '';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true, sort: numberStringSorting },
|
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true, sort: numberStringSorting, classes: addedRowHighlightClass },
|
||||||
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true },
|
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true, classes: addedRowHighlightClass },
|
||||||
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true },
|
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: addedRowHighlightClass },
|
||||||
{ name: 'user', label: '最後更新者', field: 'user', align: 'left', sortable: true},
|
{ name: 'user', label: '最後更新者', field: 'user', align: 'left', sortable: true, classes: addedRowHighlightClass},
|
||||||
{ name: 'updatetime', label: '最後更新日', field: 'updatetime', align: 'left', sortable: true},
|
{ name: 'updatetime', label: '最後更新日', field: 'updatetime', align: 'left', sortable: true, classes: addedRowHighlightClass },
|
||||||
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true},
|
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true, sort: numberStringSorting, classes: addedRowHighlightClass },
|
||||||
{ name: 'actions', label: '操作', field: 'actions' }
|
{ name: 'actions', label: '操作', field: 'actions', classes: addedRowHighlightClass }
|
||||||
];
|
];
|
||||||
|
|
||||||
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
|
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const filter = ref('');
|
const filter = ref('');
|
||||||
const rows = ref<IAppDisplay[]>([]);
|
const rows = ref<IAppDisplay[]>([]);
|
||||||
|
const rowIds = new Set<string>();
|
||||||
|
|
||||||
const store = useFlowEditorStore();
|
const store = useFlowEditorStore();
|
||||||
|
const appDialog = ref();
|
||||||
|
const showSelectApp=ref(false);
|
||||||
|
const isAdding = ref(false);
|
||||||
|
const addedRowId = ref('');
|
||||||
|
|
||||||
const getApps = async () => {
|
const getApps = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
rowIds.clear();
|
||||||
const result = await api.get('api/apps');
|
const result = await api.get('api/apps');
|
||||||
rows.value = result.data.map((item: IManagedApp) => {
|
rows.value = result.data.map((item: IManagedApp) => {
|
||||||
return {
|
rowIds.add(item.appid);
|
||||||
id: item.appid,
|
return appToAppDisplay(item)
|
||||||
name: item.appname,
|
}).sort((a: IAppDisplay, b: IAppDisplay) => a.sortId - b.sortId); // set default order
|
||||||
url: `${item.domainurl}/k/${item.appid}`,
|
|
||||||
user: `${item.user.first_name} ${item.user.last_name}` ,
|
|
||||||
updatetime:date.formatDate(item.update_time, 'YYYY/MM/DD HH:mm'),
|
|
||||||
version: Number(item.version)
|
|
||||||
}
|
|
||||||
}).sort((a: IAppDisplay, b: IAppDisplay) => numberStringSorting(a.id, b.id)); // set default order
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +114,44 @@ watch(() => authStore.currentDomain.id, async () => {
|
|||||||
await getApps();
|
await getApps();
|
||||||
});
|
});
|
||||||
|
|
||||||
const addRow = () => {
|
const filterInitRows = (row: {id: string}) => {
|
||||||
return
|
return !rowIds.has(row.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showAddAppDialog = () => {
|
||||||
|
showSelectApp.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeSelectAppDialog = async (val: 'OK'|'Cancel') => {
|
||||||
|
showSelectApp.value = true;
|
||||||
|
if (val == 'OK' && appDialog.value.selected[0]) {
|
||||||
|
isAdding.value = true;
|
||||||
|
const data = appDialog.value.selected[0];
|
||||||
|
const appInfo = {
|
||||||
|
domainurl: authStore.currentDomain.kintoneUrl,
|
||||||
|
appid: data.id ,
|
||||||
|
appname: data.name
|
||||||
|
};
|
||||||
|
const result = await api.post('api/apps', appInfo);
|
||||||
|
const item = result?.data;
|
||||||
|
if (item) {
|
||||||
|
// binarysearch is better
|
||||||
|
const newItem = appToAppDisplay(item)
|
||||||
|
const index = rows.value.findIndex(element => element.sortId >= newItem.sortId);
|
||||||
|
if (index === -1) {
|
||||||
|
rows.value.push(newItem);
|
||||||
|
} else {
|
||||||
|
rows.value.splice(index, 0, newItem);
|
||||||
|
}
|
||||||
|
rowIds.add(newItem.id);
|
||||||
|
addedRowId.value = newItem.id;
|
||||||
|
setTimeout(() => {
|
||||||
|
addedRowId.value = ''
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showSelectApp.value = false;
|
||||||
|
isAdding.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeRow = (app:IAppDisplay) => {
|
const removeRow = (app:IAppDisplay) => {
|
||||||
@@ -112,6 +162,18 @@ const showHistory = (app:IAppDisplay) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const appToAppDisplay = (app: IManagedApp) => {
|
||||||
|
return {
|
||||||
|
id: app.appid,
|
||||||
|
sortId: parseInt(app.appid, 10),
|
||||||
|
name: app.appname,
|
||||||
|
url: `${app.domainurl}/k/${app.appid}`,
|
||||||
|
user: `${app.user.first_name} ${app.user.last_name}` ,
|
||||||
|
updatetime:date.formatDate(app.update_time, 'YYYY/MM/DD HH:mm'),
|
||||||
|
version: app.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const editFlow = (app:IAppDisplay) => {
|
const editFlow = (app:IAppDisplay) => {
|
||||||
store.setApp({
|
store.setApp({
|
||||||
appId: app.id,
|
appId: app.id,
|
||||||
@@ -121,3 +183,13 @@ const editFlow = (app:IAppDisplay) => {
|
|||||||
router.push('/FlowChart/' + app.id);
|
router.push('/FlowChart/' + app.id);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.q-table td.highlight-row {
|
||||||
|
animation: breathe 0.6s ease-in-out 2 forwards;
|
||||||
|
}
|
||||||
|
@keyframes breathe {
|
||||||
|
0% { background-color: #ffffff; }
|
||||||
|
50% { background-color: #fffde7; }
|
||||||
|
100% { background-color: #ffffff; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user