386 lines
8.1 KiB
Vue
386 lines
8.1 KiB
Vue
<template>
|
|
<view class="filter-select">
|
|
<view class="filter-box" :class="{ 'filter__actived': showSelector }">
|
|
<view class="filter-select border-default" :class="{ 'filter-select--disabled': disabled }">
|
|
<view class="filter-select__input-box" @click="toggleSelector">
|
|
<input
|
|
v-model="searchText"
|
|
:disabled="disabled"
|
|
placeholder-class="input-placeholder"
|
|
:placeholder="multiple ? '请选择(可多选)' : '请选择'"
|
|
style="border:none;width:88%;padding-left: 10upx;"
|
|
/>
|
|
<view v-if="shouldShowClear && clear" @click.stop="clearVal">
|
|
<uni-icons type="clear" size="20" color="#c0c4cc" />
|
|
</view>
|
|
<uni-icons :type="showSelector ? 'top' : 'bottom'" size="14" />
|
|
</view>
|
|
<!-- 遮罩 -->
|
|
<view v-if="showSelector" class="filter-select--mask" @click="toggleSelector" />
|
|
<!-- 下拉 -->
|
|
<view v-if="showSelector" class="filter-select__selector" :style="offsetStyle">
|
|
<scroll-view scroll-y style="height:200px" @scrolltolower="loadMoreData">
|
|
<!-- 单选 -->
|
|
<view
|
|
v-if="!multiple"
|
|
v-for="item in filterSelectData"
|
|
:key="item.value"
|
|
class="filter-select__selector-item"
|
|
@click="change(item)"
|
|
>
|
|
<text>{{ item.text }}</text>
|
|
</view>
|
|
<!-- 多选 -->
|
|
<checkbox-group v-else @change="checkBoxChange">
|
|
<label
|
|
v-for="item in filterSelectData"
|
|
:key="item.value"
|
|
class="filter-select__selector-item"
|
|
>
|
|
<checkbox
|
|
:value="item.value"
|
|
:checked="modelValue.includes(item.value)"
|
|
/>
|
|
<text>{{ item.text }}</text>
|
|
</label>
|
|
</checkbox-group>
|
|
</scroll-view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'data-select',
|
|
emits: ['update:modelValue', 'change', 'clear', 'open', 'close'],
|
|
props: {
|
|
modelValue: {
|
|
type: [String, Number, Array],
|
|
default: () => []
|
|
},
|
|
apiobjs: {
|
|
type: Function, // ✅ 修正
|
|
required: true
|
|
},
|
|
multiple: { type: Boolean, default: false },
|
|
clear: { type: Boolean, default: true },
|
|
disabled: { type: Boolean, default: false },
|
|
placement: { type: String, default: 'bottom' }
|
|
},
|
|
data() {
|
|
return {
|
|
showSelector: false,
|
|
searchText: '',
|
|
filterSelectData: [],
|
|
params: { page: 1, page_size: 10, search: '' },
|
|
hasMore: true
|
|
}
|
|
},
|
|
computed: {
|
|
shouldShowClear() {
|
|
return this.multiple
|
|
? this.modelValue.length > 0
|
|
: !!this.modelValue
|
|
},
|
|
offsetStyle() {
|
|
return this.placement === 'bottom'
|
|
? 'top:100%'
|
|
: 'bottom:100%'
|
|
}
|
|
},
|
|
watch: {
|
|
searchText() {
|
|
this.resetAndLoad()
|
|
}
|
|
},
|
|
methods: {
|
|
async getData() {
|
|
if (!this.hasMore) return
|
|
const res = await this.apiobjs(this.params)
|
|
const list = res?.results || []
|
|
if (list.length < 10) this.hasMore = false
|
|
this.filterSelectData.push(
|
|
...list.map(i => ({ text: i.name, value: i.id }))
|
|
)
|
|
},
|
|
resetAndLoad() {
|
|
this.params.page = 1
|
|
this.hasMore = true
|
|
this.filterSelectData = []
|
|
this.params.search = this.searchText
|
|
this.getData()
|
|
},
|
|
loadMoreData() {
|
|
if (!this.hasMore) return
|
|
this.params.page++
|
|
this.getData()
|
|
},
|
|
change(item) {
|
|
this.emit(item.value)
|
|
this.searchText = item.text
|
|
this.showSelector = false
|
|
},
|
|
checkBoxChange(e) {
|
|
const values = e.detail.value
|
|
this.emit(values)
|
|
},
|
|
clearVal() {
|
|
this.searchText = ''
|
|
this.emit(this.multiple ? [] : '')
|
|
this.$emit('clear')
|
|
},
|
|
emit(val) {
|
|
this.$emit('update:modelValue', val)
|
|
this.$emit('change', val)
|
|
},
|
|
toggleSelector() {
|
|
if (this.disabled) return
|
|
this.showSelector = !this.showSelector
|
|
this.$emit(this.showSelector ? 'open' : 'close')
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
$uni-base-color: #6a6a6a !default;
|
|
$uni-main-color: #333 !default;
|
|
$uni-secondary-color: #909399 !default;
|
|
$uni-border-3: #e5e5e5;
|
|
$uni-primary: #2979ff !default;
|
|
$uni-success: #4cd964 !default;
|
|
$uni-warning: #f0ad4e !default;
|
|
$uni-error: #dd524d !default;
|
|
$uni-info: #909399 !default;
|
|
|
|
.filter-select {
|
|
display: flex;
|
|
align-items: center;
|
|
width: 100%;
|
|
flex: 1;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.filter-box {
|
|
background-color: #fff;
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
|
|
.filter__actived {
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
|
|
.border-bottom {
|
|
border-bottom: solid 1px $uni-border-3;
|
|
}
|
|
|
|
.border-default {
|
|
border: 1px solid $uni-border-3;
|
|
}
|
|
|
|
.filter-select {
|
|
font-size: 14px;
|
|
box-sizing: border-box;
|
|
border-radius: 4px;
|
|
padding: 0;
|
|
position: relative;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
width: 100%;
|
|
flex: 1;
|
|
min-height: 35px;
|
|
|
|
&--disabled {
|
|
background-color: #f5f7fa;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
&--wrap {
|
|
height: auto;
|
|
min-height: 35px;
|
|
}
|
|
}
|
|
|
|
.filter-select__label {
|
|
font-size: 16px;
|
|
height: 35px;
|
|
padding-right: 10px;
|
|
color: $uni-secondary-color;
|
|
}
|
|
|
|
.filter-select__input-box {
|
|
position: relative;
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
|
|
.padding-top-bottom {
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
.slot-content {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
|
|
.filter-select__input {
|
|
flex: 1;
|
|
font-size: 14px;
|
|
height: 22px;
|
|
line-height: 22px;
|
|
}
|
|
|
|
.filter-select__input-plac {
|
|
font-size: 14px;
|
|
color: $uni-secondary-color;
|
|
}
|
|
|
|
.filter-select__selector {
|
|
/* #ifndef APP-NVUE */
|
|
box-sizing: border-box;
|
|
/* #endif */
|
|
position: absolute;
|
|
left: 0;
|
|
width: 100%;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #EBEEF5;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
z-index: 3;
|
|
padding: 4px 0;
|
|
}
|
|
|
|
.filter-select__selector-scroll {
|
|
/* #ifndef APP-NVUE */
|
|
height: 200px;
|
|
box-sizing: border-box;
|
|
/* #endif */
|
|
}
|
|
|
|
/* #ifdef H5 */
|
|
@media (min-width: 768px) {
|
|
.filter-select__selector-scroll {
|
|
max-height: 600px;
|
|
}
|
|
}
|
|
|
|
/* #endif */
|
|
|
|
.filter-select__selector-empty,
|
|
.filter-select__selector-item {
|
|
/* #ifndef APP-NVUE */
|
|
display: flex;
|
|
cursor: pointer;
|
|
/* #endif */
|
|
flex-direction: row;
|
|
align-items: center;
|
|
line-height: 35px;
|
|
font-size: 14px;
|
|
padding: 0px 10px;
|
|
}
|
|
|
|
|
|
|
|
.filter-select__selector-item-check {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.filter-select__selector-empty:last-child,
|
|
.filter-select__selector-item:last-child {
|
|
/* #ifndef APP-NVUE */
|
|
border-bottom: none;
|
|
/* #endif */
|
|
}
|
|
|
|
.filter-select__selector__disabled {
|
|
opacity: 0.4;
|
|
cursor: default;
|
|
}
|
|
|
|
/* picker 弹出层通用的指示小三角 */
|
|
.uni-popper__arrow_bottom,
|
|
.uni-popper__arrow_bottom::after,
|
|
.uni-popper__arrow_top,
|
|
.uni-popper__arrow_top::after {
|
|
position: absolute;
|
|
display: block;
|
|
width: 0;
|
|
height: 0;
|
|
border-color: transparent;
|
|
border-style: solid;
|
|
border-width: 6px;
|
|
}
|
|
|
|
.uni-popper__arrow_bottom {
|
|
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
|
|
top: -6px;
|
|
left: 10%;
|
|
margin-right: 3px;
|
|
border-top-width: 0;
|
|
border-bottom-color: #EBEEF5;
|
|
}
|
|
|
|
.uni-popper__arrow_bottom::after {
|
|
content: " ";
|
|
top: 1px;
|
|
margin-left: -6px;
|
|
border-top-width: 0;
|
|
border-bottom-color: #fff;
|
|
}
|
|
|
|
.uni-popper__arrow_top {
|
|
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
|
|
bottom: -6px;
|
|
left: 10%;
|
|
margin-right: 3px;
|
|
border-bottom-width: 0;
|
|
border-top-color: #EBEEF5;
|
|
}
|
|
|
|
.uni-popper__arrow_top::after {
|
|
content: " ";
|
|
bottom: 1px;
|
|
margin-left: -6px;
|
|
border-bottom-width: 0;
|
|
border-top-color: #fff;
|
|
}
|
|
|
|
|
|
.input-placeholder {
|
|
color: $uni-base-color;
|
|
font-size: 12px;
|
|
margin: 1px 0;
|
|
}
|
|
|
|
.filter-select--mask {
|
|
position: fixed;
|
|
top: 0;
|
|
bottom: 0;
|
|
right: 0;
|
|
left: 0;
|
|
z-index: 2;
|
|
}
|
|
|
|
.align-left {
|
|
text-align: left;
|
|
}
|
|
|
|
.align-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.align-right {
|
|
text-align: right;
|
|
}
|
|
|
|
</style>
|