feat: 统一恢复管理端列表上下文
- 统一列表路由状态、分页初始化与安全返回契约 - 接入知识库、工作流、插件、审批、Skill、Agent、Bot、反馈和定时任务链路 - 补齐公共能力与关键返回路径自动化测试
This commit is contained in:
@@ -5,9 +5,10 @@ import type {
|
||||
ActionButton,
|
||||
CardPrimaryAction,
|
||||
} from '#/components/page/CardList.vue';
|
||||
import type { OfflineImpactCheck } from '#/views/ai/shared/offline-impact';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useAccess } from '@easyflow/access';
|
||||
import { EasyFlowFormModal } from '@easyflow/common-ui';
|
||||
@@ -43,15 +44,20 @@ import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
|
||||
import CardPage from '#/components/page/CardList.vue';
|
||||
import PageData from '#/components/page/PageData.vue';
|
||||
import PageSide from '#/components/page/PageSide.vue';
|
||||
import {
|
||||
buildListRouteFullPath,
|
||||
mergeListRouteStateQuery,
|
||||
parseListRouteState,
|
||||
watchListRouteState,
|
||||
} from '#/composables/useListRouteState';
|
||||
import { withListReturnTo } from '#/router/list-return-context';
|
||||
import { copyTextWithFeedback } from '#/utils/clipboard-feedback';
|
||||
import { createLazyComponentController } from '#/utils/lazy-component';
|
||||
import { buildAbsoluteAppRouteUrl } from '#/utils/share-route-context';
|
||||
import { documentCollectionListRouteSchema } from '#/views/ai/documentCollection/document-collection-list-route-state';
|
||||
import AiResourceCornerMeta from '#/views/ai/shared/AiResourceCornerMeta.vue';
|
||||
import { confirmPublishSubmission } from '#/views/ai/shared/approval-application-reason';
|
||||
import {
|
||||
buildOfflineImpactMessage,
|
||||
type OfflineImpactCheck,
|
||||
} from '#/views/ai/shared/offline-impact';
|
||||
import { buildOfflineImpactMessage } from '#/views/ai/shared/offline-impact';
|
||||
import {
|
||||
canAiResourceDelete,
|
||||
canAiResourceOffline,
|
||||
@@ -61,7 +67,35 @@ import {
|
||||
resolveAiResourceDisplayStatus,
|
||||
} from '#/views/ai/shared/publish-status';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const initialListState = parseListRouteState(
|
||||
route.query,
|
||||
documentCollectionListRouteSchema,
|
||||
);
|
||||
const selectedCategoryId = ref(initialListState.categoryId);
|
||||
const searchKeyword = ref(initialListState.keyword);
|
||||
const currentPageNumber = ref(initialListState.pageNumber);
|
||||
const currentPageSize = ref(initialListState.pageSize);
|
||||
let initialCategoryChangePending = Boolean(initialListState.categoryId);
|
||||
watchListRouteState(route, documentCollectionListRouteSchema, (state) => {
|
||||
if (state.categoryId !== selectedCategoryId.value) {
|
||||
initialCategoryChangePending = Boolean(state.categoryId);
|
||||
}
|
||||
selectedCategoryId.value = state.categoryId;
|
||||
searchKeyword.value = state.keyword;
|
||||
currentPageNumber.value = state.pageNumber;
|
||||
currentPageSize.value = state.pageSize;
|
||||
pageDataRef.value?.restoreState?.({
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
categoryId: state.categoryId || undefined,
|
||||
isQueryOr: true,
|
||||
title: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
const userStore = useUserStore();
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
const collectionTypeLabelMap = {
|
||||
@@ -153,6 +187,20 @@ function resolveNavTitle(row: Record<string, any>) {
|
||||
return row?.title || row?.name || '';
|
||||
}
|
||||
|
||||
function currentListFullPath() {
|
||||
return buildListRouteFullPath(
|
||||
router,
|
||||
route.query,
|
||||
{
|
||||
categoryId: selectedCategoryId.value,
|
||||
keyword: searchKeyword.value,
|
||||
pageNumber: currentPageNumber.value,
|
||||
pageSize: currentPageSize.value,
|
||||
},
|
||||
documentCollectionListRouteSchema,
|
||||
);
|
||||
}
|
||||
|
||||
function openKnowledgeDetail(row: {
|
||||
id: string;
|
||||
name?: string;
|
||||
@@ -164,6 +212,7 @@ function openKnowledgeDetail(row: {
|
||||
id: row.id,
|
||||
pageKey: '/ai/documentCollection',
|
||||
navTitle: resolveNavTitle(row),
|
||||
...withListReturnTo(currentListFullPath()),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -243,6 +292,7 @@ const actions: ActionButton[] = [
|
||||
pageKey: '/ai/documentCollection',
|
||||
navTitle: resolveNavTitle(row),
|
||||
activeMenu: 'knowledgeSearch',
|
||||
...withListReturnTo(currentListFullPath()),
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -451,18 +501,18 @@ function resolvePublishStatusMeta(
|
||||
tone: 'danger',
|
||||
};
|
||||
}
|
||||
case 'OFFLINE_PENDING': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusOfflinePending'),
|
||||
tone: 'pending',
|
||||
};
|
||||
}
|
||||
case 'OFFLINE': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusOffline'),
|
||||
tone: 'draft',
|
||||
};
|
||||
}
|
||||
case 'OFFLINE_PENDING': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusOfflinePending'),
|
||||
tone: 'pending',
|
||||
};
|
||||
}
|
||||
case 'PUBLISH_PENDING': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusPublishPending'),
|
||||
@@ -537,7 +587,12 @@ const formRules = computed(() => {
|
||||
return rules;
|
||||
});
|
||||
const handleSearch = (params: any) => {
|
||||
pageDataRef.value.setQuery({ title: params, isQueryOr: true });
|
||||
searchKeyword.value = String(params || '');
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
});
|
||||
};
|
||||
const reloadKnowledgeList = () => {
|
||||
pageDataRef.value?.reload?.();
|
||||
@@ -567,6 +622,19 @@ const getCategoryList = async () => {
|
||||
},
|
||||
...res.data,
|
||||
];
|
||||
if (
|
||||
selectedCategoryId.value &&
|
||||
!categoryList.value.some(
|
||||
(item) => String(item.id) === selectedCategoryId.value,
|
||||
)
|
||||
) {
|
||||
selectedCategoryId.value = '';
|
||||
initialCategoryChangePending = false;
|
||||
pageDataRef.value?.setQuery?.({
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
function removeCategory(row: any) {
|
||||
@@ -686,7 +754,42 @@ function handleSubmit() {
|
||||
});
|
||||
}
|
||||
function changeCategory(category: any) {
|
||||
pageDataRef.value.setQuery({ categoryId: category.id });
|
||||
const categoryId = String(category?.id || '');
|
||||
if (initialCategoryChangePending && categoryId === selectedCategoryId.value) {
|
||||
initialCategoryChangePending = false;
|
||||
return;
|
||||
}
|
||||
initialCategoryChangePending = false;
|
||||
selectedCategoryId.value = categoryId;
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: categoryId || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
});
|
||||
}
|
||||
function handlePageStateChange(state: {
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
}) {
|
||||
currentPageNumber.value = state.pageNumber;
|
||||
currentPageSize.value = state.pageSize;
|
||||
const query = mergeListRouteStateQuery(
|
||||
route.query,
|
||||
{
|
||||
categoryId: selectedCategoryId.value,
|
||||
keyword: searchKeyword.value,
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
},
|
||||
documentCollectionListRouteSchema,
|
||||
);
|
||||
const target = router.resolve({
|
||||
path: documentCollectionListRouteSchema.path,
|
||||
query,
|
||||
});
|
||||
if (target.fullPath !== route.fullPath) {
|
||||
void router.replace(target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -695,6 +798,7 @@ function changeCategory(category: any) {
|
||||
<div class="knowledge-header">
|
||||
<HeaderSearch
|
||||
:buttons="headerButtons"
|
||||
:initial-value="searchKeyword"
|
||||
@search="handleSearch"
|
||||
@button-click="handleButtonClick"
|
||||
/>
|
||||
@@ -706,15 +810,22 @@ function changeCategory(category: any) {
|
||||
:menus="categoryList"
|
||||
:control-btns="controlBtns"
|
||||
:footer-button="footerButton"
|
||||
:default-selected="selectedCategoryId"
|
||||
@change="changeCategory"
|
||||
/>
|
||||
<div class="h-full flex-1 overflow-auto">
|
||||
<PageData
|
||||
ref="pageDataRef"
|
||||
page-url="/api/v1/documentCollection/page"
|
||||
:page-size="12"
|
||||
:page-size="initialListState.pageSize"
|
||||
:page-sizes="[12, 24, 36, 48]"
|
||||
:init-query-params="{ status: 1 }"
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
categoryId: initialListState.categoryId || undefined,
|
||||
title: initialListState.keyword || undefined,
|
||||
isQueryOr: true,
|
||||
}"
|
||||
@state-change="handlePageStateChange"
|
||||
>
|
||||
<template #default="{ pageList }">
|
||||
<CardPage
|
||||
|
||||
Reference in New Issue
Block a user