This commit is contained in:
iqudoo
2026-06-14 04:12:21 +08:00
parent 1e03b7750a
commit ccb5b119de
6 changed files with 181 additions and 114 deletions

View File

@@ -1,6 +1,10 @@
import React, { useMemo } from 'react';
import { ExportRangeConfig, LabelTemplate, PaperConfig, RecordRow } from '../types';
import { buildPrintablePages, extractTemplateVariables } from '../utils';
import { ExportRangeConfig, LabelTemplate, PaperConfig, RecordRow, VisibilityRule } from '../types';
import {
buildPrintablePages,
extractTemplateVariables,
getDataFilteredIndices,
} from '../utils';
import { PageInput } from './PreviewGrid';
import { FieldSelect } from './PsSelect';
import { VariableFilterRulesEditor } from './VariableFilterRulesEditor';
@@ -9,82 +13,104 @@ interface DataRangeFieldsProps {
variant?: 'ps' | 'mobile';
exportRange: ExportRangeConfig;
onChangeExportRange: (range: ExportRangeConfig) => void;
onChangeDataFilterRules: (rules: VisibilityRule[]) => void;
rows: RecordRow[];
paper: PaperConfig;
template: LabelTemplate;
disabled?: boolean;
/** 排版过滤在无数据时不可用;数据过滤始终可编辑 */
layoutDisabled?: boolean;
}
export const DataRangeFields: React.FC<DataRangeFieldsProps> = ({
variant = 'ps',
exportRange,
onChangeExportRange,
onChangeDataFilterRules,
rows,
paper,
template,
disabled = false,
layoutDisabled = false,
}) => {
const isMobile = variant === 'mobile';
const variableDefaults = template.variableDefaults ?? null;
const templateVariables = useMemo(() => extractTemplateVariables(template), [template]);
const dataFilteredCount = useMemo(
() => getDataFilteredIndices(rows, template, variableDefaults).length,
[rows, template, variableDefaults]
);
const rangeStats = buildPrintablePages(rows, paper, template, exportRange, variableDefaults);
const patchRange = (patch: Partial<ExportRangeConfig>) => {
onChangeExportRange({ ...exportRange, ...patch });
};
const sectionTitleClass = isMobile ? 'mobile-field-label mb-1 block' : 'ps-section-title';
const hintClass = isMobile ? 'text-[10px] text-slate-500' : 'text-[10px] text-[#888]';
return (
<div className={isMobile ? 'space-y-4' : 'space-y-2'}>
{!isMobile && <div className="ps-section-title"></div>}
<div>
{isMobile && <label className="mobile-field-label"></label>}
<FieldSelect
value={exportRange.mode}
disabled={disabled}
onChange={(e) => patchRange({ mode: e.target.value as ExportRangeConfig['mode'] })}
className={isMobile ? 'mobile-field' : 'ps-field text-[11px]'}
>
<option value="all"></option>
<option value="odd"></option>
<option value="even"></option>
<option value="custom"></option>
<option value="conditional"></option>
</FieldSelect>
</div>
{exportRange.mode === 'custom' && (
<div>
{isMobile ? (
<label className="mobile-field-label"></label>
) : (
<label className="ps-field-label"></label>
)}
<PageInput
type="text"
value={exportRange.custom ?? ''}
disabled={disabled}
onCommit={(val) => patchRange({ custom: val })}
placeholder="如 1,3,5-10"
inputClassName={isMobile ? 'mobile-field font-mono' : 'ps-field font-mono text-[11px]'}
<div className={isMobile ? 'space-y-4' : 'space-y-3'}>
<div className="space-y-2">
{rows.length > 0 && (
<div className="space-y-2">
<div className={sectionTitleClass}></div>
<p className={hintClass}></p>
<div>
{isMobile && <label className="mobile-field-label"></label>}
<FieldSelect
value={exportRange.mode}
disabled={layoutDisabled}
onChange={(e) => patchRange({ mode: e.target.value as ExportRangeConfig['mode'] })}
className={isMobile ? 'mobile-field' : 'ps-field text-[11px]'}
>
<option value="all"></option>
<option value="odd"></option>
<option value="even"></option>
<option value="custom"></option>
</FieldSelect>
</div>
{exportRange.mode === 'custom' && (
<div>
{isMobile ? (
<label className="mobile-field-label"></label>
) : (
<label className="ps-field-label"></label>
)}
<PageInput
type="text"
value={exportRange.custom ?? ''}
disabled={layoutDisabled}
onCommit={(val) => patchRange({ custom: val })}
placeholder="如 1,3,5-10"
inputClassName={isMobile ? 'mobile-field font-mono' : 'ps-field font-mono text-[11px]'}
/>
</div>
)}
<p className={hintClass}>
{rangeStats.selectedCount} / {rows.length}
</p>
</div>
)}
<div className="space-y-3">
<div className={sectionTitleClass}></div>
<p className={hintClass}></p>
<VariableFilterRulesEditor
rules={template.dataFilterRules ?? []}
templateVariables={templateVariables}
emptyHint="尚未添加过滤条件,将使用全部数据行"
description="按数据行与变量默认值判断;上传数据后可预览过滤结果。"
addTitle="添加数据过滤条件"
editTitle="编辑数据过滤条件"
onChangeRules={onChangeDataFilterRules}
/>
{rows.length > 0 && (
<p className={hintClass}>
{dataFilteredCount} / {rows.length}
</p>
)}
</div>
)}
{exportRange.mode === 'conditional' && (
<VariableFilterRulesEditor
rules={exportRange.filterRules ?? []}
templateVariables={templateVariables}
disabled={disabled}
emptyHint="尚未添加过滤条件,将使用全部数据行"
description="全部条件满足的数据行才纳入排版与导出;按数据行与变量默认值判断。"
addTitle="添加数据范围条件"
editTitle="编辑数据范围条件"
onChangeRules={(filterRules) => patchRange({ filterRules })}
/>
)}
{rows.length > 0 && (
<p className={isMobile ? 'text-[10px] text-slate-500' : 'text-[10px] text-[#888]'}>
{rangeStats.selectedCount} / {rows.length}
</p>
)}
</div>
</div>
);
};

View File

@@ -24,6 +24,7 @@ interface ExportSidebarProps {
rows: RecordRow[];
draftExportRange: ExportRangeConfig;
onChangeDraftExportRange: (range: ExportRangeConfig) => void;
onChangeDataFilterRules: (rules: VisibilityRule[]) => void;
cutLine: CutLineConfig;
onChangeCutLine: (cutLine: CutLineConfig) => void;
previewReady: boolean;
@@ -64,6 +65,7 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
rows,
draftExportRange,
onChangeDraftExportRange,
onChangeDataFilterRules,
cutLine,
onChangeCutLine,
previewReady,
@@ -102,10 +104,11 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
variant={compact ? 'ps' : 'ps'}
exportRange={draftExportRange}
onChangeExportRange={onChangeDraftExportRange}
onChangeDataFilterRules={onChangeDataFilterRules}
rows={rows}
paper={paper}
template={template}
disabled={rows.length === 0}
layoutDisabled={rows.length === 0}
/>
);
@@ -155,6 +158,7 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
templateVariables={templateVariables}
/>
</div>
{dataRangeSection}
{hasData && (
<>
<div>
@@ -167,7 +171,6 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
variableDefaults={template.variableDefaults}
/>
</div>
{dataRangeSection}
{applyDataSection}
</>
)}
@@ -194,6 +197,7 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
templateName={templateName}
templateVariables={templateVariables}
/>
{dataRangeSection}
{hasData && (
<>
<VariableMappingPanel
@@ -204,7 +208,6 @@ export const ExportSidebar: React.FC<ExportSidebarProps> = ({
onChangeMapping={onChangeMapping}
variableDefaults={template.variableDefaults}
/>
{dataRangeSection}
{applyDataSection}
</>
)}

View File

@@ -53,6 +53,7 @@ interface MobileExportViewProps {
rows: RecordRow[];
draftExportRange: ExportRangeConfig;
onChangeDraftExportRange: (range: ExportRangeConfig) => void;
onChangeDataFilterRules: (rules: VisibilityRule[]) => void;
cutLine: CutLineConfig;
onChangeCutLine: (cutLine: CutLineConfig) => void;
printablePages: (PrintableLabel | null)[][];
@@ -97,6 +98,7 @@ export const MobileExportView: React.FC<MobileExportViewProps> = ({
rows,
draftExportRange,
onChangeDraftExportRange,
onChangeDataFilterRules,
cutLine,
onChangeCutLine,
printablePages,
@@ -247,28 +249,27 @@ export const MobileExportView: React.FC<MobileExportViewProps> = ({
templateVariables={templateVariables}
/>
{hasData && (
<>
<div>
<p className="text-[11px] font-semibold text-[#e8e8e8] mb-2"></p>
<VariableMappingPanel
variant="ps"
variables={templateVariables}
columns={dataColumns}
mapping={variableColumnMapping}
onChangeMapping={onChangeMapping}
variableDefaults={template.variableDefaults}
/>
</div>
<DataRangeFields
<div>
<p className="text-[11px] font-semibold text-[#e8e8e8] mb-2"></p>
<VariableMappingPanel
variant="ps"
exportRange={draftExportRange}
onChangeExportRange={onChangeDraftExportRange}
rows={rows}
paper={paper}
template={template}
variables={templateVariables}
columns={dataColumns}
mapping={variableColumnMapping}
onChangeMapping={onChangeMapping}
variableDefaults={template.variableDefaults}
/>
</>
</div>
)}
<DataRangeFields
variant="ps"
exportRange={draftExportRange}
onChangeExportRange={onChangeDraftExportRange}
onChangeDataFilterRules={onChangeDataFilterRules}
rows={rows}
paper={paper}
template={template}
/>
{/* {exportBlockedReason && !pdfGenerating && (
<p className="text-[10px] text-amber-500/90 text-center">{exportBlockedReason}</p>
)} */}