67 lines
3.5 KiB
TypeScript
67 lines
3.5 KiB
TypeScript
const fields = ['student_no', 'name', 'topic'] as const;
|
||
type IdentityField = typeof fields[number];
|
||
const labels = { student_no: '学号', name: '姓名', topic: '选题' };
|
||
const categories: Record<string, string> = { stage1: '阶段报告1', stage2: '阶段报告2', stage3: '阶段报告3',
|
||
final_report: '最终报告', presentation: '汇报PPT/PDF' };
|
||
const text = (value: unknown) => String(value ?? '').trim();
|
||
const fileName = (file: any) => text(file.relative_path).split('/').pop() || '文件';
|
||
|
||
export function reviewImport(project: any) {
|
||
const selected = (project.files || []).filter((file: any) => file.status !== 'ignored');
|
||
const metadata = (project.files || []).some((file: any) => file.detected_identity != null);
|
||
const identity: Record<IdentityField, string> = { student_no: '', name: '', topic: '' };
|
||
const warnings: string[] = [];
|
||
for (const field of fields) {
|
||
const sources = new Map<string, string[]>();
|
||
for (const file of selected) {
|
||
const value = text(file.detected_identity?.[field]);
|
||
if (value) sources.set(value, [...(sources.get(value) || []), fileName(file)]);
|
||
}
|
||
const folderValue = text(project.folder_identity?.[field]);
|
||
const values = [...sources.keys()];
|
||
identity[field] = folderValue || (values.length === 1 ? values[0] : '');
|
||
if (values.length > 1) {
|
||
warnings.push(`${labels[field]}冲突:${values.map(value => `${value}(${sources.get(value)!.join('、')})`).join(';')}`);
|
||
} else if (folderValue && values.length === 1 && folderValue !== values[0]) {
|
||
warnings.push(`${labels[field]}与目录不一致:封面为${values[0]}(${sources.get(values[0])!.join('、')}),目录为${folderValue}`);
|
||
}
|
||
}
|
||
if (!metadata) warnings.push(...(project.warnings || []));
|
||
|
||
const errors: string[] = [];
|
||
if (!/^[0-9]{5,20}$/.test(text(project.student_no))) errors.push('学号必须为5–20位数字');
|
||
if (!text(project.name)) errors.push('请填写姓名');
|
||
if (!text(project.topic)) errors.push('请填写选题');
|
||
if (!selected.length) errors.push('至少选择1份资料');
|
||
const counts = new Map<string, number>();
|
||
for (const file of selected) {
|
||
if (!categories[file.category]) errors.push(`${fileName(file)}:请选择材料类型`);
|
||
else counts.set(file.category, (counts.get(file.category) || 0) + 1);
|
||
if (file.error) errors.push(`${fileName(file)}:无法读取,请修复文件或设为忽略`);
|
||
}
|
||
for (const [category, count] of counts) {
|
||
if (count > 1) errors.push(`${categories[category]}选了${count}份,请只保留1份,其余设为忽略`);
|
||
}
|
||
const missing = Object.keys(categories).filter(category => !selected.some((file: any) => file.category === category));
|
||
return { selected, metadata, identity, warnings, errors, missing };
|
||
}
|
||
|
||
export function fillImportIdentity(project: any, force = false) {
|
||
const review = reviewImport(project);
|
||
if (!review.metadata) return project;
|
||
for (const field of fields) {
|
||
if (force || !project.identity_edits?.[field]) project[field] = review.identity[field];
|
||
}
|
||
if (force) project.identity_edits = {};
|
||
return project;
|
||
}
|
||
|
||
export function importErrors(projects: any[] | undefined, versionId: number | undefined) {
|
||
const errors = versionId ? [] : ['请选择已发布课程版本'];
|
||
if (!projects?.length) return [...errors, '当前目录没有PDF、DOCX或PPTX资料,请重新扫描资料目录'];
|
||
for (const project of projects) {
|
||
errors.push(...reviewImport(project).errors.map(error => `${project.relative_dir}:${error}`));
|
||
}
|
||
return errors;
|
||
}
|