feat(admin): 报名导入新增「从已有名单勾选报名」
原来只能上传 CSV/XLSX。现在同一个 tab 上方多一个选择器:选小项 → 组别 → 单位, 从该单位已有运动员里勾人,一键加入报名。 - 个人项目:勾 N 人 → N 条报名(一人一行) - 团体/对抗项目:勾 N 人 → 1 条报名,姓名拼进 member_names(再次提交即更新队员名单) - 已报名的人/单位会标「已报名」并禁止重复勾选 - 复用服务端唯一报名入口 POST /entries/import:勾选结果转成同格式 CSV 提交, 字段校验、按 (组别,单位,运动员) upsert、行级报错回显与表格导入完全一致 - 纯函数 entrySelection.ts(表头唯一来源 + RFC 4180 转义)+ 4 个单测; 表头定义从 ImportSection/mockBaseData 收敛到一处 - 号码布/种子仍走表格导入,界面与文档都写明 自检:vue-tsc + pnpm build 通过,前端 node --test 30/30。
This commit is contained in:
1 parent
7531ea29e1
commit
c9d5b8ea89
6 files changed
+499
-77
No files matched your search
@@ -0,0 +1,53 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { REQUIRED_COLUMNS, buildSelectionRows, toCsv } from '../components/baseData/entrySelection'
|
||||
|
||||
test('toCsv 按 RFC 4180 转义逗号 / 引号 / 换行', () => {
|
||||
assert.equal(toCsv([['a', 'b']]), 'a,b')
|
||||
assert.equal(toCsv([['张,伟', 'x']]), '"张,伟",x')
|
||||
assert.equal(toCsv([['张"伟', 'x']]), '"张""伟",x')
|
||||
assert.equal(toCsv([['第一行\n第二行', 'x']]), '"第一行\n第二行",x')
|
||||
// 同一格同时含逗号与引号:包裹一次,内部引号翻倍。
|
||||
assert.equal(toCsv([['a,"b"', 'c']]), '"a,""b""",c')
|
||||
})
|
||||
|
||||
test('buildSelectionRows 个人项目:一人一行,共 8 列', () => {
|
||||
const rows = buildSelectionRows({
|
||||
eventCode: 'M100',
|
||||
categoryName: '男子组',
|
||||
teamCode: 'F1',
|
||||
mode: 'individual',
|
||||
athletes: [{ name: '张伟' }, { name: '李强' }],
|
||||
})
|
||||
assert.equal(rows.length, 3, '表头 + 2 名运动员')
|
||||
assert.deepEqual(rows[0], [...REQUIRED_COLUMNS], '第一行复用导入表头')
|
||||
assert.equal(rows[1]?.length, 8)
|
||||
assert.deepEqual(rows[1], ['M100', '男子组', 'F1', '张伟', '', '', 'registered', ''])
|
||||
assert.deepEqual(rows[2], ['M100', '男子组', 'F1', '李强', '', '', 'registered', ''])
|
||||
})
|
||||
|
||||
test('buildSelectionRows 团体项目:整体一条,member_names 按列表顺序拼接', () => {
|
||||
const rows = buildSelectionRows({
|
||||
eventCode: 'BB',
|
||||
categoryName: '混合组',
|
||||
teamCode: 'F2',
|
||||
mode: 'team',
|
||||
athletes: [{ name: '王芳' }, { name: '刘洋' }, { name: '陈晨' }],
|
||||
})
|
||||
assert.equal(rows.length, 2, '表头 + 1 条团体报名')
|
||||
assert.equal(rows[1]?.length, 8)
|
||||
assert.deepEqual(rows[1], ['BB', '混合组', 'F2', '', '', '', 'registered', '王芳/刘洋/陈晨'])
|
||||
})
|
||||
|
||||
test('buildSelectionRows 团体项目可用 memberNames 覆盖名单与顺序', () => {
|
||||
const rows = buildSelectionRows({
|
||||
eventCode: 'BB',
|
||||
categoryName: '混合组',
|
||||
teamCode: 'F2',
|
||||
status: 'scratched',
|
||||
mode: 'team',
|
||||
athletes: [{ name: '王芳' }],
|
||||
memberNames: ['刘洋', '王芳'],
|
||||
})
|
||||
assert.deepEqual(rows[1], ['BB', '混合组', 'F2', '', '', '', 'scratched', '刘洋/王芳'])
|
||||
})
|
||||
Reference in new issue
Block a user