fix: clarify document type grouping hierarchy
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user