| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- <template>
- <FlexRow
- center
- :innerStyle="{
- ...themeStyles.view.value,
- ...innerStyle,
- background: theme.resolveThemeColor(inputBackgroundColor),
- }"
- :flexShrink="0"
- overflow="hidden"
- >
- <FlexRow
- center
- :innerStyle="{
- ...themeStyles.input.value,
- ...containerStyle
- }"
- :gap="20"
- >
- <slot name="leftIcon">
- <Icon
- v-if="leftIcon"
- :icon="leftIcon"
- :size="leftIconSize"
- :color="leftIconColor"
- v-bind="leftIconProps"
- />
- </slot>
- <input
- :style="{
- ...themeStyles.inputInner.value,
- ...inputStyle,
- color: theme.resolveThemeColor(inputColor)
- }"
- :value="valueTemp"
- :placeholder="placeholder"
- :placeholder-style="`color: ${theme.resolveThemeColor(placeholderTextColor)}`"
- type="text"
- confirm-type="search"
- @input="onInput"
- @focus="onFocus"
- @blur="onBlur"
- @confirm="onSumit"
- />
- <slot name="rightIcon" />
- </FlexRow>
- <slot
- v-if="(
- cancelState === 'show' ||
- (inputFocus && (cancelState === 'show-active' || cancelState === 'show-active-or-no-empty')) ||
- (valueTemp !== '' && (cancelState === 'show-no-empty' || cancelState === 'show-active-or-no-empty'))
- )"
- name="cancelButton"
- >
- <Button
- type="text"
- size="small"
- :text="cancelText"
- :textStyle="{
- color: theme.resolveThemeColor(cancelTextColor),
- ...cancelTextStyle
- }"
- :innerStyle="{ ...themeStyles.cancel.value, ...cancelStyle }"
- @click="onCancelPressed"
- />
- </slot>
- <slot
- v-if="(
- searchState === 'show' ||
- (inputFocus && (searchState === 'show-active' || searchState === 'show-active-or-no-empty')) ||
- (valueTemp !== '' && (searchState === 'show-no-empty' || searchState === 'show-active-or-no-empty'))
- )"
- name="searchButton"
- :onSearchButtonClick="onSearchButtonClick"
- >
- <IconButton
- :icon="searchIcon"
- :size="searchIconSize"
- :color="searchIconColor"
- v-bind="searchIconProps"
- @click="onSearchButtonClick"
- />
- </slot>
- <slot name="rightButton" :onCustomButtonClick="onCustomButtonClick"/>
- </FlexRow>
- </template>
- <script setup lang="ts">
- import { ref, watch } from 'vue';
- import type { IconProps } from '../basic/Icon.vue';
- import { type ViewStyle, type TextStyle, useTheme, propGetThemeVar } from '../theme/ThemeDefine';
- import FlexRow from '../layout/FlexRow.vue';
- import Button from '../basic/Button.vue';
- import { DynamicSize } from '../theme/ThemeTools';
- import Icon from '../basic/Icon.vue';
- import IconButton from '../basic/IconButton.vue';
- export interface SearchBarProp {
- /**
- * 输入框初始值
- */
- modelValue?: string;
- /**
- * 输入框的文字颜色
- * @default text.content
- */
- inputColor?: string;
- /**
- * 输入框的背景颜色
- * @default white
- */
- inputBackgroundColor?: string;
- /**
- * 输入框的placeholder
- */
- placeholder?: string;
- /**
- * 输入框的placeholderTextColor
- * @default text.second
- */
- placeholderTextColor?: string;
- /**
- * 取消按钮的文字
- * @default '取消'
- */
- cancelText?: string;
- /**
- * 取消按钮的文字颜色
- * @default primary
- */
- cancelTextColor?: string;
- /**
- * 点击取消按钮是否自动让输入框失去焦点
- * @default true
- */
- cancalLostFocus?: boolean;
- /**
- * 点击取消按钮是否自动清空输入框
- * @default true
- */
- cancalClear?: boolean;
- /**
- * 左侧图标
- * @default 'search'
- */
- leftIcon?: string|null;
- leftIconSize?: string|number;
- leftIconColor?: string;
- /**
- * 图标自定义属性
- */
- leftIconProps?: Partial<IconProps>;
- /**
- * 取消按钮显示
- * * hidden 不显示
- * * show 一直显示
- * * show-active 当输入框激活时显示
- * * show-no-empty 当输入框有文字时显示
- * * show-active-or-no-empty 当输入框激活或者有文字时显示
- * @default 'show-active-or-no-empty'
- */
- cancelState?: 'hidden'|'show'|'show-active'|'show-no-empty'|'show-active-or-no-empty';
- /**
- * 搜索按钮显示
- * * hidden 不显示
- * * show 一直显示
- * * show-active 当输入框激活时显示
- * * show-no-empty 当输入框有文字时显示
- * * show-active-or-no-empty 当输入框激活或者有文字时显示
- * @default 'hidden'
- */
- searchState?: 'hidden'|'show'|'show-active'|'show-no-empty'|'show-active-or-no-empty';
- /**
- * 搜索按钮图标
- * @default 'search'
- */
- searchIcon?: string;
- /**
- * 搜索按钮图标大小
- * @default 40
- */
- searchIconSize?: string|number;
- /**
- * 搜索按钮图标颜色
- * @default text.content
- */
- searchIconColor?: string;
- /**
- * 图标自定义属性
- */
- searchIconProps?: Partial<IconProps>;
- /**
- * 自定义样式
- */
- innerStyle?: ViewStyle;
- /**
- * 容器自定义样式
- */
- containerStyle?: ViewStyle;
- /**
- * 自定义样式
- */
- inputStyle?: TextStyle;
- /**
- * 自定义样式
- */
- cancelStyle?: ViewStyle;
- /**
- * 自定义样式
- */
- cancelTextStyle?: TextStyle;
- }
- const emit = defineEmits([ 'search', 'update:modelValue', 'blur', 'focus', 'cancel' ])
- const theme = useTheme();
- const props = withDefaults(defineProps<SearchBarProp>(), {
- inputColor: () => propGetThemeVar('SearchBarTextColor', 'text.content'),
- inputBackgroundColor: () => propGetThemeVar('SearchBarBackgroundColor', 'white'),
- searchState: 'hidden',
- searchIcon: () => propGetThemeVar('SearchBarSearchIcon', 'search'),
- searchIconSize: () => propGetThemeVar('SearchBarSearchIconSize', 40),
- searchIconColor: () => propGetThemeVar('SearchBarSearchIconColor', 'text.content'),
- placeholder: '',
- placeholderTextColor: () => propGetThemeVar('SearchBarCancelTextColor', 'text.second'),
- cancelText: '取消',
- cancelTextColor: () => propGetThemeVar('SearchBarCancelTextColor', 'primary'),
- cancalLostFocus: true,
- cancalClear: true,
- leftIcon: () => propGetThemeVar('SearchBarLeftIcon', 'search'),
- leftIconSize: () => propGetThemeVar('SearchBarLeftIconSize', 40),
- leftIconColor: () => propGetThemeVar('SearchBarLeftIconColor', 'text.content'),
- cancelState: 'show-active-or-no-empty',
- });
- const valueTemp = ref(props.modelValue ?? '');
- watch(() => props.modelValue, () => {
- valueTemp.value = props.modelValue ?? '';
- });
- function updateValue(v: string) {
- valueTemp.value = v;
- emit('update:modelValue', v);
- }
- const themeStyles = theme.useThemeStyles({
- view: {
- position: 'relative',
- borderRadius: DynamicSize('SearchBarBorderRadius', 40),
- paddingHorizontal: DynamicSize('SearchBarPaddingHorizontal', 28),
- paddingVertical: DynamicSize('SearchBarPaddingVertical', 16),
- },
- input: {
- flex: 1,
- position: 'relative',
- },
- inputInner: {
- flex: 1,
- paddingHorizontal: DynamicSize('SearchBarCancelPaddingHorizontal', 20),
- paddingVertical: DynamicSize('SearchBarCancelPaddingVertical', 16),
- },
- cancel: {
- paddingTop: DynamicSize('SearchBarCancelPaddingVertical', 0),
- paddingBottom: DynamicSize('SearchBarCancelPaddingVertical', 0),
- paddingHorizontal: DynamicSize('SearchBarCancelPaddingHorizontal', 20),
- },
- });
- const inputFocus = ref(false);
- function onInput(e: any) {
- inputFocus.value = true;
- updateValue(e.detail.value);
- }
- function onFocus() {
- inputFocus.value = true;
- emit('focus');
- }
- function onBlur() {
- inputFocus.value = false;
- emit('blur');
- }
- function onCancelPressed() {
- if (props.cancalLostFocus)
- uni.hideKeyboard()
- if (props.cancalClear)
- updateValue('');
- emit('cancel');
- }
- function onSearchButtonClick() {
- emit('search', valueTemp.value);
- uni.hideKeyboard()
- }
- function onSumit() {
- emit('search', valueTemp.value);
- uni.hideKeyboard()
- }
- function onCustomButtonClick(type: 'search'|'cancel') {
- if (type === 'search') onSumit();
- else if (type === 'cancel') onCancelPressed();
- }
- </script>
|