feat: 统一恢复管理端列表上下文
- 统一列表路由状态、分页初始化与安全返回契约 - 接入知识库、工作流、插件、审批、Skill、Agent、Bot、反馈和定时任务链路 - 补齐公共能力与关键返回路径自动化测试
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'element-plus';
|
||||
|
||||
import { markRaw, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import {
|
||||
CaretRight,
|
||||
@@ -26,17 +25,47 @@ import { api } from '#/api/request';
|
||||
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
|
||||
import ListPageShell from '#/components/page/ListPageShell.vue';
|
||||
import PageData from '#/components/page/PageData.vue';
|
||||
import {
|
||||
buildListRouteFullPath,
|
||||
mergeListRouteStateQuery,
|
||||
parseListRouteState,
|
||||
watchListRouteState,
|
||||
} from '#/composables/useListRouteState';
|
||||
import { $t } from '#/locales';
|
||||
import { router } from '#/router';
|
||||
import { withListReturnTo } from '#/router/list-return-context';
|
||||
import { useDictStore } from '#/store';
|
||||
|
||||
import { sysJobListRouteSchema } from './sys-job-list-route-state';
|
||||
import SysJobModal from './SysJobModal.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const initialListState = parseListRouteState(
|
||||
route.query,
|
||||
sysJobListRouteSchema,
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initDict();
|
||||
});
|
||||
|
||||
const pageDataRef = ref();
|
||||
const searchKeyword = ref(initialListState.keyword);
|
||||
const currentPageNumber = ref(initialListState.pageNumber);
|
||||
const currentPageSize = ref(initialListState.pageSize);
|
||||
watchListRouteState(route, sysJobListRouteSchema, (state) => {
|
||||
searchKeyword.value = state.keyword;
|
||||
currentPageNumber.value = state.pageNumber;
|
||||
currentPageSize.value = state.pageSize;
|
||||
pageDataRef.value?.restoreState?.({
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
isQueryOr: true,
|
||||
jobName: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
const saveDialog = ref();
|
||||
const dictStore = useDictStore();
|
||||
const headerButtons = [
|
||||
@@ -57,11 +86,14 @@ function initDict() {
|
||||
dictStore.fetchDictionary('misfirePolicy');
|
||||
}
|
||||
const handleSearch = (params: string) => {
|
||||
pageDataRef.value.setQuery({ jobName: params, isQueryOr: true });
|
||||
searchKeyword.value = params;
|
||||
pageDataRef.value.setQuery({
|
||||
jobName: params || undefined,
|
||||
isQueryOr: true,
|
||||
});
|
||||
};
|
||||
function reset(formEl?: FormInstance) {
|
||||
formEl?.resetFields();
|
||||
pageDataRef.value.setQuery({});
|
||||
function reloadCurrentList() {
|
||||
pageDataRef.value?.reload?.();
|
||||
}
|
||||
function showDialog(row: any) {
|
||||
saveDialog.value.openDialog({ ...row });
|
||||
@@ -80,7 +112,7 @@ function remove(row: any) {
|
||||
instance.confirmButtonLoading = false;
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success(res.message);
|
||||
reset();
|
||||
reloadCurrentList();
|
||||
done();
|
||||
}
|
||||
})
|
||||
@@ -105,7 +137,7 @@ function start(row: any) {
|
||||
instance.confirmButtonLoading = false;
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success(res.message);
|
||||
reset();
|
||||
reloadCurrentList();
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -127,7 +159,7 @@ function stop(row: any) {
|
||||
instance.confirmButtonLoading = false;
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success(res.message);
|
||||
reset();
|
||||
reloadCurrentList();
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -142,18 +174,52 @@ function toLogPage(row: any) {
|
||||
name: 'SysJobLog',
|
||||
query: {
|
||||
jobId: row.id,
|
||||
...withListReturnTo(currentListFullPath()),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function currentListFullPath() {
|
||||
return buildListRouteFullPath(
|
||||
router,
|
||||
route.query,
|
||||
{
|
||||
keyword: searchKeyword.value,
|
||||
pageNumber: currentPageNumber.value,
|
||||
pageSize: currentPageSize.value,
|
||||
},
|
||||
sysJobListRouteSchema,
|
||||
);
|
||||
}
|
||||
|
||||
function handlePageStateChange(state: {
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
}) {
|
||||
currentPageNumber.value = state.pageNumber;
|
||||
currentPageSize.value = state.pageSize;
|
||||
const query = mergeListRouteStateQuery(
|
||||
route.query,
|
||||
{
|
||||
keyword: searchKeyword.value,
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
},
|
||||
sysJobListRouteSchema,
|
||||
);
|
||||
const target = router.resolve({ path: sysJobListRouteSchema.path, query });
|
||||
if (target.fullPath !== route.fullPath) void router.replace(target);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col gap-6 p-6">
|
||||
<SysJobModal ref="saveDialog" @reload="reset" />
|
||||
<SysJobModal ref="saveDialog" @reload="reloadCurrentList" />
|
||||
<ListPageShell>
|
||||
<template #filters>
|
||||
<HeaderSearch
|
||||
:buttons="headerButtons"
|
||||
:initial-value="searchKeyword"
|
||||
@search="handleSearch"
|
||||
@button-click="showDialog({})"
|
||||
/>
|
||||
@@ -161,7 +227,13 @@ function toLogPage(row: any) {
|
||||
<PageData
|
||||
ref="pageDataRef"
|
||||
page-url="/api/v1/sysJob/page"
|
||||
:page-size="10"
|
||||
:page-size="initialListState.pageSize"
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
jobName: initialListState.keyword || undefined,
|
||||
isQueryOr: true,
|
||||
}"
|
||||
@state-change="handlePageStateChange"
|
||||
>
|
||||
<template #default="{ pageList }">
|
||||
<ElTable :data="pageList" border>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import DictSelect from '#/components/dict/DictSelect.vue';
|
||||
import PageData from '#/components/page/PageData.vue';
|
||||
import { $t } from '#/locales';
|
||||
import { navigateBackToList } from '#/router/list-return-context';
|
||||
import { useDictStore } from '#/store';
|
||||
|
||||
const router = useRouter();
|
||||
@@ -43,15 +44,20 @@ function reset(formEl: FormInstance | undefined) {
|
||||
formEl?.resetFields();
|
||||
pageDataRef.value.setQuery({});
|
||||
}
|
||||
async function backToJobList() {
|
||||
await navigateBackToList(
|
||||
router,
|
||||
$route.query,
|
||||
['/sys/sysJob'],
|
||||
'/sys/sysJob',
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="mb-3">
|
||||
<ElButton
|
||||
:icon="ArrowLeft"
|
||||
@click="router.replace({ path: '/sys/sysJob' })"
|
||||
>
|
||||
<ElButton :icon="ArrowLeft" @click="backToJobList">
|
||||
{{ $t('button.back') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
defineListRouteStateSchema,
|
||||
positiveIntegerListRouteField,
|
||||
stringListRouteField,
|
||||
} from '#/composables/useListRouteState';
|
||||
|
||||
interface SysJobListRouteState {
|
||||
keyword: string;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
const sysJobListRouteSchema = defineListRouteStateSchema<SysJobListRouteState>({
|
||||
path: '/sys/sysJob',
|
||||
fields: {
|
||||
keyword: stringListRouteField(),
|
||||
pageNumber: positiveIntegerListRouteField({ defaultValue: 1 }),
|
||||
pageSize: positiveIntegerListRouteField({
|
||||
allowedValues: [10, 20, 50, 100],
|
||||
defaultValue: 10,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export { sysJobListRouteSchema };
|
||||
export type { SysJobListRouteState };
|
||||
Reference in New Issue
Block a user