You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
5.8 KiB
Vue
244 lines
5.8 KiB
Vue
<template>
|
|
<view :class="rootClass" :style="customStyle">
|
|
<!--自定义label插槽-->
|
|
<!--搜索框主体-->
|
|
<view class="wd-search__block">
|
|
<slot name="prefix"></slot>
|
|
<view class="wd-search__field">
|
|
<view v-if="!placeholderLeft" :style="coverStyle" class="wd-search__cover" @click="closeCover">
|
|
<wd-icon name="search" size="18px" custom-class="wd-search__search-icon"></wd-icon>
|
|
<text class="wd-search__placeholder-txt">{{ placeholder || '搜索' }}</text>
|
|
</view>
|
|
<!--icon:search-->
|
|
<wd-icon v-if="showInput || str || placeholderLeft" name="search" size="18px" custom-class="wd-search__search-left-icon"></wd-icon>
|
|
<!--搜索框-->
|
|
<input
|
|
v-if="showInput || str || placeholderLeft"
|
|
:placeholder="placeholder || '搜索'"
|
|
placeholder-class="wd-search__placeholder-txt"
|
|
confirm-type="search"
|
|
v-model="str"
|
|
class="wd-search__input"
|
|
@focus="searchFocus"
|
|
@input="inputValue"
|
|
@blur="searchBlur"
|
|
@confirm="search"
|
|
:disabled="disabled"
|
|
:maxlength="maxlength"
|
|
:focus="isFocused"
|
|
/>
|
|
<!--icon:clear-->
|
|
<wd-icon v-if="str" custom-class="wd-search__clear wd-search__clear-icon" name="error-fill" size="16px" @click="clearSearch" />
|
|
</view>
|
|
</view>
|
|
<!--the button behind input,care for hideCancel without displaying-->
|
|
<block v-if="!hideCancel">
|
|
<!--有插槽就不用默认的按钮了-->
|
|
<slot v-if="userSuffixSlot" name="suffix"></slot>
|
|
<!--默认button-->
|
|
<view v-else class="wd-search__cancel" @click="handleCancel">
|
|
{{ cancelTxt || '取消' }}
|
|
</view>
|
|
</block>
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'wd-search',
|
|
options: {
|
|
virtualHost: true,
|
|
addGlobalClass: true,
|
|
styleIsolation: 'shared'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts" setup>
|
|
import { type CSSProperties, computed, onMounted, ref, watch } from 'vue'
|
|
import { objToStyle, requestAnimationFrame } from '../common/util'
|
|
|
|
interface Props {
|
|
userSuffixSlot?: boolean
|
|
placeholder?: string
|
|
cancelTxt?: string
|
|
light?: boolean
|
|
hideCancel?: boolean
|
|
disabled?: boolean
|
|
maxlength?: number | string
|
|
modelValue?: string
|
|
placeholderLeft?: boolean
|
|
focus?: boolean
|
|
focusWhenClear?: boolean
|
|
customClass?: string
|
|
customStyle?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
modelValue: '',
|
|
customClass: '',
|
|
customStyle: '',
|
|
userSuffixSlot: false,
|
|
placeholder: '搜索',
|
|
cancelTxt: '取消',
|
|
light: false,
|
|
focus: false,
|
|
focusWhenClear: false,
|
|
hideCancel: false,
|
|
disabled: false,
|
|
maxlength: -1,
|
|
placeholderLeft: false
|
|
})
|
|
|
|
const isFocused = ref<boolean>(false) // 是否聚焦中
|
|
const showInput = ref<boolean>(false) // 是否显示输入框 用于实现聚焦的hack
|
|
const str = ref('')
|
|
const showPlaceHolder = ref<boolean>(true)
|
|
const clearing = ref<boolean>(false)
|
|
|
|
watch(
|
|
() => props.modelValue,
|
|
(newValue) => {
|
|
str.value = newValue
|
|
if (newValue) {
|
|
showInput.value = true
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
watch(
|
|
() => props.focus,
|
|
(newValue) => {
|
|
if (newValue) {
|
|
if (props.disabled) return
|
|
closeCover()
|
|
}
|
|
}
|
|
)
|
|
|
|
onMounted(() => {
|
|
if (props.focus) {
|
|
closeCover()
|
|
}
|
|
})
|
|
|
|
const rootClass = computed(() => {
|
|
return `wd-search ${props.light ? 'is-light' : ''} ${props.hideCancel ? 'is-without-cancel' : ''} ${props.customClass}`
|
|
})
|
|
|
|
const coverStyle = computed(() => {
|
|
const coverStyle: CSSProperties = {
|
|
display: str.value === '' && showPlaceHolder.value ? 'flex' : 'none'
|
|
}
|
|
|
|
return objToStyle(coverStyle)
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change', 'clear', 'search', 'focus', 'blur', 'cancel'])
|
|
|
|
function hackFocus(focus: boolean) {
|
|
showInput.value = focus
|
|
requestAnimationFrame(() => {
|
|
isFocused.value = focus
|
|
})
|
|
}
|
|
|
|
function closeCover() {
|
|
if (props.disabled) return
|
|
requestAnimationFrame()
|
|
.then(() => requestAnimationFrame())
|
|
.then(() => requestAnimationFrame())
|
|
.then(() => {
|
|
showPlaceHolder.value = false
|
|
hackFocus(true)
|
|
})
|
|
}
|
|
/**
|
|
* @description input的input事件handle
|
|
* @param value
|
|
*/
|
|
function inputValue(event: any) {
|
|
str.value = event.detail.value
|
|
emit('update:modelValue', event.detail.value)
|
|
emit('change', {
|
|
value: event.detail.value
|
|
})
|
|
}
|
|
/**
|
|
* @description 点击清空icon的handle
|
|
*/
|
|
function clearSearch() {
|
|
str.value = ''
|
|
clearing.value = true
|
|
if (props.focusWhenClear) {
|
|
isFocused.value = false
|
|
}
|
|
requestAnimationFrame()
|
|
.then(() => requestAnimationFrame())
|
|
.then(() => requestAnimationFrame())
|
|
.then(() => {
|
|
if (props.focusWhenClear) {
|
|
showPlaceHolder.value = false
|
|
hackFocus(true)
|
|
} else {
|
|
showPlaceHolder.value = true
|
|
hackFocus(false)
|
|
}
|
|
emit('clear')
|
|
emit('change', {
|
|
value: ''
|
|
})
|
|
emit('update:modelValue', '')
|
|
})
|
|
}
|
|
/**
|
|
* @description 点击搜索按钮时的handle
|
|
* @param value
|
|
*/
|
|
function search({ detail: { value } }) {
|
|
// 组件触发search事件
|
|
emit('search', {
|
|
value
|
|
})
|
|
}
|
|
/**
|
|
* @description 输入框聚焦时的handle
|
|
*/
|
|
function searchFocus() {
|
|
if (clearing.value) {
|
|
clearing.value = false
|
|
return
|
|
}
|
|
showPlaceHolder.value = false
|
|
emit('focus', {
|
|
value: str.value
|
|
})
|
|
}
|
|
/**
|
|
* @description 输入框失焦的handle
|
|
*/
|
|
function searchBlur() {
|
|
if (clearing.value) return
|
|
// 组件触发blur事件
|
|
showPlaceHolder.value = !str.value
|
|
showInput.value = !showPlaceHolder.value
|
|
isFocused.value = false
|
|
emit('blur', {
|
|
value: str.value
|
|
})
|
|
}
|
|
/**
|
|
* @description 点击取消搜索按钮的handle
|
|
*/
|
|
function handleCancel() {
|
|
// 组件触发cancel事件
|
|
emit('cancel', {
|
|
value: str.value
|
|
})
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
@import './index.scss';
|
|
</style>
|