1
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
| 页面/组件 | 操作与承载 | 本次调整 | 验收 |
|
||||
|---|---|---|---|
|
||||
| AppointmentCalendarView | 调整计划到仓,原520px居中弹框及全屏能力 | 1处日期时间选择器;分钟精度沿用原请求格式;标签宽度150px,说明图标与标签并排,控件沿用公共满宽样式 | 自动检查通过;待人工1920×1080、100%截图确认 |
|
||||
| AppointmentCalendarView | 调整计划到仓,原520px居中弹框及全屏能力 | 1处日期时间选择器;分钟精度沿用原请求格式;标签宽度150px,说明图标与标签并排,控件沿用公共满宽样式 | 真实浏览器检查通过:日期面板、选值、空值禁用、全屏保值、未保存保护及放弃返回;未提交业务保存 |
|
||||
| WorkReceivingPanel | 原业务深链中的客户计划、后送与跟进,原表单 | 5处日期/日期时间输入;日期与秒精度的当地时间分别保存;调整开始/结束时段后仍清除原时差依据 | 自动检查通过;待人工截图确认 |
|
||||
| WorkCommandForm | 跟进、收货要求,原表单 | 3处日期、2处时间输入;跨日结束日期显式保留;不推断次日、不转换地点时区 | 自动检查通过;待人工截图确认 |
|
||||
| AppointmentBatchForm | 原分批/合并表单 | 2处可空目标日期,保留跨批分配、版本、幂等与原写权限 | 自动检查通过;待人工截图确认 |
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
自动验证:受影响7个测试文件88项通过;类型检查与生产构建通过。全量回归执行464个文件4203项,4201项通过,两项旧公共样式断言(标签宽度、追加样式类)更新后,该样式文件65项全部复验通过。后台业务模板原生日期/时间控件残留检查通过。本轮未部署TEST,未进行真实HTTP或人工页面截图验收。
|
||||
|
||||
补充浏览器验收:用户明确要求打开页面后,使用已发布TEST页面,页面报告1920×1080、DPR1、visual scale1。TSTU5446539的默认计划、Element日期面板、快捷选值、全屏保值、清空禁用、未保存保护与放弃返回通过;原计划仍2026-10-01,未点击保存。MSCU7264017现已有入库计划,按冻结时间显示且无调整入口。本轮仅覆盖日历与调整到仓弹框,其余三表单仍待逐页操作;输入09:30的最终确认未单独完成,不计通过。证据见 `.logs/appointment-element-release-20261020/ui/result.json` 及同目录截图。
|
||||
|
||||
## 2026-10-20 蓝白工作台续批(写权已协调)
|
||||
|
||||
本次续批修改文件(相对本模块):
|
||||
|
||||
@@ -891,6 +891,41 @@ describe('GlobalSmartLoadingView', () => {
|
||||
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
|
||||
})
|
||||
|
||||
it('keeps assignment failures and diagnostic IDs inside the open dialog without losing the quantity', async () => {
|
||||
vi.spyOn(ElMessage, 'error').mockImplementation(() => undefined as never)
|
||||
vi.mocked(assignSmartLoadingItem).mockRejectedValueOnce(new AxiosError(
|
||||
'forbidden', 'ERR_BAD_REQUEST', undefined, undefined,
|
||||
{ data: { code: 'A0403', message: 'Access denied', requestId: 'assign-request', traceId: 'assign-trace' }, status: 403, statusText: 'Forbidden', headers: {}, config: {} as never },
|
||||
))
|
||||
const wrapper = mount(GlobalSmartLoadingView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
await wrapper.get('.candidate-pane .el-table__body-wrapper').findAll('button').find(button => button.text() === '加入')!.trigger('click')
|
||||
await flushPromises()
|
||||
const popup = wrapper.getComponent(AppDialog)
|
||||
const input = popup.getComponent(ElInputNumber)
|
||||
input.vm.$emit('update:modelValue', 7)
|
||||
await flushPromises()
|
||||
|
||||
// 箱数与上方资料使用同一标签列,保留输入名称与原范围。
|
||||
const label = popup.get('label[for="global-loading-assignment-count"]')
|
||||
expect(label.element.parentElement?.tagName).toBe('TH')
|
||||
expect(label.element.parentElement?.nextElementSibling?.getAttribute('colspan')).toBe('3')
|
||||
expect(popup.find('input#global-loading-assignment-count').exists()).toBe(true)
|
||||
expect(input.props('max')).toBe(candidate.availablePackageCount)
|
||||
popup.vm.$emit('confirm')
|
||||
await flushPromises()
|
||||
|
||||
expect(assignSmartLoadingItem).toHaveBeenCalledWith(plan.id, { inboundId: candidate.inboundId, packageCount: 7, expectedVersion: plan.version })
|
||||
expect(popup.props('modelValue')).toBe(true)
|
||||
expect(input.props('modelValue')).toBe(7)
|
||||
expect(popup.text()).toContain('加入当前柜次失败')
|
||||
expect(popup.text()).toContain('当前账号无权访问该数据')
|
||||
expect(popup.text()).toContain('assign-request')
|
||||
expect(popup.text()).toContain('assign-trace')
|
||||
expect(wrapper.get('.global-loading-page').findAll('.load-error-state')).toHaveLength(1)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('refreshes the latest plan after A0409 without losing the selected container', async () => {
|
||||
const warningMessage = vi.spyOn(ElMessage, 'warning').mockImplementation(() => undefined as never)
|
||||
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
|
||||
|
||||
@@ -494,6 +494,7 @@ function openAssign(source: object) {
|
||||
const candidate = candidateRow(source)
|
||||
if (candidateBlocked(candidate)) return
|
||||
selectedCandidate.value = candidate
|
||||
mutationError.value = null
|
||||
assignForm.packageCount = candidate.coLoadGroupId
|
||||
? Number(candidate.groupTotalBoxes || candidate.availablePackageCount)
|
||||
: candidate.availablePackageCount
|
||||
@@ -716,7 +717,7 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<template #details><ApiErrorMeta :details="loadError" /></template>
|
||||
</LoadErrorState>
|
||||
<template v-else>
|
||||
<LoadErrorState v-if="mutationError" title="装柜操作失败" :message="mutationError.message" compact retry-text="刷新工作台" @retry="loadWorkbenchData()">
|
||||
<LoadErrorState v-if="mutationError && !assignOpen" title="装柜操作失败" :message="mutationError.message" compact retry-text="刷新工作台" @retry="loadWorkbenchData()">
|
||||
<template #details><ApiErrorMeta :details="mutationError" /></template>
|
||||
</LoadErrorState>
|
||||
<section class="dispatch-grid" aria-label="智能装柜调度台">
|
||||
@@ -839,6 +840,9 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
</section>
|
||||
|
||||
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="assignOpen" :title="assignmentTitle" width="min(920px, calc(100vw - 28px))" fullscreenable :confirm-loading="mutating" :confirm-disabled="Boolean(selectedCandidate?.blockReason || selectedCandidate?.locked)" :confirm-text="selectedCandidate?.unitType === 'SEALED_PALLET' ? '确认整托分配' : selectedCandidate?.coLoadGroupId ? '确认整组分配' : '确认加入'" @confirm="assign">
|
||||
<LoadErrorState v-if="mutationError" title="加入当前柜次失败" :message="mutationError.message" compact :show-retry="false">
|
||||
<template #details><ApiErrorMeta :details="mutationError" /></template>
|
||||
</LoadErrorState>
|
||||
<div v-if="selectedCandidate" class="density-dialog-sections">
|
||||
<section class="density-dialog-section" data-tone="source">
|
||||
<div class="workbench-evidence"><table class="density-table assignment-context-table" aria-label="全局装柜分配来源"><tbody>
|
||||
@@ -856,7 +860,7 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<template v-else>
|
||||
<tr><th>入仓码</th><td>{{ selectedCandidate.inboundCode || '—' }}</td><th>可用箱数</th><td>{{ selectedCandidate.availablePackageCount }} 箱</td></tr>
|
||||
<tr><th>平均单箱毛重</th><td>{{ formatMeasurement(selectedCandidate.unitGrossWeightKg, 'kg', 1) }}</td><th>平均单箱体积</th><td>{{ formatMeasurement(selectedCandidate.averageUnitVolumeCbm, 'm³') }}</td></tr>
|
||||
<tr><td colspan="4"><el-form-item label="本次分配箱数" label-position="left" label-width="128px" required><el-input-number v-model="assignForm.packageCount" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></td></tr>
|
||||
<tr><th><label for="global-loading-assignment-count"><span class="assignment-required" aria-hidden="true">*</span>本次分配箱数</label></th><td colspan="3"><el-form-item><el-input-number id="global-loading-assignment-count" v-model="assignForm.packageCount" aria-label="本次分配箱数" aria-required="true" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></td></tr>
|
||||
</template>
|
||||
</tbody></table></div>
|
||||
</el-form>
|
||||
@@ -880,6 +884,7 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<style scoped>
|
||||
.assignment-context-table { table-layout: fixed; }
|
||||
.assignment-context-table th { width: 120px; }
|
||||
.assignment-required { margin-right: var(--admin-space-1); color: var(--el-color-danger); }
|
||||
.global-loading-page { display: flex; min-width: 0; min-height: 0; flex-direction: column; gap: 12px; }
|
||||
.embedded-smart-loading { display: flex; width: 100%; min-width: 0; min-height: 0; flex: 1; overflow: hidden; }
|
||||
.embedded-smart-loading .global-loading-page { width: 100%; min-height: 0; flex: 1; overflow: hidden; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 尾程订单汇总文字隐藏(2026-10-20)
|
||||
|
||||
按用户截图`codex-clipboard-168f9aa5-5478-4085-a9b4-d59e7b08ca1f.png`及明确要求,移除查询区下方“仓储转运·全筛选”和“当前筛选中的退货”两段长汇总,不再挂载仅提供转运汇总的组件。订单明细、退货行维度、筛选、分页和错误反馈保持;不删交易数据或改变统计口径。原列表顶部矩阵中的“数量汇总”不再指这两段常驻文字。待人工截图确认。
|
||||
按用户截图`codex-clipboard-168f9aa5-5478-4085-a9b4-d59e7b08ca1f.png`及明确要求,移除查询区下方“仓储转运·全筛选”和“当前筛选中的退货”两段长汇总,不再挂载仅提供转运汇总的组件。订单明细、退货行维度、筛选、分页和错误反馈保持;不删交易数据或改变统计口径。原列表顶部矩阵中的“数量汇总”不再指这两段常驻文字。本页行为回归75项通过,差异检查通过;待人工截图确认。
|
||||
|
||||
## 2026-10-20 列表顶部紧凑收尾
|
||||
|
||||
|
||||
@@ -173,6 +173,43 @@ describe('UnifiedOrdersView list request coordination', () => {
|
||||
expect(wrapper.findAll('[role="tab"]')[3]!.text()).toBe('测量数据审核(3)')
|
||||
})
|
||||
|
||||
it('keeps every count and the toolbar state stable when switching between ordinary and review tabs', async () => {
|
||||
vi.mocked(getPlatformUnifiedOrderStatusCounts).mockResolvedValueOnce({ counts: { PENDING_REVIEW: 12, WAIT_MEASURE: 0, MEASUREMENT_REVIEW: 4, SIGNED: 3 } })
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
const labels = wrapper.findAll('[role="tab"]').map(tab => tab.text())
|
||||
vi.mocked(getPlatformUnifiedOrderStatusCounts).mockImplementation(() => new Promise(() => {}))
|
||||
for (const label of ['待审核(12)', '测量数据审核(4)', '待测量数据(0)', '全部']) {
|
||||
await wrapper.findAll('[role="tab"]').find(tab => tab.text() === label)!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.findAll('[role="tab"]').map(tab => tab.text())).toEqual(labels)
|
||||
expect(wrapper.get('[aria-selected="true"]').text()).toBe(label)
|
||||
expect(wrapper.get('[role="tablist"]').attributes('aria-busy')).toBe('false')
|
||||
expect(wrapper.find('.order-status-feedback').exists()).toBe(false)
|
||||
expect(getPlatformUnifiedOrderStatusCounts).toHaveBeenCalledTimes(1)
|
||||
}
|
||||
})
|
||||
|
||||
it.each(['success', 'failure'] as const)('ignores an older tab request %s after the current tab has loaded', async (outcome) => {
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
let resolveOlder!: (value: Awaited<ReturnType<typeof listPlatformUnifiedOrders>>) => void
|
||||
let rejectOlder!: (reason: Error) => void
|
||||
vi.mocked(listPlatformUnifiedOrders).mockImplementationOnce(() => new Promise((resolve, reject) => { resolveOlder = resolve; rejectOlder = reject }))
|
||||
await wrapper.findAll('[role="tab"]').find(tab => tab.text() === '待审核')!.trigger('click')
|
||||
const latest = { ...emptyPage(), records: [{ orderId: 'new-order', tenantId: '8', orderVersion: 1, inboundCode: 'IN-NEW', internalOrderStatus: 'SUBMITTED', mainStatus: 'WAIT_MEASURE', expectedArrivalAt: null, estimatedVolumeCbm: null }], total: 1, pages: 1 } as Awaited<ReturnType<typeof listPlatformUnifiedOrders>>
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValueOnce(latest)
|
||||
await wrapper.findAll('[role="tab"]').find(tab => tab.text() === '待测量数据')!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'DataTable' }).props('data')).toEqual(latest.records)
|
||||
if (outcome === 'success') resolveOlder(emptyPage())
|
||||
else rejectOlder(new Error('旧页签请求失败'))
|
||||
await flushPromises()
|
||||
expect(wrapper.get('[aria-selected="true"]').text()).toBe('待测量数据')
|
||||
expect(wrapper.getComponent({ name: 'DataTable' }).props('data')).toEqual(latest.records)
|
||||
expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('loads once initially and once for query, reset, status tab and pagination', async () => {
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
|
||||
@@ -418,6 +418,14 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Page/detail wrappers use their parent's inset; independent cards keep theirs. */
|
||||
@media screen {
|
||||
.admin-workbench.workbench-panel.is-page-surface:not(.admin-page),
|
||||
.admin-workbench .workbench-panel.is-page-surface:not(.admin-page) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main lists share one compact header. Nested tables and dialog density stay local. */
|
||||
@media (min-width: 721px) {
|
||||
.admin-workbench .workbench-table.is-page-surface > .data-table > .data-table__toolbar,
|
||||
|
||||
@@ -201,6 +201,36 @@ describe('Admin workbench opt-in styles', () => {
|
||||
expect(declarationsFor(get('#card')).get('padding')).toBe('var(--workbench-card-space)')
|
||||
})
|
||||
|
||||
it('removes the detail wrapper inset without flattening inner cards or changing page and print spacing', () => {
|
||||
const get = fixture(`
|
||||
<main id="page" class="admin-page admin-workbench">
|
||||
<section id="detail" class="workbench-panel is-compact is-page-surface">
|
||||
<article id="card" class="workbench-panel is-compact">
|
||||
<section id="nested-detail" class="admin-workbench workbench-panel is-compact is-page-surface"></section>
|
||||
</article>
|
||||
<article id="regular-card" class="workbench-panel"></article>
|
||||
</section>
|
||||
</main>
|
||||
<section id="own-root" class="admin-workbench workbench-panel is-compact is-page-surface"></section>
|
||||
<main id="page-surface" class="admin-page admin-workbench workbench-panel is-compact is-page-surface"></main>
|
||||
<section id="outside" class="workbench-panel is-compact is-page-surface"></section>
|
||||
`)
|
||||
for (const selector of ['#detail', '#nested-detail', '#own-root']) {
|
||||
expect(declarationsFor(get(selector)).get('padding')).toBe('0')
|
||||
}
|
||||
for (const selector of ['#page', '#page-surface']) {
|
||||
expect(declarationsFor(get(selector)).get('padding')).toBe('var(--workbench-page-padding)')
|
||||
}
|
||||
expect(declarationsFor(get('#card')).get('padding')).toBe('var(--workbench-compact-card-space)')
|
||||
expect(declarationsFor(get('#regular-card')).get('padding')).toBe('var(--workbench-card-space)')
|
||||
expect(declarationsFor(get('#outside')).has('padding')).toBe(false)
|
||||
root(styles).walkRules(rule => {
|
||||
if (!rule.selector.includes('.workbench-panel.is-page-surface:not(.admin-page)')) return
|
||||
expect(rule.parent?.type === 'atrule' && rule.parent.name).toBe('media')
|
||||
expect(rule.parent?.type === 'atrule' && rule.parent.params).toBe('screen')
|
||||
})
|
||||
})
|
||||
|
||||
it('compacts only main list headers and aligns their filters without fixing content height', () => {
|
||||
const table = (id: string) => `
|
||||
<div class="data-table data-table--integrated">
|
||||
|
||||
Reference in New Issue
Block a user