- 批量解析:智能区分字段分隔符(Tab优先,否则中英逗号/分号),避免竖线误伤;支持工作组被切到多个字段后自动合并 - 工作组匹配:新增多种分隔符与忽略空格匹配;当义工号字段非纯数字且命中工作组时自动识别为工作组(兼容3列格式:班级,姓名,工作组) - 列表展示:前端二次按班级locale排序(中文+数字感知)+ 姓名,与后端SQL排序互补
This commit is contained in:
@@ -112,6 +112,11 @@ export default function Users() {
|
|||||||
group_ids: x.group_ids ?? [],
|
group_ids: x.group_ids ?? [],
|
||||||
group_names: x.group_names ?? [],
|
group_names: x.group_names ?? [],
|
||||||
}));
|
}));
|
||||||
|
filled.sort((a, b) => {
|
||||||
|
const c = (a.class || '').localeCompare(b.class || '', 'zh-Hans-CN', { numeric: true });
|
||||||
|
if (c !== 0) return c;
|
||||||
|
return (a.name || '').localeCompare(b.name || '', 'zh-Hans-CN');
|
||||||
|
});
|
||||||
setList(filled);
|
setList(filled);
|
||||||
setGroups(g);
|
setGroups(g);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -188,36 +193,80 @@ export default function Users() {
|
|||||||
const rows: BatchParsedRow[] = [];
|
const rows: BatchParsedRow[] = [];
|
||||||
const lines = batchText.split(/\r?\n/);
|
const lines = batchText.split(/\r?\n/);
|
||||||
const groupMap = new Map<string, number>();
|
const groupMap = new Map<string, number>();
|
||||||
for (const g of groups) groupMap.set(g.name.trim(), g.id);
|
const groupNameNormalized = new Map<string, string>();
|
||||||
|
for (const g of groups) {
|
||||||
|
const key = g.name.trim();
|
||||||
|
groupMap.set(key, g.id);
|
||||||
|
groupNameNormalized.set(key, g.name);
|
||||||
|
groupMap.set(g.name, g.id);
|
||||||
|
groupNameNormalized.set(g.name, g.name);
|
||||||
|
const noSpace = key.replace(/\s+/g, '');
|
||||||
|
if (noSpace !== key) {
|
||||||
|
groupMap.set(noSpace, g.id);
|
||||||
|
groupNameNormalized.set(noSpace, g.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitGroupNames = (s: string): string[] => {
|
||||||
|
const result: string[] = [];
|
||||||
|
if (!s) return result;
|
||||||
|
const segs = s.split(/[\/、||,,;;\s\u3000]+/);
|
||||||
|
for (const seg of segs) {
|
||||||
|
const n = seg.trim().replace(/\s+/g, '');
|
||||||
|
if (n) result.push(n);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
if (!line.trim()) continue;
|
if (!line.trim()) continue;
|
||||||
const parts = line.split(/[,,\t|;;]/).map((p) => p.trim());
|
let parts: string[];
|
||||||
const [cls, name, volunteer_no = '', groupsStr = ''] = parts;
|
if (line.includes('\t')) {
|
||||||
|
parts = line.split(/\t/).map((p) => p.trim());
|
||||||
|
} else {
|
||||||
|
parts = line.split(/[,,;;]/).map((p) => p.trim());
|
||||||
|
}
|
||||||
|
const [cls = '', name = '', volunteer_no = '', ...rest] = parts;
|
||||||
|
const groupsStr = rest.join(',');
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
if (!cls) errors.push('缺少班级');
|
if (!cls) errors.push('缺少班级');
|
||||||
if (!name) errors.push('缺少姓名');
|
if (!name) errors.push('缺少姓名');
|
||||||
const groupIds: number[] = [];
|
const groupIds: number[] = [];
|
||||||
const groupNames: string[] = [];
|
const groupNames: string[] = [];
|
||||||
if (groupsStr) {
|
const rawNames = splitGroupNames([volunteer_no, groupsStr].filter(Boolean).join('/'));
|
||||||
const rawNames = groupsStr.split(/[/、|\s]+/).map((x) => x.trim()).filter(Boolean);
|
const volunteerLooksLikeNo = /^\d+$/.test(volunteer_no.trim());
|
||||||
for (const n of rawNames) {
|
const realRawNames: string[] = [];
|
||||||
|
if (volunteer_no.trim() && !volunteerLooksLikeNo) {
|
||||||
|
const probe = splitGroupNames(volunteer_no);
|
||||||
|
const allMatch = probe.length > 0 && probe.every((n) => groupMap.has(n));
|
||||||
|
if (allMatch) {
|
||||||
|
realRawNames.push(...probe);
|
||||||
|
} else {
|
||||||
|
realRawNames.push(...splitGroupNames(groupsStr));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
realRawNames.push(...splitGroupNames(groupsStr));
|
||||||
|
}
|
||||||
|
for (const n of realRawNames) {
|
||||||
|
if (!n) continue;
|
||||||
const gid = groupMap.get(n);
|
const gid = groupMap.get(n);
|
||||||
if (gid === undefined) {
|
if (gid === undefined) {
|
||||||
errors.push(`工作组「${n}」不存在`);
|
errors.push(`工作组「${n}」不存在`);
|
||||||
} else {
|
} else {
|
||||||
if (!groupIds.includes(gid)) {
|
if (!groupIds.includes(gid)) {
|
||||||
groupIds.push(gid);
|
groupIds.push(gid);
|
||||||
groupNames.push(n);
|
const display = groupNameNormalized.get(n) || n;
|
||||||
}
|
if (!groupNames.includes(display)) groupNames.push(display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const vol = volunteerLooksLikeNo ? volunteer_no : '';
|
||||||
rows.push({
|
rows.push({
|
||||||
idx: i,
|
idx: i,
|
||||||
class: cls || '',
|
class: cls || '',
|
||||||
name: name || '',
|
name: name || '',
|
||||||
volunteer_no,
|
volunteer_no: vol,
|
||||||
group_ids: groupIds,
|
group_ids: groupIds,
|
||||||
group_names: groupNames,
|
group_names: groupNames,
|
||||||
raw: line,
|
raw: line,
|
||||||
|
|||||||
Reference in New Issue
Block a user