feat(phase3): complete loading workbenches
This commit is contained in:
@@ -102,20 +102,20 @@ const occupiedLocation: PlatformLocationResource = {
|
||||
version: 5,
|
||||
}
|
||||
|
||||
async function mountView() {
|
||||
async function mountView(permissions = [
|
||||
'platform:location:list',
|
||||
'platform:location:create',
|
||||
'platform:location:edit',
|
||||
'platform:location:status',
|
||||
'platform:location:delete',
|
||||
]) {
|
||||
vi.stubGlobal('localStorage', createMemoryStorage())
|
||||
vi.stubGlobal('sessionStorage', createMemoryStorage())
|
||||
const pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
const auth = useAuthStore()
|
||||
auth.token = 'token'
|
||||
auth.permissions = [
|
||||
'platform:location:list',
|
||||
'platform:location:create',
|
||||
'platform:location:edit',
|
||||
'platform:location:status',
|
||||
'platform:location:delete',
|
||||
]
|
||||
auth.permissions = permissions
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
const wrapper = mount(LocationsView, {
|
||||
attachTo: document.body,
|
||||
@@ -254,6 +254,51 @@ describe('LocationsView', () => {
|
||||
expect(bodyRows[1]?.text()).not.toContain('更多')
|
||||
})
|
||||
|
||||
it('shows all available actions directly when a row has no more than three', async () => {
|
||||
const wrapper = await mountView(['platform:location:list', 'platform:location:status'])
|
||||
const row = wrapper.find('tbody tr')
|
||||
|
||||
expect(row.text()).toContain('详情')
|
||||
expect(row.text()).toContain('停用')
|
||||
expect(row.text()).not.toContain('更多')
|
||||
})
|
||||
|
||||
it('keeps the first three actions visible and moves only the fourth action into more', async () => {
|
||||
vi.mocked(listPlatformLocations).mockResolvedValue({
|
||||
records: [{ ...availableLocation, status: 'DISABLED' }], page: 1, size: 20, total: 1, pages: 1,
|
||||
})
|
||||
const wrapper = await mountView()
|
||||
const row = wrapper.find('tbody tr')
|
||||
|
||||
expect(row.text()).toContain('详情')
|
||||
expect(row.text()).toContain('编辑')
|
||||
expect(row.text()).toContain('启用')
|
||||
expect(row.text()).not.toContain('删除')
|
||||
expect(row.find('button[aria-label="A1-01的更多操作"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('uses unique fallback labels for more actions across rows', async () => {
|
||||
vi.mocked(listPlatformLocations).mockResolvedValue({
|
||||
records: [
|
||||
{ ...availableLocation, id: 'location-1', locationCode: '', locationName: '东侧库位', status: 'DISABLED' },
|
||||
{ ...availableLocation, id: 'location-2', locationCode: '', locationName: '', status: 'DISABLED' },
|
||||
{ ...availableLocation, id: undefined, locationCode: '', locationName: '', status: 'DISABLED' } as unknown as PlatformLocationResource,
|
||||
],
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: 3,
|
||||
pages: 1,
|
||||
})
|
||||
const wrapper = await mountView()
|
||||
const moreButtons = wrapper.findAll('button[aria-label$="的更多操作"]')
|
||||
const labels = moreButtons.map((button) => button.attributes('aria-label'))
|
||||
|
||||
expect(labels).toEqual(['东侧库位的更多操作', 'location-2的更多操作', '当前库位的更多操作'])
|
||||
expect(moreButtons.map((button) => button.attributes('title'))).toEqual(labels)
|
||||
expect(new Set(labels).size).toBe(labels.length)
|
||||
expect(labels.every((label) => Boolean(label) && !/undefined|null/.test(label ?? ''))).toBe(true)
|
||||
})
|
||||
|
||||
it('does not offer editing for a location outside a domestic warehouse', async () => {
|
||||
vi.mocked(listPlatformLocations).mockResolvedValue({
|
||||
records: [{ ...availableLocation, warehouseId: fbaWarehouse.id }, availableLocation], page: 1, size: 20, total: 2, pages: 1,
|
||||
|
||||
@@ -221,6 +221,17 @@ function canDelete(row: PlatformLocationResource) {
|
||||
return access.can('platform:location:delete') && !row.sharedOverflow && row.status === 'DISABLED'
|
||||
}
|
||||
|
||||
function hasMoreLocationActions(row: PlatformLocationResource) {
|
||||
return 1 + Number(canEdit(row)) + Number(canChangeStatus(row)) + Number(canDelete(row)) > 3
|
||||
}
|
||||
|
||||
function locationMoreActionsLabel(row: PlatformLocationResource) {
|
||||
const identifier = [row.locationCode, row.locationName, row.id]
|
||||
.map((value) => String(value ?? '').trim())
|
||||
.find(Boolean) || '当前库位'
|
||||
return `${identifier}的更多操作`
|
||||
}
|
||||
|
||||
function actionError(error: unknown, fallback: string) {
|
||||
const details = getApiErrorDetails(error, fallback)
|
||||
ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message)
|
||||
@@ -562,19 +573,31 @@ onMounted(() => {
|
||||
:disabled="loading"
|
||||
@click="loadDetail(row as PlatformLocationResource, 'edit')"
|
||||
><Pencil :size="15" />编辑</el-button>
|
||||
<el-button
|
||||
v-if="canChangeStatus(row as PlatformLocationResource)"
|
||||
link
|
||||
:type="(row as PlatformLocationResource).status === 'AVAILABLE' ? 'danger' : 'success'"
|
||||
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
|
||||
@click="toggleStatus(row as PlatformLocationResource)"
|
||||
><Power :size="15" />{{ (row as PlatformLocationResource).status === 'AVAILABLE' ? '停用' : '启用' }}</el-button>
|
||||
<el-button
|
||||
v-if="canDelete(row as PlatformLocationResource) && !hasMoreLocationActions(row as PlatformLocationResource)"
|
||||
link
|
||||
type="danger"
|
||||
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
|
||||
@click="removeLocation(row as PlatformLocationResource)"
|
||||
><Trash2 :size="15" />删除</el-button>
|
||||
<el-dropdown
|
||||
v-if="canChangeStatus(row as PlatformLocationResource) || canDelete(row as PlatformLocationResource)"
|
||||
v-if="hasMoreLocationActions(row as PlatformLocationResource)"
|
||||
trigger="click"
|
||||
popper-class="location-action-menu"
|
||||
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
|
||||
@command="handleMoreAction(row as PlatformLocationResource, String($event))"
|
||||
>
|
||||
<el-button link type="primary" aria-label="更多库位操作" title="更多库位操作"><MoreHorizontal :size="16" /></el-button>
|
||||
<el-button link type="primary" :aria-label="locationMoreActionsLabel(row as PlatformLocationResource)" :title="locationMoreActionsLabel(row as PlatformLocationResource)"><MoreHorizontal :size="16" /></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-if="canChangeStatus(row as PlatformLocationResource)" command="status">
|
||||
<Power :size="15" />{{ (row as PlatformLocationResource).status === 'AVAILABLE' ? '停用' : '启用' }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="canDelete(row as PlatformLocationResource)" command="delete" divided>
|
||||
<el-dropdown-item v-if="canDelete(row as PlatformLocationResource)" command="delete" class="location-action-menu__danger">
|
||||
<Trash2 :size="15" />删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -794,6 +817,7 @@ onMounted(() => {
|
||||
.readonly-status { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; padding: 12px; background: #f7f9fc; }
|
||||
.readonly-status > span { color: var(--cc-text); font-size: 13px; font-weight: 600; }
|
||||
.readonly-status small { color: var(--cc-muted); font-size: 11px; }
|
||||
:global(.location-action-menu .location-action-menu__danger:not(.is-disabled)) { color: var(--el-color-danger); }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.detail-identity { grid-template-columns: 44px minmax(0, 1fr); }
|
||||
|
||||
Reference in New Issue
Block a user