Files
label-designer/src/components/MobileExportView.tsx

220 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import {
ChevronLeft,
Printer,
Loader2,
Database,
Link2,
Download,
} from 'lucide-react';
import {
ImportData,
LabelTemplate,
RecordRow,
ExportRangeConfig,
PaperConfig,
CutLineConfig,
} from '../types';
import { isVariableMappingConfigured } from '../utils';
import { DataImporter, downloadDataTemplate } from './DataImporter';
import { VariableMappingPanel } from './VariableMappingPanel';
import { MobileExportPropsPanel } from './MobileExportPropsPanel';
import { DataRangeFields } from './DataRangeFields';
interface MobileExportViewProps {
template: LabelTemplate;
templateName: string;
templateVariables: string[];
importData: ImportData | null;
onDataLoaded: (data: ImportData) => void;
onClear: () => void;
dataColumns: string[];
variableColumnMapping: Record<string, string>;
onChangeMapping: (mapping: Record<string, string>) => void;
paper: PaperConfig;
onChangePaper: (paper: PaperConfig) => void;
rows: RecordRow[];
draftExportRange: ExportRangeConfig;
onChangeDraftExportRange: (range: ExportRangeConfig) => void;
cutLine: CutLineConfig;
onChangeCutLine: (cutLine: CutLineConfig) => void;
selectedCount: number;
totalPages: number;
pdfGenerating: boolean;
onBack: () => void;
onExportPdf: () => void;
}
export const MobileExportView: React.FC<MobileExportViewProps> = ({
template,
templateName,
templateVariables,
importData,
onDataLoaded,
onClear,
dataColumns,
variableColumnMapping,
onChangeMapping,
paper,
onChangePaper,
rows,
draftExportRange,
onChangeDraftExportRange,
cutLine,
onChangeCutLine,
selectedCount,
totalPages,
pdfGenerating,
onBack,
onExportPdf,
}) => {
const hasData = rows.length > 0;
const mappedCount = templateVariables.filter((v) =>
isVariableMappingConfigured(variableColumnMapping, v, dataColumns)
).length;
const needsMapping = templateVariables.length > 0;
const mappingComplete = !needsMapping || (hasData && mappedCount === templateVariables.length);
const exportBlockedReason = !hasData
? '请先上传 Excel 数据'
: !mappingComplete
? `还有 ${templateVariables.length - mappedCount} 个变量未关联`
: selectedCount === 0
? '当前数据范围内无标签'
: null;
return (
<div className="mobile-export-view">
<header className="no-print bg-slate-950 border-b border-slate-900 shrink-0 shadow mobile-export-header mobile-template-header px-4 py-3">
<div className="flex items-center gap-2 min-w-0 w-full">
<button
type="button"
onClick={onBack}
className="mobile-export-back"
aria-label="返回"
>
<ChevronLeft className="w-5 h-5" />
</button>
<div className="flex items-center gap-3 min-w-0 flex-1">
<div className="min-w-0">
{/* <h1 className="text-sm font-bold tracking-wider flex items-center gap-2 text-slate-100 uppercase">
<span className="truncate">批量标签排版印刷生成器</span>
</h1> */}
<p className="text-sm font-bold tracking-wider flex items-center gap-2 text-slate-100 uppercase" title={templateName}>
{templateName}
</p>
</div>
</div>
</div>
</header>
<main className="mobile-export-content">
<section className="mobile-export-card">
<div className="mobile-export-card-head">
<Database className="w-4 h-4 text-indigo-500 shrink-0" />
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2">
<div className="min-w-0">
<h2></h2>
<p>{hasData ? '已上传,可设置数据范围' : '上传 Excel 或 CSV 文件'}</p>
</div>
<button
type="button"
onClick={() => downloadDataTemplate(templateName, templateVariables)}
className="mobile-export-icon-btn"
aria-label="下载数据模板"
>
<Download className="w-5 h-5" />
</button>
</div>
</div>
</div>
<div className="mobile-export-card-body space-y-5">
<DataImporter
variant="mobile"
importData={importData}
onDataLoaded={onDataLoaded}
onClear={onClear}
templateName={templateName}
templateVariables={templateVariables}
/>
{hasData && (
<DataRangeFields
variant="mobile"
exportRange={draftExportRange}
onChangeExportRange={onChangeDraftExportRange}
rows={rows}
paper={paper}
template={template}
/>
)}
</div>
</section>
{hasData && (
<section className="mobile-export-card">
<div className="mobile-export-card-head">
<Link2 className="w-4 h-4 text-indigo-500 shrink-0" />
<div className="flex-1 min-w-0">
<h2></h2>
<p>
{needsMapping
? `已关联 ${mappedCount}/${templateVariables.length}`
: '此模板无需变量映射'}
</p>
</div>
</div>
<div className="mobile-export-card-body">
<VariableMappingPanel
variant="mobile"
variables={templateVariables}
columns={dataColumns}
mapping={variableColumnMapping}
onChangeMapping={onChangeMapping}
variableDefaults={template.variableDefaults}
/>
</div>
</section>
)}
<MobileExportPropsPanel
template={template}
paper={paper}
onChangePaper={onChangePaper}
rows={rows}
exportRange={draftExportRange}
cutLine={cutLine}
onChangeCutLine={onChangeCutLine}
locked={!hasData}
/>
</main>
<footer className="mobile-export-footer">
{exportBlockedReason && !pdfGenerating && (
<p className="mobile-export-footer-hint">{exportBlockedReason}</p>
)}
<button
type="button"
onClick={onExportPdf}
disabled={!!exportBlockedReason || pdfGenerating}
className="mobile-export-submit"
>
{pdfGenerating ? (
<>
<Loader2 className="w-5 h-5 animate-spin shrink-0" />
PDF
</>
) : (
<>
<Printer className="w-5 h-5 shrink-0" />
{exportBlockedReason
? '生成标签PDF文件'
: `生成标签PDF文件${selectedCount} 枚 · ${totalPages} 页)`}
</>
)}
</button>
</footer>
</div>
);
};