@@ -0,0 +1,837 @@
< template >
< view class = "uni-stat__select" >
< span v-if = "label" class="uni-label-text hide-on-phone" > {{ label + ' : ' }} < / span >
< view class = "uni-stat-box" : class = "{'uni-stat__actived': current}" >
< view class = "uni-select" : class = "{'uni-select--disabled':disabled, 'uni-select--wrap': shouldWrap , 'border-default': mode == 'default','border-bottom': mode == 'underline'}" >
< view class = "uni-select__input-box" @click ="toggleSelector" : class = "{'uni-select__input-box--wrap': shouldWrap}" >
< view v-if = "slotSelected" class="slot-content padding-top-bottom" :class="{'uni-select__input-text--wrap': shouldWrap}" >
< slot name = "selected" :selectedItems = "getSelectedItems()" > < / slot >
< / view >
< template v-else >
< view v-if = "textShow" class="uni-select__input-text" :class="{'uni-select__input-text--wrap': shouldWrap}" >
< view class = "padding-top-bottom" :class = "'align-'+align" > { { textShow } } < / view >
< / view >
< view v-else class = "uni-select__input-text uni-select__input-placeholder" :class = "'align-'+align" > { { typePlaceholder } } < / view >
< / template >
< view key = "clear-button" v-if = "!hideRight && shouldShowClear && clear && !disabled" @click.stop="clearVal" >
< uni -icons type = "clear" color = "#c0c4cc" size = "24" / >
< / view >
< view key = "arrow-button" v-else-if = "!hideRight" >
< uni -icons : type = "showSelector? 'top' : 'bottom'" size = "14" color = "#999" / >
< / view >
< / view >
< view class = "uni-select--mask" v-if = "showSelector" @click="toggleSelector" / >
< view class = "uni-select__selector" :style = "getOffsetByPlacement" v-if = "showSelector" >
< view :class = "placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'" > < / view >
< scroll-view scroll -y = " true " class = "uni-select__selector-scroll" >
< template v-if = "slotEmpty && mixinDatacomResData.length === 0" >
< view class = "uni-select__selector-empty" >
< slot name = "empty" :empty = "emptyTips" > < / slot >
< / view >
< / template >
< template v-else >
< view v-if = "mixinDatacomResData.length === 0" class="uni-select__selector-empty" >
< text > { { emptyTips } } < / text >
< / view >
< / template >
< template v-if = "slotOption" >
< view v-for = "(itemData,index) in mixinDatacomResData" :key="index" @click="change(itemData)" >
< slot name = "option" :item = "itemData" : itemSelected = "multiple? getCurrentValues().includes(itemData.value):getCurrentValues() == itemData.value" > < / slot >
< / view >
< / template >
< template v-else >
< view v-if = "!multiple && mixinDatacomResData.length > 0" class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
@click ="change(item)" >
< text : class = "{'uni-select__selector__disabled': item.disable}" > { { formatItemName ( item ) } } < / text >
< / view >
< view v-if = "multiple && mixinDatacomResData.length > 0" >
< checkbox-group @change ="checkBoxChange" >
< label class = "uni-select__selector-item" v-for = "(item,index) in mixinDatacomResData" :key="index" >
< checkbox :value = "index+''" :checked = "getCurrentValues().includes(item.value)" :disabled = "item.disable" > < / checkbox >
< view : class = "{'uni-select__selector__disabled': item.disable}" > { { formatItemName ( item ) } } < / view >
< / label >
< / checkbox-group >
< / view >
< / template >
< / scroll-view >
< / view >
< / view >
< / view >
< / view >
< / template >
< script >
/**
* DataChecklist 数据选择器
* @description 通过数据渲染的下拉框组件
* @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
* @property {String|Array} value 默认值,多选时为数组
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
* @property {Boolean} clear 是否可以清空已选项
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
* @property {String} label 左侧标题
* @property {String} placeholder 输入框的提示文字
* @property {Boolean} disabled 是否禁用
* @property {Boolean} multiple 是否多选模式
* @property {Boolean} wrap 是否允许选中文本换行显示
* @property {String} placement 弹出位置
* @value top 顶部弹出
* @value bottom 底部弹出(default)
* @property {String} align 选择文字的位置
* @value left 显示左侧
* @value center 显示中间
* @value right 显示 右侧
* @property {Boolean} hideRight 是否隐藏右侧按钮
* @property {String} mode 边框样式
* @value default 四周边框
* @value underline 下边框
* @value none 无边框
* @event {Function} change 选中发生变化触发
* @event {Function} open 选择框开启时触发
* @event {Function} close 选择框关闭时触发
* @event {Function} clear 点击清除按钮之后触发
*/
export default {
name : "uni-data-select" ,
mixins : [ uniCloud . mixinDatacom || { } ] ,
emits : [
'open' ,
'close' ,
'update:modelValue' ,
'input' ,
'clear' ,
'change'
] ,
model : {
prop : 'modelValue' ,
event : 'update:modelValue'
} ,
options : {
// #ifdef MP-TOUTIAO
virtualHost : false ,
// #endif
// #ifndef MP-TOUTIAO
virtualHost : true
// #endif
} ,
props : {
localdata : {
type : Array ,
default ( ) {
return [ ]
}
} ,
value : {
type : [ String , Number , Array ] ,
default : ''
} ,
modelValue : {
type : [ String , Number , Array ] ,
default : ''
} ,
label : {
type : String ,
default : ''
} ,
placeholder : {
type : String ,
default : '请选择'
} ,
emptyTips : {
type : String ,
default : '无选项'
} ,
clear : {
type : Boolean ,
default : true
} ,
defItem : {
type : Number ,
default : 0
} ,
disabled : {
type : Boolean ,
default : false
} ,
// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
format : {
type : String ,
default : ''
} ,
placement : {
type : String ,
default : 'bottom'
} ,
multiple : {
type : Boolean ,
default : false
} ,
wrap : {
type : Boolean ,
default : false
} ,
align : {
type : String ,
default : "left"
} ,
hideRight : {
type : Boolean ,
default : false
} ,
mode : {
type : String ,
default : 'default'
}
} ,
data ( ) {
return {
showSelector : false ,
current : '' ,
mixinDatacomResData : [ ] ,
apps : [ ] ,
channels : [ ] ,
cacheKey : "uni-data-select-lastSelectedValue" ,
} ;
} ,
created ( ) {
this . debounceGet = this . debounce ( ( ) => {
this . query ( ) ;
} , 300 ) ;
if ( this . collection && ! this . localdata . length ) {
this . debounceGet ( ) ;
}
} ,
computed : {
typePlaceholder ( ) {
const text = {
'opendb-stat-app-versions' : '版本' ,
'opendb-app-channels' : '渠道' ,
'opendb-app-list' : '应用'
}
const common = this . placeholder
const placeholder = text [ this . collection ]
return placeholder ?
common + placeholder :
common
} ,
valueCom ( ) {
if ( this . value === '' ) return this . modelValue
if ( this . modelValue === '' ) return this . value
return this . value
} ,
textShow ( ) {
// 长文本显示
if ( this . multiple ) {
const currentValues = this . getCurrentValues ( ) ;
if ( Array . isArray ( currentValues ) && currentValues . length > 0 ) {
const selectedItems = this . mixinDatacomResData . filter ( item => currentValues . includes ( item . value ) ) ;
return selectedItems . map ( item => this . formatItemName ( item ) ) . join ( ', ' ) ;
} else {
return '' ; // 空数组时返回空字符串,显示占位符
}
} else {
return this . current ;
}
} ,
shouldShowClear ( ) {
if ( this . multiple ) {
const currentValues = this . getCurrentValues ( ) ;
return Array . isArray ( currentValues ) && currentValues . length > 0 ;
} else {
return ! ! this . current ;
}
} ,
shouldWrap ( ) {
// 只有在多选模式、开启换行、且有内容时才应用换行样式
return this . multiple && this . wrap && ! ! this . textShow ;
} ,
getOffsetByPlacement ( ) {
switch ( this . placement ) {
case 'top' :
return "bottom:calc(100% + 12px);" ;
case 'bottom' :
return "top:calc(100% + 12px);" ;
}
} ,
slotSelected ( ) {
// #ifdef VUE2
return this . $scopedSlots ? this . $scopedSlots . selected : false
// #endif
// #ifdef VUE3
return this . $slots ? this . $slots . selected : false
// #endif
} ,
slotEmpty ( ) {
// #ifdef VUE2
return this . $scopedSlots ? this . $scopedSlots . empty : false
// #endif
// #ifdef VUE3
return this . $slots ? this . $slots . empty : false
// #endif
} ,
slotOption ( ) {
// #ifdef VUE2
return this . $scopedSlots ? this . $scopedSlots . option : false
// #endif
// #ifdef VUE3
return this . $slots ? this . $slots . option : false
// #endif
}
} ,
watch : {
showSelector : {
handler ( val , old ) {
val ? this . $emit ( 'open' ) : this . $emit ( 'close' )
}
} ,
localdata : {
immediate : true ,
handler ( val , old ) {
if ( Array . isArray ( val ) && old !== val ) {
this . mixinDatacomResData = val
}
}
} ,
valueCom ( val , old ) {
this . initDefVal ( )
} ,
mixinDatacomResData : {
immediate : true ,
handler ( val ) {
if ( val . length ) {
this . initDefVal ( )
}
}
} ,
} ,
methods : {
getSelectedItems ( ) {
const currentValues = this . getCurrentValues ( ) ;
let _minxData = this . mixinDatacomResData
// #ifdef MP-WEIXIN || MP-TOUTIAO
_minxData = JSON . parse ( JSON . stringify ( this . mixinDatacomResData ) )
// #endif
if ( this . multiple ) {
return _minxData . filter ( item => currentValues . includes ( item . value ) ) || [ ] ;
} else {
return _minxData . filter ( item => item . value === currentValues ) || [ ] ;
}
} ,
debounce ( fn , time = 100 ) {
let timer = null
return function ( ... args ) {
if ( timer ) clearTimeout ( timer )
timer = setTimeout ( ( ) => {
fn . apply ( this , args )
} , time )
}
} ,
// 检查项目是否已选中
isSelected ( item ) {
if ( this . multiple ) {
const currentValues = this . getCurrentValues ( ) ;
return Array . isArray ( currentValues ) && currentValues . includes ( item . value ) ;
} else {
return this . getCurrentValues ( ) === item . value ;
}
} ,
// 获取当前选中的值
getCurrentValues ( ) {
if ( this . multiple ) {
return Array . isArray ( this . valueCom ) ? this . valueCom : ( this . valueCom ? [ this . valueCom ] : [ ] ) ;
} else {
return this . valueCom ;
}
} ,
// 执行数据库查询
query ( ) {
this . mixinDatacomEasyGet ( ) ;
} ,
// 监听查询条件变更事件
onMixinDatacomPropsChange ( ) {
if ( this . collection ) {
this . debounceGet ( ) ;
}
} ,
initDefVal ( ) {
let defValue = this . multiple ? [ ] : ''
if ( ( this . valueCom || this . valueCom === 0 ) && ! this . isDisabled ( this . valueCom ) ) {
defValue = this . valueCom
} else {
let strogeValue
if ( this . collection ) {
strogeValue = this . getCache ( )
}
if ( strogeValue || strogeValue === 0 ) {
defValue = strogeValue
} else {
let defItem = this . multiple ? [ ] : ''
if ( this . defItem > 0 && this . defItem <= this . mixinDatacomResData . length ) {
defItem = this . multiple ? [ this . mixinDatacomResData [ this . defItem - 1 ] . value ] : this . mixinDatacomResData [ this . defItem - 1 ] . value
}
defValue = defItem
}
if ( defValue || defValue === 0 || ( this . multiple && Array . isArray ( defValue ) && defValue . length > 0 ) ) {
this . emit ( defValue )
}
}
if ( this . multiple ) {
const selectedValues = Array . isArray ( defValue ) ? defValue : ( defValue ? [ defValue ] : [ ] ) ;
const selectedItems = this . mixinDatacomResData . filter ( item => selectedValues . includes ( item . value ) ) ;
this . current = selectedItems . map ( item => this . formatItemName ( item ) ) ;
} else {
const def = this . mixinDatacomResData . find ( item => item . value === defValue )
this . current = def ? this . formatItemName ( def ) : ''
}
} ,
/**
* @param {[String, Number, Array]} value
* 判断用户给的 value 是否同时为禁用状态
*/
isDisabled ( value ) {
if ( Array . isArray ( value ) ) {
// 对于数组,如果任意一个值被禁用,则认为整体被禁用
return value . some ( val => {
return this . mixinDatacomResData . some ( item => item . value === val && item . disable ) ;
} ) ;
} else {
let isDisabled = false ;
this . mixinDatacomResData . forEach ( item => {
if ( item . value === value ) {
isDisabled = item . disable
}
} )
return isDisabled ;
}
} ,
clearVal ( ) {
const emptyValue = this . multiple ? [ ] : '' ;
this . emit ( emptyValue )
this . current = this . multiple ? [ ] : ''
if ( this . collection ) {
this . removeCache ( )
}
this . $emit ( 'clear' )
} ,
checkBoxChange ( res ) {
let range = res . detail . value
let currentValues = range && range . length > 0 ? range . map ( ( item ) => {
const index = parseInt ( item , 10 ) ;
if ( isNaN ( index ) ) {
console . error ( ` 无效索引: ${ item } ` ) ;
}
if ( index < 0 || index >= this . mixinDatacomResData . length ) {
console . error ( ` 索引越界: ${ index } ` ) ;
}
return this . mixinDatacomResData [ index ] . value ;
} ) : [ ]
const selectedItems = this . mixinDatacomResData . filter ( dataItem => currentValues . includes ( dataItem . value ) ) ;
this . current = selectedItems . map ( dataItem => this . formatItemName ( dataItem ) ) ;
this . emit ( currentValues ) ;
} ,
change ( item ) {
if ( ! item . disable ) {
if ( this . multiple ) {
// 多选模式
let currentValues = this . getCurrentValues ( ) ;
if ( ! Array . isArray ( currentValues ) ) {
currentValues = currentValues ? [ currentValues ] : [ ] ;
}
const itemValue = item . value ;
const index = currentValues . indexOf ( itemValue ) ;
if ( index > - 1 ) {
currentValues . splice ( index , 1 ) ;
} else {
currentValues . push ( itemValue ) ;
}
const selectedItems = this . mixinDatacomResData . filter ( dataItem => currentValues . includes ( dataItem . value ) ) ;
this . current = selectedItems . map ( dataItem => this . formatItemName ( dataItem ) ) ;
this . emit ( currentValues ) ;
} else {
// 单选模式
this . showSelector = false
this . current = this . formatItemName ( item )
this . emit ( item . value )
}
}
} ,
emit ( val ) {
this . $emit ( 'input' , val )
this . $emit ( 'update:modelValue' , val )
this . $emit ( 'change' , val )
if ( this . collection ) {
this . setCache ( val ) ;
}
} ,
toggleSelector ( ) {
if ( this . disabled ) {
return
}
this . showSelector = ! this . showSelector
} ,
formatItemName ( item ) {
let {
text ,
value ,
channel _code
} = item
channel _code = channel _code ? ` ( ${ channel _code } ) ` : ''
if ( this . format ) {
// 格式化输出
let str = "" ;
str = this . format ;
for ( let key in item ) {
str = str . replace ( new RegExp ( ` { ${ key } } ` , "g" ) , item [ key ] ) ;
}
return str ;
} else {
return this . collection . indexOf ( 'app-list' ) > 0 ?
` ${ text } ( ${ value } ) ` :
(
text ?
text :
` 未命名 ${ channel _code } `
)
}
} ,
// 获取当前加载的数据
getLoadData ( ) {
return this . mixinDatacomResData ;
} ,
// 获取当前缓存key
getCurrentCacheKey ( ) {
return this . collection ;
} ,
// 获取缓存
getCache ( name = this . getCurrentCacheKey ( ) ) {
let cacheData = uni . getStorageSync ( this . cacheKey ) || { } ;
return cacheData [ name ] ;
} ,
// 设置缓存
setCache ( value , name = this . getCurrentCacheKey ( ) ) {
let cacheData = uni . getStorageSync ( this . cacheKey ) || { } ;
cacheData [ name ] = value ;
uni . setStorageSync ( this . cacheKey , cacheData ) ;
} ,
// 删除缓存
removeCache ( name = this . getCurrentCacheKey ( ) ) {
let cacheData = uni . getStorageSync ( this . cacheKey ) || { } ;
delete cacheData [ name ] ;
uni . setStorageSync ( this . cacheKey , cacheData ) ;
} ,
}
}
< / script >
< style lang = "scss" >
$uni - base - color : # 6 a6a6a ! default ;
$uni - main - color : # 333 ! default ;
$uni - secondary - color : # 909399 ! default ;
$uni - border - 3 : # e5e5e5 ;
$uni - primary : # 2979 ff ! default ;
$uni - success : # 4 cd964 ! default ;
$uni - warning : # f0ad4e ! default ;
$uni - error : # dd524d ! default ;
$uni - info : # 909399 ! default ;
/* #ifndef APP-NVUE */
@ media screen and ( max - width : 500 px ) {
. hide - on - phone {
display : none ;
}
}
/* #endif */
. uni - stat _ _select {
display : flex ;
align - items : center ;
// padding: 15px;
/* #ifdef H5 */
cursor : pointer ;
/* #endif */
width : 100 % ;
flex : 1 ;
box - sizing : border - box ;
}
. uni - stat - box {
background - color : # fff ;
width : 100 % ;
flex : 1 ;
}
. uni - stat _ _actived {
width : 100 % ;
flex : 1 ;
// outline: 1px solid #2979ff;
}
. uni - label - text {
font - size : 14 px ;
font - weight : bold ;
color : $uni - base - color ;
margin : auto 0 ;
margin - right : 5 px ;
}
. border - bottom {
border - bottom : solid 1 px $uni - border - 3 ;
}
. border - default {
border : 1 px solid $uni - border - 3 ;
}
. uni - select {
font - size : 14 px ;
box - sizing : border - box ;
border - radius : 4 px ;
padding : 0 5 px ;
padding - left : 10 px ;
position : relative ;
/* #ifndef APP-NVUE */
display : flex ;
user - select : none ;
/* #endif */
flex - direction : row ;
align - items : center ;
width : 100 % ;
flex : 1 ;
min - height : 35 px ;
& -- disabled {
background - color : # f5f7fa ;
cursor : not - allowed ;
}
& -- wrap {
height : auto ;
min - height : 35 px ;
// align-items: flex-start;
}
}
. uni - select _ _label {
font - size : 16 px ;
// line-height: 22px;
height : 35 px ;
padding - right : 10 px ;
color : $uni - secondary - color ;
}
. uni - select _ _input - box {
// height: 35px;
width : 0 px ;
position : relative ;
/* #ifndef APP-NVUE */
display : flex ;
/* #endif */
flex : 1 ;
flex - direction : row ;
align - items : center ;
& -- wrap {
. uni - select _ _input - text {
margin - right : 8 px ;
}
}
. padding - top - bottom {
padding - top : 5 px ;
padding - bottom : 5 px ;
}
. slot - content {
width : 100 % ;
display : flex ;
flex - direction : row ;
flex - wrap : wrap ;
}
}
. uni - select _ _input {
flex : 1 ;
font - size : 14 px ;
height : 22 px ;
line - height : 22 px ;
}
. uni - select _ _input - plac {
font - size : 14 px ;
color : $uni - secondary - color ;
}
. uni - select _ _selector {
/* #ifndef APP-NVUE */
box - sizing : border - box ;
/* #endif */
position : absolute ;
left : 0 ;
width : 100 % ;
background - color : # FFFFFF ;
border : 1 px solid # EBEEF5 ;
border - radius : 6 px ;
box - shadow : 0 2 px 12 px 0 rgba ( 0 , 0 , 0 , 0.1 ) ;
z - index : 3 ;
padding : 4 px 0 ;
}
. uni - select _ _selector - scroll {
/* #ifndef APP-NVUE */
max - height : 200 px ;
box - sizing : border - box ;
/* #endif */
}
/* #ifdef H5 */
@ media ( min - width : 768 px ) {
. uni - select _ _selector - scroll {
max - height : 600 px ;
}
}
/* #endif */
. uni - select _ _selector - empty ,
. uni - select _ _selector - item {
/* #ifndef APP-NVUE */
display : flex ;
cursor : pointer ;
/* #endif */
flex - direction : row ;
align - items : center ;
line - height : 35 px ;
font - size : 14 px ;
/* border-bottom: solid 1px $uni-border-3; */
padding : 0 px 10 px ;
}
. uni - select _ _selector - item - check {
margin - left : auto ;
}
. uni - select _ _selector - empty : last - child ,
. uni - select _ _selector - item : last - child {
/* #ifndef APP-NVUE */
border - bottom : none ;
/* #endif */
}
. uni - 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 : 6 px ;
}
. uni - popper _ _arrow _bottom {
filter : drop - shadow ( 0 2 px 12 px rgba ( 0 , 0 , 0 , 0.03 ) ) ;
top : - 6 px ;
left : 10 % ;
margin - right : 3 px ;
border - top - width : 0 ;
border - bottom - color : # EBEEF5 ;
}
. uni - popper _ _arrow _bottom : : after {
content : " " ;
top : 1 px ;
margin - left : - 6 px ;
border - top - width : 0 ;
border - bottom - color : # fff ;
}
. uni - popper _ _arrow _top {
filter : drop - shadow ( 0 2 px 12 px rgba ( 0 , 0 , 0 , 0.03 ) ) ;
bottom : - 6 px ;
left : 10 % ;
margin - right : 3 px ;
border - bottom - width : 0 ;
border - top - color : # EBEEF5 ;
}
. uni - popper _ _arrow _top : : after {
content : " " ;
bottom : 1 px ;
margin - left : - 6 px ;
border - bottom - width : 0 ;
border - top - color : # fff ;
}
. uni - select _ _input - text {
// width: 280px;
width : 100 % ;
color : $uni - main - color ;
white - space : nowrap ;
text - overflow : ellipsis ;
- o - text - overflow : ellipsis ;
overflow : hidden ;
& -- wrap {
white - space : normal ;
text - overflow : initial ;
- o - text - overflow : initial ;
overflow : visible ;
word - wrap : break - word ;
word - break : break - all ;
// line-height: 1.5;
}
}
. uni - select _ _input - placeholder {
color : $uni - base - color ;
font - size : 12 px ;
margin : 1 px 0 ;
}
. uni - 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 >