| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- <template>
- <!-- 蓝湖风格 A:关键词 + 查询 + 多条件 + 刷新 | 操作(最多 3 个,其余进 …) -->
- <div class="list-page-toolbar">
- <div class="list-page-toolbar__left">
- <el-input
- v-if="showKeyword"
- :model-value="keyword"
- :placeholder="keywordPlaceholder"
- clearable
- class="list-page-toolbar__keyword"
- @update:model-value="emit('update:keyword', String($event ?? ''))"
- @keyup.enter="emit('search')"
- >
- <template #prefix>
- <el-icon><Search /></el-icon>
- </template>
- </el-input>
- <el-button type="primary" @click="emit('search')">查询</el-button>
- <el-popover
- v-if="$slots.filters"
- v-model:visible="filterOpen"
- placement="bottom-start"
- :width="filterWidth"
- trigger="click"
- >
- <template #reference>
- <el-button>
- 多条件查询
- <el-icon class="list-page-toolbar__caret"><ArrowDown /></el-icon>
- </el-button>
- </template>
- <div class="list-page-toolbar__filters">
- <slot name="filters" />
- <div class="list-page-toolbar__filter-footer">
- <el-button @click="onFilterCancel">取消</el-button>
- <el-button type="primary" @click="onFilterSearch">查询</el-button>
- </div>
- </div>
- </el-popover>
- <el-button
- class="list-page-toolbar__refresh"
- title="刷新"
- @click="emit('refresh')"
- >
- <el-icon :size="16"><RefreshRight /></el-icon>
- </el-button>
- </div>
- <div v-if="visibleActions.length || $slots.actions" class="list-page-toolbar__right">
- <slot name="actions">
- <el-button
- v-for="act in primaryActions"
- :key="act.key"
- :type="act.type || 'default'"
- :loading="act.loading"
- :disabled="act.disabled"
- @click="act.onClick"
- >
- {{ act.label }}
- </el-button>
- <el-dropdown v-if="moreActions.length" trigger="click">
- <el-button class="list-page-toolbar__more" title="更多">
- <el-icon :size="16"><MoreFilled /></el-icon>
- </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item
- v-for="act in moreActions"
- :key="act.key"
- :disabled="act.disabled || act.loading"
- @click="act.onClick"
- >
- {{ act.label }}
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </slot>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, ref } from 'vue'
- import { ArrowDown, MoreFilled, RefreshRight, Search } from '@element-plus/icons-vue'
- export interface ListToolbarAction {
- key: string
- label: string
- type?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'default'
- loading?: boolean
- disabled?: boolean
- /** 默认 true */
- visible?: boolean
- onClick: () => void
- }
- const props = withDefaults(
- defineProps<{
- keyword?: string
- keywordPlaceholder?: string
- showKeyword?: boolean
- /** 主区最多展示几个按钮,其余进「…」 */
- maxActions?: number
- actions?: ListToolbarAction[]
- filterWidth?: number
- }>(),
- {
- keyword: '',
- keywordPlaceholder: '请输入关键词查询',
- showKeyword: true,
- maxActions: 3,
- actions: () => [],
- filterWidth: 360,
- },
- )
- const emit = defineEmits<{
- 'update:keyword': [value: string]
- search: []
- refresh: []
- 'filter-cancel': []
- }>()
- const filterOpen = ref(false)
- const visibleActions = computed(() =>
- (props.actions || []).filter((a) => a.visible !== false),
- )
- const primaryActions = computed(() =>
- visibleActions.value.slice(0, props.maxActions),
- )
- const moreActions = computed(() =>
- visibleActions.value.slice(props.maxActions),
- )
- function onFilterCancel() {
- filterOpen.value = false
- emit('filter-cancel')
- }
- function onFilterSearch() {
- filterOpen.value = false
- emit('search')
- }
- </script>
- <style scoped lang="less">
- .list-page-toolbar {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- margin-bottom: 12px;
- }
- .list-page-toolbar__left,
- .list-page-toolbar__right {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 8px;
- }
- .list-page-toolbar__keyword {
- width: 220px;
- }
- .list-page-toolbar__caret {
- margin-left: 4px;
- }
- .list-page-toolbar__refresh,
- .list-page-toolbar__more {
- width: 32px;
- padding: 0 !important;
- }
- .list-page-toolbar__filters {
- display: grid;
- gap: 12px;
- }
- .list-page-toolbar__filter-footer {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- padding-top: 4px;
- }
- :deep(.el-button) {
- min-height: 32px !important;
- height: 32px !important;
- }
- :deep(.el-input__wrapper) {
- min-height: 32px !important;
- }
- </style>
|