完善规则配置页交互细节
This commit is contained in:
@@ -13,12 +13,14 @@ interface FilterSelectProps {
|
|||||||
options: FilterOption[];
|
options: FilterOption[];
|
||||||
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
|
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 筛选下拉选择框组件
|
* 筛选下拉选择框组件
|
||||||
*/
|
*/
|
||||||
const FilterSelect = ({ label, name, value, options, onChange, className = '' }: FilterSelectProps) => (
|
const FilterSelect = ({ label, name, value, options, onChange, className = '', disabled = false, placeholder = '全部' }: FilterSelectProps) => (
|
||||||
<div className={`filter-item ${className}`}>
|
<div className={`filter-item ${className}`}>
|
||||||
<label className="filter-label">{label}</label>
|
<label className="filter-label">{label}</label>
|
||||||
<select
|
<select
|
||||||
@@ -26,8 +28,9 @@ const FilterSelect = ({ label, name, value, options, onChange, className = '' }:
|
|||||||
name={name}
|
name={name}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<option value="">全部</option>
|
<option value="">{placeholder}</option>
|
||||||
{options.map(option => (
|
{options.map(option => (
|
||||||
<option key={option.value} value={option.value}>{option.label}</option>
|
<option key={option.value} value={option.value}>{option.label}</option>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type LoaderFunctionArgs, type MetaFunction } from '@remix-run/node';
|
import { type LoaderFunctionArgs, type MetaFunction } from '@remix-run/node';
|
||||||
import { Link, useLoaderData } from '@remix-run/react';
|
import { Link, useLoaderData } from '@remix-run/react';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Button } from '~/components/ui/Button';
|
import { Button } from '~/components/ui/Button';
|
||||||
import { Card } from '~/components/ui/Card';
|
import { Card } from '~/components/ui/Card';
|
||||||
import { Table } from '~/components/ui/Table';
|
import { Table } from '~/components/ui/Table';
|
||||||
@@ -328,6 +328,7 @@ export default function RulesTestDetail() {
|
|||||||
const [showValidation, setShowValidation] = useState(false);
|
const [showValidation, setShowValidation] = useState(false);
|
||||||
const [showYamlPreview, setShowYamlPreview] = useState(false);
|
const [showYamlPreview, setShowYamlPreview] = useState(false);
|
||||||
const [draftSaved, setDraftSaved] = useState(false);
|
const [draftSaved, setDraftSaved] = useState(false);
|
||||||
|
const promptEditorRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRules(pack.rules);
|
setRules(pack.rules);
|
||||||
@@ -448,6 +449,33 @@ export default function RulesTestDetail() {
|
|||||||
setDependencyDialogOpen(false);
|
setDependencyDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const insertDependencyVariable = (value: string) => {
|
||||||
|
const variable = `{{${value}}}`;
|
||||||
|
const textarea = promptEditorRef.current;
|
||||||
|
const prompt = ruleDraft.prompt || '';
|
||||||
|
const start = textarea?.selectionStart ?? prompt.length;
|
||||||
|
const end = textarea?.selectionEnd ?? prompt.length;
|
||||||
|
const prefix = prompt.slice(0, start);
|
||||||
|
const suffix = prompt.slice(end);
|
||||||
|
const nextPrompt = `${prefix}${variable}${suffix}`;
|
||||||
|
|
||||||
|
setRuleDraft({ ...ruleDraft, prompt: nextPrompt });
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
if (!promptEditorRef.current) return;
|
||||||
|
const nextCursor = start + variable.length;
|
||||||
|
promptEditorRef.current.focus();
|
||||||
|
promptEditorRef.current.setSelectionRange(nextCursor, nextCursor);
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeDependency = (value: string) => {
|
||||||
|
setRuleDraft({
|
||||||
|
...ruleDraft,
|
||||||
|
dependencies: ruleDraft.dependencies.filter(dependency => dependency !== value)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const saveRule = () => {
|
const saveRule = () => {
|
||||||
if (!editor || editor.kind !== 'rule') return;
|
if (!editor || editor.kind !== 'rule') return;
|
||||||
const existingRule = editor.id ? rules.find(rule => rule.id === editor.id) : undefined;
|
const existingRule = editor.id ? rules.find(rule => rule.id === editor.id) : undefined;
|
||||||
@@ -488,7 +516,6 @@ export default function RulesTestDetail() {
|
|||||||
render: (_: unknown, record: DependencyOption) => (
|
render: (_: unknown, record: DependencyOption) => (
|
||||||
<div className="rule-name">
|
<div className="rule-name">
|
||||||
<strong>{record.label}</strong>
|
<strong>{record.label}</strong>
|
||||||
<span>YAML引用:{record.value}</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -540,7 +567,7 @@ export default function RulesTestDetail() {
|
|||||||
{draftSaved && (
|
{draftSaved && (
|
||||||
<div className="draft-tip">
|
<div className="draft-tip">
|
||||||
<i className="ri-checkbox-circle-line"></i>
|
<i className="ri-checkbox-circle-line"></i>
|
||||||
草稿已保存到当前页面状态。本次验证不提交后端,也不会更新 OSS 文件。
|
草稿已保存。
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
@@ -615,10 +642,16 @@ export default function RulesTestDetail() {
|
|||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{(pack.fields.length > 0 || pack.subDocuments.length > 0 || pack.visualElements.length > 0) && (
|
||||||
|
<>
|
||||||
|
{pack.fields.length > 0 && <span id="fields" className="section-anchor" aria-hidden="true"></span>}
|
||||||
|
{pack.subDocuments.length > 0 && <span id="sub-documents" className="section-anchor" aria-hidden="true"></span>}
|
||||||
|
{pack.visualElements.length > 0 && <span id="visual-elements" className="section-anchor" aria-hidden="true"></span>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Card
|
<Card
|
||||||
className="ant-card"
|
className="ant-card"
|
||||||
title={`依赖字段 (${currentRule.dependencies.length}项)`}
|
title={`依赖字段 (${currentRule.dependencies.length}项)`}
|
||||||
extra={<Button size="small" type="primary" icon="ri-add-line" onClick={() => openRuleEditor(currentRule)}>维护依赖</Button>}
|
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
className="rules-test-table"
|
className="rules-test-table"
|
||||||
@@ -629,6 +662,7 @@ export default function RulesTestDetail() {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<span id="rules" className="section-anchor" aria-hidden="true"></span>
|
||||||
<Card className="ant-card" title="评查规则">
|
<Card className="ant-card" title="评查规则">
|
||||||
<div className="rule-content-stack">
|
<div className="rule-content-stack">
|
||||||
{currentRule.subRules.length > 0 && (
|
{currentRule.subRules.length > 0 && (
|
||||||
@@ -636,13 +670,12 @@ export default function RulesTestDetail() {
|
|||||||
<div className="drawer-subsection-header">
|
<div className="drawer-subsection-header">
|
||||||
<div>
|
<div>
|
||||||
<strong>{currentRule.type === 'rule_group' ? `子规则与逻辑(${currentRule.subRules.length}步)` : `规则步骤(${currentRule.subRules.length}步)`}</strong>
|
<strong>{currentRule.type === 'rule_group' ? `子规则与逻辑(${currentRule.subRules.length}步)` : `规则步骤(${currentRule.subRules.length}步)`}</strong>
|
||||||
<span>{currentRule.type === 'rule_group' ? '规则组合只维护子规则编号、内容和逻辑表达式,不在此处维护字段库。' : '展示当前评查点 YAML stages 中的每一个检查步骤。'}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="subrule-list">
|
<div className="subrule-list">
|
||||||
{currentRule.subRules.map(subRule => (
|
{currentRule.subRules.map(subRule => (
|
||||||
<div key={subRule.id} className="subrule-item">
|
<div key={subRule.id} className="subrule-item">
|
||||||
<Tag color="gray" size="sm">{subRule.id}</Tag>
|
<span className="subrule-index">{subRule.id}</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>
|
<strong>
|
||||||
{checkTypeLabel(subRule.check)}
|
{checkTypeLabel(subRule.check)}
|
||||||
@@ -671,7 +704,6 @@ export default function RulesTestDetail() {
|
|||||||
<div className="drawer-subsection-header">
|
<div className="drawer-subsection-header">
|
||||||
<div>
|
<div>
|
||||||
<strong>子规则与逻辑</strong>
|
<strong>子规则与逻辑</strong>
|
||||||
<span>规则组合只维护子规则编号、内容和逻辑表达式,不在此处维护字段库。</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="subrule-list">
|
<div className="subrule-list">
|
||||||
@@ -679,10 +711,10 @@ export default function RulesTestDetail() {
|
|||||||
const referencedRule = rulesById.get(ruleId);
|
const referencedRule = rulesById.get(ruleId);
|
||||||
return (
|
return (
|
||||||
<div key={ruleId} className="subrule-item">
|
<div key={ruleId} className="subrule-item">
|
||||||
<Tag color="gray" size="sm">{ruleId}</Tag>
|
<span className="subrule-index">{ruleId}</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>{referencedRule?.name || '引用规则'}</strong>
|
<strong>{referencedRule?.name || '引用规则'}</strong>
|
||||||
<span>{referencedRule ? `${ruleTypeLabel(referencedRule.type)} / ${referencedRule.group}` : '当前 YAML 未找到对应规则内容'}</span>
|
<span>{referencedRule ? `${ruleTypeLabel(referencedRule.type)} / ${referencedRule.group}` : '未找到对应规则内容'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -702,7 +734,6 @@ export default function RulesTestDetail() {
|
|||||||
<div className="drawer-subsection-header">
|
<div className="drawer-subsection-header">
|
||||||
<div>
|
<div>
|
||||||
<strong>智能语义检查提示词</strong>
|
<strong>智能语义检查提示词</strong>
|
||||||
<span>提示词属于当前评查点规则,不属于案卷文书或字段库。</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre className="rule-prompt-preview">{currentRule.prompt || '当前评查点尚未维护提示词。'}</pre>
|
<pre className="rule-prompt-preview">{currentRule.prompt || '当前评查点尚未维护提示词。'}</pre>
|
||||||
@@ -732,7 +763,6 @@ export default function RulesTestDetail() {
|
|||||||
<div className="rules-drawer-header">
|
<div className="rules-drawer-header">
|
||||||
<div>
|
<div>
|
||||||
<h3>{editor.mode === 'edit' ? '编辑评查点' : '新增评查点'}</h3>
|
<h3>{editor.mode === 'edit' ? '编辑评查点' : '新增评查点'}</h3>
|
||||||
<p>这里只维护当前评查点的依赖字段和规则定义。</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button type="button" className="drawer-close" onClick={() => setEditor(null)}><i className="ri-close-line"></i></button>
|
<button type="button" className="drawer-close" onClick={() => setEditor(null)}><i className="ri-close-line"></i></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -792,6 +822,7 @@ export default function RulesTestDetail() {
|
|||||||
<label>
|
<label>
|
||||||
<span>提示词</span>
|
<span>提示词</span>
|
||||||
<textarea
|
<textarea
|
||||||
|
ref={promptEditorRef}
|
||||||
className="prompt-editor"
|
className="prompt-editor"
|
||||||
value={ruleDraft.prompt}
|
value={ruleDraft.prompt}
|
||||||
onChange={event => setRuleDraft({ ...ruleDraft, prompt: event.target.value })}
|
onChange={event => setRuleDraft({ ...ruleDraft, prompt: event.target.value })}
|
||||||
@@ -804,13 +835,12 @@ export default function RulesTestDetail() {
|
|||||||
<div className="drawer-subsection-header">
|
<div className="drawer-subsection-header">
|
||||||
<div>
|
<div>
|
||||||
<strong>规则内容</strong>
|
<strong>规则内容</strong>
|
||||||
<span>展示当前规则内部的子规则编号和内容,逻辑运算式按编号填写。</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="subrule-list">
|
<div className="subrule-list">
|
||||||
{ruleDraft.subRules.length > 0 ? ruleDraft.subRules.map(subRule => (
|
{ruleDraft.subRules.length > 0 ? ruleDraft.subRules.map(subRule => (
|
||||||
<div key={subRule.id} className="subrule-item">
|
<div key={subRule.id} className="subrule-item">
|
||||||
<Tag color="gray" size="sm">{subRule.id}</Tag>
|
<span className="subrule-index">{subRule.id}</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>{checkTypeLabel(subRule.check)}</strong>
|
<strong>{checkTypeLabel(subRule.check)}</strong>
|
||||||
<span>{subRule.content}</span>
|
<span>{subRule.content}</span>
|
||||||
@@ -820,10 +850,10 @@ export default function RulesTestDetail() {
|
|||||||
const referencedRule = rulesById.get(ruleId);
|
const referencedRule = rulesById.get(ruleId);
|
||||||
return (
|
return (
|
||||||
<div key={ruleId} className="subrule-item">
|
<div key={ruleId} className="subrule-item">
|
||||||
<Tag color="gray" size="sm">{ruleId}</Tag>
|
<span className="subrule-index">{ruleId}</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>{referencedRule?.name || '引用规则'}</strong>
|
<strong>{referencedRule?.name || '引用规则'}</strong>
|
||||||
<span>{referencedRule ? `${ruleTypeLabel(referencedRule.type)} / ${referencedRule.group}` : '当前 YAML 未找到对应规则内容'}</span>
|
<span>{referencedRule ? `${ruleTypeLabel(referencedRule.type)} / ${referencedRule.group}` : '未找到对应规则内容'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -847,18 +877,27 @@ export default function RulesTestDetail() {
|
|||||||
{selectedDependencyOptions.length === 0 ? (
|
{selectedDependencyOptions.length === 0 ? (
|
||||||
<div className="drawer-empty">暂未添加依赖字段。</div>
|
<div className="drawer-empty">暂未添加依赖字段。</div>
|
||||||
) : selectedDependencyOptions.map(option => (
|
) : selectedDependencyOptions.map(option => (
|
||||||
<Tag
|
<span
|
||||||
key={option.value}
|
key={option.value}
|
||||||
color="green"
|
className="dependency-variable-button"
|
||||||
size="sm"
|
>
|
||||||
closable
|
<button
|
||||||
onClose={() => setRuleDraft({
|
type="button"
|
||||||
...ruleDraft,
|
className="dependency-variable-main"
|
||||||
dependencies: ruleDraft.dependencies.filter(dependency => dependency !== option.value)
|
onClick={() => insertDependencyVariable(option.value)}
|
||||||
})}
|
title={`插入变量 {{${option.value}}}`}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</Tag>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="dependency-variable-remove"
|
||||||
|
onClick={() => removeDependency(option.value)}
|
||||||
|
aria-label={`移除依赖字段 ${option.label}`}
|
||||||
|
>
|
||||||
|
<i className="ri-close-line"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
))}
|
))}
|
||||||
<Button size="small" type="default" icon="ri-add-line" onClick={openDependencyDialog}>追加字段</Button>
|
<Button size="small" type="default" icon="ri-add-line" onClick={openDependencyDialog}>追加字段</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -880,7 +919,6 @@ export default function RulesTestDetail() {
|
|||||||
<div className="dependency-dialog-header">
|
<div className="dependency-dialog-header">
|
||||||
<div>
|
<div>
|
||||||
<h3>追加依赖字段</h3>
|
<h3>追加依赖字段</h3>
|
||||||
<p>从当前文档类型的字段库中选择,仅写入当前评查点依赖。</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button type="button" className="drawer-close" onClick={() => setDependencyDialogOpen(false)}><i className="ri-close-line"></i></button>
|
<button type="button" className="drawer-close" onClick={() => setDependencyDialogOpen(false)}><i className="ri-close-line"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -104,12 +104,19 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
? requestedFilters.subtype
|
? requestedFilters.subtype
|
||||||
: ''
|
: ''
|
||||||
};
|
};
|
||||||
const scopedPacks = packs.filter(pack =>
|
const scopedByMainTypePacks = packs.filter(pack =>
|
||||||
pack.documentType === scopedFilters.documentType &&
|
pack.documentType === scopedFilters.documentType &&
|
||||||
(!scopedFilters.mainType || pack.mainType === scopedFilters.mainType) &&
|
(!scopedFilters.mainType || pack.mainType === scopedFilters.mainType)
|
||||||
(!scopedFilters.subtype || pack.subtype === scopedFilters.subtype)
|
|
||||||
);
|
);
|
||||||
const ruleGroupOptions = unique(scopedPacks.flatMap(pack => pack.rules.map(rule => rule.group))).sort((a, b) => a.localeCompare(b, 'zh-CN'));
|
const subtypeOptions = unique(scopedByMainTypePacks.map(pack => pack.subtype));
|
||||||
|
const ruleGroupSourcePacks = scopedFilters.subtype
|
||||||
|
? scopedByMainTypePacks.filter(pack => pack.subtype === scopedFilters.subtype)
|
||||||
|
: subtypeOptions.length <= 1
|
||||||
|
? scopedByMainTypePacks
|
||||||
|
: [];
|
||||||
|
const ruleGroupOptions = unique(
|
||||||
|
ruleGroupSourcePacks.flatMap(pack => pack.rules.map(rule => rule.group))
|
||||||
|
).sort((a, b) => a.localeCompare(b, 'zh-CN'));
|
||||||
const filters = {
|
const filters = {
|
||||||
...scopedFilters,
|
...scopedFilters,
|
||||||
ruleGroup: ruleGroupOptions.includes(requestedFilters.ruleGroup) ? requestedFilters.ruleGroup : ''
|
ruleGroup: ruleGroupOptions.includes(requestedFilters.ruleGroup) ? requestedFilters.ruleGroup : ''
|
||||||
@@ -189,10 +196,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
options: {
|
options: {
|
||||||
documentTypes,
|
documentTypes,
|
||||||
mainTypes: unique(packs.filter(pack => pack.documentType === filters.documentType).map(pack => pack.mainType)),
|
mainTypes: unique(packs.filter(pack => pack.documentType === filters.documentType).map(pack => pack.mainType)),
|
||||||
subtypes: unique(packs.filter(pack =>
|
subtypes: subtypeOptions,
|
||||||
pack.documentType === filters.documentType &&
|
|
||||||
(!filters.mainType || pack.mainType === filters.mainType)
|
|
||||||
).map(pack => pack.subtype)),
|
|
||||||
ruleGroups: ruleGroupOptions
|
ruleGroups: ruleGroupOptions
|
||||||
}
|
}
|
||||||
} satisfies LoaderData);
|
} satisfies LoaderData);
|
||||||
@@ -222,6 +226,10 @@ export default function RulesTestList() {
|
|||||||
|
|
||||||
const handleFilterChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
const handleFilterChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
|
if (name === 'subtype') {
|
||||||
|
updateParams({ subtype: value, documentAttributeType: undefined, ruleGroup: undefined });
|
||||||
|
return;
|
||||||
|
}
|
||||||
updateParams({ [name]: value });
|
updateParams({ [name]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -336,15 +344,6 @@ export default function RulesTestList() {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FilterSelect
|
|
||||||
label="所属规则组"
|
|
||||||
name="ruleGroup"
|
|
||||||
value={filters.ruleGroup}
|
|
||||||
options={options.ruleGroups.map(group => ({ value: group, label: group }))}
|
|
||||||
onChange={handleFilterChange}
|
|
||||||
className="mr-3 w-[22%]"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FilterSelect
|
<FilterSelect
|
||||||
label="子类型"
|
label="子类型"
|
||||||
name="subtype"
|
name="subtype"
|
||||||
@@ -354,6 +353,17 @@ export default function RulesTestList() {
|
|||||||
className="mr-3 w-[18%]"
|
className="mr-3 w-[18%]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FilterSelect
|
||||||
|
label="所属规则组"
|
||||||
|
name="ruleGroup"
|
||||||
|
value={filters.ruleGroup}
|
||||||
|
options={options.ruleGroups.map(group => ({ value: group, label: group }))}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
className="mr-3 w-[22%]"
|
||||||
|
disabled={options.ruleGroups.length === 0}
|
||||||
|
placeholder={filters.subtype || options.subtypes.length <= 1 ? '全部' : '请先选择子类型'}
|
||||||
|
/>
|
||||||
|
|
||||||
<SearchFilter
|
<SearchFilter
|
||||||
key={filters.keyword}
|
key={filters.keyword}
|
||||||
label="搜索"
|
label="搜索"
|
||||||
|
|||||||
@@ -9,6 +9,14 @@
|
|||||||
scroll-margin-top: 100px;
|
scroll-margin-top: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rules-test-page .tag.tag-sm {
|
||||||
|
min-height: 20px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.rules-test-page #fields,
|
.rules-test-page #fields,
|
||||||
.rules-test-page #sub-documents,
|
.rules-test-page #sub-documents,
|
||||||
.rules-test-page #visual-elements,
|
.rules-test-page #visual-elements,
|
||||||
@@ -844,6 +852,63 @@
|
|||||||
background: #fbfdfc;
|
background: #fbfdfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: stretch;
|
||||||
|
min-height: 32px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #00684a;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #00684a;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-button:hover {
|
||||||
|
border-color: #005a3f;
|
||||||
|
background: #005a3f;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-main,
|
||||||
|
.rules-test-page .dependency-variable-remove {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-main {
|
||||||
|
max-width: 260px;
|
||||||
|
padding: 7px 10px 7px 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-remove {
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.28);
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-remove:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-test-page .dependency-variable-main:focus-visible,
|
||||||
|
.rules-test-page .dependency-variable-remove:focus-visible {
|
||||||
|
outline: 2px solid rgba(0, 104, 74, 0.18);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
.rules-test-page .subrule-list {
|
.rules-test-page .subrule-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -851,7 +916,7 @@
|
|||||||
|
|
||||||
.rules-test-page .subrule-item {
|
.rules-test-page .subrule-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto minmax(0, 1fr);
|
grid-template-columns: 34px minmax(0, 1fr);
|
||||||
align-items: start;
|
align-items: start;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 9px 10px;
|
padding: 9px 10px;
|
||||||
@@ -860,6 +925,22 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rules-test-page .subrule-index {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
min-width: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #00684a;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 104, 74, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.rules-test-page .subrule-item div {
|
.rules-test-page .subrule-item div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -882,6 +963,12 @@
|
|||||||
line-height: 1.45;
|
line-height: 1.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rules-test-page .subrule-item > .subrule-index {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.rules-test-page .drawer-field-item {
|
.rules-test-page .drawer-field-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -54,12 +54,13 @@ export function getDefaultExpandedDependencyGroups(options: DependencyOption[],
|
|||||||
|
|
||||||
export function collectDependencyOptions(config: Pick<EditableRuleConfig, 'fields' | 'subDocuments' | 'visualElements'>): DependencyOption[] {
|
export function collectDependencyOptions(config: Pick<EditableRuleConfig, 'fields' | 'subDocuments' | 'visualElements'>): DependencyOption[] {
|
||||||
const topLevelFields = config.fields.flatMap(field => {
|
const topLevelFields = config.fields.flatMap(field => {
|
||||||
const source = field.group ? `字段抽取 / ${field.group}` : '字段抽取';
|
const source = '字段抽取';
|
||||||
|
const group = field.group || '未分组';
|
||||||
const options = [{
|
const options = [{
|
||||||
value: field.name,
|
value: field.name,
|
||||||
label: field.name,
|
label: field.name,
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
}];
|
}];
|
||||||
if (field.group === '派生字段') {
|
if (field.group === '派生字段') {
|
||||||
options.push({
|
options.push({
|
||||||
@@ -74,7 +75,7 @@ export function collectDependencyOptions(config: Pick<EditableRuleConfig, 'field
|
|||||||
value: field.name.replace('[*].', '.'),
|
value: field.name.replace('[*].', '.'),
|
||||||
label: field.name.replace('[*].', ' / '),
|
label: field.name.replace('[*].', ' / '),
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
@@ -105,31 +106,32 @@ export function collectDependencyOptions(config: Pick<EditableRuleConfig, 'field
|
|||||||
|
|
||||||
const visualElements = config.visualElements.flatMap(item => {
|
const visualElements = config.visualElements.flatMap(item => {
|
||||||
const label = item.name || item.id;
|
const label = item.name || item.id;
|
||||||
const source = `视觉要素 / ${item.type}`;
|
const source = '视觉要素';
|
||||||
|
const group = item.type || '未分组';
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label,
|
label,
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: item.name || item.id,
|
value: item.name || item.id,
|
||||||
label,
|
label,
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: `visual.${item.id}`,
|
value: `visual.${item.id}`,
|
||||||
label,
|
label,
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: `visual.${item.name || item.id}`,
|
value: `visual.${item.name || item.id}`,
|
||||||
label,
|
label,
|
||||||
source,
|
source,
|
||||||
group: source
|
group
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: item.type,
|
value: item.type,
|
||||||
|
|||||||
Reference in New Issue
Block a user