一、效果
废话不多说,上效果图:
- 在下方的:
- 在上方的:
二、源码
一般是个输入框,输入关键词,下拉一个搜索列表。
ElementUI
有提供<el-autocomplete>
,但uniapp官网没提供这么细,特简单扩展了一下:
1、/ components / input-search.vue
<template><view class="uni-stat__select" :class="'uni-stat__select_'+algin"><view class="uni-stat-box" :class="{'uni-stat__actived': current}"><view class="uni-select__input-box" @click="toggleSelector"><slot ref="slot"></slot></view><view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /><view class="uni-select__selector" v-if="showSelector && current"><view class="uni-popper__arrow"></view><scroll-view scroll-y="true" class="uni-select__selector-scroll"><view class="uni-select__selector-empty" v-if="loadState==0"><text class="cbbb">加载中...</text></view><view class="uni-select__selector-empty" v-else-if="loadState== 1"><text class="cbbb">请求失败,请稍后重试!</text></view><view class="uni-select__selector-empty" v-else-if="loadState== 3"><text class="cbbb">请输入关键词联想~</text></view><view class="uni-select__selector-empty" v-else-if="loadState== 4"><text class="cbbb">没有相关数据!</text></view><view class="uni-select__selector-empty" v-else-if="!list || list.length === 0"><text>{{emptyTips}}</text></view><view v-else class="uni-select__selector-item" v-for="(item,index) in list" :key="index" @click="change(item)"><text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text></view></scroll-view></view></view></view>
</template>
<script>export default {name: "input-search",props: {type: {type: [String, Number],require: true,},algin: {type: String,default: 'bottom',},value: {type: [String, Number],default: ''},modelValue: {type: [String, Number],default: ''},emptyTips: {type: String,default: '无选项'},// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}"format: {type: String,default: ''},},watch: {value(newVal) {this.current = newVal;},modelValue(newVal) {this.current = newVal;},current() {this.search();},},data() {return {showSelector: false,current: '',loadState: -1, //-1初始化0加载中1请求失败2成功3请输入4没有数据list: [],searchSyncString: '',};},created() {this.$nextTick(this.init);},methods: {isDisabled(value) {let isDisabled = false;this.mixinDatacomResData.forEach(item => {if (item.value === value) {isDisabled = item.disable}})return isDisabled;},change(item) {if (item.disable) return;this.showSelector = false;this.current = this.formatItemName(item);this.confirm(item);// console.warn(item)},toggleSelector() {if (this.disabled) return;this.showSelector = !this.showSelector;if (this.showSelector) this.search();},formatItemName(item) {let {text,value,channel_code} = itemchannel_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;}return `${text}`;},init() {this.current = this.value || this.modelValue || '';if (this.current) this.search();this.computeButtom();},getDefaultSlotElem() {let temp = this.$scopedSlots.default;if (temp && typeof(temp) == 'function') {temp = temp();temp = temp && temp[0] ? temp[0].elm : null;}return temp || null;},computeButtom() {this.soltHeight = (this.getDefaultSlotElem() || {}).clientHeight || 40;},search() {let searchSyncString = this.searchSyncString = Math.random();setTimeout(() => {if (searchSyncString != this.searchSyncString) return;this.searchSync();}, 500);},searchSync() {console.log('searchSync::::::', this.loadState, this.current);if (this.loadState == 0 || !this.current) return;this.loadState = 0;// if (!this.current) {// this.loadState = 3;// return;// }if (this.copySearchText == this.current && this.list && this.list.length > 0) {this.loadState = 2;return;}let params = {};params.categoryCode = this.type;params.searchText = this.copySearchText = this.current || '';this.$request.post('texts/list', params).then((j) => {this.loadState = 2;if (!j.code || j.code != 1) {this.loadState = 1;return this.$errToast(j, '联想词条');}let list = j.data || [];if (!list || list.length < 1) {this.loadState = 4;return;}list.forEach((item, index) => {item.value = item.id;item.text = item.name || '-';});this.list = list;}).catch((res) => {this.loadState = 1;return this.$errToast(j, '联想词条');});this.computeButtom();},confirm(item) {this.$emit('confirm', item.text || item.name|| '');this.loadState = 0;setTimeout(() => {this.loadState = 2;this.computeButtom();}, 800);},},}
</script>
<style lang="scss" scoped>$uni-border-3: #e5e5e5;.uni-stat__select {position: relative;}.uni-stat-box {width: 100%;flex: 1;}.uni-stat__actived {width: 100%;flex: 1;// outline: 1px solid #2979ff;}.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-stat__select_top .uni-select__selector {bottom: calc(100% + 12px);top: auto;.uni-popper__arrow {bottom: -6px;top: auto;transform: rotate(180deg);}}.uni-select__selector-scroll {/* #ifndef APP-NVUE */max-height: 200px;box-sizing: border-box;/* #endif */}/* #ifdef H5 */@media (min-width: 768px) {.uni-select__selector-scroll {max-height: 600px;}}/* #endif */.uni-select__selector-empty,.uni-select__selector-item {/* #ifndef APP-NVUE */display: flex;cursor: pointer;/* #endif */line-height: 1.3;font-size: 14px;text-align: left;/* border-bottom: solid 1px $uni-border-3; */padding: 2px 10px;margin: 6px 0;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;}.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--mask {position: fixed;top: 0;bottom: 0;right: 0;left: 0;z-index: 2;}.cbbb {color: #bbb !important;}
</style>
2、/ pages / xxx / demo.vue
<template><input-search :type="1" :value="handleResult" algin="top" @confirm="confirmInputSearch($event, 'handleResult')"><input class="uni-input" type="text" placeholder="请填写结果" v-model="handleResult" /></input-search><!-- ...... -->
</template>
import inputSearch from "@/components/input-search.vue";
export default {components: {inputSearch,},data() {handleResult: "",},methods: {confirmInputSearch(val, key) {this.$set(this, key, val);},},//......
},
三、参数说明:
名称 | 类型 | 说明 |
---|---|---|
type | int | 词条类型,如果同个页面有多个联想, 且联想内容不一致时,用此字段与接口对接 |
value | String | 词条内容 |
algin | String | null | top,弹出框的方向,默认bottom |
emptyTips | String | 当词条内容为空时,显示的文本内容(未纳入) |
@confirm | Method | 选中事件,点击了联想内容的一个。返回联想内容text |
四、续
-
功能优势:
- 官方样式,好看啦
- 可扩展
- 支持input、textarea等控件
-
扩展
- 输出格式 format
- 禁用 item内容
- 未完待续…
[The end]