fix: 物料页两个作废入口的按钮宽度对齐

「作废」和「撤销作废」轮流出现在同一列,2 字对 4 字,宽度不一样,最右边按钮的
位置会参差。把「作废」改成「作废物料」,两边都是 4 个中文字,同字体同字号下自然
等宽,不依赖写死的 px 值——本项目的测试环境是 jsdom,不做布局计算,量不了像素,
写死的值只能靠猜。

顺带修了测试里的一个自欺:那行物料原本叫「作废物料」,和按钮文案撞名,而按钮
自身就在那一行里,用 toContain 断言行归属会恒为真。改成「旧螺丝」才真的在测。

测试补 1 条(锁住两个按钮的文案字数相同)。变异验证:把「作废物料」改回「作废」,
文案断言和字数断言都变红。
This commit is contained in:
文睿 张 2026-09-22 15:15:54 +08:00
parent b9c0681c47
commit 688dffebaa
2 changed files with 27 additions and 9 deletions

View File

@ -329,9 +329,11 @@ describe('物料列表的作废入口文案', () => {
beforeEach(() => { beforeEach(() => {
materialCategoriesApi.list.mockResolvedValue({ data: categories }) materialCategoriesApi.list.mockResolvedValue({ data: categories })
unitsApi.list.mockResolvedValue({ data: [] }) unitsApi.list.mockResolvedValue({ data: [] })
// 名字特意避开按钮文案(「作废物料」/「撤销作废」):否则按钮自身就在那一行里,
// 用 toContain 断言行归属会恒为真,测了等于没测。
materialsStore.materials = [ materialsStore.materials = [
{ id: 1, material_code: 'A010201', name: '作废物料', status: 'obsolete' }, { id: 1, material_code: 'A010201', name: '旧螺丝', status: 'obsolete' },
{ id: 2, material_code: 'A010202', name: '有效物料', status: 'active' } { id: 2, material_code: 'A010202', name: '新螺丝', status: 'active' }
] ]
}) })
@ -339,7 +341,8 @@ describe('物料列表的作废入口文案', () => {
materialsStore.materials = [] materialsStore.materials = []
}) })
// 按钮文案得精确匹配:「撤销作废」里也含「作废」,用 includes 会把两个按钮看成同一个。 // 按钮文案得精确匹配:「作废物料」和「撤销作废」都含「作废」,用 includes 会把
// 两个按钮看成同一个。
function exactButton(wrapper, text) { function exactButton(wrapper, text) {
return wrapper.findAll('button').find(button => button.text().trim() === text) return wrapper.findAll('button').find(button => button.text().trim() === text)
} }
@ -347,18 +350,33 @@ describe('物料列表的作废入口文案', () => {
// 这一页没有状态列(文案即状态),所以按钮文案本身得说清这条物料怎么了。 // 这一页没有状态列(文案即状态),所以按钮文案本身得说清这条物料怎么了。
// 「撤销」在中文 UI 里默认指撤销上一步(Ctrl+Z),看不出在撤销什么;「撤销作废」 // 「撤销」在中文 UI 里默认指撤销上一步(Ctrl+Z),看不出在撤销什么;「撤销作废」
// 才读得出「这条被作废了,点这里撤销那次作废」。 // 才读得出「这条被作废了,点这里撤销那次作废」。
test('作废的那行显示「撤销作废」,有效的那行显示「作废」', async () => { test('作废的那行显示「撤销作废」,有效的那行显示「作废物料」', async () => {
const wrapper = mountMaterials() const wrapper = mountMaterials()
await flushPromises() await flushPromises()
const restore = exactButton(wrapper, '撤销作废') const restore = exactButton(wrapper, '撤销作废')
const obsolete = exactButton(wrapper, '作废') const obsolete = exactButton(wrapper, '作废物料')
expect(restore).toBeTruthy() expect(restore).toBeTruthy()
expect(obsolete).toBeTruthy() expect(obsolete).toBeTruthy()
// 两个入口互斥:作废的那行不该再有「作废」,有效的那行不该有「撤销作废」。 // 两个入口互斥:作废的那行不该再有「作废物料」,有效的那行不该有「撤销作废」。
expect(restore.element.closest('tr').textContent).toContain('作废物料') // 注意断言的是行归属,不是「页面上有没有这个词」——两个词都含「作废」。
expect(obsolete.element.closest('tr').textContent).toContain('有效物料') expect(restore.element.closest('tr').textContent).toContain('旧螺丝')
expect(obsolete.element.closest('tr').textContent).toContain('新螺丝')
wrapper.unmount()
})
// 两个按钮轮流出现在同一列,宽度不一样会让「编辑」右边参差不齐。这里靠的是两边
// 都是 4 个中文字,同字体同字号下自然等宽——jsdom 不做布局计算,量不了像素,但
// 字数不等就一定会参差,所以把字数锁住。
test('两个按钮的文案字数相同,保证宽度一致', async () => {
const wrapper = mountMaterials()
await flushPromises()
const restore = exactButton(wrapper, '撤销作废')
const obsolete = exactButton(wrapper, '作废物料')
expect(restore.text().trim().length).toBe(obsolete.text().trim().length)
wrapper.unmount() wrapper.unmount()
}) })
}) })

View File

@ -61,7 +61,7 @@
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" @click="showDetail(row)">详情</el-button> <el-button size="small" @click="showDetail(row)">详情</el-button>
<el-button v-if="authStore.isAdmin" size="small" type="primary" @click="showEditModal(row)">编辑</el-button> <el-button v-if="authStore.isAdmin" size="small" type="primary" @click="showEditModal(row)">编辑</el-button>
<el-button v-if="authStore.isAdmin && row.status !== 'obsolete'" size="small" type="danger" @click="handleDelete(row.id)">作废</el-button> <el-button v-if="authStore.isAdmin && row.status !== 'obsolete'" size="small" type="danger" @click="handleDelete(row.id)">作废物料</el-button>
<el-button v-if="authStore.isAdmin && row.status === 'obsolete'" size="small" type="success" @click="handleRestore(row.id)">撤销作废</el-button> <el-button v-if="authStore.isAdmin && row.status === 'obsolete'" size="small" type="success" @click="handleRestore(row.id)">撤销作废</el-button>
</template> </template>
</el-table-column> </el-table-column>