-
Notifications
You must be signed in to change notification settings - Fork 288
feat(select): 重构use-select.ts文件,拆分特性 #1128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e5db1fd
ca33e4c
9fc26de
48121e6
513ef92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { computed } from 'vue'; | ||
| import { SelectProps, allowCreateOption, useAllowCreateReturn } from '../select-types'; | ||
|
|
||
| export default function useAllowCreate(props: SelectProps, option: allowCreateOption): useAllowCreateReturn { | ||
| const { filterQuery, injectOptionsArray } = option; | ||
|
|
||
| // allow-create | ||
| const isShowCreateOption = computed(() => { | ||
| const hasCommonOption = injectOptionsArray.value.filter((item) => !item.create).some((item) => item.name === filterQuery.value); | ||
| return props.filter === true && props.allowCreate && !!filterQuery.value && !hasCommonOption; | ||
| }); | ||
|
|
||
| return { | ||
| isShowCreateOption, | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { ref, computed } from 'vue'; | ||
| import { SelectProps, useFilterReturn } from '../select-types'; | ||
| import { isFunction, debounce } from 'lodash'; | ||
|
|
||
| export default function useFilter(props: SelectProps): useFilterReturn { | ||
| const filterQuery = ref(''); | ||
| const debounceTime = computed(() => (props.remote ? 300 : 0)); | ||
| const isSupportFilter = computed(() => isFunction(props.filter) || props.filter === true); | ||
|
|
||
| const queryChange = (query: string) => { | ||
| filterQuery.value = query; | ||
| }; | ||
|
|
||
| const handlerQueryFunc = (query: string) => { | ||
| if (isFunction(props.filter)) { | ||
| props.filter(query); | ||
| } else { | ||
| queryChange(query); | ||
| } | ||
| }; | ||
|
|
||
| const debounceQueryFilter = debounce((query: string) => { | ||
| handlerQueryFunc(query); | ||
| }, debounceTime.value); | ||
|
|
||
| return { | ||
| filterQuery, | ||
| isSupportFilter, | ||
| debounceQueryFilter, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import type { SetupContext } from 'vue'; | ||
| import { SelectProps, OptionObjectItem, useMultipleOption, useMultipleReturn } from '../select-types'; | ||
|
|
||
| export default function useMultipleSelect(props: SelectProps, ctx: SetupContext, multipleOption: useMultipleOption): useMultipleReturn { | ||
| const { filterQuery, isSupportFilter, isObjectOption, mergeOptions, injectOptions, getValuesOption, getInjectOptions } = multipleOption; | ||
|
|
||
| const getMultipleSelected = (items: (string | number)[]) => { | ||
| if (mergeOptions.value.length) { | ||
| ctx.emit( | ||
| 'value-change', | ||
| getValuesOption(items).filter((item) => (item ? true : false)) | ||
jxhhdx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
| } else if (isObjectOption.value) { | ||
| const selectItems = getInjectOptions(items).filter((item) => (item ? true : false)); | ||
| ctx.emit('value-change', selectItems); | ||
| } else { | ||
| ctx.emit('value-change', items); | ||
| } | ||
| }; | ||
| const multipleValueChange = (item: OptionObjectItem) => { | ||
| let { modelValue } = props; | ||
|
|
||
| const checkedItems = Array.isArray(modelValue) ? modelValue.slice() : []; | ||
| const index = checkedItems.indexOf(item.value); | ||
| const option = getInjectOptions([item.value])[0]; | ||
| if (option) { | ||
| option._checked = !option._checked; | ||
| } | ||
| const mergeOption = getValuesOption([item.value])[0]; | ||
| if (mergeOption) { | ||
| mergeOption._checked = !mergeOption._checked; | ||
| } | ||
| if (index > -1) { | ||
| checkedItems.splice(index, 1); | ||
| } else { | ||
| checkedItems.push(item.value); | ||
| } | ||
| modelValue = checkedItems; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. props的数据流应该是单向流动的,在子组件中修改父元素props的值,会报warning吧? |
||
| ctx.emit('update:modelValue', modelValue); | ||
| if (item.create) { | ||
| filterQuery.value = ''; | ||
| } | ||
| if (isSupportFilter.value) { | ||
| focus(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. focus方法在当前文件没有找到声明的位置,此处是如何工作的? |
||
| } | ||
| getMultipleSelected(checkedItems); | ||
| }; | ||
|
|
||
| const tagDelete = (data: OptionObjectItem) => { | ||
| const checkedItems = []; | ||
| for (const child of injectOptions.value.values()) { | ||
| if (data.value === child.value) { | ||
| child._checked = false; | ||
| } | ||
| if (child._checked) { | ||
| checkedItems.push(child.value); | ||
| } | ||
| } | ||
| ctx.emit('update:modelValue', checkedItems); | ||
| ctx.emit('remove-tag', data.value); | ||
| getMultipleSelected(checkedItems); | ||
| }; | ||
|
|
||
| return { | ||
| multipleValueChange, | ||
| tagDelete, | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { computed } from 'vue'; | ||
| import { SelectProps, useNoDataOption, useNoDataReturn } from '../select-types'; | ||
|
|
||
| export default function useNoDataText(props: SelectProps, option: useNoDataOption): useNoDataReturn { | ||
| const { filterQuery, isSupportFilter, injectOptionsArray, t } = option; | ||
|
|
||
| // no-data-text | ||
| const isLoading = computed(() => typeof props.loading === 'boolean' && props.loading); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里建议不对props.loading进行是否为boolean的检查,可以消除isLoading中间变量,直接使用props.loading。可以讨论一下,当用户传入的loading值为一个非空的字符串或者数字的时候,我们是否希望loading是工作的? |
||
| const emptyText = computed(() => { | ||
| const visibleOptionsCount = injectOptionsArray.value.filter((item) => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. visibleOptionsCount主要是用来判断是否存在visibleOptions,所以是否可以将名字改为hasVisibleOptions,同时后面的filter方法替换为some方法。 |
||
| const label = item.name || item.value; | ||
| return label.toString().toLocaleLowerCase().includes(filterQuery.value.toLocaleLowerCase()); | ||
| }).length; | ||
| if (isLoading.value) { | ||
| return props.loadingText || (t('loadingText') as string); | ||
| } | ||
| if (isSupportFilter.value && filterQuery.value && injectOptionsArray.value.length > 0 && visibleOptionsCount === 0) { | ||
| return props.noMatchText || (t('noMatchText') as string); | ||
| } | ||
| if (injectOptionsArray.value.length === 0) { | ||
| return props.noDataText || (t('noDataText') as string); | ||
| } | ||
| return ''; | ||
| }); | ||
|
|
||
| const isShowEmptyText = computed(() => { | ||
| return !!emptyText.value && (!props.allowCreate || isLoading.value || (props.allowCreate && injectOptionsArray.value.length === 0)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里的判断条件有些复杂,存在&&与||的嵌套,建议填加注释,不然比较难读懂。 |
||
| }); | ||
|
|
||
| return { | ||
| isLoading, | ||
| emptyText, | ||
| isShowEmptyText, | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
判断条件较为复杂,建议添加注释。