version with backend

This commit is contained in:
xue jiahao
2024-12-09 14:31:24 +08:00
parent 305868f091
commit b95548e7f7
10 changed files with 133 additions and 49 deletions

View File

@@ -41,7 +41,7 @@
</q-card-section> </q-card-section>
<q-card-actions align="right" class="text-primary"> <q-card-actions align="right" class="text-primary">
<q-btn flat label="確定" @click="close" /> <q-btn flat label="確定" @click="checkClose" />
<q-btn flat label="キャンセル" @click="close" /> <q-btn flat label="キャンセル" @click="close" />
</q-card-actions> </q-card-actions>
</q-card> </q-card>
@@ -54,6 +54,7 @@ import { IDomainOwnerDisplay } from '../../types/DomainTypes';
import { IUser, IUserDisplay } from '../../types/UserTypes'; import { IUser, IUserDisplay } from '../../types/UserTypes';
import { api } from 'boot/axios'; import { api } from 'boot/axios';
import SharingUserList from 'components/ShareDomain/SharingUserList.vue'; import SharingUserList from 'components/ShareDomain/SharingUserList.vue';
import { Dialog } from 'quasar'
interface Props { interface Props {
modelValue: boolean; modelValue: boolean;
@@ -111,6 +112,28 @@ watch(
} }
); );
const checkClose = () => {
if (!canSharedUserFilter.value) {
close();
return;
}
Dialog.create({
title: '注意',
message: '選択済だがまだ付与未完了のユーザーがあります。<br>必要な操作を選んでください。',
html: true,
persistent: true,
ok: {
color: 'primary',
label: '付与'
},
cancel: '直接閉じる',
}).onCancel(() => {
close();
}).onOk(() => {
shareTo(canSharedUserFilter.value as IUserDisplay);
});
};
const close = () => { const close = () => {
emit('close'); emit('close');
}; };

View File

@@ -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="確定" :loading="okBtnLoading" :v-close-popup="okBtnAutoClose" @click="CloseDialogue('OK')" /> <q-btn flat :label="okBtnLabel || '確定'" :loading="okBtnLoading" :v-close-popup="okBtnAutoClose" @click="CloseDialogue('OK')" />
<q-btn flat label="キャンセル" :disable="okBtnLoading" v-close-popup @click="CloseDialogue('Cancel')" /> <q-btn flat label="キャンセル" :disable="okBtnLoading" v-close-popup @click="CloseDialogue('Cancel')" />
</q-card-actions> </q-card-actions>
</q-card> </q-card>
@@ -30,6 +30,7 @@ export default {
height:String, height:String,
minWidth:String, minWidth:String,
minHeight:String, minHeight:String,
okBtnLabel:String,
okBtnLoading:Boolean, okBtnLoading:Boolean,
okBtnAutoClose:{ okBtnAutoClose:{
type: Boolean, type: Boolean,

View File

@@ -6,13 +6,21 @@
<q-table v-else class="app-table" :selection="type" row-key="id" v-model:selected="selected" flat bordered <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]" virtual-scroll :columns="columns" :rows="rows" :pagination="pagination" :rows-per-page-options="[0]"
:filter="filter" style="max-height: 65vh;" @update:selected="emitSelected"> :filter="filter" style="max-height: 65vh;" @update:selected="emitSelected">
<template v-slot:[`body-cell-${detailField}`]="props">
<template v-for="col in columns" :key="col.name" v-slot:[`body-cell-${col.name}`]="props">
<q-td :props="props"> <q-td :props="props">
<q-scroll-area class="description-cell"> <!-- 使用动态插槽名称 -->
<slot v-if="col.name !== detailField" :name="`body-cell-${col.name}`" :row="props.row" :column="props.col">
<!-- 默认内容 -->
<span>{{ props.row[col.name] }}</span>
</slot>
<q-scroll-area v-else class="description-cell">
<div v-html="props.row[detailField]"></div> <div v-html="props.row[detailField]"></div>
</q-scroll-area> </q-scroll-area>
</q-td> </q-td>
</template> </template>
</q-table> </q-table>
</div> </div>
</template> </template>
@@ -41,6 +49,14 @@ export default {
fetchData: { fetchData: {
type: Function as PropType<() => Promise<IRow[]>>, type: Function as PropType<() => Promise<IRow[]>>,
required: true required: true
},
sortBy: {
type: String,
required: false
},
sortDesc: {
type: Boolean,
required: false
} }
}, },
emits: ['update:selected'], emits: ['update:selected'],
@@ -64,6 +80,8 @@ export default {
selected, selected,
isLoaded, isLoaded,
pagination: ref({ pagination: ref({
sortBy: props.sortBy || undefined,
descending: props.sortDesc || undefined,
rowsPerPage: 10 rowsPerPage: 10
}), }),
emitSelected emitSelected

View File

@@ -3,19 +3,32 @@
detailField="comment" detailField="comment"
type="single" type="single"
:columns="columns" :columns="columns"
sortBy="id"
:sortDesc="true"
:fetchData="fetchVersionHistory" :fetchData="fetchVersionHistory"
@update:selected="(item) => { selected = item }" @update:selected="(item) => { selected = item }"
/> >
<template v-slot:body-cell-id="p">
<div class="flex justify-between">
<span>{{ p.row.id }}</span>
<q-badge v-if="p.row.isActive" color="primary">現在</q-badge>
</div>
</template>
</detail-field-table>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue'; import { defineComponent, PropType, ref } from 'vue';
import { IAppDisplay, IAppVersion } from 'src/types/AppTypes'; import { IAppDisplay, IAppVersion, IAppVersionDisplay } from 'src/types/AppTypes';
import { date } from 'quasar'; import { date } from 'quasar';
import { api } from 'boot/axios'; import { api } from 'boot/axios';
import DetailFieldTable from './DetailFieldTable.vue'; import DetailFieldTable from './DetailFieldTable.vue';
import { IUser, IUserDisplay } from 'src/types/UserTypes'; import { IUser, IUserDisplay } from 'src/types/UserTypes';
interface IVersionDisplay extends IAppVersionDisplay {
isActive : boolean
}
export default defineComponent({ export default defineComponent({
name: 'VersionHistory', name: 'VersionHistory',
components: { components: {
@@ -28,23 +41,23 @@ export default defineComponent({
}, },
}, },
setup(props, { emit }) { setup(props, { emit }) {
const selected = ref<IAppVersion[]>([]); const selected = ref<IVersionDisplay[]>();
const columns = [ const columns = [
{ name: 'version', label: 'ID', field: 'version', align: 'left', sortable: true }, { name: 'id', label: 'バージョン', field: 'version', align: 'left', sortable: true },
{ name: 'name', label: 'バージョン名', field: 'name', align: 'left', sortable: true }, { name: 'name', label: 'バージョン名', field: 'name', align: 'left', sortable: true },
{ name: 'comment', label: 'コメント', field: 'comment', 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: 'creator', label: '作成者', field: (row: IVersionDisplay) => row.creator.fullName, align: 'left', sortable: true },
{ name: 'createTime', label: '作成日時', field: 'createTime', 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: 'updater', label: '更新者', field: (row: IVersionDisplay) => row.updater.fullName, align: 'left', sortable: true },
{ name: 'updateTime', label: '更新日時', field: 'updateTime', align: 'left', sortable: true }, // { name: 'updateTime', label: '更新日時', field: 'updateTime', align: 'left', sortable: true },
]; ];
const formatDate = (dateStr: string) => { const formatDate = (dateStr: string) => {
return date.formatDate(dateStr, 'YYYY/MM/DD HH:mm:ss'); return date.formatDate(dateStr, 'YYYY/MM/DD HH:mm:ss');
}; };
const toUserDisaplay = (user: IUser) => { const toUserDisplay = (user: IUser) => {
return { return {
id: user.id, id: user.id,
firstName: user.first_name, firstName: user.first_name,
@@ -58,24 +71,29 @@ export default defineComponent({
} }
const fetchVersionHistory = async () => { const fetchVersionHistory = async () => {
const { data } = await api.get(`api/apps/${props.app.id}/versions`); const { data } = await api.get(`api/appversions/${props.app.id}`);
return data.data.map((item: any) => ({ return data.data.reduce((arr: IVersionDisplay[], item: any) => {
id: item.id, const val = {
version: item.version, id: item.version,
appid: item.appid, isActive: item.version === props.app.version,
name: item.name, version: item.version,
comment: item.comment, appid: item.appid,
updater: toUserDisaplay(item.updateuser), name: item.versionname,
updateTime: formatDate(item.updatetime), comment: item.comment,
creator: toUserDisaplay(item.createuser), // updater: toUserDisplay(item.updateuser),
createTime: formatDate(item.createtime), // updateTime: formatDate(item.updatetime),
} as IAppVersion)); // creator: toUserDisplay(item.createuser),
}; // createTime: formatDate(item.createtime),
} as IVersionDisplay;
arr.push(val);
return arr;
}, []);
}
return { return {
fetchVersionHistory, fetchVersionHistory,
columns, columns,
selected selected,
}; };
}, },
}); });

View File

@@ -7,7 +7,7 @@
:rules="[(val) => !val || val.length <= 30 || '30字以内で入力ください']" :rules="[(val) => !val || val.length <= 30 || '30字以内で入力ください']"
/> />
<q-input <q-input
v-model="versionInfo.desc" v-model="versionInfo.comment"
filled filled
type="textarea" type="textarea"
:rules="[(val) => !val || val.length <= 80 || '80字以内で入力ください']" :rules="[(val) => !val || val.length <= 80 || '80字以内で入力ください']"
@@ -17,10 +17,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, defineProps, defineEmits } from 'vue'; import { ref, watch, defineProps, defineEmits } from 'vue';
import { QInput } from 'quasar'; import { QInput } from 'quasar';
import { IVersionInfo } from 'src/types/AppTypes'; import { IVersionSubmit } from 'src/types/AppTypes';
const props = defineProps<{ const props = defineProps<{
modelValue: IVersionInfo; modelValue: IVersionSubmit;
}>(); }>();
const defaultTitle = `${new Date().toLocaleString()}`; const defaultTitle = `${new Date().toLocaleString()}`;
@@ -28,6 +28,7 @@ const defaultTitle = `${new Date().toLocaleString()}`;
const versionInfo = ref({ const versionInfo = ref({
...props.modelValue, ...props.modelValue,
name: props.modelValue.name || defaultTitle, name: props.modelValue.name || defaultTitle,
comment: props.modelValue.comment || '',
}); });
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);

View File

@@ -32,7 +32,7 @@ export class FlowCtrl {
* @returns * @returns
*/ */
async UpdateFlow(jsonData: any): Promise<boolean> { async UpdateFlow(jsonData: any): Promise<boolean> {
const result = await api.put('api/flow/' + jsonData.flowid, jsonData); const result = await api.put('api/flow', jsonData);
console.info(result.data); console.info(result.data);
return true; return true;
} }

View File

@@ -47,8 +47,9 @@
</show-dialog> </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"> <show-dialog v-model:visible="showVersionHistory" :name="targetRow?.name + 'のバージョン履歴'" @close="closeHistoryDg" min-width="30vw" :ok-btn-auto-close="false" :ok-btn-loading="isAdding"
<version-history ref="versionDialog" :app="targetRow as IAppDisplay" /> ok-btn-label="選択">
<version-history ref="versionDialog" :app="targetRow" />
</show-dialog> </show-dialog>
</div> </div>
</template> </template>
@@ -91,6 +92,7 @@ const rowIds = new Set<string>();
const $q = useQuasar() const $q = useQuasar()
const store = useFlowEditorStore(); const store = useFlowEditorStore();
const appDialog = ref(); const appDialog = ref();
const versionDialog = ref();
const showSelectApp=ref(false); const showSelectApp=ref(false);
const showVersionHistory=ref(false); const showVersionHistory=ref(false);
const isAdding = ref(false); const isAdding = ref(false);
@@ -161,12 +163,13 @@ function showHistory(app:IAppDisplay) {
} }
const closeHistoryDg = async (val: 'OK'|'Cancel') => { const closeHistoryDg = async (val: 'OK'|'Cancel') => {
showSelectApp.value = true; showVersionHistory.value = true;
if (val == 'OK' && appDialog.value.selected[0]) { if (val == 'OK' && versionDialog.value.selected[0]) {
isAdding.value = true; isAdding.value = true;
await api.put(`api/appversions/${targetRow.value?.id}/${versionDialog.value.selected[0].id}`)
await getApps(); await getApps();
} }
showSelectApp.value = false; showVersionHistory.value = false;
isAdding.value = false; isAdding.value = false;
} }

View File

@@ -19,7 +19,7 @@
<q-icon name="bookmark_border"></q-icon> <q-icon name="bookmark_border"></q-icon>
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
<q-item-label>新バージョン保存</q-item-label> <q-item-label>保存して新バージョン</q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
@@ -85,8 +85,8 @@
<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 --> <!-- save version dialog -->
<ShowDialog v-model:visible="saveVersionAction" name="新バージョン保存" @close="closeSaveVersionDg" min-width="500px"> <ShowDialog v-model:visible="saveVersionAction" name="保存して新バージョン" @close="closeSaveVersionDg" min-width="500px">
<version-input v-model="versionInfo" /> <version-input v-model="versionSubmit" />
</ShowDialog> </ShowDialog>
<q-inner-loading <q-inner-loading
:showing="initLoading" :showing="initLoading"
@@ -100,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 { IAppDisplay, IManagedApp, IVersionInfo } from 'src/types/AppTypes'; import { IAppDisplay, IManagedApp, IVersionSubmit } 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';
@@ -119,7 +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 versionSubmit = ref<IVersionSubmit>();
const $q = useQuasar(); const $q = useQuasar();
const store = useFlowEditorStore(); const store = useFlowEditorStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
@@ -262,16 +262,18 @@ const onSaveActionProps=(props:IActionProperty[])=>{
}; };
const onSaveVersion = async () => { const onSaveVersion = async () => {
versionInfo.value = { versionSubmit.value = { appId: store.appInfo?.appId }
id: '1' // TODO
}
saveVersionAction.value = true; saveVersionAction.value = true;
// await onSaveAllFlow();
} }
const closeSaveVersionDg = (val: 'OK'|'CANCEL') => { const closeSaveVersionDg = async (val: 'OK'|'CANCEL') => {
if (val == 'OK') { if (val == 'OK') {
console.log(versionInfo.value); await onSaveAllFlow();
await api.post('api/apps', {
'appid': versionSubmit.value?.appId,
'versionname': versionSubmit.value?.name,
'comment': versionSubmit.value?.comment
})
} }
} }

View File

@@ -163,7 +163,7 @@ const columns = [
// classes: inactiveRowClass // classes: inactiveRowClass
// }, // },
{ name: 'name', label: '環境名', field: 'name', align: 'left', sortable: true, classes: inactiveRowClass }, { name: 'name', label: '環境名', field: 'name', align: 'left', sortable: true, classes: inactiveRowClass },
{ name: 'active', label: 'x', align: 'left', field: 'domainActive', classes: inactiveRowClass }, { name: 'active', label: '', 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: IDomainOwnerDisplay) => row.owner.fullName, align: 'left', classes: inactiveRowClass }, { name: 'owner', label: '所有者', field: (row: IDomainOwnerDisplay) => row.owner.fullName, align: 'left', classes: inactiveRowClass },

View File

@@ -38,7 +38,25 @@ export interface IVersionInfo {
desc?: string; desc?: string;
} }
export interface IVersionSubmit {
appId: string;
name?: string;
comment?: string;
}
export interface IAppVersion { export interface IAppVersion {
id: number;
version: number;
appid: string;
versionname: string
comment: string;
updater: IUserDisplay;
updateTime: string;
creator: IUserDisplay;
createTime: string;
}
export interface IAppVersionDisplay {
id: number; id: number;
version: number; version: number;
appid: string; appid: string;