<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}"> <view class="uni-select__input-box" @click="toggleSelector"> <view v-if="current" class="uni-select__input-text">{{current}}</view> <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> <uni-icons v-if="current && clear" type="clear" color="#c0c4cc" size="24" @click="clearVal" /> <uni-icons v-else :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> </view> <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> <view class="uni-select__selector" v-if="showSelector"> <view class="uni-popper__arrow"></view> <scroll-view scroll-y="true" class="uni-select__selector-scroll"> <view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> <text>{{emptyTips}}</text> </view> <view v-else 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> </scroll-view> </view> </view> </view> </view> </template> <script> /** * DataChecklist 数据选择器 * @description 通过数据渲染的下拉框组件 * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select * @property {String} value 默认值 * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] * @property {Boolean} clear 是否可以清空已选项 * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 * @property {String} label 左侧标题 * @property {String} placeholder 输入框的提示文字 * @property {Boolean} disabled 是否禁用 * @event {Function} change 选中发生变化触发 */ export default { name: "uni-stat-select", mixins: [uniCloud.mixinDatacom || {}], data() { return { showSelector: false, current: '', mixinDatacomResData: [], apps: [], channels: [] }; }, props: { localdata: { type: Array, default () { return [] } }, value: { type: [String, Number], default: '' }, modelValue: { type: [String, Number], 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 } }, created() { this.last = `${this.collection}_last_selected_option_value` if (this.collection && !this.localdata.length) { this.query(); } }, 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 } }, watch: { localdata: { immediate: true, handler(val, old) { if (Array.isArray(val) && old !== val) { this.mixinDatacomResData = val } } }, // #ifndef VUE3 value() { this.initDefVal() }, // #endif // #ifdef VUE3 modelValue() { this.initDefVal() }, // #endif mixinDatacomResData: { immediate: true, handler(val) { if (val.length) { this.initDefVal() } } } }, methods: { // 执行数据库查询 query(){ this.mixinDatacomEasyGet(); }, // 监听查询条件变更事件 onMixinDatacomPropsChange(){ this.query(); }, initDefVal() { let defValue = '' if ((this.value || this.value === 0) && !this.isDisabled(this.value)) { defValue = this.value } else if ((this.modelValue || this.modelValue === 0) && !this.isDisabled(this.modelValue)) { defValue = this.modelValue } else { let strogeValue if (this.collection) { strogeValue = uni.getStorageSync(this.last) } if (strogeValue || strogeValue === 0) { defValue = strogeValue } else { let defItem = '' if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { defItem = this.mixinDatacomResData[this.defItem - 1].value } defValue = defItem } if (defValue || defValue === 0) { this.emit(defValue) } } const def = this.mixinDatacomResData.find(item => item.value === defValue) this.current = def ? this.formatItemName(def) : '' }, /** * @param {[String, Number]} value * 判断用户给的 value 是否同时为禁用状态 */ isDisabled(value) { let isDisabled = false; this.mixinDatacomResData.forEach(item => { if (item.value === value) { isDisabled = item.disable } }) return isDisabled; }, clearVal() { this.emit('') if (this.collection) { uni.removeStorageSync(this.last) } }, change(item) { if (!item.disable) { this.showSelector = false this.current = this.formatItemName(item) this.emit(item.value) } }, emit(val) { this.$emit('change', val) this.$emit('input', val) this.$emit('update:modelValue', val) if (this.collection) { uni.setStorageSync(this.last, val) } }, toggleSelector() { if (this.disabled) { return } this.showSelector = !this.showSelector }, formatItemName(item) { let { text, value, channel_code } = item channel_code = channel_code ? `(${channel_code})` : '' return this.collection.indexOf('app-list') > 0 ? `${text}(${value})` : ( text ? text : `未命名${channel_code}` ) } } } </script> <style lang="scss"> $uni-base-color: #6a6a6a !default; $uni-main-color: #333 !default; $uni-secondary-color: #909399 !default; $uni-border-3: #e5e5e5; /* #ifndef APP-NVUE */ @media screen and (max-width: 500px) { .hide-on-phone { display: none; } } /* #endif */ .uni-stat__select { display: flex; align-items: center; // padding: 15px; cursor: pointer; width: 100%; flex: 1; box-sizing: border-box; } .uni-stat-box { width: 100%; flex: 1; } .uni-stat__actived { width: 100%; flex: 1; // outline: 1px solid #2979ff; } .uni-label-text { font-size: 14px; font-weight: bold; color: $uni-base-color; margin: auto 0; margin-right: 5px; } .uni-select { font-size: 14px; border: 1px solid $uni-border-3; box-sizing: border-box; border-radius: 4px; padding: 0 5px; padding-left: 10px; position: relative; /* #ifndef APP-NVUE */ display: flex; user-select: none; /* #endif */ flex-direction: row; align-items: center; border-bottom: solid 1px $uni-border-3; width: 100%; flex: 1; height: 35px; &--disabled { background-color: #f5f7fa; cursor: not-allowed; } } .uni-select__label { font-size: 16px; // line-height: 22px; height: 35px; padding-right: 10px; color: $uni-secondary-color; } .uni-select__input-box { height: 35px; position: relative; /* #ifndef APP-NVUE */ display: flex; /* #endif */ flex: 1; flex-direction: row; align-items: center; } .uni-select__input { flex: 1; font-size: 14px; height: 22px; line-height: 22px; } .uni-select__input-plac { font-size: 14px; color: $uni-secondary-color; } .uni-select__selector { /* #ifndef APP-NVUE */ box-sizing: border-box; /* #endif */ position: absolute; top: calc(100% + 12px); 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; } .uni-select__selector-scroll { /* #ifndef APP-NVUE */ max-height: 200px; box-sizing: border-box; /* #endif */ } .uni-select__selector-empty, .uni-select__selector-item { /* #ifndef APP-NVUE */ display: flex; cursor: pointer; /* #endif */ line-height: 35px; font-size: 14px; text-align: center; /* border-bottom: solid 1px $uni-border-3; */ padding: 0px 10px; } .uni-select__selector-item:hover { background-color: #f9f9f9; } .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, .uni-popper__arrow::after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; border-width: 6px; } .uni-popper__arrow { 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::after { content: " "; top: 1px; margin-left: -6px; border-top-width: 0; border-bottom-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; } .uni-select__input-placeholder { color: $uni-base-color; font-size: 12px; } .uni-select--mask { position: fixed; top: 0; bottom: 0; right: 0; left: 0; } </style>