Compare commits

...

2 Commits

Author SHA1 Message Date
xue jiahao
fd9d590be2 [UI] version page 2024-12-06 23:29:16 +08:00
xue jiahao
eaedab505c Add save version dialog
# Conflicts:
#	frontend/src/types/AppTypes.ts
2024-12-06 13:04:42 +08:00
10 changed files with 415 additions and 131 deletions

View File

@@ -1,24 +1,19 @@
<template> <template>
<div class="q-px-xs"> <detail-field-table
<div v-if="!isLoaded" class="spinner flex flex-center"> detailField="description"
<q-spinner color="primary" size="3em" /> :name="name"
</div> :type="type"
<q-table v-else class="app-table" :selection="type" row-key="id" v-model:selected="selected" flat bordered :filter="filter"
virtual-scroll :columns="columns" :rows="rows" :pagination="pagination" :rows-per-page-options="[0]" :columns="columns"
:filter="filter" style="max-height: 65vh;"> :fetchData="fetchApps"
<template v-slot:body-cell-description="props"> @update:selected="(item) => { selected = item }"
<q-td :props="props"> />
<q-scroll-area class="description-cell">
<div v-html="props.row.description"></div>
</q-scroll-area>
</q-td>
</template>
</q-table>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, onMounted, reactive, watchEffect, PropType } from 'vue' import { ref, PropType } from 'vue';
import { api } from 'boot/axios'; import { api } from 'boot/axios';
import DetailFieldTable from './dialog/DetailFieldTable.vue';
interface IAppDisplay { interface IAppDisplay {
id: string; id: string;
@@ -29,50 +24,35 @@ interface IAppDisplay {
export default { export default {
name: 'AppSelectBox', name: 'AppSelectBox',
components: {
DetailFieldTable
},
props: { props: {
name: String, name: String,
type: String, type: String,
filter: String, filter: String,
filterInitRowsFunc: { filterInitRowsFunc: {
type: Function as PropType<(app: IAppDisplay) => boolean>, type: Function as PropType<(app: IAppDisplay) => boolean>,
},
updateSelectApp: {
type: Function
} }
}, },
setup(props) { setup(props) {
const selected = ref<IAppDisplay[]>([]);
const columns = [ const columns = [
{ name: 'id', required: true, label: 'ID', align: 'left', field: 'id', sortable: true, sort: (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10) }, { 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 rows = reactive<IAppDisplay[]>([]);
const selected = ref([])
watchEffect(()=>{ const fetchApps = async () => {
if (selected.value && selected.value[0] && props.updateSelectApp) { const res = await api.get('api/v1/allapps');
props.updateSelectApp(selected.value[0]) return res.data.apps.map((item: any) => ({
}
});
onMounted(() => {
api.get('api/v1/allapps').then(res => {
res.data.apps.forEach((item: any) => {
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)
} })).filter(app => !props.filterInitRowsFunc || props.filterInitRowsFunc(app));
if (props.filterInitRowsFunc && !props.filterInitRowsFunc(row)) { };
return;
}
rows.push(row);
});
isLoaded.value = true;
});
});
const dateFormat = (dateStr: string) => { const dateFormat = (dateStr: string) => {
const date = new Date(dateStr); const date = new Date(dateStr);
@@ -84,31 +64,13 @@ export default {
const minutes = pad(date.getMinutes()); const minutes = pad(date.getMinutes());
const seconds = pad(date.getSeconds()); const seconds = pad(date.getSeconds());
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
} };
return { return {
columns, columns,
rows, fetchApps,
selected, selected
isLoaded, };
pagination: ref({
rowsPerPage: 10
})
} }
}, };
}
</script> </script>
<style lang="scss">
.description-cell {
height: 60px;
width: 300px;
max-height: 60px;
max-width: 300px;
white-space: break-spaces;
}
.spinner {
min-height: 300px;
min-width: 400px;
}
</style>

View File

@@ -4,7 +4,7 @@
<q-card class="" style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle"> <q-card class="" style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
<q-toolbar class="bg-grey-4"> <q-toolbar class="bg-grey-4">
<q-toolbar-title>{{ name }}</q-toolbar-title> <q-toolbar-title>{{ name }}</q-toolbar-title>
<q-space></q-space> <q-space v-if="$slots.toolbar"></q-space>
<slot name="toolbar"></slot> <slot name="toolbar"></slot>
<q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" /> <q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" />
</q-toolbar> </q-toolbar>
@@ -41,7 +41,8 @@ export default {
} }
}, },
emits: [ emits: [
'close' 'close',
'update:visible'
], ],
setup(props, context) { setup(props, context) {
const CloseDialogue = (val) => { const CloseDialogue = (val) => {

View File

@@ -0,0 +1,88 @@
<template>
<div class="q-px-xs">
<div v-if="!isLoaded" class="spinner flex flex-center">
<q-spinner color="primary" size="3em" />
</div>
<q-table v-else class="app-table" :selection="type" row-key="id" v-model:selected="selected" flat bordered
virtual-scroll :columns="columns" :rows="rows" :pagination="pagination" :rows-per-page-options="[0]"
:filter="filter" style="max-height: 65vh;" @update:selected="emitSelected">
<template v-slot:[`body-cell-${detailField}`]="props">
<q-td :props="props">
<q-scroll-area class="description-cell">
<div v-html="props.row[detailField]"></div>
</q-scroll-area>
</q-td>
</template>
</q-table>
</div>
</template>
<script lang="ts">
import { ref, onMounted, reactive, PropType } from 'vue'
interface IRow {
[key: string]: any;
}
export default {
name: 'DetailFieldTable',
props: {
name: String,
type: String,
filter: String,
detailField: {
type: String,
required: true
},
columns: {
type: Array as PropType<any[]>,
required: true
},
fetchData: {
type: Function as PropType<() => Promise<IRow[]>>,
required: true
}
},
emits: ['update:selected'],
setup(props, { emit }) {
const isLoaded = ref(false);
const rows = reactive<IRow[]>([]);
const selected = ref([]);
onMounted(async () => {
const data = await props.fetchData();
rows.push(...data);
isLoaded.value = true;
});
const emitSelected = (selectedItems: any[]) => {
emit('update:selected', selectedItems);
};
return {
rows,
selected,
isLoaded,
pagination: ref({
rowsPerPage: 10
}),
emitSelected
};
}
};
</script>
<style lang="scss">
.description-cell {
height: 60px;
width: 300px;
max-height: 60px;
max-width: 300px;
white-space: break-spaces;
}
.spinner {
min-height: 300px;
min-width: 400px;
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<detail-field-table
detailField="comment"
type="single"
:columns="columns"
:fetchData="fetchVersionHistory"
@update:selected="(item) => { selected = item }"
/>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue';
import { IAppDisplay, IAppVersion } from 'src/types/AppTypes';
import { date } from 'quasar';
import { api } from 'boot/axios';
import DetailFieldTable from './DetailFieldTable.vue';
import { IUser, IUserDisplay } from 'src/types/UserTypes';
export default defineComponent({
name: 'VersionHistory',
components: {
DetailFieldTable
},
props: {
app: {
type: Object as PropType<IAppDisplay>,
required: true,
},
},
setup(props, { emit }) {
const selected = ref<IAppVersion[]>([]);
const columns = [
{ name: 'version', label: 'ID', field: 'version', align: 'left', sortable: true },
{ name: 'name', label: 'バージョン名', field: 'name', align: 'left', sortable: true },
{ name: 'comment', label: 'コメント', field: 'comment', align: 'left', sortable: true },
{ name: 'creator', label: '作成者', field: (row: IAppVersion) => row.creator.fullName, align: 'left', sortable: true },
{ name: 'createTime', label: '作成日時', field: 'createTime', align: 'left', sortable: true },
{ name: 'updater', label: '更新者', field: (row: IAppVersion) => row.updater.fullName, align: 'left', sortable: true },
{ name: 'updateTime', label: '更新日時', field: 'updateTime', align: 'left', sortable: true },
];
const formatDate = (dateStr: string) => {
return date.formatDate(dateStr, 'YYYY/MM/DD HH:mm:ss');
};
const toUserDisaplay = (user: IUser) => {
return {
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,
isActive: user.is_active,
isSuperuser: user.is_superuser,
}
}
const fetchVersionHistory = async () => {
const { data } = await api.get(`api/apps/${props.app.id}/versions`);
return data.data.map((item: any) => ({
id: item.id,
version: item.version,
appid: item.appid,
name: item.name,
comment: item.comment,
updater: toUserDisaplay(item.updateuser),
updateTime: formatDate(item.updatetime),
creator: toUserDisaplay(item.createuser),
createTime: formatDate(item.createtime),
} as IAppVersion));
};
return {
fetchVersionHistory,
columns,
selected
};
},
});
</script>

View File

@@ -0,0 +1,42 @@
<template>
<q-input
v-model="versionInfo.name"
filled
autofocus
label="バージョン名"
:rules="[(val) => !val || val.length <= 30 || '30字以内で入力ください']"
/>
<q-input
v-model="versionInfo.desc"
filled
type="textarea"
:rules="[(val) => !val || val.length <= 80 || '80字以内で入力ください']"
label="説明"
/>
</template>
<script setup lang="ts">
import { ref, watch, defineProps, defineEmits } from 'vue';
import { QInput } from 'quasar';
import { IVersionInfo } from 'src/types/AppTypes';
const props = defineProps<{
modelValue: IVersionInfo;
}>();
const defaultTitle = `${new Date().toLocaleString()}`;
const versionInfo = ref({
...props.modelValue,
name: props.modelValue.name || defaultTitle,
});
const emit = defineEmits(['update:modelValue']);
watch(
versionInfo,
() => {
emit('update:modelValue', { ...versionInfo.value });
},
{ immediate: true, deep: true }
);
</script>

View File

@@ -38,8 +38,10 @@ import { computed, onMounted, reactive } from 'vue';
import EssentialLink, { EssentialLinkProps } from 'components/EssentialLink.vue'; import EssentialLink, { EssentialLinkProps } from 'components/EssentialLink.vue';
import DomainSelector from 'components/DomainSelector.vue'; import DomainSelector from 'components/DomainSelector.vue';
import { useAuthStore } from 'stores/useAuthStore'; import { useAuthStore } from 'stores/useAuthStore';
import { useRoute } from 'vue-router';
const authStore = useAuthStore(); const authStore = useAuthStore();
const route = useRoute()
const noDomain = computed(() => !authStore.hasDomain); const noDomain = computed(() => !authStore.hasDomain);
const essentialLinks: EssentialLinkProps[] = reactive([ const essentialLinks: EssentialLinkProps[] = reactive([
@@ -128,7 +130,7 @@ const version = process.env.version;
const productName = process.env.productName; const productName = process.env.productName;
onMounted(() => { onMounted(() => {
authStore.setLeftMenu(true); authStore.setLeftMenu(!route.path.startsWith('/FlowChart/'));
}); });
function toggleLeftDrawer() { function toggleLeftDrawer() {

View File

@@ -37,13 +37,18 @@
<show-dialog v-model:visible="showSelectApp" name="アプリ選択" @close="closeSelectAppDialog" min-width="50vw" min-height="50vh" :ok-btn-auto-close="false" :ok-btn-loading="isAdding"> <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> <template v-slot:toolbar>
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable> <q-input dense debounce="300" v-model="dgFilter" placeholder="検索" clearable>
<template v-slot:before> <template v-slot:before>
<q-icon name="search" /> <q-icon name="search" />
</template> </template>
</q-input> </q-input>
</template> </template>
<app-select-box ref="appDialog" name="アプリ" type="single" :filter="filter" :filterInitRowsFunc="filterInitRows" /> <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="50vw" min-height="50vh" :ok-btn-auto-close="false" :ok-btn-loading="isAdding">
<version-history ref="versionDialog" :app="targetRow as IAppDisplay" />
</show-dialog> </show-dialog>
</div> </div>
</template> </template>
@@ -56,20 +61,11 @@ import { useAuthStore } from 'stores/useAuthStore';
import { useFlowEditorStore } from 'stores/flowEditor'; 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, IAppDisplay, IAppVersion } from 'src/types/AppTypes';
import ShowDialog from 'src/components/ShowDialog.vue'; import ShowDialog from 'src/components/ShowDialog.vue';
import AppSelectBox from 'src/components/AppSelectBox.vue'; import AppSelectBox from 'src/components/AppSelectBox.vue';
import TableActionMenu from 'components/TableActionMenu.vue'; import TableActionMenu from 'components/TableActionMenu.vue';
import VersionHistory from 'components/dialog/VersionHistory.vue';
interface IAppDisplay{
id:string;
sortId: number;
name:string;
url:string;
user:string;
version:string;
updatetime:string;
}
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);
@@ -78,8 +74,8 @@ 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 },
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true }, { name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true },
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true }, { name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true },
{ name: 'user', label: '最後更新者', field: 'user', 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: 'updateTime', label: '最後更新日', field: 'updateTime', align: 'left', sortable: true},
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true, sort: numberStringSorting }, { name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true, sort: numberStringSorting },
{ name: 'actions', label: '', field: 'actions' } { name: 'actions', label: '', field: 'actions' }
]; ];
@@ -87,13 +83,16 @@ const columns = [
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 dgFilter = ref('');
const rows = ref<IAppDisplay[]>([]); const rows = ref<IAppDisplay[]>([]);
const targetRow = ref<IAppDisplay>();
const rowIds = new Set<string>(); const rowIds = new Set<string>();
const $q = useQuasar() const $q = useQuasar()
const store = useFlowEditorStore(); const store = useFlowEditorStore();
const appDialog = ref(); const appDialog = ref();
const showSelectApp=ref(false); const showSelectApp=ref(false);
const showVersionHistory=ref(false);
const isAdding = ref(false); const isAdding = ref(false);
const actionList = [ const actionList = [
@@ -137,6 +136,7 @@ const filterInitRows = (row: {id: string}) => {
const showAddAppDialog = () => { const showAddAppDialog = () => {
showSelectApp.value = true; showSelectApp.value = true;
dgFilter.value = ''
} }
const closeSelectAppDialog = async (val: 'OK'|'Cancel') => { const closeSelectAppDialog = async (val: 'OK'|'Cancel') => {
@@ -150,31 +150,54 @@ const closeSelectAppDialog = async (val: 'OK'|'Cancel') => {
} }
function removeRow(app:IAppDisplay) { function removeRow(app:IAppDisplay) {
targetRow.value = app;
return return
} }
function showHistory(app:IAppDisplay) { function showHistory(app:IAppDisplay) {
return targetRow.value = app;
showVersionHistory.value = true;
dgFilter.value = ''
}
const closeHistoryDg = async (val: 'OK'|'Cancel') => {
showSelectApp.value = true;
if (val == 'OK' && appDialog.value.selected[0]) {
isAdding.value = true;
await getApps();
}
showSelectApp.value = false;
isAdding.value = false;
} }
const appToAppDisplay = (app: IManagedApp) => { const appToAppDisplay = (app: IManagedApp) => {
const user = app.updateuser;
return { return {
id: app.appid, id: app.appid,
sortId: parseInt(app.appid, 10), sortId: parseInt(app.appid, 10),
name: app.appname, name: app.appname,
url: `${app.domainurl}/k/${app.appid}`, url: `${app.domainurl}/k/${app.appid}`,
user: `${app.updateuser.first_name} ${app.updateuser.last_name}` , version: app.version,
updatetime:date.formatDate(app.update_time, 'YYYY/MM/DD HH:mm'), updateTime:date.formatDate(app.update_time, 'YYYY/MM/DD HH:mm'),
version: app.version 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
} }
function toEditFlowPage(app:IAppDisplay) { async function toEditFlowPage(app:IAppDisplay) {
store.setApp({ store.setApp({
appId: app.id, appId: app.id,
name: app.name name: app.name
}); });
store.selectFlow(undefined); store.selectFlow(undefined);
router.push('/FlowChart/' + app.id); await router.push('/FlowChart/' + app.id);
}; };
</script> </script>

View File

@@ -14,6 +14,15 @@
<q-space></q-space> <q-space></q-space>
<q-btn-dropdown color="primary" label="保存" icon="save" :loading="saveLoading" > <q-btn-dropdown color="primary" label="保存" icon="save" :loading="saveLoading" >
<q-list> <q-list>
<q-item clickable v-close-popup @click="onSaveVersion">
<q-item-section avatar >
<q-icon name="bookmark_border"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label>新バージョン保存</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="onSaveFlow"> <q-item clickable v-close-popup @click="onSaveFlow">
<q-item-section avatar > <q-item-section avatar >
<q-icon name="save" color="primary"></q-icon> <q-icon name="save" color="primary"></q-icon>
@@ -75,6 +84,10 @@
</template> </template>
<action-select ref="appDg" name="model" :filter="filter" type="single" @clearFilter="onClearFilter" ></action-select> <action-select ref="appDg" name="model" :filter="filter" type="single" @clearFilter="onClearFilter" ></action-select>
</ShowDialog> </ShowDialog>
<!-- save version dialog -->
<ShowDialog v-model:visible="saveVersionAction" name="新バージョン保存" @close="closeSaveVersionDg" min-width="500px">
<version-input v-model="versionInfo" />
</ShowDialog>
<q-inner-loading <q-inner-loading
:showing="initLoading" :showing="initLoading"
color="primary" color="primary"
@@ -87,7 +100,7 @@
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { IActionNode, ActionNode, IActionFlow, ActionFlow, RootAction, IActionProperty } from 'src/types/ActionTypes'; import { IActionNode, ActionNode, IActionFlow, ActionFlow, RootAction, IActionProperty } from 'src/types/ActionTypes';
import { IManagedApp } from 'src/types/AppTypes'; import { IAppDisplay, IManagedApp, IVersionInfo } from 'src/types/AppTypes';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useFlowEditorStore } from 'stores/flowEditor'; import { useFlowEditorStore } from 'stores/flowEditor';
import { useAuthStore } from 'stores/useAuthStore'; import { useAuthStore } from 'stores/useAuthStore';
@@ -98,6 +111,7 @@ import ShowDialog from 'components/ShowDialog.vue';
import ActionSelect from 'components/ActionSelect.vue'; import ActionSelect from 'components/ActionSelect.vue';
import PropertyPanel from 'components/right/PropertyPanel.vue'; import PropertyPanel from 'components/right/PropertyPanel.vue';
import EventTree from 'components/left/EventTree.vue'; import EventTree from 'components/left/EventTree.vue';
import VersionInput from 'components/dialog/VersionInput.vue';
import { FlowCtrl } from '../control/flowctrl'; import { FlowCtrl } from '../control/flowctrl';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@@ -105,6 +119,7 @@ const deployLoading = ref(false);
const saveLoading = ref(false); const saveLoading = ref(false);
const initLoading = ref(true); const initLoading = ref(true);
const drawerLeft = ref(false); const drawerLeft = ref(false);
const versionInfo = ref<IVersionInfo>();
const $q = useQuasar(); const $q = useQuasar();
const store = useFlowEditorStore(); const store = useFlowEditorStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
@@ -117,6 +132,7 @@ const prevNodeIfo = ref({
}); });
// const refFlow = ref<ActionFlow|null>(null); // const refFlow = ref<ActionFlow|null>(null);
const showAddAction = ref(false); const showAddAction = ref(false);
const saveVersionAction = ref(false);
const drawerRight = ref(false); const drawerRight = ref(false);
const filter=ref(""); const filter=ref("");
const model = ref(""); const model = ref("");
@@ -177,7 +193,7 @@ const onDeleteAllNextNodes = (node: IActionNode) => {
} }
const closeDg = (val: any) => { const closeDg = (val: any) => {
console.log("Dialog closed->", val); console.log("Dialog closed->", val);
if (val == 'OK') { if (val == 'OK' && appDg?.value?.selected?.length > 0) {
const data = appDg.value.selected[0]; const data = appDg.value.selected[0];
const actionProps = JSON.parse(data.property); const actionProps = JSON.parse(data.property);
const outputPoint = JSON.parse(data.outputPoints); const outputPoint = JSON.parse(data.outputPoints);
@@ -245,6 +261,20 @@ const onSaveActionProps=(props:IActionProperty[])=>{
} }
}; };
const onSaveVersion = async () => {
versionInfo.value = {
id: '1' // TODO
}
saveVersionAction.value = true;
// await onSaveAllFlow();
}
const closeSaveVersionDg = (val: 'OK'|'CANCEL') => {
if (val == 'OK') {
console.log(versionInfo.value);
}
}
const onSaveFlow = async () => { const onSaveFlow = async () => {
const targetFlow = store.selectedFlow; const targetFlow = store.selectedFlow;
if (targetFlow === undefined) { if (targetFlow === undefined) {
@@ -316,11 +346,9 @@ const onSaveAllFlow= async ()=>{
const fetchData = async () => { const fetchData = async () => {
initLoading.value = true; initLoading.value = true;
if (store.appInfo === undefined && route?.params?.id !== undefined) { if (store.appInfo === undefined && route?.params?.id !== undefined) {
const { appid, appname } = await fetchAppById(route.params.id as string); // only for page refreshed
store.setApp({ const app = await fetchAppById(route.params.id as string);
appId: appid, store.setApp(app);
name: appname
});
}; };
await store.loadFlow(); await store.loadFlow();
initLoading.value = false initLoading.value = false
@@ -328,24 +356,39 @@ const fetchData = async () => {
} }
const fetchAppById = async(id: string) => { const fetchAppById = async(id: string) => {
try { let result = await api.get('api/apps');
const result = await api.get('api/apps'); const app = result.data?.data?.find((item: IManagedApp) => item.appid === id ) as IManagedApp;
return result.data.find((item: IManagedApp) => item.appid === id ) as IManagedApp; if (app) {
} catch (e) { return convertManagedAppToAppInfo(app);
console.error(e); }
const result = await api.get(`api/v1/app?app=${id}`);
const data = result?.data; result = await api.get(`api/v1/app?app=${id}`);
if (data?.message) { const kApp = result?.data as IAppDisplay | KErrorMsg;
if (isErrorMsg(kApp)) {
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
caption: "エラー", caption: 'エラー',
message: data.message message: kApp.message,
}); });
} }
return { appid: data.appId, appname: data.name }; return kApp;
}
} }
type KErrorMsg = {
message: string;
}
const isErrorMsg = (e: IAppDisplay | KErrorMsg): e is KErrorMsg => {
return 'message' in e;
};
const convertManagedAppToAppInfo = (app: IManagedApp): AppInfo => {
return {
appId: app.appid,
name: app.appname
}
};
const onClearFilter=()=>{ const onClearFilter=()=>{
filter.value=''; filter.value='';
} }

View File

@@ -145,7 +145,7 @@ import { useAuthStore } from 'stores/useAuthStore';
import { useDomainStore } from 'stores/useDomainStore'; import { useDomainStore } from 'stores/useDomainStore';
import ShareDomainDialog from 'components/ShareDomain/ShareDomainDialog.vue'; import ShareDomainDialog from 'components/ShareDomain/ShareDomainDialog.vue';
import TableActionMenu from 'components/TableActionMenu.vue'; import TableActionMenu from 'components/TableActionMenu.vue';
import { IDomain, IDomainOwnerDisplay, IDomainSubmit } from '../types/DomainTypes'; import { IDomain, IDomainDisplay, IDomainOwnerDisplay, IDomainSubmit } from '../types/DomainTypes';
const authStore = useAuthStore(); const authStore = useAuthStore();
const domainStore = useDomainStore(); const domainStore = useDomainStore();
@@ -166,7 +166,7 @@ const columns = [
{ name: 'active', label: 'x', align: 'left', field: 'domainActive', classes: inactiveRowClass }, { name: 'active', label: 'x', align: 'left', field: 'domainActive', classes: inactiveRowClass },
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: inactiveRowClass }, { name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: inactiveRowClass },
{ name: 'user', label: 'ログイン名', field: 'user', align: 'left', classes: inactiveRowClass }, { name: 'user', label: 'ログイン名', field: 'user', align: 'left', classes: inactiveRowClass },
{ name: 'owner', label: '所有者', field: row => row.owner.fullName, align: 'left', classes: inactiveRowClass }, { name: 'owner', label: '所有者', field: (row: IDomainOwnerDisplay) => row.owner.fullName, align: 'left', classes: inactiveRowClass },
{ name: 'actions', label: '', field: 'actions', classes: inactiveRowClass } { name: 'actions', label: '', field: 'actions', classes: inactiveRowClass }
]; ];

View File

@@ -1,10 +1,51 @@
import { IUser } from './UserTypes'; import { IUser, IUserDisplay } from './UserTypes';
export interface IManagedApp { export interface IManagedApp {
appid: string; appid: string;
appname: string; appname: string;
domainurl: string; domainurl: string;
version: string; version: string;
user: IUser;
updateuser: IUser; updateuser: IUser;
create_time: string;
update_time: string; update_time: string;
} }
export interface IManagedApp {
appid: string;
appname: string;
domainurl: string;
version: string;
user: IUser;
updateuser: IUser;
create_time: string;
update_time: string;
}
export interface IAppDisplay{
id:string;
sortId: number;
name:string;
url:string;
updateUser: IUserDisplay;
updateTime:string;
version:string;
}
export interface IVersionInfo {
id: string;
name?: string;
desc?: string;
}
export interface IAppVersion {
id: number;
version: number;
appid: string;
name: string
comment: string;
updater: IUserDisplay;
updateTime: string;
creator: IUserDisplay;
createTime: string;
}