feat 搭建架构骨架

This commit is contained in:
Ironsp
2026-09-24 09:09:47 +08:00
parent dd9f6b77a4
commit 7a2efa3fea
180 changed files with 18967 additions and 9 deletions

View File

@@ -0,0 +1,162 @@
<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: @spacing-base @spacing-lg;
border-radius: @radius-lg;
background: @white;
box-shadow: @shadow-base;
&__bar {
.flex(row, flex-start);
gap: @spacing-sm;
}
&__input {
width: 240px;
height: 34px;
padding: 0 @spacing-base;
border: 1px solid @border-color;
border-radius: @radius-base;
background: @bg-color-light;
font-size: @font-size-base;
outline: none;
transition: border-color 0.2s;
&:focus {
border-color: @primary-color;
}
}
&__btn {
height: 34px;
padding: 0 @spacing-base;
border: 1px solid @border-color-dark;
border-radius: @radius-base;
background: @white;
color: @text-color;
font-size: @font-size-base;
transition: all 0.2s;
&:hover {
border-color: @primary-color;
color: @primary-color;
}
&--primary {
border-color: @primary-color;
background: @primary-color;
color: @white;
&:hover {
background: @primary-hover;
color: @white;
}
}
}
&__chips {
.flex(row, flex-start, center);
flex-wrap: wrap;
gap: @spacing-sm;
margin-top: @spacing-base;
font-size: @font-size-sm;
}
&__chip {
height: 26px;
padding: 0 @spacing-sm;
border: 1px solid @primary-light;
border-radius: @radius-round;
background: @primary-light;
color: @primary-color;
font-size: @font-size-sm;
transition: all 0.2s;
&:hover {
border-color: @primary-color;
}
&--clear {
border-color: @border-color;
background: @white;
color: @text-color-secondary;
}
}
&__body {
margin-top: @spacing-base;
.flex(column, flex-start);
gap: @spacing-base;
}
}
</style>