89 lines
2.6 KiB
Vue
89 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { MoreHorizontal } from '@lucide/vue'
|
|
|
|
export type RowActionTone = 'neutral' | 'warning' | 'purple' | 'primary' | 'success' | 'danger'
|
|
|
|
export interface RowActionItem {
|
|
key: string
|
|
label: string
|
|
ariaLabel?: string
|
|
visible?: boolean
|
|
disabled?: boolean
|
|
loading?: boolean
|
|
tone?: RowActionTone
|
|
priority?: number
|
|
divided?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<{
|
|
actions: RowActionItem[]
|
|
maxVisible?: number
|
|
moreLabel?: string
|
|
}>(), {
|
|
maxVisible: 3,
|
|
moreLabel: '更多操作',
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
action: [key: string]
|
|
}>()
|
|
|
|
const availableActions = computed(() => props.actions.filter(action => action.visible !== false))
|
|
|
|
const directKeys = computed(() => new Set(
|
|
availableActions.value
|
|
.map((action, index) => ({ action, index }))
|
|
.sort((left, right) => (right.action.priority ?? 0) - (left.action.priority ?? 0) || left.index - right.index)
|
|
.slice(0, Math.max(0, props.maxVisible))
|
|
.map(({ action }) => action.key),
|
|
))
|
|
|
|
const directActions = computed(() => availableActions.value.filter(action => directKeys.value.has(action.key)))
|
|
const overflowActions = computed(() => availableActions.value.filter(action => !directKeys.value.has(action.key)))
|
|
|
|
function select(action: RowActionItem) {
|
|
if (action.disabled || action.loading) return
|
|
emit('action', action.key)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="density-actions row-actions">
|
|
<el-button
|
|
v-for="action in directActions"
|
|
:key="action.key"
|
|
class="density-action"
|
|
:data-tone="action.tone || 'neutral'"
|
|
size="small"
|
|
:disabled="action.disabled"
|
|
:loading="action.loading"
|
|
:aria-label="action.ariaLabel"
|
|
:title="action.ariaLabel"
|
|
@click.stop="select(action)"
|
|
>{{ action.label }}</el-button>
|
|
<el-dropdown v-if="overflowActions.length" trigger="click">
|
|
<el-button
|
|
class="density-action"
|
|
data-tone="neutral"
|
|
size="small"
|
|
:aria-label="moreLabel"
|
|
:title="moreLabel"
|
|
@click.stop
|
|
><MoreHorizontal :size="15" aria-hidden="true" /></el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item
|
|
v-for="action in overflowActions"
|
|
:key="action.key"
|
|
:class="{ 'danger-menu-item': action.tone === 'danger' }"
|
|
:disabled="action.disabled || action.loading"
|
|
:divided="action.divided"
|
|
@click="select(action)"
|
|
>{{ action.label }}</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</template>
|