This commit is contained in:
2026-08-15 12:12:38 +08:00
parent e000f8bed2
commit e214672779
4 changed files with 87 additions and 16 deletions
@@ -559,7 +559,7 @@ describe('CustomersView', () => {
expect(document.querySelector('.el-table__fixed')).toBeNull()
})
it('keeps every permitted customer action visible without a more menu', async () => {
it('keeps three high-frequency actions visible and moves overflow actions into a menu', async () => {
const auth = useAuthStore()
auth.permissions = [...auth.permissions, 'platform:customer:reset-password', 'platform:customer:impersonate', 'platform:customer:status', 'platform:customer:delete']
useAccessPreviewStore().initializeAfterLogin()
@@ -570,11 +570,10 @@ describe('CustomersView', () => {
expect(tableButtonByText('详情')).toBeDefined()
expect(tableButtonByText('编辑')).toBeDefined()
expect(document.querySelector('.el-table__body button[aria-label^="更多客户操作:"]')).toBeNull()
expect(document.querySelector('.el-table__body button[aria-label^="更多客户操作:"]')).not.toBeNull()
expect(tableButtonByText('重置密码')).toBeDefined()
expect(tableButtonByText('跳转登录')).toBeDefined()
tableButtonByText('跳转登录')?.click()
await flushPromises()
expect(tableButtonByText('跳转登录')).toBeUndefined()
await clickMoreAction('跳转登录')
expect(document.body.textContent).toContain('代登录原因')
})
@@ -596,7 +595,7 @@ describe('CustomersView', () => {
expect(tableButtonByText('删除')).toBeUndefined()
})
it('renders all permitted actions directly for multiple customers', async () => {
it('renders one overflow menu per customer when permitted actions exceed three', async () => {
const auth = useAuthStore()
auth.permissions = [...auth.permissions, 'platform:customer:reset-password', 'platform:customer:impersonate', 'platform:customer:status', 'platform:customer:delete']
useAccessPreviewStore().initializeAfterLogin()
@@ -612,8 +611,8 @@ describe('CustomersView', () => {
mount(CustomersView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
expect(document.querySelectorAll('.el-table__body button[aria-label^="更多客户操作:"]').length).toBe(0)
expect(document.querySelectorAll('.el-table__body button').length).toBeGreaterThan(9)
expect(document.querySelectorAll('.el-table__body button[aria-label^="更多客户操作:"]').length).toBe(3)
expect(document.querySelectorAll('.el-table__body .table-actions > .el-button').length).toBe(9)
})
it('keeps the action row usable when all customer identifiers are missing', async () => {
@@ -628,7 +627,7 @@ describe('CustomersView', () => {
mount(CustomersView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
expect(document.querySelector('.el-table__body button[aria-label^="更多客户操作:"]')).toBeNull()
expect(document.querySelector('.el-table__body button[aria-label="更多客户操作:当前客户"]')).not.toBeNull()
expect(tableButtonByText('详情')).toBeDefined()
})
})
+22 -3
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, type Component } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { CheckCircle2, Download, Eye, FileText, FileUp, KeyRound, LogIn, Pencil, Plus, Power, Save, ShieldCheck, ShieldX, Trash2 } from '@lucide/vue'
import { CheckCircle2, Download, Eye, FileText, FileUp, KeyRound, LogIn, MoreHorizontal, Pencil, Plus, Power, Save, ShieldCheck, ShieldX, Trash2 } from '@lucide/vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { getApiErrorDetails, getApiErrorMessage, type ApiErrorDetails } from '@/shared/api/http'
import { createPlatformExportTask } from '@/modules/downloads'
@@ -194,7 +194,7 @@ const creditCodeEstablished = ref(false)
const unifiedSocialCreditCodePattern = /^[0-9A-Z]{18}$/
const customerColumns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 500, slotName: 'actions' },
{ key: 'actions', label: '操作', width: 330, slotName: 'actions' },
{ key: 'tenantCode', prop: 'tenantCode', label: '客户编号', width: 220, className: 'customer-code-column' },
{ key: 'company', label: '公司名称', width: 250, slotName: 'company' },
{ key: 'unifiedSocialCreditCode', prop: 'unifiedSocialCreditCode', label: '统一社会信用代码', width: 220, formatter: (row: PlatformTenantResource) => displayText(row.unifiedSocialCreditCode) },
@@ -668,6 +668,21 @@ function customerSecondaryActions(row: PlatformTenantResource): CustomerRowActio
...(canArchive(row) ? ['delete' as const] : []),
]
}
function customerPrimaryActionCount(row: PlatformTenantResource) {
return Number(access.can('platform:customer:list'))
+ Number(access.can('platform:customer:edit') && row.status !== 'ARCHIVED')
+ Number(hasReviewAction(row))
}
function customerDirectSecondaryActions(row: PlatformTenantResource) {
return customerSecondaryActions(row).slice(0, Math.max(0, 3 - customerPrimaryActionCount(row)))
}
function customerOverflowActions(row: PlatformTenantResource) {
return customerSecondaryActions(row).slice(Math.max(0, 3 - customerPrimaryActionCount(row)))
}
function customerMoreActionsLabel(row: PlatformTenantResource) {
const identity = row.tenantName || row.tenantCode || String(row.id || '当前客户')
return `更多客户操作:${identity}`
}
function customerActionLabel(row: PlatformTenantResource, action: CustomerRowAction) {
if (action === 'reset-password') return '重置密码'
if (action === 'impersonate') return '跳转登录'
@@ -1019,7 +1034,11 @@ onMounted(() => { void loadCustomers() })
<el-button v-if="access.can('platform:customer:list')" class="customer-action-button customer-action-detail" size="small" @click="showDetail(row as PlatformTenantResource)"><Eye :size="14" />详情</el-button>
<el-button v-if="access.can('platform:customer:edit') && (row as PlatformTenantResource).status !== 'ARCHIVED'" class="customer-action-button customer-action-edit" size="small" @click="openEdit(row as PlatformTenantResource)"><Pencil :size="14" />编辑</el-button>
<el-button v-if="hasReviewAction(row as PlatformTenantResource)" class="customer-action-button customer-action-review" size="small" :disabled="reviewSubmitting" @click="openDecision(row as PlatformTenantResource, access.can('platform:customer:approve') ? 'approve' : 'reject')"><ShieldCheck :size="14" />审核</el-button>
<el-button v-for="action in customerSecondaryActions(row as PlatformTenantResource)" :key="action" :class="[`customer-action-button`, `customer-action-${action}`, { 'is-enable': action === 'status' && (row as PlatformTenantResource).status !== 'ACTIVE', 'is-disable': action === 'status' && (row as PlatformTenantResource).status === 'ACTIVE' }]" size="small" :disabled="customerActionDisabled(action)" @click="handleMoreAction(row as PlatformTenantResource, action)"><component :is="customerRowActionIcons[action]" :size="14" />{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-button>
<el-button v-for="action in customerDirectSecondaryActions(row as PlatformTenantResource)" :key="action" :class="[`customer-action-button`, `customer-action-${action}`, { 'is-enable': action === 'status' && (row as PlatformTenantResource).status !== 'ACTIVE', 'is-disable': action === 'status' && (row as PlatformTenantResource).status === 'ACTIVE' }]" size="small" :disabled="customerActionDisabled(action)" @click="handleMoreAction(row as PlatformTenantResource, action)"><component :is="customerRowActionIcons[action]" :size="14" />{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-button>
<el-dropdown v-if="customerOverflowActions(row as PlatformTenantResource).length" trigger="click" popper-class="customer-action-menu" @command="handleMoreAction(row as PlatformTenantResource, $event as string)">
<el-button class="customer-action-button customer-action-more" size="small" plain :aria-label="customerMoreActionsLabel(row as PlatformTenantResource)" :title="customerMoreActionsLabel(row as PlatformTenantResource)"><MoreHorizontal :size="16" /></el-button>
<template #dropdown><el-dropdown-menu><el-dropdown-item v-for="action in customerOverflowActions(row as PlatformTenantResource)" :key="action" :command="action" :disabled="customerActionDisabled(action)" :class="{ 'customer-action-menu__danger': customerActionType(row as PlatformTenantResource, action) === 'danger' }"><component :is="customerRowActionIcons[action]" :size="15" />{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-dropdown-item></el-dropdown-menu></template>
</el-dropdown>
</div>
</template>
<template #company="{ row }"><strong class="customer-company-name">{{ (row as PlatformTenantResource).tenantName }}</strong></template>