搜索结果列表页面开发

This commit is contained in:
付民康
2025-03-12 18:41:20 +08:00
commit 7cebe8fc00
739 changed files with 88149 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
# SearchBar
### 说明
- 基于d-select、RichBar等组件组合而成的SearchBar
``` js
import SearchBar from '@/components/SearchBar/index.vue';
<SearchBar
:typeOptions="typeOptions"
:searchOptions="searchOptions"
:filter="filterFunc"
@onSearch="handleSearch"
>
<!-- 右侧内容插槽 可以为空 -->
<template #right>
<RichLabel icon="milestone" :number="6522223" black group>里程碑</RichLabel>
</template>
</SearchBar>
const typeOptions = ref([{ name: '全部', value: '' }, { name: '类1', value: 'class1' }]);
const searchOptions = ref([{ name: '全部', value: 'all' }, { name: '类1', value: 'class1' }]);
const filterFunc = (query: string): void => {
// query为搜索框输入的文字自动触发。d-select已自动debounce
// 直接更新searchOptions数据
};
const handleSearch = ({ type, query }): void => {
// type对应左侧类型query对应搜索框的搜索条件
};
```
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|-----------|------------------------|----------------------------|----------|-----------|
| searchOptions? | 搜索框搜索词关联的下拉框数据 | Array<Option> | - | - |
| typeOptions? | 左侧类型下拉框数据。若数据为空切不插槽,则隐藏类型选择组件 | Array<Option> | - | - |
| placeholder? | 搜索框提示语 | string | - | '搜索...' |
| resetTip? | 搜索框下方重置的文字 | string | - | '清空当前搜索、筛选及排序条件' |
| filter? | d-select参数透传是否开启过滤筛选也可以直接传入过滤方法 | boolean \| function | - | false |
| loading? | d-select参数透传数据加载中(需搭配filter和remote) | boolean | - | false |
| remote? | d-select参数透传是否开启远程搜索需搭配filter和loading | boolean | - | false |
| defaultType | d-select 默认选中项 | string | - | '' |
其中type Option = { name: string, value: string }
### Emits
| 方法 | 说明 | 返回值 |
|-----------------|------------------------------------|---------------------|
| onSearch | 搜索框或者左侧下拉框选中 | { type: string, search: string } |
| onReset | 重置搜索,清空左侧下拉框和搜索框输入 | |
### Slots
| 名称 | 说明 |
|---------------------|---------------------------------------------|
| default | 搜索框下拉内容 |
| search | 搜索框整体插槽 |
| type | 左侧类型下拉框 |
| right | 搜索框右侧 |
| reset | 重置行整体插槽 |

View File

@@ -0,0 +1,131 @@
<template>
<div class="g-search-bar">
<div class="g-search-bar-row">
<div class="g-search-bar-row-search">
<!-- 搜索框-左侧下拉框 -->
<slot name="type">
<d-select
v-if="typeOptions?.length"
class="g-search-bar-row-search-type"
v-model="search.type"
:options="typeOptions"
@value-change="handleSearch"
/>
</slot>
<!-- 搜索框 -->
<slot name="search">
<d-select
class="g-search-bar-row-search-input"
v-model="search.query"
:filter="filter"
:loading="loading"
remote
allow-clear
:placeholder="placeholder"
@value-change="handleSearch"
>
<slot name="default">
<gc-option v-for="item in searchOptions" :key="item.value" :name="item.name" :value="item.value" />
</slot>
</d-select>
</slot>
</div>
<!-- 搜索框右侧内容 -->
<slot name="right"></slot>
</div>
<!-- 搜索框下方清空过滤 -->
<slot name="reset">
<div class="g-search-bar-reset">
<span @click="handleReset">
<i class="icon icon-remove" /> {{ resetTip }}
</span>
</div>
</slot>
</div>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue';
// Option的定义与dev-ui保持一致
type Option = {
name: string,
value: string | number | null,
disabled?: boolean
}
interface IProps {
searchOptions?: Array<Option>,
typeOptions?: Array<Option>,
placeholder?: string,
resetTip?: string,
remote?: boolean,
filter?: boolean | any,
loading?: boolean,
defaultType?: string
}
const props = withDefaults(defineProps<IProps>(), {
defaultType: '',
placeholder: '搜索...',
resetTip: '清空当前搜索、筛选及排序条件'
});
const search: Ref<{
query: string,
type: string
}> = ref({
query: '',
type: props.defaultType
});
const emit = defineEmits(['onSearch', 'onReset']);
// 搜索条件发生变更
const handleSearch = () => {
emit('onSearch', search.value);
};
// 重置搜索条件
const handleReset = () => {
search.value.query = '';
search.value.type = '';
emit('onReset');
};
</script>
<style lang="scss" scoped>
.g-search-bar {
&-row {
display: flex;
gap: 16px;
&-search {
display: inline-flex;
flex: 1;
&-type {
width: 120px;
margin-right: -1px;
:deep(.devui-select__selection) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
& + .g-search-bar-row-search-input {
:deep(.devui-select__selection) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}
&-input {
flex: 1;
}
.devui-select--open {
z-index: 1;
}
}
}
&-reset {
margin-top: 8px;
cursor: pointer;
}
}
</style>