fix: clarify document type grouping hierarchy

This commit is contained in:
wren
2026-05-06 10:54:42 +08:00
parent 22ef99754c
commit f1d77db79a
3 changed files with 108 additions and 11 deletions
+52 -5
View File
@@ -11,6 +11,7 @@ import {
type DocumentType,
type EntryModuleOption,
} from "~/api/document-types/document-types";
import { getDocumentSubtypeGroups, type DocumentSubtypeGroup } from "~/api/files/files-upload";
import documentTypesStyles from "~/styles/pages/document-types_index.css?url";
export function links() {
@@ -27,6 +28,7 @@ export const meta: MetaFunction = () => {
interface LoaderData {
types: DocumentType[];
entryModules: EntryModuleOption[];
subtypeGroupsByTypeId: Record<number, DocumentSubtypeGroup[]>;
frontendJWT?: string | null;
error?: string;
}
@@ -41,13 +43,22 @@ export async function loader({ request }: LoaderFunctionArgs) {
getEntryModules(frontendJWT),
]);
const types = typesRes.data?.types || [];
const subtypeGroupEntries = await Promise.all(
types.map(async (type) => {
const groupsRes = await getDocumentSubtypeGroups(type.id, frontendJWT, type.entryModuleId || undefined);
return [type.id, "data" in groupsRes && groupsRes.data ? groupsRes.data : []] as const;
}),
);
return {
types: typesRes.data?.types || [],
types,
entryModules: modulesRes.data || [],
subtypeGroupsByTypeId: Object.fromEntries(subtypeGroupEntries),
frontendJWT,
};
} catch (error) {
return { types: [], entryModules: [], error: "加载失败" };
return { types: [], entryModules: [], subtypeGroupsByTypeId: {}, error: "加载失败" };
}
}
@@ -56,6 +67,7 @@ export default function DocumentTypesIndex() {
const loaderData = useLoaderData<LoaderData>();
const [types, setTypes] = useState<DocumentType[]>(loaderData.types || []);
const entryModules = loaderData.entryModules || [];
const subtypeGroupsByTypeId = loaderData.subtypeGroupsByTypeId || {};
useEffect(() => {
setTypes(loaderData.types || []);
@@ -66,6 +78,17 @@ export default function DocumentTypesIndex() {
return entryModules.find((m) => m.id === id)?.name || `#${id}`;
};
const getSubtypeGroups = (typeId: number) => subtypeGroupsByTypeId[typeId] || [];
const getRootGroupSummary = (typeId: number) => {
const groups = getSubtypeGroups(typeId);
const rootNames = Array.from(new Set(groups.map((group) => group.rootGroupName).filter(Boolean)));
if (rootNames.length > 0) {
return rootNames.join("、");
}
return "未映射一级分组";
};
const handleDelete = async (type: DocumentType) => {
const confirmed = window.confirm(`确定要删除文档类型「${type.name}」吗?`);
if (!confirmed) return;
@@ -103,7 +126,8 @@ export default function DocumentTypesIndex() {
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
@@ -113,8 +137,31 @@ export default function DocumentTypesIndex() {
{types.map((type) => (
<tr key={type.id}>
<td><code>{type.code}</code></td>
<td>{type.name}</td>
<td>{getEntryModuleName(type.entryModuleId)}</td>
<td>
<div className="type-cell">
<strong>{type.name}</strong>
<span></span>
</div>
</td>
<td>
<div className="type-cell">
<strong>{getEntryModuleName(type.entryModuleId)}</strong>
<span>{getRootGroupSummary(type.id)}</span>
</div>
</td>
<td>
<div className="groups-container">
{getSubtypeGroups(type.id).length > 0 ? (
getSubtypeGroups(type.id).map((group) => (
<span key={group.id} className="type-badge" title={group.displayHint || group.code}>
{group.displayName || group.name}
</span>
))
) : (
<span className="subtle-text"></span>
)}
</div>
</td>
<td>
<span className="tag">{type.ruleSetIds?.length || 0} </span>
</td>
+40 -6
View File
@@ -79,6 +79,16 @@ export default function DocumentTypeNew() {
const selectedModule = loaderData.entryModules.find((m) => m.id === entryModuleId);
const runtimeSubtypeGroups = loaderData.runtimeSubtypeGroups || [];
const runtimeRootGroups = Array.from(
new Map(
runtimeSubtypeGroups
.filter((group) => group.rootGroupId || group.rootGroupName)
.map((group) => [group.rootGroupId || group.rootGroupName || group.id, {
id: group.rootGroupId || null,
name: group.rootGroupName || "未归属一级分组",
}]),
).values(),
);
const ruleSetsReadonly = isEdit && runtimeSubtypeGroups.length > 0;
const selectedRuleSets = loaderData.ruleSets.filter((rs) => selectedRuleSetIds.includes(rs.id));
const selectedUnavailableRuleSets = selectedRuleSets.filter((rs) => !rs.hasUsableVersion);
@@ -304,13 +314,24 @@ export default function DocumentTypeNew() {
</div>
<div className="binding-preview">
<div className="binding-preview-label"></div>
<div className="binding-preview-label"></div>
<div className="binding-preview-value">
<i className="ri-route-line"></i>
<span>{selectedModule?.name || "未绑定入口模块,上传入口不会主动露出此类型"}</span>
</div>
</div>
<div className="rule-set-warning">
<i className="ri-git-merge-line"></i>
<div>
<strong></strong>
<span>
/
-
</span>
</div>
</div>
{isEdit ? (
<div className="rule-set-warning">
<i className="ri-node-tree"></i>
@@ -318,7 +339,7 @@ export default function DocumentTypeNew() {
<strong></strong>
<span>
{runtimeSubtypeGroups.length > 0
? `当前文档类型在评查点分组中已${runtimeSubtypeGroups.length}二级分组;上传时会先按入口模块和子类型命中,再决定实际规则集。`
? `当前文档类型在评查点分组中已映射${runtimeSubtypeGroups.length}运行子类型;上传时会先命中所属大类,再按所选子类型决定实际规则集。`
: "当前文档类型还没有在评查点分组中挂到任何二级分组,上传时无法形成完整的新链路。"}
</span>
</div>
@@ -328,8 +349,18 @@ export default function DocumentTypeNew() {
<div className="selected-rule-sets-panel">
<div className="selected-panel-header">
<div>
<strong></strong>
<span></span>
<strong></strong>
<span> / </span>
</div>
</div>
<div className="rule-set-warning">
<i className="ri-stack-line"></i>
<div>
<strong></strong>
<span>
{runtimeRootGroups.map((group) => group.name).join("、")}
{selectedModule?.name ? ` · 所属入口模块:${selectedModule.name}` : ""}
</span>
</div>
</div>
<div className="selected-rule-grid">
@@ -337,9 +368,12 @@ export default function DocumentTypeNew() {
<div key={group.id} className="selected-rule-card">
<div className="selected-rule-main">
<strong>{group.displayName || group.name}</strong>
<span>{group.rootGroupName || "未归属一级分组"}{group.entryModuleName ? ` · ${group.entryModuleName}` : ""}</span>
<span>
{group.rootGroupName || "未归属一级分组"}
{group.entryModuleName ? ` · 入口模块:${group.entryModuleName}` : ""}
</span>
</div>
<span className="selected-rule-meta">{group.code}</span>
<span className="selected-rule-meta"> · {group.code}</span>
</div>
))}
</div>
+16
View File
@@ -62,6 +62,22 @@
@apply flex flex-wrap gap-1 max-w-md;
}
.document-types-page .type-cell {
@apply flex flex-col gap-1;
}
.document-types-page .type-cell strong {
@apply text-sm font-medium text-gray-800;
}
.document-types-page .type-cell span {
@apply text-xs text-gray-500;
}
.document-types-page .subtle-text {
@apply text-xs text-gray-400;
}
.document-types-page .operation-btn {
@apply inline-flex items-center text-sm px-2 py-1 rounded hover:bg-gray-100;
}