174 lines
3.5 KiB
Vue
174 lines
3.5 KiB
Vue
<script setup lang="ts">
|
||
export interface FilterChip {
|
||
key: string
|
||
label: string
|
||
}
|
||
|
||
interface Props {
|
||
chips?: FilterChip[]
|
||
keywordPlaceholder?: string
|
||
searchLabel?: string
|
||
showResultCount?: boolean
|
||
resultCount?: number
|
||
}
|
||
|
||
withDefaults(defineProps<Props>(), {
|
||
chips: () => [],
|
||
keywordPlaceholder: '关键字搜索',
|
||
searchLabel: '搜索',
|
||
showResultCount: false,
|
||
resultCount: 0,
|
||
})
|
||
|
||
const keyword = defineModel<string>('keyword', { default: '' })
|
||
|
||
const emit = defineEmits<{
|
||
search: []
|
||
reset: []
|
||
removeChip: [key: string]
|
||
clearChips: []
|
||
}>()
|
||
</script>
|
||
|
||
<template>
|
||
<div class="filter-panel">
|
||
<div class="filter-panel__bar">
|
||
<input
|
||
v-model="keyword"
|
||
class="filter-panel__input"
|
||
:placeholder="keywordPlaceholder"
|
||
@keyup.enter="emit('search')"
|
||
>
|
||
<button class="filter-panel__btn filter-panel__btn--primary" @click="emit('search')">
|
||
{{ searchLabel }}<template v-if="showResultCount">({{ resultCount }}个结果)</template>
|
||
</button>
|
||
<button class="filter-panel__btn" @click="emit('reset')">
|
||
重置
|
||
</button>
|
||
</div>
|
||
|
||
<div v-if="chips.length > 0" class="filter-panel__chips">
|
||
<span class="filter-panel__chips-label text-secondary">已选条件({{ chips.length }}个)</span>
|
||
<button
|
||
v-for="chip in chips"
|
||
:key="chip.key"
|
||
class="filter-panel__chip"
|
||
:title="`移除 ${chip.label}`"
|
||
@click="emit('removeChip', chip.key)"
|
||
>
|
||
{{ chip.label }} ×
|
||
</button>
|
||
<button class="filter-panel__chip filter-panel__chip--clear" @click="emit('clearChips')">
|
||
清空
|
||
</button>
|
||
</div>
|
||
|
||
<div class="filter-panel__body">
|
||
<slot />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="less" scoped>
|
||
.filter-panel {
|
||
padding: 24px 36px;
|
||
border-radius: @radius-base;
|
||
background: @white;
|
||
|
||
&__bar {
|
||
.flex(row, flex-start);
|
||
gap: 18px;
|
||
}
|
||
|
||
&__input {
|
||
flex: 1;
|
||
max-width: 456px;
|
||
margin-right: 28px;
|
||
height: 38px;
|
||
padding: 0 @spacing-base;
|
||
border: 1px solid @cases-input-border;
|
||
border-radius: @radius-sm;
|
||
background: @white;
|
||
color: @cases-text;
|
||
font-size: 15px;
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
|
||
&::placeholder {
|
||
color: @cases-input-border;
|
||
}
|
||
|
||
&:focus {
|
||
border-color: @cases-accent;
|
||
}
|
||
}
|
||
|
||
&__btn {
|
||
min-width: 77px;
|
||
height: 38px;
|
||
padding: 0 @spacing-base;
|
||
border-radius: @radius-sm;
|
||
background: @cases-accent;
|
||
color: @white;
|
||
font-size: 15px;
|
||
transition: all 0.2s;
|
||
|
||
&:hover {
|
||
background: @primary-hover;
|
||
}
|
||
|
||
&--primary {
|
||
background: @cases-accent;
|
||
color: @white;
|
||
|
||
&:hover {
|
||
background: @primary-hover;
|
||
color: @white;
|
||
}
|
||
}
|
||
}
|
||
|
||
&__chips {
|
||
.flex(row, flex-start, center);
|
||
flex-wrap: wrap;
|
||
gap: 11px 10px;
|
||
margin-top: @spacing-base;
|
||
padding: 12px 18px 16px;
|
||
border-radius: @radius-base;
|
||
background: @chips-panel-bg;
|
||
font-size: 15px;
|
||
}
|
||
|
||
&__chips-label {
|
||
color: @cases-text;
|
||
font-weight: 500;
|
||
}
|
||
|
||
&__chip {
|
||
height: 25px;
|
||
padding: 0 @spacing-sm;
|
||
border-radius: @radius-round;
|
||
background: @white;
|
||
color: @cases-accent;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
transition: all 0.2s;
|
||
|
||
&:hover {
|
||
color: @primary-hover;
|
||
}
|
||
|
||
&--clear {
|
||
background: transparent;
|
||
color: @primary-strong;
|
||
}
|
||
}
|
||
|
||
&__body {
|
||
margin-top: @spacing-base;
|
||
.flex(column, flex-start);
|
||
gap: @spacing-base;
|
||
}
|
||
}
|
||
</style>
|