Skip to content

Commit

Permalink
fix: 修复 select 组件
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyh2001 committed May 11, 2024
1 parent 1580f2c commit 3aaf433
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 175 deletions.
84 changes: 41 additions & 43 deletions packages/fighting-design/option/src/option.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import { useRun } from '../../_hooks'
import { isString, isArray, isObject } from '../../_utils'
import { TRIGGER_CLOSE_KEY } from '../../trigger/src/props'
import { inject, toRefs, useSlots, computed } from 'vue'
import { inject, useSlots, computed } from 'vue'
import { warning } from '../../_utils'
import { SELECT_PROPS_TOKEN } from '../../select/src/props'
import type { SelectProvide, SelectModelValue } from '../../select'
import type { TriggerProvide } from '../../trigger'
Expand All @@ -27,9 +28,16 @@
return ''
}
const _slot: VNodeNormalizedChildren | string = slot.default()[0].children
/** 获取到插槽的内容 */
const content: VNodeNormalizedChildren | string = slot.default()[0].children
return isString(_slot) ? _slot : ''
// 如果不是字符串,暂时无法处理,先警告信息,返回空字符串
if (!isString(content)) {
warning('f-option', 'slot content is not a string')
return ''
}
return content
})
/** 控制是否显示 */
Expand All @@ -39,20 +47,8 @@
return false
}
// 没有 filter 属性,则展示
if (!parentInject.filter) {
return true
}
/** 获取到 label */
const label = slotLabel.value || prop.label?.toString() || prop.value?.toString()
/** 获取到选中的项目 */
const currentItem = parentInject.childrenLabels.find(item => {
return item.slot === label
})
if (currentItem) {
return currentItem.show
if (parentInject.filter && parentInject.isFiltering) {
return (currentLabel as string).includes(parentInject.inputValue)
}
return true
Expand Down Expand Up @@ -83,7 +79,7 @@
*
* @param {*} values 参数集合
*/
const getEffectiveValue = (...values: any[]): any => {
const getEffectiveValue = (...values: any[]): string => {
// 没有数据返回空字符串
if (!values || !values.length) {
return ''
Expand Down Expand Up @@ -113,6 +109,32 @@
return effectiveValue !== void 0 ? effectiveValue : values[values.length - 1]
}
/**
* 最新的 label
*
* 返回优先级:插槽 > label > value
*/
const currentLabel: SelectModelValue = getEffectiveValue(
slotLabel.value,
prop.label,
prop.value
)
/**
* 最新的 value
*
* 返回优先级:value > label > 插槽
*/
const currentValue: SelectModelValue = getEffectiveValue(
prop.value,
prop.label,
slotLabel.value
)
if (currentValue === parentInject?.modelValue) {
parentInject && run(parentInject.setValue, currentValue, currentLabel)
}
/**
* 点击传入指定的 value
*
Expand All @@ -124,32 +146,8 @@
// 如果没有获取到注入的依赖项或者禁用状态 则返回
if (!parentInject || prop.disabled) return
const { value, label } = toRefs(prop)
/**
* 最新的 label
*
* 返回优先级:插槽 > label > value
*/
const currentLabel: SelectModelValue = getEffectiveValue(
slotLabel.value,
label.value,
value.value
)
/**
* 最新的 value
*
* 返回优先级:value > label > 插槽
*/
const currentValue: SelectModelValue = getEffectiveValue(
value.value,
label.value,
slotLabel.value
)
// 执行父组件的设置方法
parentInject && run(parentInject.setValue, currentValue, currentLabel, evt)
run(parentInject.setValue, currentValue, currentLabel, evt)
// 点击之后关闭
triggerInject && run(triggerInject.close)
}
Expand Down
164 changes: 42 additions & 122 deletions packages/fighting-design/select/src/select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,14 @@
import { Props, SELECT_PROPS_TOKEN } from './props'
import { FInput } from '../../input'
import { useList, useRun } from '../../_hooks'
import {
provide,
computed,
useSlots,
ref,
reactive,
toRef,
nextTick,
watch,
onBeforeUnmount
} from 'vue'
import { provide, computed, useSlots, ref, reactive, toRef, nextTick } from 'vue'
import { FDropdown } from '../../dropdown'
import { getChildren, isFunction } from '../../_utils'
import { FSvgIcon } from '../../svg-icon'
import { FEmpty } from '../../empty'
import { FIconChevronDown } from '../../_svg'
import type { VNode, Slots, WatchStopHandle } from 'vue'
import type { SelectProvide, SelectModelValue, SelectChildren } from './interface'
import type { VNode, Slots } from 'vue'
import type { SelectProvide, SelectModelValue } from './interface'
defineOptions({ name: 'FSelect' })
Expand All @@ -42,6 +33,10 @@
const selectContentRef = ref<HTMLDivElement | undefined>()
/** 是否有搜索内容 */
const isHaveValues = ref(true)
/** 文本框绑定的值 */
const inputValue = ref('')
/** 是否正在输入过滤搜索中 */
const isFiltering = ref(false)
/**
* 获取子元素 option
Expand All @@ -55,64 +50,6 @@
return getChildren(slot.default(), 'FOption')
})
/** 当前绑定的值 */
const keyword = computed({
get: (): string => {
// 如果插槽没内容,则返回空字符串
if (!options.value.length) return ''
/**
* 通过插槽内容
*
* 过滤出和绑定值相同的那一项
*/
const currentOption: VNode[] = options.value.filter((node: VNode): boolean => {
/** 获取到子组件的 props */
const optionProp = node.props
// 判断是否有传递 props
if (optionProp) {
return optionProp.value
? `${optionProp.value}` === `${prop.modelValue}`
: `${optionProp.label}` === `${prop.modelValue}`
}
/**
* 如果没有传递 props 则根据插槽来判断
*
* 放心,这里一定会有插槽,子组件已经做了判断
*/
return (node as SelectChildren).children.default()[0].children === prop.modelValue
})
/**
* 如果没有通过插槽找出和绑定值相同的
*
* 则返回空
*/
if (!currentOption.length) return ''
/** 获取到当前满足要求的子元素 */
const firstChildren: VNode = currentOption[0]
/** 获取到当前子元素的插槽内容 */
const slot: string | undefined =
firstChildren.children &&
(firstChildren.children as { default: Function }).default()[0].children
/** 获取到当前子元素的 label 参数 */
const label: string | undefined = firstChildren.props?.label
/** 获取到当前子元素的 value 参数 */
const value: string | undefined = firstChildren.props?.value
// 返回优先级:插槽 > label > value
return slot || label || (value && value.toString()) || ''
},
set: (val: string): string => {
modelValue.value = val
return val
}
})
/**
* 设置新的值
*
Expand All @@ -123,41 +60,21 @@
const setValue = (
currentValue: SelectModelValue,
currentLabel: SelectModelValue,
evt: MouseEvent
evt?: MouseEvent
): void => {
// 设置文本框展示的内容
keyword.value = currentValue.toString()
// 如果最新的 value 和绑定的 value 不一致时,才触发 change 事件
if (currentLabel !== prop.modelValue) {
/**
* 如果最新的 value 和绑定的 value 不一致时
*
* 而且还需要带有 evt 对象的时候才需要触发 change 事件
*
* 如果没有 evt 对象,则代表是首次赋值,则不予触发
*/
if (currentLabel !== prop.modelValue && evt) {
run(prop.onChange, currentValue, currentLabel, evt)
}
modelValue.value = currentValue
}
/**
* 设置子节点的展示状态和 label
*/
const setChildrenLabels = (): void => {
if (!options.value || !options.value.length) {
childrenLabels.value = []
}
childrenLabels.value = options.value.map((item: VNode) => {
/** 获取到当前子元素的插槽内容 */
const slot: string =
item.children && (item.children as { default: Function }).default()[0].children
return { slot, show: !!(slot && slot.includes(modelValue.value.toString())) }
})
// 如果是过滤状态,判断是否存在搜索结果
if (prop.filter) {
const isHave: boolean = childrenLabels.value.some(y => y.show)
isHaveValues.value = isHave
}
inputValue.value = currentLabel?.toString()
}
/** 下拉菜单开启之后的回调 */
Expand Down Expand Up @@ -187,31 +104,16 @@
}
}
/** 监听器实例 */
const watchStopHandle = ((): WatchStopHandle | undefined => {
if (!prop.filter) {
return
}
return watch((): SelectModelValue => modelValue.value, setChildrenLabels, {
immediate: true
})
})()
// 页面卸载的时候,如何有监听器 则停止
onBeforeUnmount(() => {
if (watchStopHandle) {
watchStopHandle() // 监听器实例
}
})
// 向子组件注入依赖项
provide<SelectProvide>(
SELECT_PROPS_TOKEN,
reactive({
setValue,
childrenLabels,
modelValue: toRef(prop, 'modelValue'),
filter: toRef(prop, 'filter')
filter: toRef(prop, 'filter'),
inputValue,
isFiltering
})
)
Expand All @@ -221,27 +123,45 @@
const inputBlur = (): void => {
isFocus.value = false
if (prop.filter) {
isFiltering.value = false
}
// 失去焦点的时候如果子节点的 title 中不包含输入项目,则情况文本框的内容
if (!isHaveValues.value) {
keyword.value = ''
inputValue.value = ''
}
}
const inputFocus = (): void => {
isFocus.value = true
// if (prop.filter) {
// isFiltering.value = true
// }
}
const inputInput = (): void => {
console.log('change')
isFiltering.value = true
}
</script>

<template>
<div class="f-select" :style="style">
<f-dropdown trigger="click" :disabled :width :on-open="onOpen">
<f-input
v-model="keyword"
v-model="inputValue"
:readonly="!filter"
:name
:size
:disabled
:width
:placeholder
:clear
:on-focus="() => (isFocus = true)"
:on-focus="inputFocus"
:on-blur="inputBlur"
:on-input="filter ? inputInput : void 0"
>
<template #after>
<f-svg-icon
Expand Down
22 changes: 12 additions & 10 deletions start/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
<template>
{{ value }}
<f-select v-model="value" placeholder="请选择……" filter :width="300">
<f-option>香蕉</f-option>
<f-option>苹果</f-option>
<f-option>哈密瓜</f-option>
<f-option>樱桃</f-option>
<f-option>樱桃2</f-option>
<f-option>樱桃3</f-option>
<f-option>樱桃4</f-option>
<f-option>黄瓜</f-option>
<f-select v-model="value" placeholder="请选择……" :width="300">
<f-option :value="1">香蕉</f-option>
<f-option :value="2">苹果</f-option>
<f-option :value="3">哈密瓜</f-option>
<f-option :value="4">樱桃</f-option>
<f-option :value="5">樱桃2</f-option>
<f-option :value="6">樱桃3</f-option>
<f-option :value="7">樱桃4</f-option>
<f-option :value="8">黄瓜</f-option>
</f-select>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('')
// const a = 1
const value = ref(5)
</script>

0 comments on commit 3aaf433

Please sign in to comment.