import { TreeStore } from '@opentiny/utils'
export const close =
({ state, emit }) =>
() => {
state.search.show = false
emit('update:visible', false)
emit('close', false)
}
export const syncCheckStatus =
({ state, props, api }) =>
(value) => {
const { valueField, textField, modelValue: propsModelValue } = props
const { lazy, isLeaf: leafField } = api.getNodeConfig()
let currentData
let navList = []
let modelValue = value || propsModelValue || []
let len = modelValue.length
let isLeaf
for (let i = 0; i < len; i++) {
const id = modelValue[i]
currentData = state.store.getNode(id)
if (currentData) {
navList.push(currentData)
} else {
isLeaf = i === len - 1
navList.push({
data: { [valueField]: id, [textField]: id, [leafField]: isLeaf },
loaded: false,
isLeaf,
childNodes: [],
level: i,
loading: false,
selfWaitLoad: true
})
}
}
state.isLeaf = !hasChildren(navList[len - 1])
state.navList = navList
state.level = lazy && state.waitLoadList.length ? 0 : Math.max(len - 1, 0)
}
export const watchVisible =
({ emit, state, props, api }) =>
(visible) => {
const { modelValue } = props
if (visible) {
api.watchModelValue(modelValue)
}
setTimeout(() => {
state.toggle = visible
}, 0)
emit('update:visible', visible)
}
export const watchModelValue =
({ props, state, emit }) =>
() => {
const { textSplit } = props
emit('update:text', state.checkLabels.join(textSplit))
}
const hasChildren = (option) => option && !option.isLeaf
export const selectOption =
({ emit, state, props, api }) =>
(option) => {
const { valueField } = props
const { lazy, load } = api.getNodeConfig()
const { navList, level, waitLoadList } = state
const len = navList.length
const appendNavList = () => {
const isSame = level < len && navList[level] === option
if (!isSame) {
state.navList = [...navList.slice(0, level), option]
}
if (hasChildren(option)) {
state.level += 1
}
state.isLeaf = !hasChildren(state.navList[state.navList.length - 1])
emit('node-expand', option)
}
if (lazy && load && (option.selfWaitLoad || (!option.loaded && !option.isLeaf))) {
option.loading = true
state.loading = true
load(option, (nodes) => {
let index, node
option.loading = false
state.loading = false
option.loaded = true
nodes.forEach((data) => {
state.store.append(data, option.data)
index = waitLoadList.indexOf(data[valueField])
if (index !== -1) {
node = state.store.getNode(data)
state.navList = state.navList.map((option) => {
if (node && option.data[valueField] === data[valueField]) {
option = node
}
return option
})
}
})
appendNavList()
})
} else {
appendNavList()
}
emit('click', option)
}
export const confirm =
({ emit, state, api }) =>
() => {
if (state.search.show) {
api.searchBoxToggle(false)
} else {
emit('confirm', state)
emit('update:visible', false)
emit('update:modelValue', state.checkIds)
}
}
const loopSearchOption = ({
options,
input,
childrenField,
valueField,
textField,
prefixValue,
prefixText,
textSplit = '/',
filterMethod,
level,
checkStrictly
}) => {
const list = []
options.forEach((item) => {
if (item.disabled) {
return
}
const text = item[textField]
const textNavStr = prefixText ? prefixText + textSplit + text : text
const value = [...prefixValue, item[valueField]]
const children = item[childrenField]
if (checkStrictly === true || !children || children.length === 0) {
if (typeof filterMethod === 'function') {
const matched = filterMethod({ data: item, label: item.label, value: item.value, level }, input)
if (matched) {
list.push({
[valueField]: value,
[textField]: textNavStr
})
}
} else if (text.includes(input)) {
list.push({
[valueField]: value,
[textField]: textNavStr
})
}
}
if (children && children.length) {
const arr = loopSearchOption({
options: children,
input,
childrenField,
valueField,
textField,
prefixValue: value,
prefixText: textNavStr,
textSplit,
filterMethod,
level: level + 1,
checkStrictly
})
list.push(...arr)
}
})
return list
}
export const searchMethod =
({ state, props, api }) =>
() => {
const { searchConfig, valueField, textField } = props
const { children: childrenField } = api.getNodeConfig()
const { input, options } = state.search
let list = []
if (searchConfig && searchConfig.searchMethod) {
list = searchConfig.searchMethod({ input, options, state }) || []
} else if (input) {
list = loopSearchOption({
options,
input,
childrenField,
valueField,
textField,
prefixValue: [],
prefixText: '',
filterMethod: searchConfig.filterMethod,
level: 0,
checkStrictly: props.nodeConfig.checkStrictly
})
}
if (typeof list.then === 'function') {
list.then((data) => (state.search.filterOptions = data))
} else {
state.search.filterOptions = list
}
}
export const searchBoxToggle =
({ state, props, api }) =>
(isShow) => {
const { searchConfig = {} } = props
const { lazy } = api.getNodeConfig()
state.search.show = isShow
state.search.input = ''
if (isShow) {
state.search.options =
!lazy && searchConfig.options && searchConfig.options.length ? searchConfig.options : state.store.getAllData()
api.searchMethod()
}
}
export const searchSelectHandler =
({ emit, api, props }) =>
(option) => {
const { valueField } = props
api.searchBoxToggle(false)
api.syncCheckStatus(option[valueField])
emit('search-click', option)
}
export const isSelected =
({ state }) =>
(option) => {
const { navList } = state
return navList.includes(option)
}
export const setLevel =
({ state, api }) =>
(level, option) => {
if (!api.disabledNavOption(level, option)) {
state.level = level
}
}
export const created =
({ api }) =>
() => {
api.initTreeStore()
}
export const loadData =
({ props, state }) =>
(data) => {
if (!props.visible) {
return
}
state.store.setData(data)
}
export const renderSearchOption =
({ state }) =>
(text) => {
return (text || '').split(state.search.input)
}
export const computedNavListHandler =
({ state, props }) =>
() => {
const { textField, placeholder } = props
const { isLeaf, navList } = state
const placeholderArr = []
const len = navList.length
if (!Array.isArray(placeholder)) {
placeholderArr.push({ data: { [textField]: placeholder }, selfWaitLoad: true })
} else if (len < placeholder.length) {
for (let i = len; i < placeholder.length; i++) {
placeholderArr.push({ data: { [textField]: placeholder[i] }, selfWaitLoad: true })
}
}
return isLeaf && len ? state.navList : [...state.navList, ...placeholderArr]
}
export const disabledNavOption =
({ state }) =>
(level, option) => {
const node = state.navList[level - 1]
return option.selfWaitLoad && (!node || !node.childNodes.length)
}
export const getNodeConfig =
({ constants, props }) =>
() => {
const { nodeConfig, valueField } = props
return Object.assign({ nodeKey: valueField }, constants.defaultNodeConfig, nodeConfig)
}
export const initTreeStore =
({ props, state, api }) =>
() => {
const { data, textField } = props
const {
nodeKey,
lazy,
load,
afterLoad,
children,
isLeaf,
checkStrictly,
currentNodeKey,
checkDescendants,
defaultCheckedKeys,
defaultExpandedKeys,
autoExpandParent,
defaultExpandAll,
filterNodeMethod
} = api.getNodeConfig()
state.store = new TreeStore({
key: nodeKey,
data,
lazy,
props: {
children,
label: textField,
isLeaf
},
load,
afterLoad: () => {
state.rootData = state.root.childNodes || []
api.syncCheckStatus()
afterLoad && afterLoad()
},
currentNodeKey,
checkStrictly,
checkDescendants,
defaultCheckedKeys,
defaultExpandedKeys,
autoExpandParent,
defaultExpandAll,
filterNodeMethod
})
state.root = state.store.root
if (!lazy) {
state.rootData = state.root.childNodes || []
api.syncCheckStatus()
}
}