refactor(F006-W17.6.1): TableActions 按数量自动收纳 (≤3 全显示, >3 前2+⋯)
用户反馈: 操作列只显示一个文字按钮+⋯ 不方便, 期望按按钮数量智能显示 - 过滤 when 后 ≤3 个: 全部显示文字按钮, 不显示 ⋯ - 过滤 when 后 ≥4 个: 前 2 个常驻文字按钮 + ⋯ (收纳剩余) - 默认 visibleCount=2 (可配置) TableActions API 调整 (W17.6 → W17.6.1): - 原来: :primary=[A] + :more=[B,C,D] (人为主次划分) - 现在: :actions=[A,B,C,D] (按数组顺序自动前 2 显 + ⋯) 16 文件改动 (1 组件 + 15 view 调用方式): - 15 view 合并 :primary/:more → :actions - 3 按钮页 (10 个): 现在全部显示文字按钮无 ⋯, 体验更直接 - 4/5 按钮页 (5 个): 前 2 显 + ⋯, 比之前详情+⋯更直观
This commit is contained in:
parent
cb2790d318
commit
2f7ec67903
@ -1,17 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* F006 通用表格操作列组件 (W17.6 主次分离)
|
||||
* F006 通用表格操作列组件 (W17.6 主次分离, W17.6.1 按数量自动收纳)
|
||||
*
|
||||
* 主操作 (primary) 常驻文字按钮, 次操作 (more) 收进「⋯」下拉菜单。
|
||||
* 解决按钮多时操作列拥挤、占宽大的问题。
|
||||
* 按钮数量决定显示方式 (规则符合直觉, 无需人为划分主次):
|
||||
* - 过滤 when 后 ≤3 个: 全部显示为文字按钮, 不显示 ⋯
|
||||
* - 过滤 when 后 ≥4 个: 前 visibleCount 个显示为文字按钮, 其余收进 ⋯ 下拉
|
||||
*
|
||||
* 用法:
|
||||
* <TableActions
|
||||
* :row="row"
|
||||
* :primary="[{ label: '详情', type: 'primary', onClick: onView }]"
|
||||
* :more="[
|
||||
* :actions="[
|
||||
* { label: '详情', type: 'primary', onClick: onView },
|
||||
* { label: '审核', type: 'primary', when: r => r.status === 1, onClick: onAudit },
|
||||
* { label: r => r.onShelf === 1 ? '下架' : '上架', type: r => r.onShelf === 1 ? 'warning' : 'success', onClick: onToggle },
|
||||
* { label: '改分类', type: 'primary', onClick: onCategory },
|
||||
* { label: '删除', type: 'danger', divided: true, confirm: '确认删除?', onClick: onDelete }
|
||||
* ]"
|
||||
* />
|
||||
@ -23,7 +25,13 @@
|
||||
* divided boolean 菜单项前加分隔线 (危险操作前用)
|
||||
* confirm boolean | string true 用默认提示, string 用自定义提示
|
||||
* onClick (row) => void 点击处理
|
||||
*
|
||||
* Props:
|
||||
* row 当前行数据 (透传给 label/type/when/onClick)
|
||||
* actions 操作列表 (按顺序, 前面的是主操作)
|
||||
* visibleCount 常驻文字按钮数, 默认 2 (仅在按钮 >3 时生效)
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { MoreFilled } from '@element-plus/icons-vue'
|
||||
|
||||
@ -38,11 +46,14 @@ interface ActionItem {
|
||||
onClick: (row: any) => void
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
row: any
|
||||
primary?: ActionItem[]
|
||||
more?: ActionItem[]
|
||||
}>()
|
||||
actions: ActionItem[]
|
||||
/** 常驻文字按钮数, 仅在按钮 >3 时生效 (默认 2) */
|
||||
visibleCount?: number
|
||||
}>(), {
|
||||
visibleCount: 2
|
||||
})
|
||||
|
||||
// 解析 label (string 或函数)
|
||||
function resolveLabel(item: ActionItem): string {
|
||||
@ -55,10 +66,23 @@ function resolveType(item: ActionItem): ActionType {
|
||||
return t || 'primary'
|
||||
}
|
||||
|
||||
// more 列表过滤 when 条件后的有效项
|
||||
function visibleMore(): ActionItem[] {
|
||||
return (props.more || []).filter(item => !item.when || item.when(props.row))
|
||||
}
|
||||
// 过滤 when 条件后的有效项
|
||||
const visibleActions = computed<ActionItem[]>(() =>
|
||||
props.actions.filter(item => !item.when || item.when(props.row))
|
||||
)
|
||||
|
||||
// 按钮总数 ≤3 全部常驻, >3 才拆分前 visibleCount 个常驻 + 其余收纳
|
||||
const isCollapsing = computed(() => visibleActions.value.length > 3)
|
||||
|
||||
// 常驻文字按钮 (前 N 个, N = isCollapsing ? visibleCount : 全部)
|
||||
const primaryActions = computed<ActionItem[]>(() =>
|
||||
isCollapsing.value ? visibleActions.value.slice(0, props.visibleCount) : visibleActions.value
|
||||
)
|
||||
|
||||
// 收进 ⋯ 的按钮 (第 visibleCount 个及之后, 仅 isCollapsing 时有值)
|
||||
const moreActions = computed<ActionItem[]>(() =>
|
||||
isCollapsing.value ? visibleActions.value.slice(props.visibleCount) : []
|
||||
)
|
||||
|
||||
// 执行操作 (处理二次确认)
|
||||
async function runAction(item: ActionItem) {
|
||||
@ -80,9 +104,9 @@ async function runAction(item: ActionItem) {
|
||||
|
||||
<template>
|
||||
<div class="rr-table-actions">
|
||||
<!-- 主操作: 常驻文字按钮 -->
|
||||
<!-- 常驻文字按钮 -->
|
||||
<el-button
|
||||
v-for="(item, i) in (primary || [])"
|
||||
v-for="(item, i) in primaryActions"
|
||||
:key="`p${i}`"
|
||||
text
|
||||
:type="resolveType(item) as any"
|
||||
@ -90,11 +114,11 @@ async function runAction(item: ActionItem) {
|
||||
@click="runAction(item)"
|
||||
>{{ resolveLabel(item) }}</el-button>
|
||||
|
||||
<!-- 次操作: 收进 ⋯ 下拉 (过滤 when 后有剩余项才渲染) -->
|
||||
<!-- 收纳下拉 (仅在按钮 >3 时渲染) -->
|
||||
<el-dropdown
|
||||
v-if="visibleMore().length"
|
||||
v-if="moreActions.length"
|
||||
trigger="click"
|
||||
@command="(cmd: number) => runAction(visibleMore()[cmd])"
|
||||
@command="(cmd: number) => runAction(moreActions[cmd])"
|
||||
>
|
||||
<span class="rr-more-trigger">
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
@ -102,7 +126,7 @@ async function runAction(item: ActionItem) {
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="(item, i) in visibleMore()"
|
||||
v-for="(item, i) in moreActions"
|
||||
:key="`m${i}`"
|
||||
:command="i"
|
||||
:divided="item.divided"
|
||||
|
||||
@ -302,10 +302,10 @@ onMounted(() => { load(); loadCustomers() })
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '查看动态', type: 'primary', onClick: activityHandle }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '查看动态', type: 'primary', onClick: activityHandle },
|
||||
{ label: '接管', type: 'primary', onClick: takeoverHandle },
|
||||
{ label: '解绑', type: 'danger', divided: true, onClick: unbindHandle }
|
||||
{ label: '解绑', type: 'danger', onClick: unbindHandle }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -374,11 +374,11 @@ onMounted(() => { load(); loadCustomers() })
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: detailHandle }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: detailHandle },
|
||||
{ label: '重发二维码', type: 'primary', when: (r: any) => r.status === 0 || r.status === 1, onClick: resendQrHandle },
|
||||
{ label: '重置密钥', type: 'warning', when: (r: any) => r.status === 0 || r.status === 1, onClick: resetSecretHandle },
|
||||
{ label: '撤销', type: 'danger', when: (r: any) => r.status === 0 || r.status === 1, onClick: revokeHandle },
|
||||
{ label: '撤销', type: 'danger', divided: true, when: (r: any) => r.status === 0 || r.status === 1, onClick: revokeHandle },
|
||||
{ label: '删除', type: 'danger', divided: true, when: (r: any) => r.status === 2, onClick: deleteHandle }
|
||||
]"
|
||||
/>
|
||||
|
||||
@ -276,8 +276,8 @@ onMounted(load)
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: onViewDetail }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: onViewDetail },
|
||||
{ label: '付款', type: 'primary', when: (r: any) => r.status === 1, onClick: onPay },
|
||||
{ label: '发货', type: 'primary', when: (r: any) => r.status === 2, onClick: onShip },
|
||||
{ label: '收货', type: 'primary', when: (r: any) => r.status === 3, onClick: onConfirmReceipt },
|
||||
|
||||
@ -189,10 +189,10 @@ onMounted(load)
|
||||
<template #default="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '修改', type: 'primary', onClick: onEdit }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '修改', type: 'primary', onClick: onEdit },
|
||||
{ label: '新增子分类', type: 'primary', when: (r: any) => r.level < 3, onClick: onAdd },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -150,10 +150,10 @@ onMounted(load)
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '修改', type: 'primary', onClick: onEdit }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '修改', type: 'primary', onClick: onEdit },
|
||||
{ label: (r: any) => r.status === 0 ? '禁用' : '启用', type: (r: any) => r.status === 0 ? 'warning' : 'success', onClick: onToggleStatus },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -184,8 +184,8 @@ onMounted(load)
|
||||
<template #default="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '编辑', type: 'primary', when: (r: any) => r.sourceType === 2, onClick: onEdit }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '编辑', type: 'primary', when: (r: any) => r.sourceType === 2, onClick: onEdit },
|
||||
{ label: (r: any) => r.onShelf === 1 ? '停用' : '启用', type: (r: any) => r.onShelf === 1 ? 'warning' : 'success', onClick: onToggleStatus },
|
||||
{ label: '领取记录', type: 'primary', when: (r: any) => r.sourceType === 2, onClick: onShowRecords },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
|
||||
@ -392,8 +392,8 @@ onMounted(() => Promise.all([load(), loadStats()]))
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: onShowDetail }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: onShowDetail },
|
||||
{ label: '审核', type: 'primary', when: (r: any) => r.checkStatus === 1, onClick: onShowCheckDialog },
|
||||
{ label: (r: any) => r.onShelf === 1 ? '下架' : '上架', type: (r: any) => r.onShelf === 1 ? 'warning' : 'success', onClick: onOnShelf },
|
||||
{ label: '改分类', type: 'primary', onClick: onShowCategoryDialog },
|
||||
|
||||
@ -112,10 +112,10 @@ onMounted(load)
|
||||
<template #actions="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: onView }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: onView },
|
||||
{ label: '回复', type: 'primary', when: (r: any) => !r.parentId, onClick: onReply },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -106,10 +106,10 @@ onMounted(load)
|
||||
<template #actions="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: onView }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: onView },
|
||||
{ label: '编辑', type: 'primary', onClick: onEdit },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -259,8 +259,8 @@ onMounted(() => Promise.all([load(), loadStats()]))
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '详情', type: 'primary', onClick: onShowDetail }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: onShowDetail },
|
||||
{ label: '审核', type: 'primary', when: (r: any) => r.checkStatus === 1, onClick: onShowCheck },
|
||||
{ label: (r: any) => r.onShelf === 1 ? '下架' : '上架', type: (r: any) => r.onShelf === 1 ? 'warning' : 'success', onClick: onOnShelf },
|
||||
{ label: '改分类', type: 'primary', onClick: onShowCategory },
|
||||
|
||||
@ -173,11 +173,11 @@ onMounted(load)
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '执行', type: 'primary', onClick: runHandle }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '执行', type: 'primary', onClick: runHandle },
|
||||
{ label: '暂停', type: 'warning', when: (r: any) => r.status === 1, onClick: pauseHandle },
|
||||
{ label: '恢复', type: 'primary', when: (r: any) => r.status !== 1, onClick: resumeHandle },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: deleteHandle }
|
||||
{ label: '删除', type: 'danger', onClick: deleteHandle }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -296,10 +296,10 @@ onMounted(() => { load() })
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: (r: any) => r.status === 1 ? '停用' : '启用', type: (r: any) => r.status === 1 ? 'warning' : 'success', onClick: toggleStatus }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: (r: any) => r.status === 1 ? '停用' : '启用', type: (r: any) => r.status === 1 ? 'warning' : 'success', onClick: toggleStatus },
|
||||
{ label: '重置密码', type: 'primary', onClick: resetPwdHandle },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: deleteHandle }
|
||||
{ label: '删除', type: 'danger', onClick: deleteHandle }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -143,10 +143,10 @@ onMounted(load)
|
||||
<template #default="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '修改', type: 'primary', onClick: onEdit }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '修改', type: 'primary', onClick: onEdit },
|
||||
{ label: '新增下级', type: 'primary', when: (r: any) => r.treeLevel < 4, onClick: onAdd },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -343,10 +343,10 @@ onMounted(load)
|
||||
<template #action="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '修改', type: 'primary', onClick: onEdit }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '修改', type: 'primary', onClick: onEdit },
|
||||
{ label: (r: any) => r.enabled === 0 ? '禁用' : '启用', type: (r: any) => r.enabled === 0 ? 'warning' : 'success', onClick: toggleEnabled },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: deleteUser }
|
||||
{ label: '删除', type: 'danger', onClick: deleteUser }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -92,10 +92,10 @@ onMounted(load)
|
||||
<template #actions="{ row }">
|
||||
<TableActions
|
||||
:row="row"
|
||||
:primary="[{ label: '查看', type: 'primary', onClick: onView }]"
|
||||
:more="[
|
||||
:actions="[
|
||||
{ label: '查看', type: 'primary', onClick: onView },
|
||||
{ label: '编辑', type: 'primary', onClick: onEdit },
|
||||
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
||||
{ label: '删除', type: 'danger', onClick: onDelete }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user