feat(board): 支持批量添加与批量删除任务

- 批量添加面板:一行一条,整批共用目标列、优先级、标签与截止日期
- 快速新增支持粘贴多行文本,直接拆成多条任务
- 多选模式支持全选可见任务、整批删除,撤销时连原顺序一起还原
- 批量创建与删除走 store 的批量方法,一次落盘、一次广播
- 补充单元测试与 E2E 用例
This commit is contained in:
chenshoulin
2026-10-19 17:21:42 +08:00
parent 4aba003a9e
commit 2d6e24dc91
21 changed files with 1518 additions and 37 deletions
+55
View File
@@ -202,3 +202,58 @@ describe('TaskCard 键盘操作', () => {
expect(event.defaultPrevented).toBe(true)
})
})
describe('TaskCard 多选态', () => {
function mountSelectable(selected = false) {
return mount(TaskCard, {
props: { task: makeTask({ id: 'task-9' }), selectable: true, selected },
})
}
it('平时不带 checkbox 语义,多选时才加上', () => {
expect(mountCard().find('article').attributes('role')).toBeUndefined()
expect(mountSelectable().find('article').attributes('role')).toBe('checkbox')
})
it('选中态通过 aria-checked 暴露给读屏', () => {
expect(mountSelectable(false).find('article').attributes('aria-checked')).toBe('false')
expect(mountSelectable(true).find('article').attributes('aria-checked')).toBe('true')
})
it('选中态有可辨识的高亮样式', () => {
const classes = mountSelectable(true).find('article').classes().join(' ')
expect(classes).toContain('bg-sky-50')
})
it('多选时点击是「选中」而不是「打开详情」', async () => {
const wrapper = mountSelectable()
await wrapper.find('article').trigger('click')
expect(wrapper.emitted('select')).toEqual([['task-9']])
expect(wrapper.emitted('open')).toBeUndefined()
})
it('多选时 Enter 与空格切换选中', async () => {
const wrapper = mountSelectable()
await wrapper.find('article').trigger('keydown', { key: 'Enter' })
await wrapper.find('article').trigger('keydown', { key: ' ' })
expect(wrapper.emitted('select')).toEqual([['task-9'], ['task-9']])
expect(wrapper.emitted('open')).toBeUndefined()
})
it('多选时方向键不再移动卡片', async () => {
const wrapper = mountSelectable()
await wrapper.find('article').trigger('keydown', { key: 'ArrowRight' })
expect(wrapper.emitted('keymove')).toBeUndefined()
})
it('多选时不显示「完成」勾选按钮,避免与选中勾选混淆', () => {
expect(mountCard().find('article button').exists()).toBe(true)
expect(mountSelectable().find('article button').exists()).toBe(false)
})
})