Commit 7b2ec68f authored by drigle's avatar drigle

feat: support configurable view filter fields

parent 5c519a8f
...@@ -156,7 +156,7 @@ export default function SubjectWorkbench() { ...@@ -156,7 +156,7 @@ export default function SubjectWorkbench() {
pageSize, pageSize,
filters: { filters: {
...filters, ...filters,
__fieldDefs: Object.fromEntries(columns.map((column) => [column.field, column.fieldDef])), __fieldDefs: Object.fromEntries(filterSchema.map((field) => [field.key, field.fieldDef])),
}, },
pagination: paginationEnabled, pagination: paginationEnabled,
}) })
...@@ -175,7 +175,7 @@ export default function SubjectWorkbench() { ...@@ -175,7 +175,7 @@ export default function SubjectWorkbench() {
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [subjectName, activeView, page, pageSize, filters, columns, paginationEnabled, refreshKey]); }, [subjectName, activeView, page, pageSize, filters, filterSchema, paginationEnabled, refreshKey]);
useEffect(() => { useEffect(() => {
if (!showCreate) setCreateValues(createFields.reduce((values, field) => ({ ...values, [field.fieldDef.name]: restrictionDefaults.metadata?.[field.fieldDef.name] ?? emptyValueForField(field.fieldDef) }), {})); if (!showCreate) setCreateValues(createFields.reduce((values, field) => ({ ...values, [field.fieldDef.name]: restrictionDefaults.metadata?.[field.fieldDef.name] ?? emptyValueForField(field.fieldDef) }), {}));
...@@ -311,6 +311,7 @@ export default function SubjectWorkbench() { ...@@ -311,6 +311,7 @@ export default function SubjectWorkbench() {
activeViewName={activeViewName} activeViewName={activeViewName}
onViewChange={(viewName) => { onViewChange={(viewName) => {
setActiveViewName(viewName); setActiveViewName(viewName);
setFilters({});
setSelectedIds(EMPTY_ARRAY); setSelectedIds(EMPTY_ARRAY);
setPage(0); setPage(0);
}} }}
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
1. 进入页面后并行调用 `GET /subject/:name` 和 `GET /subject/:name/view`;打开详情后再按 View 名调用 `GET /subject/:subjectName/view/:viewName` 获取完整 Detail 配置。 1. 进入页面后并行调用 `GET /subject/:name` 和 `GET /subject/:name/view`;打开详情后再按 View 名调用 `GET /subject/:subjectName/view/:viewName` 获取完整 Detail 配置。
2. 只展示当前用户有权访问的 `grid` 视图;授权只读取 `user.applications[]` 中 `name = integrity_practice_management` 的项目。系统管理员、系统账号和该应用“管理”角色可旁路,但不能旁路 `disableColumns`。配置包含 `filterView` 且非空时,只展示指定视图。 2. 只展示当前用户有权访问的 `grid` 视图;授权只读取 `user.applications[]` 中 `name = integrity_practice_management` 的项目。系统管理员、系统账号和该应用“管理”角色可旁路,但不能旁路 `disableColumns`。配置包含 `filterView` 且非空时,只展示指定视图。
3. 切换视图后,根据视图的 `settings.restrictions`、`settings.filters.conditions`、列排序配置调用 `POST /record/query`;筛选控件由当前 View 的可见字段自动生成,文本字段使用远程模糊查询,Select/日期字段使用字段配置值。 3. 切换视图后,根据视图的 `settings.restrictions`、`settings.filters.conditions`、列排序配置调用 `POST /record/query`;筛选控件默认由当前 View 的可见字段自动生成。若 View 配置了 `settings.filters.fields`,则仅按该字段数组的顺序生成筛选控件,支持 `metadata.field`、`field` 字符串及 `{ field }`、`{ path }`、`{ name }` 对象项,显式配置可引用隐藏列;空数组表示该视图不展示筛选控件,无效字段会被忽略。文本字段使用远程模糊查询,Select/日期字段使用字段配置值。
4. `gridRecordConfig.pagination = false` 时单次最多读取 1000 条;其余页面使用服务端分页。 4. `gridRecordConfig.pagination = false` 时单次最多读取 1000 条;其余页面使用服务端分页。
5. 列表不再提供独立关键词搜索;用户通过当前视图字段筛选,筛选条件直接写入 `/record/query` 的 `filter`,清空筛选后回到第 1 页。 5. 列表不再提供独立关键词搜索;用户通过当前视图字段筛选,筛选条件直接写入 `/record/query` 的 `filter`,清空筛选后回到第 1 页。
6. 列定义、列顺序、初始排序、固定列和字段显示格式由 Subject View 与 Subject 字段定义共同决定;HeroUI Pro DataGrid 仅在真实横向溢出时固定首列和操作列,并允许当前页原生列排序和列宽调整,不改变服务端分页口径。 6. 列定义、列顺序、初始排序、固定列和字段显示格式由 Subject View 与 Subject 字段定义共同决定;HeroUI Pro DataGrid 仅在真实横向溢出时固定首列和操作列,并允许当前页原生列排序和列宽调整,不改变服务端分页口径。
......
...@@ -39,6 +39,34 @@ import * as XLSX from 'xlsx'; ...@@ -39,6 +39,34 @@ import * as XLSX from 'xlsx';
export const DEFAULT_PAGE_SIZE = 10; export const DEFAULT_PAGE_SIZE = 10;
function normalizeFilterFieldPath(field) {
const value = typeof field === 'string' ? field.trim() : '';
if (!value) return '';
return value === 'title' || value.startsWith('metadata.')
? value
: `metadata.${value}`;
}
function resolveFilterColumns(view, columns = [], subject) {
const configuredFields = view?.settings?.filters?.fields;
// An absent setting keeps the existing visible-column fallback. An empty
// array is intentional and disables the filter controls for this view.
if (!Array.isArray(configuredFields)) return columns;
const allViewColumns = buildViewColumns(view, subject, { includeHidden: true });
const columnsByField = new Map(
[...allViewColumns, ...columns].map((column) => [column.field, column]),
);
return configuredFields
.map((item) => {
const configuredField = typeof item === 'string'
? item
: item?.field || item?.path || item?.name;
return columnsByField.get(normalizeFilterFieldPath(configuredField));
})
.filter(Boolean);
}
export function buildIntegrityCreateFormFields(gridView, subject) { export function buildIntegrityCreateFormFields(gridView, subject) {
const allColumns = buildViewColumns(gridView, subject, { includeHidden: true }); const allColumns = buildViewColumns(gridView, subject, { includeHidden: true });
const includeTitle = gridView?.settings?.shortTitle !== true; const includeTitle = gridView?.settings?.shortTitle !== true;
...@@ -170,7 +198,8 @@ export function buildSubjectColumns(view, subject) { ...@@ -170,7 +198,8 @@ export function buildSubjectColumns(view, subject) {
} }
export function buildSubjectFilterSchema(view, subject) { export function buildSubjectFilterSchema(view, subject) {
return buildViewColumns(view, subject).filter((column) => column?.field).map((column) => { const visibleColumns = buildViewColumns(view, subject);
return resolveFilterColumns(view, visibleColumns, subject).filter((column) => column?.field).map((column) => {
const fieldDef = column.fieldDef || {}; const fieldDef = column.fieldDef || {};
const type = String(fieldDef.type || 'text').toLowerCase(); const type = String(fieldDef.type || 'text').toLowerCase();
const options = getSelectOptions(fieldDef); const options = getSelectOptions(fieldDef);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment