Files
KintoneAppBuilder/frontend/src/pages/AppManagement.vue
xue jiahao 3ecb08b872 delete app
2024-12-09 21:46:23 +08:00

240 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="q-pa-md">
<div class="q-gutter-sm row items-start">
<q-breadcrumbs>
<q-breadcrumbs-el icon="widgets" label="アプリ管理" />
</q-breadcrumbs>
</div>
<q-table title="Treats" :rows="rows" :columns="columns" row-key="id" :filter="filter" :loading="loading" :pagination="pagination">
<template v-slot:top>
<q-btn color="primary" :disable="loading" label="新規" @click="showAddAppDialog" />
<q-space />
<q-input borderless dense filled debounce="300" v-model="filter" placeholder="検索">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</template>
<template v-slot:body-cell-name="prop">
<q-td :props="prop">
<q-btn flat dense :label="prop.row.name" @click="toEditFlowPage(prop.row)" ></q-btn>
</q-td>
</template>
<template v-slot:body-cell-url="prop">
<q-td :props="prop">
<a :href="prop.row.url" target="_blank" :title="prop.row.name" >
{{ prop.row.url }}
</a>
</q-td>
</template>
<template v-slot:body-cell-actions="p">
<q-td :props="p">
<table-action-menu :row="p.row" :actions="actionList" />
</q-td>
</template>
</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="dgFilter" placeholder="検索" clearable>
<template v-slot:before>
<q-icon name="search" />
</template>
</q-input>
</template>
<app-select-box ref="appDialog" name="アプリ" type="single" :filter="dgFilter" :filterInitRowsFunc="filterInitRows" />
</show-dialog>
<show-dialog v-model:visible="showVersionHistory" :name="targetRow?.name + 'のバージョン履歴'" @close="closeHistoryDg" min-width="30vw" :ok-btn-auto-close="false" :ok-btn-loading="isAdding"
ok-btn-label="選択">
<version-history ref="versionDialog" :app="targetRow" />
</show-dialog>
<q-dialog v-model="deleteDialog" persistent>
<q-card>
<q-card-section class="row items-center">
<q-icon name="warning" color="warning" size="2em" />
<span class="q-ml-sm">削除してもよろしいですか</span>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn flat label="OK" color="primary" :loading="deleteUserLoading" @click="deleteApp" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, reactive } from 'vue';
import { useQuasar } from 'quasar'
import { api } from 'boot/axios';
import { useAuthStore } from 'stores/useAuthStore';
import { useFlowEditorStore } from 'stores/flowEditor';
import { router } from 'src/router';
import { date } from 'quasar'
import { IManagedApp, IAppDisplay, IAppVersion } from 'src/types/AppTypes';
import ShowDialog from 'src/components/ShowDialog.vue';
import AppSelectBox from 'src/components/AppSelectBox.vue';
import TableActionMenu from 'components/TableActionMenu.vue';
import VersionHistory from 'components/dialog/VersionHistory.vue';
const authStore = useAuthStore();
const numberStringSorting = (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10);
const columns = [
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true, sort: numberStringSorting },
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true },
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true },
{ name: 'updateUser', label: '最後更新者', field: (row: IAppDisplay) => row.updateUser.fullName, align: 'left', sortable: true},
{ name: 'updateTime', label: '最後更新日', field: 'updateTime', align: 'left', sortable: true},
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true, sort: numberStringSorting },
{ name: 'actions', label: '', field: 'actions' }
];
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
const loading = ref(false);
const filter = ref('');
const dgFilter = ref('');
const rows = ref<IAppDisplay[]>([]);
const targetRow = ref<IAppDisplay>();
const rowIds = new Set<string>();
const $q = useQuasar()
const store = useFlowEditorStore();
const appDialog = ref();
const versionDialog = ref();
const showSelectApp=ref(false);
const showVersionHistory=ref(false);
const isAdding = ref(false);
const deleteDialog = ref(false);
const deleteUserLoading = ref(false);
const actionList = [
{ label: '設定', icon: 'account_tree', action: toEditFlowPage },
{ label: '履歴', icon: 'history', action: showHistory },
{ separator: true },
{ label: '削除', icon: 'delete_outline', class: 'text-red', action: removeRow },
];
const getApps = async () => {
loading.value = true;
rowIds.clear();
try {
const { data } = await api.get('api/apps');
rows.value = data.data.map((item: IManagedApp) => {
rowIds.add(item.appid);
return appToAppDisplay(item)
}).sort((a: IAppDisplay, b: IAppDisplay) => a.sortId - b.sortId); // set default order
} catch (error) {
$q.notify({
icon: 'error',
color: 'negative',
message: 'アプリ一覧の読み込みに失敗しました'
});
} finally {
loading.value = false;
}
}
onMounted(async () => {
await getApps();
});
watch(() => authStore.currentDomain.id, async () => {
await getApps();
});
const filterInitRows = (row: {id: string}) => {
return !rowIds.has(row.id);
}
const showAddAppDialog = () => {
showSelectApp.value = true;
dgFilter.value = ''
}
const closeSelectAppDialog = async (val: 'OK'|'Cancel') => {
showSelectApp.value = true;
if (val == 'OK' && appDialog.value.selected[0]) {
isAdding.value = true;
toEditFlowPage(appDialog.value.selected[0]);
}
showSelectApp.value = false;
isAdding.value = false;
}
function removeRow(app:IAppDisplay) {
targetRow.value = app;
deleteDialog.value = true;
}
const deleteApp = async () => {
deleteUserLoading.value = true;
try {
await api.delete(`api/apps/${targetRow.value?.id}`);
await getApps();
} catch (error) {
$q.notify({
icon: 'error',
color: 'negative',
message: 'アプリの削除に失敗しました'
});
} finally {
deleteUserLoading.value = false;
deleteDialog.value = false;
}
}
function showHistory(app:IAppDisplay) {
targetRow.value = app;
showVersionHistory.value = true;
dgFilter.value = ''
}
const closeHistoryDg = async (val: 'OK'|'Cancel') => {
showVersionHistory.value = true;
if (val == 'OK' && versionDialog.value.selected[0]) {
isAdding.value = true;
await api.put(`api/appversions/${targetRow.value?.id}/${versionDialog.value.selected[0].id}`)
await getApps();
}
showVersionHistory.value = false;
isAdding.value = false;
}
const appToAppDisplay = (app: IManagedApp) => {
const user = app.updateuser;
return {
id: app.appid,
sortId: parseInt(app.appid, 10),
name: app.appname,
url: `${app.domainurl}/k/${app.appid}`,
version: app.version,
updateTime:date.formatDate(app.update_time, 'YYYY/MM/DD HH:mm'),
updateUser: {
id: user.id,
firstName: user.first_name,
lastName: user.last_name,
fullNameSearch: (user.last_name + user.first_name).toLowerCase(),
fullName: user.last_name + ' ' + user.first_name,
email: user.email,
isSuperuser: user.is_superuser,
isActive: user.is_active,
}
} as IAppDisplay
}
async function toEditFlowPage(app:IAppDisplay) {
store.setApp({
appId: app.id,
name: app.name
});
store.selectFlow(undefined);
await router.push('/FlowChart/' + app.id);
};
</script>