feat(phase3): complete loading workbenches

This commit is contained in:
2026-08-10 17:42:17 +08:00
parent 820432a381
commit 1595fbcae2
34 changed files with 1352 additions and 159 deletions
+60 -10
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, type Component } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Building2, 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'
@@ -633,8 +633,60 @@ function canChangeStatus(row: PlatformTenantResource) {
function canArchive() {
return access.can('platform:customer:delete')
}
function hasMoreActions(row: PlatformTenantResource) {
return canResetPassword(row) || canImpersonate(row) || canChangeStatus(row) || canArchive()
type CustomerRowAction = 'reset-password' | 'impersonate' | 'status' | 'delete'
const customerRowActionIcons: Record<CustomerRowAction, Component> = {
'reset-password': KeyRound,
impersonate: LogIn,
status: Power,
delete: Trash2,
}
function hasReviewAction(row: PlatformTenantResource) {
return pendingReviewByTenantId.value.has(String(row.id))
&& access.can('platform:customer:review')
&& (access.can('platform:customer:approve') || access.can('platform:customer:reject'))
}
function customerPrimaryActionCount(row: PlatformTenantResource) {
return Number(access.can('platform:customer:view'))
+ Number(access.can('platform:customer:view') && access.can('platform:customer:edit'))
+ Number(hasReviewAction(row))
}
function customerSecondaryActions(row: PlatformTenantResource): CustomerRowAction[] {
return [
...(canResetPassword(row) ? ['reset-password' as const] : []),
...(canImpersonate(row) ? ['impersonate' as const] : []),
...(canChangeStatus(row) ? ['status' as const] : []),
...(canArchive() ? ['delete' as const] : []),
]
}
function directCustomerActions(row: PlatformTenantResource) {
return customerSecondaryActions(row).slice(0, Math.max(0, 3 - customerPrimaryActionCount(row)))
}
function moreCustomerActions(row: PlatformTenantResource) {
return customerSecondaryActions(row).slice(Math.max(0, 3 - customerPrimaryActionCount(row)))
}
function customerMoreActionsLabel(row: PlatformTenantResource) {
const compatibleTenantId = (row as PlatformTenantResource & { tenantId?: unknown }).tenantId
const identifier = [row.tenantName, row.tenantCode, row.id, compatibleTenantId]
.map((value) => String(value ?? '').trim())
.find(Boolean)
return `更多客户操作:${identifier || '当前客户'}`
}
function customerActionLabel(row: PlatformTenantResource, action: CustomerRowAction) {
if (action === 'reset-password') return '重置密码'
if (action === 'impersonate') return '跳转登录'
if (action === 'status') return row.status === 'ACTIVE' ? '停用' : '启用'
return '删除'
}
function customerActionType(row: PlatformTenantResource, action: CustomerRowAction) {
if (action === 'delete' || (action === 'status' && row.status === 'ACTIVE')) return 'danger'
if (action === 'status') return 'success'
return 'primary'
}
function customerActionDisabled(action: CustomerRowAction) {
if (action === 'reset-password') return passwordSubmitting.value
if (action === 'impersonate') return impersonationSubmitting.value
if (action === 'delete') return archiveSubmitting.value
return false
}
function handleMoreAction(row: PlatformTenantResource, command: string) {
if (command === 'reset-password') openResetPassword(row)
@@ -917,15 +969,13 @@ onMounted(() => { void loadCustomers() })
<div class="table-actions">
<el-button v-if="access.can('platform:customer:view')" link type="primary" @click="showDetail(row as PlatformTenantResource)"><Eye :size="15" />详情</el-button>
<el-button v-if="access.can('platform:customer:view') && access.can('platform:customer:edit')" link type="primary" @click="openEdit(row as PlatformTenantResource)"><Pencil :size="15" />编辑</el-button>
<el-button v-if="pendingReviewByTenantId.has(String((row as PlatformTenantResource).id)) && access.can('platform:customer:review') && (access.can('platform:customer:approve') || access.can('platform:customer:reject'))" link type="warning" :disabled="reviewSubmitting" @click="openDecision(row as PlatformTenantResource, access.can('platform:customer:approve') ? 'approve' : 'reject')"><ShieldCheck :size="15" />审核</el-button>
<el-dropdown v-if="hasMoreActions(row as PlatformTenantResource)" trigger="click" :disabled="rowActionId === String((row as PlatformTenantResource).id)" popper-class="customer-action-menu" @command="handleMoreAction(row as PlatformTenantResource, String($event))">
<el-button link type="primary" aria-label="更多客户操作" title="更多操作"><MoreHorizontal :size="16" /></el-button>
<el-button v-if="hasReviewAction(row as PlatformTenantResource)" link type="warning" :disabled="reviewSubmitting" @click="openDecision(row as PlatformTenantResource, access.can('platform:customer:approve') ? 'approve' : 'reject')"><ShieldCheck :size="15" />审核</el-button>
<el-button v-for="action in directCustomerActions(row as PlatformTenantResource)" :key="action" link :type="customerActionType(row as PlatformTenantResource, action)" :disabled="customerActionDisabled(action)" @click="handleMoreAction(row as PlatformTenantResource, action)"><component :is="customerRowActionIcons[action]" :size="15" />{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-button>
<el-dropdown v-if="moreCustomerActions(row as PlatformTenantResource).length" trigger="click" :disabled="rowActionId === String((row as PlatformTenantResource).id)" popper-class="customer-action-menu" @command="handleMoreAction(row as PlatformTenantResource, String($event))">
<el-button link type="primary" :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-if="canResetPassword(row as PlatformTenantResource)" command="reset-password" :disabled="passwordSubmitting"><KeyRound :size="15" />重置密码</el-dropdown-item>
<el-dropdown-item v-if="canImpersonate(row as PlatformTenantResource)" command="impersonate" :disabled="impersonationSubmitting"><LogIn :size="15" />跳转登录</el-dropdown-item>
<el-dropdown-item v-if="canChangeStatus(row as PlatformTenantResource)" command="status"><Power :size="15" />{{ (row as PlatformTenantResource).status === 'ACTIVE' ? '停用' : '启用' }}</el-dropdown-item>
<el-dropdown-item v-if="canArchive()" command="delete" :disabled="archiveSubmitting" divided class="customer-action-menu__danger"><Trash2 :size="15" />删除</el-dropdown-item>
<el-dropdown-item v-for="action in moreCustomerActions(row as PlatformTenantResource)" :key="action" :command="action" :disabled="customerActionDisabled(action)" :divided="action === 'delete'" :class="{ 'customer-action-menu__danger': action === 'delete' }"><component :is="customerRowActionIcons[action]" :size="15" />{{ customerActionLabel(row as PlatformTenantResource, action) }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>