发布:将已验 TEST 功能更新至既有生产灰度(2026-10-23) #21
@@ -35,7 +35,7 @@
|
||||
|
||||
## 履约硬边界
|
||||
|
||||
- 排车详情固定四个彩色图标页签:车辆信息、排车信息、单据信息、关联订单明细。五个作业阶段及审核分区、逐动作Gate由服务端返回;取消独立于阶段。装车完成须真实PDA拣货核平,装车完成进入装车后,五张装车照片全部保存后自动生成派送单并进入派送中,无独立安排派送或发车确认;Admin和PDA共用正式版本化命令。已有单写入提交recordVersion(0合法)、原幂等键和完整载荷;未知结果保留原请求核对,成功后另读当前事实。
|
||||
- 排车详情固定四个彩色图标页签:车辆信息、排车信息、单据信息、关联订单明细。五个作业阶段及审核分区、逐动作Gate由服务端返回;取消独立于阶段。装车完成须真实PDA拣货核平,装车中可同步上传五张装车照片,确认装车完成且资料齐全自动生成派送单并进入派送中,资料未齐时留在装车后,无独立安排派送或发车确认;Admin和PDA共用正式版本化命令。已有单写入提交recordVersion(0合法)、原幂等键和完整载荷;未知结果保留原请求核对,成功后另读当前事实。
|
||||
- 智能排车来源支持 `RELABEL`(待出库换标单),按服务端候选的`selection.relabelNo/sourceVersion`提交,不用展示订单号或原入仓号替代换标身份;沿用真实库存、托盘与箱唛,不生成新入库单。客户和仓库归属由服务端核验,不在前端拼接客户子排车。
|
||||
|
||||
- 托盘标签从 `pages/PalletsView.vue` → `api/pallets.ts` 的 `getPalletLabelPdf` 下载服务端 PDF,传该行仓库与托盘 ID,复用列表权限;不在前端生成标签或维护模板、尺寸、编码。模板/接口变化才查 [Print 指引](../../../../../baiyue-firstleg/print/AGENTS.md)的托盘入口及[标签维护](../../../../../docs/打印标签维护/README.md),纯按钮文案不追加打印资料。
|
||||
@@ -54,7 +54,7 @@
|
||||
| 货物计划与装车命令 | [对应条款](../../../../../docs/业务实现边界.md#admin-货物计划与装车命令) |
|
||||
| 取消与费用展示 | [对应条款](../../../../../docs/业务实现边界.md#admin-取消与费用展示) |
|
||||
| 业务导出 | [对应条款](../../../../../docs/业务实现边界.md#admin-业务导出) |
|
||||
| 装车后资料入口 | [对应条款](../../../../../docs/业务实现边界.md#admin-装车后资料入口) |
|
||||
| 装车照片与资料入口 | [对应条款](../../../../../docs/业务实现边界.md#admin-装车照片与资料入口) |
|
||||
|
||||
## 验证
|
||||
|
||||
|
||||
@@ -428,4 +428,4 @@
|
||||
|
||||
| 页面 | 操作 | 原型/确认依据 | 承载与实现 | 验收 |
|
||||
|---|---|---|---|---|
|
||||
| 排车列表:派送中、配送完成 | 管理资料 | 用户管理资料截图及同车/派送单资料共用确认;签收完成即可,其他可补 | 1100px居中可全屏弹框,签收文件/油耗图片/装车资料三个页签;按关联派送单切换,共用原派送资料编辑器;油耗沿本车附加照片保存 | 自动回归单独报告;1920×1080、100%待人工截图确认 |
|
||||
| 排车列表:装车中、装车后、派送中、配送完成 | 管理资料 | 用户管理资料截图及同车/派送单资料共用确认;签收完成即可,其他可补 | 1100px居中可全屏弹框,签收文件/油耗图片/装车资料三个页签;装车中及装车后默认打开装车资料,可同步上传,确认装车且资料齐全后自动派送;按关联派送单切换,共用原派送资料编辑器;油耗沿本车附加照片保存 | 自动回归单独报告;1920×1080、100%待人工截图确认 |
|
||||
|
||||
@@ -61,3 +61,13 @@ it('shows existing photos without exposing upload for a read-only account', asyn
|
||||
expect(wrapper.find('input[type="file"]').exists()).toBe(false)
|
||||
expect(api.uploadLoadingPhoto).not.toHaveBeenCalled(); wrapper.unmount()
|
||||
})
|
||||
|
||||
it('allows saving photos while loading and keeps the loading stage', async () => {
|
||||
vi.mocked(api.getLoadingDocuments).mockResolvedValue(data({ stage: 'LOADING' }))
|
||||
vi.mocked(api.bindLoadingPhoto).mockResolvedValue(data({ stage: 'LOADING', recordVersion: '1', photos: [photo] }))
|
||||
const wrapper = setup(); await flushPromises()
|
||||
expect(wrapper.text()).toContain('可边装车边上传')
|
||||
await upload(wrapper)
|
||||
expect(wrapper.emitted('saved')).toHaveLength(1)
|
||||
expect(wrapper.text()).toContain('可边装车边上传'); wrapper.unmount()
|
||||
})
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ApiResponseError } from '@/shared/api/http'
|
||||
import { useDispatchCommandRecovery } from './dispatchCommandRecovery'
|
||||
import { command } from './dispatchTestFixtures'
|
||||
beforeEach(() => sessionStorage.clear())
|
||||
it('rejects a loading result that skips the loading documents stage', async () => {
|
||||
it('rejects a loading result that has not completed loading', async () => {
|
||||
const owner = crypto.randomUUID(), recovery = useDispatchCommandRecovery(() => owner)
|
||||
recovery.begin('PC/1', 'LOADING_COMPLETE', { recordVersion: 0, idempotencyKey: 'key' })
|
||||
const read = vi.fn()
|
||||
await expect(recovery.execute(vi.fn().mockResolvedValue({ ...command('LOADING_COMPLETE'), stage: 'DELIVERING' }), read, vi.fn())).rejects.toThrow('装车后')
|
||||
await expect(recovery.execute(vi.fn().mockResolvedValue({ ...command('LOADING_COMPLETE'), stage: 'LOADING' }), read, vi.fn())).rejects.toThrow('装车完成')
|
||||
expect(read).not.toHaveBeenCalled()
|
||||
expect(recovery.pending.value?.result).toBeNull()
|
||||
expect(recovery.pending.value?.body.idempotencyKey).toBe('key')
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
.admin-density .el-empty__description { margin-top: 0; }
|
||||
.admin-density .el-empty__description p { font-size: var(--admin-font-body, 13px); }
|
||||
.admin-density-dialog .el-form-item__label { line-height: 22px; }
|
||||
.admin-density-dialog .el-form--inline .el-form-item__label { align-items: center; }
|
||||
.admin-density-dialog .el-form-item:not(.el-form-item--label-top) .el-form-item__label { align-items: center; }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer) .el-table th.el-table__cell {
|
||||
background: var(--cc-list-table-header-bg);
|
||||
color: var(--cc-list-table-header-text);
|
||||
|
||||
@@ -1116,7 +1116,17 @@ describe('approved Admin density ownership', () => {
|
||||
cell.innerHTML = '<table class="el-table__body"><tbody><tr><td class="el-table__cell"></td></tr></tbody></table>'
|
||||
expect(cell.querySelector('td')!.matches(stripeSelector)).toBe(false)
|
||||
expect(rules.get('.admin-density-dialog .el-descriptions__label.el-descriptions__cell')?.background).toBe('var(--admin-label-bg)')
|
||||
expect(rules.get('.admin-density-dialog .el-form--inline .el-form-item__label')?.['align-items']).toBe('center')
|
||||
const horizontalLabelSelector = '.admin-density-dialog .el-form-item:not(.el-form-item--label-top) .el-form-item__label'
|
||||
expect(rules.get(horizontalLabelSelector)?.['align-items']).toBe('center')
|
||||
const labelFixture = document.createElement('div')
|
||||
labelFixture.innerHTML = '<div class="admin-density-dialog"><form class="el-form"><div class="el-form-item el-form-item--label-left"><label class="el-form-item__label">计划送仓日期</label></div></form></div>'
|
||||
const formItem = labelFixture.querySelector('.el-form-item')!
|
||||
const label = labelFixture.querySelector('label')!
|
||||
expect(label.matches(horizontalLabelSelector)).toBe(true)
|
||||
formItem.className = 'el-form-item el-form-item--label-right'
|
||||
expect(label.matches(horizontalLabelSelector)).toBe(true)
|
||||
formItem.className = 'el-form-item el-form-item--label-top'
|
||||
expect(label.matches(horizontalLabelSelector)).toBe(false)
|
||||
expect(rules.get('.admin-density-dialog.el-dialog.admin-density-workspace > .el-dialog__body')?.flex).toBe('1 1 auto')
|
||||
for (const tone of ['warning', 'purple', 'primary', 'success', 'danger']) {
|
||||
expect(rules.get(`:is(.admin-density, .admin-density-dialog) .density-action[data-tone="${tone}"]`)?.['--action-hover-bg']).toBe(`var(--admin-action-${tone}-hover)`)
|
||||
|
||||
Reference in New Issue
Block a user