Files
element-plus/packages/components/tree-select/src/tree.ts
赵添 fc15cad898 fix(components): [tree-select] fix filter no matching data error (#17473)
* fix(components): [tree-select] fix filter no matching data error

* fix: immediate
2024-07-09 19:01:32 +08:00

265 lines
7.7 KiB
TypeScript

// @ts-nocheck
import { computed, nextTick, toRefs, watch } from 'vue'
import { isEqual, pick } from 'lodash-unified'
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { escapeStringRegexp, isFunction } from '@element-plus/utils'
import ElTree from '@element-plus/components/tree'
import TreeSelectOption from './tree-select-option'
import {
isValidArray,
isValidValue,
toValidArray,
treeEach,
treeFind,
} from './utils'
import type { CacheOption } from './cache-options'
import type { Ref } from 'vue'
import type ElSelect from '@element-plus/components/select'
import type Node from '@element-plus/components/tree/src/model/node'
import type { TreeNodeData } from '@element-plus/components/tree/src/tree.type'
import type { TreeInstance } from '@element-plus/components/tree'
export const useTree = (
props,
{ attrs, slots, emit },
{
select,
tree,
key,
}: {
select: Ref<InstanceType<typeof ElSelect> | undefined>
tree: Ref<TreeInstance | undefined>
key: Ref<string>
}
) => {
watch(
() => props.modelValue,
() => {
if (props.showCheckbox) {
nextTick(() => {
const treeInstance = tree.value
if (
treeInstance &&
!isEqual(
treeInstance.getCheckedKeys(),
toValidArray(props.modelValue)
)
) {
treeInstance.setCheckedKeys(toValidArray(props.modelValue))
}
})
}
},
{
immediate: true,
deep: true,
}
)
const propsMap = computed(() => ({
value: key.value,
label: 'label',
children: 'children',
disabled: 'disabled',
isLeaf: 'isLeaf',
...props.props,
}))
const getNodeValByProp = (
prop: 'value' | 'label' | 'children' | 'disabled' | 'isLeaf',
data: TreeNodeData
) => {
const propVal = propsMap.value[prop]
if (isFunction(propVal)) {
return propVal(
data,
tree.value?.getNode(getNodeValByProp('value', data)) as Node
)
} else {
return data[propVal as string]
}
}
const defaultExpandedParentKeys = toValidArray(props.modelValue)
.map((value) => {
return treeFind(
props.data || [],
(data) => getNodeValByProp('value', data) === value,
(data) => getNodeValByProp('children', data),
(data, index, array, parent) =>
parent && getNodeValByProp('value', parent)
)
})
.filter((item) => isValidValue(item))
const cacheOptions = computed(() => {
if (!props.renderAfterExpand && !props.lazy) return []
const options: CacheOption[] = []
treeEach(
props.data.concat(props.cacheData),
(node) => {
const value = getNodeValByProp('value', node)
options.push({
value,
currentLabel: getNodeValByProp('label', node),
isDisabled: getNodeValByProp('disabled', node),
})
},
(data) => getNodeValByProp('children', data)
)
return options
})
return {
...pick(toRefs(props), Object.keys(ElTree.props)),
...attrs,
nodeKey: key,
// only expand on click node when the `check-strictly` is false
expandOnClickNode: computed(() => {
return !props.checkStrictly && props.expandOnClickNode
}),
// show current selected node only first time,
// fix the problem of expanding multiple nodes when checking multiple nodes
defaultExpandedKeys: computed(() => {
return props.defaultExpandedKeys
? props.defaultExpandedKeys.concat(defaultExpandedParentKeys)
: defaultExpandedParentKeys
}),
renderContent: (h, { node, data, store }) => {
return h(
TreeSelectOption,
{
value: getNodeValByProp('value', data),
label: getNodeValByProp('label', data),
disabled: getNodeValByProp('disabled', data),
visible: node.visible,
},
props.renderContent
? () => props.renderContent(h, { node, data, store })
: slots.default
? () => slots.default({ node, data, store })
: undefined
)
},
filterNodeMethod: (value, data, node) => {
if (props.filterNodeMethod)
return props.filterNodeMethod(value, data, node)
if (!value) return true
const regexp = new RegExp(escapeStringRegexp(value), 'i')
return regexp.test(getNodeValByProp('label', data) || '')
},
onNodeClick: (data, node, e) => {
attrs.onNodeClick?.(data, node, e)
// `onCheck` is trigger when `checkOnClickNode`
if (props.showCheckbox && props.checkOnClickNode) return
// now `checkOnClickNode` is false, only no checkbox and `checkStrictly` or `isLeaf`
if (!props.showCheckbox && (props.checkStrictly || node.isLeaf)) {
if (!getNodeValByProp('disabled', data)) {
const option = select.value?.states.options.get(
getNodeValByProp('value', data)
)
select.value?.handleOptionSelect(option)
}
} else if (props.expandOnClickNode) {
e.proxy.handleExpandIconClick()
}
select.value?.focus()
},
onCheck: (data, params) => {
// ignore when no checkbox, like only `checkOnClickNode` is true
if (!props.showCheckbox) return
const dataValue = getNodeValByProp('value', data)
const dataMap = {}
treeEach(
[tree.value.store.root],
(node) => (dataMap[node.key] = node),
(node) => node.childNodes
)
// fix: checkedKeys has not cached keys
const uncachedCheckedKeys = params.checkedKeys
const cachedKeys = props.multiple
? toValidArray(props.modelValue).filter(
(item) => !(item in dataMap) && !uncachedCheckedKeys.includes(item)
)
: []
const checkedKeys = cachedKeys.concat(uncachedCheckedKeys)
if (props.checkStrictly) {
emit(
UPDATE_MODEL_EVENT,
// Checking for changes may come from `check-on-node-click`
props.multiple
? checkedKeys
: checkedKeys.includes(dataValue)
? dataValue
: undefined
)
}
// only can select leaf node
else {
if (props.multiple) {
emit(
UPDATE_MODEL_EVENT,
cachedKeys.concat((tree.value as TreeInstance).getCheckedKeys(true))
)
} else {
// select first leaf node when check parent
const firstLeaf = treeFind(
[data],
(data) =>
!isValidArray(getNodeValByProp('children', data)) &&
!getNodeValByProp('disabled', data),
(data) => getNodeValByProp('children', data)
)
const firstLeafKey = firstLeaf
? getNodeValByProp('value', firstLeaf)
: undefined
// unselect when any child checked
const hasCheckedChild =
isValidValue(props.modelValue) &&
!!treeFind(
[data],
(data) => getNodeValByProp('value', data) === props.modelValue,
(data) => getNodeValByProp('children', data)
)
emit(
UPDATE_MODEL_EVENT,
firstLeafKey === props.modelValue || hasCheckedChild
? undefined
: firstLeafKey
)
}
}
nextTick(() => {
const checkedKeys = toValidArray(props.modelValue)
tree.value.setCheckedKeys(checkedKeys)
attrs.onCheck?.(data, {
checkedKeys: tree.value.getCheckedKeys(),
checkedNodes: tree.value.getCheckedNodes(),
halfCheckedKeys: tree.value.getHalfCheckedKeys(),
halfCheckedNodes: tree.value.getHalfCheckedNodes(),
})
})
select.value?.focus()
},
// else
cacheOptions,
}
}