195 lines
6.3 KiB
TypeScript
195 lines
6.3 KiB
TypeScript
import assert from 'node:assert/strict'
|
||
import test from 'node:test'
|
||
import {
|
||
loadCalendarPages,
|
||
manualCalendarDays,
|
||
manualCopyText,
|
||
validateManualDraft
|
||
} from '../src/views/pl/dispatch-calendar/manual-model'
|
||
|
||
const row = (id: string, date = '2026-10-26', vehicleId = 'v1') => ({
|
||
id,
|
||
dispatchDate: date,
|
||
vehicleId,
|
||
licensePlate: '皖H12345',
|
||
productId: 'p1',
|
||
productName: '液碱',
|
||
origin: '山峡',
|
||
destination: '云栋',
|
||
quantity: '',
|
||
calendarKind: 'acid' as const,
|
||
remark: '',
|
||
createTime: '',
|
||
updateTime: ''
|
||
})
|
||
test('同车同日同路线保留独立车次,车辆筛选与汇总一致', () => {
|
||
const days = manualCalendarDays(
|
||
[row('1'), row('2'), row('3', '2026-10-26', 'v2'), row('4', '2026-11-01')],
|
||
'2026-10',
|
||
'v1'
|
||
)
|
||
const day = days.find((d) => d.date === '2026-10-26')!
|
||
assert.equal(day.rows.length, 2)
|
||
assert.equal(day.vehicles.length, 1)
|
||
assert.equal(day.products[0].count, 2)
|
||
assert.ok(days.some((d) => d.date === '2026-10-01' && d.rows.length === 0))
|
||
})
|
||
test('完整分页加载,不悄悄展示截断数据', async () => {
|
||
const calls: number[] = []
|
||
const rows = await loadCalendarPages(async ({ page }) => {
|
||
calls.push(page)
|
||
return { list: [row(String(page))], total: 3 }
|
||
})
|
||
assert.deepEqual(calls, [1, 2, 3])
|
||
assert.equal(rows?.length, 3)
|
||
await assert.rejects(
|
||
loadCalendarPages(async () => ({ list: [], total: 1 })),
|
||
/不完整/
|
||
)
|
||
})
|
||
test('过期请求不继续加载,不返回旧月份数据', async () => {
|
||
let current = true
|
||
assert.equal(
|
||
await loadCalendarPages(
|
||
async () => {
|
||
current = false
|
||
return { list: [row('1')], total: 3 }
|
||
},
|
||
() => current
|
||
),
|
||
null
|
||
)
|
||
})
|
||
test('复制保留重复车次,文本不混入实际运输计划', () => {
|
||
const text = manualCopyText('2026-10-26', [row('1'), row('2')])
|
||
assert.match(text, /2026-10-26 派车计划/)
|
||
assert.equal(text.split('山峡 → 云栋').length - 1, 2)
|
||
})
|
||
test('同一车辆按创建时间正序,复制时排在一起且每条换行', () => {
|
||
const later = {
|
||
...row('b', '2026-10-28', 'qa'),
|
||
licensePlate: '皖QA1024',
|
||
productName: '测试',
|
||
origin: '2',
|
||
destination: '3',
|
||
remark: '23',
|
||
createTime: '2026-10-28 10:00:00'
|
||
}
|
||
const earlier = {
|
||
...row('a', '2026-10-28', 'qa'),
|
||
licensePlate: '皖QA1024',
|
||
productName: '测试',
|
||
origin: '1',
|
||
destination: '2',
|
||
remark: '12',
|
||
createTime: '2026-10-28 09:00:00'
|
||
}
|
||
const other = {
|
||
...row('c', '2026-10-28', 'other'),
|
||
licensePlate: '皖H00001',
|
||
createTime: '2026-10-28 09:30:00'
|
||
}
|
||
const day = manualCalendarDays([later, other, earlier], '2026-10').find(
|
||
(item) => item.date === '2026-10-28'
|
||
)!
|
||
assert.deepEqual(
|
||
day.vehicles.find((vehicle) => vehicle.id === 'qa')!.rows.map((item) => item.id),
|
||
['a', 'b']
|
||
)
|
||
const text = manualCopyText('2026-10-28', [later, other, earlier])
|
||
assert.deepEqual(text.split('\n'), [
|
||
'2026-10-28 派车计划',
|
||
' ·皖QA1024',
|
||
' 测试 1 → 2(12)',
|
||
' 测试 2 → 3(23)',
|
||
' ·皖H00001',
|
||
' 液碱 山峡 → 云栋'
|
||
])
|
||
assert.equal(text.startsWith('2026-10-28'), true)
|
||
})
|
||
test('表单允许起止地留空,仍拒绝无效日期和超长地址', () => {
|
||
for (const calendarKind of ['acid', 'alkali', 'other', 'site'] as const) {
|
||
for (const addresses of [
|
||
{ origin: '', destination: '云栋' },
|
||
{ origin: '山峡', destination: '' },
|
||
{ origin: ' ', destination: ' ' }
|
||
]) {
|
||
assert.equal(validateManualDraft({ ...row('1'), calendarKind, ...addresses }), '')
|
||
}
|
||
}
|
||
for (const field of ['origin', 'destination']) {
|
||
assert.equal(
|
||
validateManualDraft({ ...row('1'), [field]: '地'.repeat(256) }),
|
||
'始发地和目的地最多255个字符'
|
||
)
|
||
}
|
||
assert.equal(
|
||
validateManualDraft({ ...row('1'), dispatchDate: '2026-02-30' }),
|
||
'请选择有效的派车日期'
|
||
)
|
||
assert.equal(validateManualDraft(row('1')), '')
|
||
})
|
||
test('数量是可空文本,复制逐条展示,日汇总仍只计车次', () => {
|
||
const tons = { ...row('1'), quantity: '32.50' }
|
||
const blank = { ...row('2'), quantity: ' ' }
|
||
const day = manualCalendarDays([tons, blank], '2026-10').find(
|
||
(item) => item.date === '2026-10-26'
|
||
)!
|
||
assert.deepEqual(day.products[0], { id: 'p1', name: '液碱', count: 2 })
|
||
const text = manualCopyText('2026-10-26', [tons, blank])
|
||
assert.match(text, /液碱 32\.50吨 山峡 → 云栋/)
|
||
assert.equal(text.split('山峡 → 云栋').length - 1, 2)
|
||
assert.equal((text.match(/吨/g) || []).length, 1)
|
||
assert.equal(validateManualDraft({ ...row('1'), quantity: '' }), '')
|
||
assert.equal(validateManualDraft({ ...row('1'), quantity: ' 12.5 ' }), '')
|
||
assert.equal(
|
||
validateManualDraft({ ...row('1'), quantity: '12.345' }),
|
||
'数量须为数字,最多两位小数'
|
||
)
|
||
assert.equal(validateManualDraft({ ...row('1'), quantity: '1吨' }), '数量须为数字,最多两位小数')
|
||
})
|
||
|
||
test('现场复制隐藏起止地和备注,单据类型放在每条记录末尾的中文括号中', () => {
|
||
for (const documentType of ['入库计划', '出库计划', '出入库计划', '现场调拨']) {
|
||
const record = {
|
||
...row('site'),
|
||
calendarKind: 'site' as const,
|
||
documentType,
|
||
quantity: '32.50',
|
||
remark: '内部备注'
|
||
}
|
||
assert.equal(validateManualDraft(record), '')
|
||
assert.deepEqual(
|
||
manualCopyText('2026-10-26', [record, { ...record, id: 'site-2', quantity: '' }]).split('\n'),
|
||
[
|
||
'2026-10-26 派车计划',
|
||
' ·皖H12345',
|
||
` 液碱 32.50吨(${documentType})`,
|
||
` 液碱(${documentType})`
|
||
]
|
||
)
|
||
assert.equal(
|
||
manualCalendarDays([record], '2026-10').find((d) => d.date === record.dispatchDate)?.rows
|
||
.length,
|
||
1
|
||
)
|
||
}
|
||
for (const documentType of [null, undefined, '', ' ']) {
|
||
const record = {
|
||
...row('site'),
|
||
calendarKind: 'site' as const,
|
||
documentType,
|
||
remark: '内部备注'
|
||
}
|
||
assert.equal(manualCopyText('2026-10-26', [record]), '2026-10-26 派车计划\n ·皖H12345\n 液碱')
|
||
}
|
||
})
|
||
|
||
test('单据类型选填且最多50个字符', () => {
|
||
assert.equal(validateManualDraft({ ...row('site'), calendarKind: 'site', documentType: '' }), '')
|
||
assert.match(
|
||
validateManualDraft({ ...row('site'), calendarKind: 'site', documentType: '字'.repeat(51) }),
|
||
/单据类型.*50/
|
||
)
|
||
})
|