feat(web): 完善插件前端页面 — 数据 API、筛选、视图切换和统计展示
- 新增 pluginData API 层:count/aggregate/stats 端点调用 - PluginCRUDPage 支持 visible_when 条件字段、筛选器下拉、视图切换 - PluginTabsPage 支持 tabs 布局和子实体 CRUD - PluginTreePage 实现树形数据加载和节点展开/收起 - PluginGraphPage 实现关系图谱可视化展示 - PluginDashboardPage 实现统计卡片和聚合数据展示 - PluginAdmin 状态显示优化 - plugin store 增强 schema 加载逻辑和菜单生成
This commit is contained in:
@@ -31,14 +31,14 @@ import {
|
||||
createPluginData,
|
||||
updatePluginData,
|
||||
deletePluginData,
|
||||
PluginDataListOptions,
|
||||
type PluginDataListOptions,
|
||||
} from '../api/pluginData';
|
||||
import {
|
||||
getPluginSchema,
|
||||
PluginFieldSchema,
|
||||
PluginEntitySchema,
|
||||
PluginPageSchema,
|
||||
PluginSectionSchema,
|
||||
type PluginFieldSchema,
|
||||
type PluginEntitySchema,
|
||||
type PluginPageSchema,
|
||||
type PluginSectionSchema,
|
||||
} from '../api/plugins';
|
||||
|
||||
const { Search } = Input;
|
||||
@@ -133,8 +133,12 @@ export default function PluginCRUDPage({
|
||||
// 加载 schema
|
||||
useEffect(() => {
|
||||
if (!pluginId) return;
|
||||
getPluginSchema(pluginId)
|
||||
.then((schema) => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
async function loadSchema() {
|
||||
try {
|
||||
const schema = await getPluginSchema(pluginId!);
|
||||
if (abortController.signal.aborted) return;
|
||||
const entities: PluginEntitySchema[] = (schema as { entities?: PluginEntitySchema[] }).entities || [];
|
||||
setAllEntities(entities);
|
||||
const entity = entities.find((e) => e.name === entityName);
|
||||
@@ -145,7 +149,6 @@ export default function PluginCRUDPage({
|
||||
const ui = (schema as { ui?: { pages: PluginPageSchema[] } }).ui;
|
||||
if (ui?.pages) {
|
||||
setAllPages(ui.pages);
|
||||
// 找到 detail 页面的 sections
|
||||
const detailPage = ui.pages.find(
|
||||
(p) => p.type === 'detail' && 'entity' in p && p.entity === entityName,
|
||||
);
|
||||
@@ -153,19 +156,21 @@ export default function PluginCRUDPage({
|
||||
setDetailSections(detailPage.sections);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// schema 加载失败时仍可使用
|
||||
});
|
||||
} catch {
|
||||
message.warning('Schema 加载失败,部分功能不可用');
|
||||
}
|
||||
}
|
||||
|
||||
loadSchema();
|
||||
return () => abortController.abort();
|
||||
}, [pluginId, entityName]);
|
||||
|
||||
const fetchData = useCallback(
|
||||
async (p = page) => {
|
||||
async (p = page, overrides?: { search?: string; sort_by?: string; sort_order?: 'asc' | 'desc' }) => {
|
||||
if (!pluginId || !entityName) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const options: PluginDataListOptions = {};
|
||||
// 自动添加 filterField 过滤(detail 页面内嵌 CRUD)
|
||||
const mergedFilters = { ...filters };
|
||||
if (filterField && filterValue) {
|
||||
mergedFilters[filterField] = filterValue;
|
||||
@@ -173,10 +178,13 @@ export default function PluginCRUDPage({
|
||||
if (Object.keys(mergedFilters).length > 0) {
|
||||
options.filter = mergedFilters;
|
||||
}
|
||||
if (searchText) options.search = searchText;
|
||||
if (sortBy) {
|
||||
options.sort_by = sortBy;
|
||||
options.sort_order = sortOrder;
|
||||
const effectiveSearch = overrides?.search ?? searchText;
|
||||
if (effectiveSearch) options.search = effectiveSearch;
|
||||
const effectiveSortBy = overrides?.sort_by ?? sortBy;
|
||||
const effectiveSortOrder = overrides?.sort_order ?? sortOrder;
|
||||
if (effectiveSortBy) {
|
||||
options.sort_by = effectiveSortBy;
|
||||
options.sort_order = effectiveSortOrder;
|
||||
}
|
||||
const result = await listPluginData(pluginId, entityName, p, 20, options);
|
||||
setRecords(
|
||||
@@ -505,7 +513,7 @@ export default function PluginCRUDPage({
|
||||
onSearch={(value) => {
|
||||
setSearchText(value);
|
||||
setPage(1);
|
||||
fetchData(1);
|
||||
fetchData(1, { search: value });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -529,6 +537,21 @@ export default function PluginCRUDPage({
|
||||
rowKey="_id"
|
||||
loading={loading}
|
||||
size={compact ? 'small' : undefined}
|
||||
onChange={(_pagination, _filters, sorter) => {
|
||||
if (!Array.isArray(sorter) && sorter.field) {
|
||||
const newSortBy = String(sorter.field);
|
||||
const newSortOrder = sorter.order === 'ascend' ? 'asc' as const : 'desc' as const;
|
||||
setSortBy(newSortBy);
|
||||
setSortOrder(newSortOrder);
|
||||
setPage(1);
|
||||
fetchData(1, { sort_by: newSortBy, sort_order: newSortOrder });
|
||||
} else if (!sorter || (Array.isArray(sorter) && sorter.length === 0)) {
|
||||
setSortBy(undefined);
|
||||
setSortOrder('desc');
|
||||
setPage(1);
|
||||
fetchData(1, { sort_by: undefined, sort_order: undefined });
|
||||
}
|
||||
}}
|
||||
pagination={
|
||||
compact
|
||||
? { pageSize: 5, showTotal: (t) => `共 ${t} 条` }
|
||||
|
||||
Reference in New Issue
Block a user