<template> 
 | 
    <view 
 | 
        class="u-search" 
 | 
        @tap="clickHandler" 
 | 
        :style="[{ 
 | 
            margin: margin, 
 | 
        }, $u.addStyle(customStyle)]" 
 | 
    > 
 | 
        <view 
 | 
            class="u-search__content" 
 | 
            :style="{ 
 | 
                backgroundColor: bgColor, 
 | 
                borderRadius: shape == 'round' ? '100px' : '4px', 
 | 
                borderColor: borderColor, 
 | 
            }" 
 | 
        > 
 | 
            <template v-if="$slots.label || label !== null"> 
 | 
                <slot name="label"> 
 | 
                    <text class="u-search__content__label">{{ label }}</text> 
 | 
                </slot> 
 | 
            </template> 
 | 
            <view class="u-search__content__icon"> 
 | 
                <u-icon 
 | 
                    @tap="clickIcon" 
 | 
                    :size="searchIconSize" 
 | 
                    :name="searchIcon" 
 | 
                    :color="searchIconColor ? searchIconColor : color" 
 | 
                ></u-icon> 
 | 
            </view> 
 | 
            <input 
 | 
                confirm-type="search" 
 | 
                @blur="blur" 
 | 
                :value="value" 
 | 
                @confirm="search" 
 | 
                @input="inputChange" 
 | 
                :disabled="disabled" 
 | 
                @focus="getFocus" 
 | 
                :focus="focus" 
 | 
                :maxlength="maxlength" 
 | 
                placeholder-class="u-search__content__input--placeholder" 
 | 
                :placeholder="placeholder" 
 | 
                :placeholder-style="`color: ${placeholderColor}`" 
 | 
                class="u-search__content__input" 
 | 
                type="text" 
 | 
                :style="[{ 
 | 
                    textAlign: inputAlign, 
 | 
                    color: color, 
 | 
                    backgroundColor: bgColor, 
 | 
                    height: $u.addUnit(height) 
 | 
                }, inputStyle]" 
 | 
            /> 
 | 
            <view 
 | 
                class="u-search__content__icon u-search__content__close" 
 | 
                v-if="keyword && clearabled && focused" 
 | 
                @tap="clear" 
 | 
            > 
 | 
                <u-icon 
 | 
                    name="close" 
 | 
                    size="11" 
 | 
                    color="#ffffff" 
 | 
                    customStyle="line-height: 12px" 
 | 
                ></u-icon> 
 | 
            </view> 
 | 
        </view> 
 | 
        <text 
 | 
            :style="[actionStyle]" 
 | 
            class="u-search__action" 
 | 
            :class="[(showActionBtn || show) && 'u-search__action--active']" 
 | 
            @tap.stop.prevent="custom" 
 | 
        >{{ actionText }}</text> 
 | 
    </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
    import props from './props.js'; 
 | 
  
 | 
    /** 
 | 
     * search 搜索框 
 | 
     * @description 搜索组件,集成了常见搜索框所需功能,用户可以一键引入,开箱即用。 
 | 
     * @tutorial https://www.uviewui.com/components/search.html 
 | 
     * @property {String}            shape                搜索框形状,round-圆形,square-方形(默认 'round' ) 
 | 
     * @property {String}            bgColor                搜索框背景颜色(默认 '#f2f2f2' ) 
 | 
     * @property {String}            placeholder            占位文字内容(默认 '请输入关键字' ) 
 | 
     * @property {Boolean}            clearabled            是否启用清除控件(默认 true ) 
 | 
     * @property {Boolean}            focus                是否自动获得焦点(默认 false ) 
 | 
     * @property {Boolean}            showAction            是否显示右侧控件(默认 true ) 
 | 
     * @property {Object}            actionStyle            右侧控件的样式,对象形式 
 | 
     * @property {String}            actionText            右侧控件文字(默认 '搜索' ) 
 | 
     * @property {String}            inputAlign            输入框内容水平对齐方式 (默认 'left' ) 
 | 
     * @property {Object}            inputStyle            自定义输入框样式,对象形式 
 | 
     * @property {Boolean}            disabled            是否启用输入框(默认 false ) 
 | 
     * @property {String}            borderColor            边框颜色,配置了颜色,才会有边框 (默认 'transparent' ) 
 | 
     * @property {String}            searchIconColor        搜索图标的颜色,默认同输入框字体颜色 (默认 '#909399' ) 
 | 
     * @property {Number | String}    searchIconSize 搜索图标的字体,默认22 
 | 
     * @property {String}            color                输入框字体颜色(默认 '#606266' ) 
 | 
     * @property {String}            placeholderColor    placeholder的颜色(默认 '#909399' ) 
 | 
     * @property {String}            searchIcon            输入框左边的图标,可以为uView图标名称或图片路径  (默认 'search' ) 
 | 
     * @property {String}            margin                组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px"   (默认 '0' ) 
 | 
     * @property {Boolean}             animation            是否开启动画,见上方说明(默认 false ) 
 | 
     * @property {String}            value                输入框初始值 
 | 
     * @property {String | Number}    maxlength            输入框最大能输入的长度,-1为不限制长度  (默认 '-1' ) 
 | 
     * @property {String | Number}    height                输入框高度,单位px(默认 64 ) 
 | 
     * @property {String | Number}    label                搜索框左边显示内容 
 | 
     * @property {Object}            customStyle            定义需要用到的外部样式 
 | 
     * 
 | 
     * @event {Function} change 输入框内容发生变化时触发 
 | 
     * @event {Function} search 用户确定搜索时触发,用户按回车键,或者手机键盘右下角的"搜索"键时触发 
 | 
     * @event {Function} custom 用户点击右侧控件时触发 
 | 
     * @event {Function} clear 用户点击清除按钮时触发 
 | 
     * @example <u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search> 
 | 
     */ 
 | 
    export default { 
 | 
        name: "u-search", 
 | 
        mixins: [uni.$u.mpMixin, uni.$u.mixin,props], 
 | 
        data() { 
 | 
            return { 
 | 
                keyword: '', 
 | 
                showClear: false, // 是否显示右边的清除图标 
 | 
                show: false, 
 | 
                // 标记input当前状态是否处于聚焦中,如果是,才会显示右侧的清除控件 
 | 
                focused: this.focus 
 | 
                // 绑定输入框的值 
 | 
                // inputValue: this.value 
 | 
            }; 
 | 
        }, 
 | 
        watch: { 
 | 
            keyword(nVal) { 
 | 
                // 双向绑定值,让v-model绑定的值双向变化 
 | 
                this.$emit('input', nVal); 
 | 
                // 触发change事件,事件效果和v-model双向绑定的效果一样,让用户多一个选择 
 | 
                this.$emit('change', nVal); 
 | 
            }, 
 | 
            value: { 
 | 
                immediate: true, 
 | 
                handler(nVal) { 
 | 
                    this.keyword = nVal; 
 | 
                } 
 | 
            } 
 | 
        }, 
 | 
        computed: { 
 | 
            showActionBtn() { 
 | 
                return !this.animation && this.showAction 
 | 
            } 
 | 
        }, 
 | 
        methods: { 
 | 
            // 目前HX2.6.9 v-model双向绑定无效,故监听input事件获取输入框内容的变化 
 | 
            inputChange(e) { 
 | 
                this.keyword = e.detail.value; 
 | 
            }, 
 | 
            // 清空输入 
 | 
            // 也可以作为用户通过this.$refs形式调用清空输入框内容 
 | 
            clear() { 
 | 
                this.keyword = ''; 
 | 
                // 延后发出事件,避免在父组件监听clear事件时,value为更新前的值(不为空) 
 | 
                this.$nextTick(() => { 
 | 
                    this.$emit('clear'); 
 | 
                }) 
 | 
            }, 
 | 
            // 确定搜索 
 | 
            search(e) { 
 | 
                this.$emit('search', e.detail.value); 
 | 
                try { 
 | 
                    // 收起键盘 
 | 
                    uni.hideKeyboard(); 
 | 
                } catch (e) {} 
 | 
            }, 
 | 
            // 点击右边自定义按钮的事件 
 | 
            custom() { 
 | 
                this.$emit('custom', this.keyword); 
 | 
                try { 
 | 
                    // 收起键盘 
 | 
                    uni.hideKeyboard(); 
 | 
                } catch (e) {} 
 | 
            }, 
 | 
            // 获取焦点 
 | 
            getFocus() { 
 | 
                this.focused = true; 
 | 
                // 开启右侧搜索按钮展开的动画效果 
 | 
                if (this.animation && this.showAction) this.show = true; 
 | 
                this.$emit('focus', this.keyword); 
 | 
            }, 
 | 
            // 失去焦点 
 | 
            blur() { 
 | 
                // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错 
 | 
                // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时 
 | 
                setTimeout(() => { 
 | 
                    this.focused = false; 
 | 
                }, 100) 
 | 
                this.show = false; 
 | 
                this.$emit('blur', this.keyword); 
 | 
            }, 
 | 
            // 点击搜索框,只有disabled=true时才发出事件,因为禁止了输入,意味着是想跳转真正的搜索页 
 | 
            clickHandler() { 
 | 
                if (this.disabled) this.$emit('click'); 
 | 
            }, 
 | 
            // 点击左边图标 
 | 
            clickIcon() { 
 | 
                this.$emit('clickIcon'); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
@import "../../libs/css/components.scss"; 
 | 
$u-search-content-padding: 0 10px !default; 
 | 
$u-search-label-color: $u-main-color !default; 
 | 
$u-search-label-font-size: 14px !default; 
 | 
$u-search-label-margin: 0 4px !default; 
 | 
$u-search-close-size: 20px !default; 
 | 
$u-search-close-radius: 100px !default; 
 | 
$u-search-close-bgColor: #C6C7CB !default; 
 | 
$u-search-close-transform: scale(0.82) !default; 
 | 
$u-search-input-font-size: 14px !default; 
 | 
$u-search-input-margin: 0 5px !default; 
 | 
$u-search-input-color: $u-main-color !default; 
 | 
$u-search-input-placeholder-color: $u-tips-color !default; 
 | 
$u-search-action-font-size: 14px !default; 
 | 
$u-search-action-color: $u-main-color !default; 
 | 
$u-search-action-width: 0 !default; 
 | 
$u-search-action-active-width: 40px !default; 
 | 
$u-search-action-margin-left: 5px !default; 
 | 
  
 | 
/* #ifdef H5 */ 
 | 
// iOS15在H5下,hx的某些版本,input type=search时,会多了一个搜索图标,进行移除 
 | 
[type="search"]::-webkit-search-decoration { 
 | 
    display: none; 
 | 
} 
 | 
/* #endif */ 
 | 
  
 | 
.u-search { 
 | 
    @include flex(row); 
 | 
    align-items: center; 
 | 
    flex: 1; 
 | 
  
 | 
    &__content { 
 | 
        @include flex; 
 | 
        align-items: center; 
 | 
        padding: $u-search-content-padding; 
 | 
        flex: 1; 
 | 
        justify-content: space-between; 
 | 
        border-width: 1px; 
 | 
        border-color: transparent; 
 | 
        border-style: solid; 
 | 
        overflow: hidden; 
 | 
  
 | 
        &__icon { 
 | 
            @include flex; 
 | 
            align-items: center; 
 | 
        } 
 | 
  
 | 
        &__label { 
 | 
            color: $u-search-label-color; 
 | 
            font-size: $u-search-label-font-size; 
 | 
            margin: $u-search-label-margin; 
 | 
        } 
 | 
  
 | 
        &__close { 
 | 
            width: $u-search-close-size; 
 | 
            height: $u-search-close-size; 
 | 
            border-top-left-radius: $u-search-close-radius; 
 | 
            border-top-right-radius: $u-search-close-radius; 
 | 
            border-bottom-left-radius: $u-search-close-radius; 
 | 
            border-bottom-right-radius: $u-search-close-radius; 
 | 
            background-color: $u-search-close-bgColor; 
 | 
            @include flex(row); 
 | 
            align-items: center; 
 | 
            justify-content: center; 
 | 
            transform: $u-search-close-transform; 
 | 
        } 
 | 
  
 | 
        &__input { 
 | 
            flex: 1; 
 | 
            font-size: $u-search-input-font-size; 
 | 
            line-height: 1; 
 | 
            margin: $u-search-input-margin; 
 | 
            color: $u-search-input-color; 
 | 
  
 | 
            &--placeholder { 
 | 
                color: $u-search-input-placeholder-color; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    &__action { 
 | 
        font-size: $u-search-action-font-size; 
 | 
        color: $u-search-action-color; 
 | 
        width: $u-search-action-width; 
 | 
        overflow: hidden; 
 | 
        transition-property: width; 
 | 
        transition-duration: 0.3s; 
 | 
        /* #ifndef APP-NVUE */ 
 | 
        white-space: nowrap; 
 | 
        /* #endif */ 
 | 
        text-align: center; 
 | 
  
 | 
        &--active { 
 | 
            width: $u-search-action-active-width; 
 | 
            margin-left: $u-search-action-margin-left; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
</style> 
 |