You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
999 lines
34 KiB
999 lines
34 KiB
import { |
|
Widget, |
|
extend, |
|
emptyFn, |
|
isNotNull, |
|
some, |
|
Tree, |
|
isEmpty, |
|
each, |
|
clone, |
|
isNull, |
|
UUID, |
|
size, |
|
i18nText, |
|
deepClone, |
|
isNotEmptyArray, |
|
last, |
|
has, |
|
nextTick, |
|
concat, |
|
filter, |
|
Func, |
|
any, |
|
every, |
|
map, |
|
difference, |
|
keys, |
|
isPlainObject, |
|
get, |
|
set |
|
} from "@/core"; |
|
import { TreeView } from "@/case"; |
|
|
|
export class AbstractTreeValueChooser extends Widget { |
|
_const = { perPage: 100 }; |
|
|
|
_defaultConfig() { |
|
return extend(super._defaultConfig(...arguments), { |
|
items: null, |
|
itemsCreator: emptyFn, |
|
open: false, |
|
}); |
|
} |
|
|
|
_valueFormatter(v) { |
|
let text = v; |
|
if (this.options.valueFormatter) { |
|
return this.options.valueFormatter(v); |
|
} |
|
if (isNotNull(this.items)) { |
|
some(this.items, (i, item) => { |
|
if (item.value === v || `${item.value}` === v) { |
|
text = item.text; |
|
|
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
return text; |
|
} |
|
|
|
_initData(items) { |
|
this.items = items; |
|
const nodes = Tree.treeFormat(items); |
|
this.tree = new Tree(); |
|
this.tree.initTree(nodes); |
|
} |
|
|
|
_itemsCreator(options, callback) { |
|
const call = () => { |
|
switch (options.type) { |
|
case TreeView.REQ_TYPE_INIT_DATA: |
|
this._reqInitTreeNode(options, callback); |
|
break; |
|
case TreeView.REQ_TYPE_ADJUST_DATA: |
|
this._reqAdjustTreeNode(options, callback); |
|
break; |
|
case TreeView.REQ_TYPE_SELECT_DATA: |
|
this._reqSelectedTreeNode(options, callback); |
|
break; |
|
case TreeView.REQ_TYPE_GET_SELECTED_DATA: |
|
this._reqDisplayTreeNode(options, callback); |
|
break; |
|
default: |
|
this._reqTreeNode(options, callback); |
|
break; |
|
} |
|
}; |
|
const o = this.options; |
|
if (!this.items) { |
|
o.itemsCreator({}, items => { |
|
this._initData(items); |
|
call(); |
|
}); |
|
} else { |
|
call(); |
|
} |
|
} |
|
|
|
_reqDisplayTreeNode(op, callback) { |
|
const result = []; |
|
const selectedValues = op.selectedValues; |
|
|
|
if (selectedValues == null || isEmpty(selectedValues)) { |
|
callback({}); |
|
|
|
return; |
|
} |
|
|
|
const getCount = (jo, parentValues) => { |
|
if (jo == null) { |
|
return 0; |
|
} |
|
if (isEmpty(jo)) { |
|
return this._getChildCount(parentValues); |
|
} |
|
|
|
return size(jo); |
|
}; |
|
|
|
const doCheck = (parentValues, node, selected) => { |
|
if (selected == null || isEmpty(selected)) { |
|
each(node.getChildren(), (i, child) => { |
|
const newParents = clone(parentValues); |
|
newParents.push(child.value); |
|
const llen = this._getChildCount(newParents); |
|
createOneJson(child, node.id, llen); |
|
doCheck(newParents, child, {}); |
|
}); |
|
|
|
return; |
|
} |
|
each(selected, k => { |
|
const node = this._getTreeNode(parentValues, k); |
|
// 找不到就是新增值 |
|
if (isNull(node)) { |
|
createOneJson( |
|
{ |
|
id: UUID(), |
|
text: k, |
|
value: k, |
|
}, |
|
UUID(), |
|
0 |
|
); |
|
} else { |
|
const newParents = clone(parentValues); |
|
newParents.push(node.value); |
|
createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); |
|
doCheck(newParents, node, selected[k]); |
|
} |
|
}); |
|
}; |
|
|
|
doCheck([], this.tree.getRoot(), selectedValues); |
|
|
|
callback({ |
|
items: result, |
|
}); |
|
|
|
function createOneJson(node, pId, llen) { |
|
result.push({ |
|
id: node.id, |
|
pId, |
|
text: |
|
node.text + |
|
(llen > 0 ? `(${i18nText("BI-Basic_Altogether")}${llen}${i18nText("BI-Basic_Count")})` : ""), |
|
value: node.value, |
|
open: true, |
|
disabled: node.disabled, |
|
iconCls: node.iconCls, |
|
}); |
|
} |
|
} |
|
|
|
_reqSelectedTreeNode(op, callback) { |
|
const self = this; |
|
const selectedValues = deepClone(op.selectedValues); |
|
const notSelectedValue = op.notSelectedValue || {}; |
|
const keyword = op.keyword || ""; |
|
const parentValues = op.parentValues || []; |
|
|
|
if (selectedValues == null || isEmpty(selectedValues)) { |
|
callback({}); |
|
|
|
return; |
|
} |
|
|
|
dealWithSelectedValues(selectedValues); |
|
callback(selectedValues); |
|
|
|
function dealWithSelectedValues(selectedValues) { |
|
let p = parentValues.concat(notSelectedValue); |
|
// 存储的值中存在这个值就把它删掉 |
|
// 例如选中了中国-江苏-南京, 取消中国或江苏或南京 |
|
// p长度不大于selectedValues的情况才可能找到,这样可以直接删除selectedValues的节点 |
|
if (canFindKey(selectedValues, p)) { |
|
// 如果搜索的值在父亲链中 |
|
if (isSearchValueInParent(p)) { |
|
// 例如选中了 中国-江苏, 搜索江苏, 取消江苏(干掉了江苏) |
|
self._deleteNode(selectedValues, p); |
|
} else { |
|
const searched = []; |
|
// 要找到所有以notSelectedValue为叶子节点的链路 |
|
const find = search(parentValues, notSelectedValue, [], searched); |
|
if (find && isNotEmptyArray(searched)) { |
|
each(searched, (i, arr) => { |
|
const node = self._getNode(selectedValues, arr); |
|
if (node) { |
|
// 例如选中了 中国-江苏, 搜索江苏, 取消中国(实际上只想删除中国-江苏,因为搜的是江苏) |
|
// 例如选中了 中国-江苏-南京,搜索南京,取消中国(实际上只想删除中国-江苏-南京,因为搜的是南京) |
|
self._deleteNode(selectedValues, arr); |
|
} else { |
|
// 例如选中了 中国-江苏,搜索南京,取消中国(实际上只想删除中国-江苏-南京,因为搜的是南京) |
|
expandSelectedValue(selectedValues, arr, last(arr)); |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
// 存储的值中不存在这个值,但父亲节点是全选的情况 |
|
// 例如选中了中国-江苏,取消南京 |
|
// important 选中了中国-江苏,取消了江苏,但是搜索的是南京 |
|
if (isChild(selectedValues, p)) { |
|
const result = []; |
|
let find = false; |
|
// 如果parentValues中有匹配的值,说明搜索结果不在当前值下 |
|
if (isSearchValueInParent(p)) { |
|
find = true; |
|
} else { |
|
// 从当前值开始搜 |
|
find = search(parentValues, notSelectedValue, result); |
|
p = parentValues; |
|
} |
|
|
|
if (find === true) { |
|
// 去掉点击的节点之后的结果集 |
|
expandSelectedValue(selectedValues, p, notSelectedValue); |
|
// 添加去掉搜索的结果集 |
|
if (result.length > 0) { |
|
each(result, (i, strs) => { |
|
self._buildTree(selectedValues, strs); |
|
}); |
|
} |
|
} |
|
} |
|
}; |
|
|
|
function expandSelectedValue(selectedValues, parents, notSelectedValue) { |
|
let next = selectedValues; |
|
const childrenCount = []; |
|
const path = []; |
|
// 去掉点击的节点之后的结果集 |
|
some(parents, (i, v) => { |
|
const t = next[v]; |
|
if (t == null) { |
|
if (i === 0) { |
|
return true; |
|
} |
|
if (isEmpty(next)) { |
|
const split = parents.slice(0, i); |
|
const expanded = self._getChildren(split); |
|
path.push(split); |
|
childrenCount.push(expanded.length); |
|
// 如果只有一个值且取消的就是这个值 |
|
if ( |
|
i === parents.length - 1 && |
|
expanded.length === 1 && |
|
expanded[0].value === notSelectedValue |
|
) { |
|
for (let j = childrenCount.length - 1; j >= 0; j--) { |
|
if (childrenCount[j] === 1) { |
|
self._deleteNode(selectedValues, path[j]); |
|
} else { |
|
break; |
|
} |
|
} |
|
} else { |
|
each(expanded, (m, child) => { |
|
if (i === parents.length - 1 && child.value === notSelectedValue) { |
|
return true; |
|
} |
|
next[child.value] = {}; |
|
}); |
|
} |
|
next = next[v]; |
|
} else { |
|
return true; |
|
// next = {}; |
|
// next[v] = {}; |
|
} |
|
} else { |
|
next = t; |
|
} |
|
}); |
|
}; |
|
|
|
function search(parents, current, result, searched) { |
|
const newParents = clone(parents); |
|
newParents.push(current); |
|
if (self._isMatch(parents, current, keyword)) { |
|
searched && searched.push(newParents); |
|
|
|
return true; |
|
} |
|
|
|
const children = self._getChildren(newParents); |
|
|
|
const notSearch = []; |
|
let can = false; |
|
|
|
each(children, (i, child) => { |
|
if (search(newParents, child.value, result, searched)) { |
|
can = true; |
|
} else { |
|
notSearch.push(child.value); |
|
} |
|
}); |
|
if (can === true) { |
|
each(notSearch, (i, v) => { |
|
const next = clone(newParents); |
|
next.push(v); |
|
result.push(next); |
|
}); |
|
} |
|
|
|
return can; |
|
}; |
|
|
|
function isSearchValueInParent(parentValues) { |
|
for (let i = 0, len = parentValues.length; i < len; i++) { |
|
if (self._isMatch(parentValues.slice(0, i), parentValues[i], keyword)) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
}; |
|
|
|
function canFindKey(selectedValues, parents) { |
|
let t = selectedValues; |
|
for (let i = 0; i < parents.length; i++) { |
|
const v = parents[i]; |
|
t = t[v]; |
|
if (t == null) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
function isChild(selectedValues, parents) { |
|
let t = selectedValues; |
|
for (let i = 0; i < parents.length; i++) { |
|
const v = parents[i]; |
|
if (!has(t, v)) { |
|
return false; |
|
} |
|
t = t[v]; |
|
if (isEmpty(t)) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
} |
|
|
|
_reqAdjustTreeNode(op, callback) { |
|
const result = []; |
|
const selectedValues = op.selectedValues; |
|
if (selectedValues == null || isEmpty(selectedValues)) { |
|
callback({}); |
|
|
|
return; |
|
} |
|
each(selectedValues, (k, v) => { |
|
result.push([k]); |
|
}); |
|
|
|
const isAllSelected = (selected, parents) => isEmpty(selected) || this._getChildCount(parents) === size(selected); |
|
|
|
function dealWithSelectedValues(selected, parents) { |
|
if (selected == null || isEmpty(selected)) { |
|
return true; |
|
} |
|
let can = true; |
|
each(selected, (k, v) => { |
|
const p = clone(parents); |
|
p.push(k); |
|
if (!dealWithSelectedValues(selected[k], p) || op.searcherPaneAutoShrink === false) { |
|
each(selected[k], (nk, nv) => { |
|
const t = clone(p); |
|
t.push(nk); |
|
result.push(t); |
|
}); |
|
can = false; |
|
} |
|
}); |
|
|
|
return can && isAllSelected(selected, parents); |
|
} |
|
|
|
dealWithSelectedValues(selectedValues, []); |
|
|
|
const jo = {}; |
|
each(result, (i, strs) => { |
|
this._buildTree(jo, strs); |
|
}); |
|
callback(jo); |
|
|
|
|
|
} |
|
|
|
_reqInitTreeNode(op, callback) { |
|
let result = []; |
|
const self = this; |
|
const keyword = op.keyword || ""; |
|
const selectedValues = op.selectedValues; |
|
const lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 |
|
const output = search(); |
|
nextTick(() => { |
|
callback({ |
|
hasNext: output.length > this._const.perPage, |
|
items: result, |
|
lastSearchValue: last(output), |
|
}); |
|
}); |
|
|
|
function search() { |
|
const children = self._getChildren([]); |
|
let start = children.length; |
|
if (lastSearchValue !== "") { |
|
for (let j = 0, len = start; j < len; j++) { |
|
if (children[j].value === lastSearchValue) { |
|
start = j + 1; |
|
break; |
|
} |
|
} |
|
} else { |
|
start = 0; |
|
} |
|
const output = []; |
|
for (let i = start, len = children.length; i < len; i++) { |
|
let find; |
|
if (output.length < self._const.perPage) { |
|
find = nodeSearch(1, [], children[i].value, false, result); |
|
} else if (output.length === self._const.perPage) { |
|
find = nodeSearch(1, [], children[i].value, false, []); |
|
} |
|
if (find[0] === true) { |
|
output.push(children[i].value); |
|
} |
|
if (output.length > self._const.perPage) { |
|
break; |
|
} |
|
} |
|
|
|
// 深层嵌套的比较麻烦,这边先实现的是在根节点添加 |
|
if (op.times === 1) { |
|
const nodes = self._getAddedValueNode([], selectedValues); |
|
result = concat( |
|
filter(nodes, (idx, node) => { |
|
const find = Func.getSearchResult([node.text || node.value], keyword); |
|
|
|
return find.find.length > 0 || find.match.length > 0; |
|
}), |
|
result |
|
); |
|
} |
|
|
|
return output; |
|
} |
|
|
|
function nodeSearch(deep, parentValues, current, isAllSelect, result) { |
|
if (self._isMatch(parentValues, current, keyword)) { |
|
const checked = isAllSelect || isSelected(parentValues, current); |
|
createOneJson( |
|
parentValues, |
|
current, |
|
false, |
|
checked, |
|
!isAllSelect && isHalf(parentValues, current), |
|
true, |
|
result |
|
); |
|
|
|
return [true, checked]; |
|
} |
|
const newParents = clone(parentValues); |
|
newParents.push(current); |
|
const children = self._getChildren(newParents); |
|
|
|
let can = false, |
|
checked = false; |
|
|
|
const isCurAllSelected = isAllSelect || isAllSelected(parentValues, current); |
|
each(children, (i, child) => { |
|
const state = nodeSearch(deep + 1, newParents, child.value, isCurAllSelected, result); |
|
// 当前节点的子节点是否选中,并不确定全选还是半选 |
|
if (state[1] === true) { |
|
checked = true; |
|
} |
|
// 当前节点的子节点要不要加入到结果集中 |
|
if (state[0] === true) { |
|
can = true; |
|
} |
|
}); |
|
// 子节点匹配, 补充父节点 |
|
if (can === true) { |
|
checked = isCurAllSelected || (isSelected(parentValues, current) && checked); |
|
createOneJson(parentValues, current, true, checked, false, false, result); |
|
} |
|
|
|
return [can, checked]; |
|
} |
|
|
|
function createOneJson(parentValues, value, isOpen, checked, half, flag, result) { |
|
const node = self._getTreeNode(parentValues, value); |
|
result.push({ |
|
id: node.id, |
|
pId: node.pId, |
|
text: node.text, |
|
value: node.value, |
|
title: node.title, |
|
isParent: node.getChildrenLength() > 0, |
|
open: isOpen, |
|
checked, |
|
halfCheck: half, |
|
flag, |
|
disabled: node.disabled, |
|
iconCls: node.iconCls, |
|
}); |
|
} |
|
|
|
function isHalf(parentValues, value) { |
|
const find = findSelectedObj(parentValues); |
|
if (find == null) { |
|
return null; |
|
} |
|
|
|
return any(find, (v, ob) => { |
|
if (v === value) { |
|
if (ob != null && !isEmpty(ob)) { |
|
return true; |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function isAllSelected(parentValues, value) { |
|
const find = findSelectedObj(parentValues); |
|
if (find == null) { |
|
return null; |
|
} |
|
|
|
return any(find, (v, ob) => { |
|
if (v === value) { |
|
if (ob != null && isEmpty(ob)) { |
|
return true; |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function isSelected(parentValues, value) { |
|
const find = findSelectedObj(parentValues); |
|
if (find == null) { |
|
return false; |
|
} |
|
|
|
return any(find, v => { |
|
if (v === value) { |
|
return true; |
|
} |
|
}); |
|
} |
|
|
|
function findSelectedObj(parentValues) { |
|
let find = selectedValues; |
|
if (find == null) { |
|
return null; |
|
} |
|
every(parentValues, (i, v) => { |
|
find = find[v]; |
|
if (find == null) { |
|
return false; |
|
} |
|
|
|
return true; |
|
}); |
|
|
|
return find; |
|
} |
|
} |
|
|
|
_reqTreeNode(op, callback) { |
|
const o = this.options; |
|
let result = []; |
|
const times = op.times; |
|
const checkState = op.checkState || {}; |
|
const parentValues = op.parentValues || []; |
|
const selectedValues = op.selectedValues || {}; |
|
const getCheckState = (current, parentValues, valueMap, checkState) => { |
|
// 节点本身的checked和half优先级最高 |
|
const checked = checkState.checked, |
|
half = checkState.half; |
|
let tempCheck = false, |
|
halfCheck = false; |
|
if (has(valueMap, current)) { |
|
// 可能是半选 |
|
if (valueMap[current][0] === 1) { |
|
const values = clone(parentValues); |
|
values.push(current); |
|
const childCount = this._getChildCount(values); |
|
if (childCount > 0 && childCount !== valueMap[current][1]) { |
|
halfCheck = true; |
|
} |
|
} else if (valueMap[current][0] === 2) { |
|
tempCheck = true; |
|
} |
|
} |
|
let check; |
|
// 展开的节点checked为false 且没有明确得出当前子节点是半选或者全选, 则check状态取决于valueMap |
|
if (!checked && !halfCheck && !tempCheck) { |
|
check = has(valueMap, current); |
|
} else { |
|
// 不是上面那种情况就先看在节点没有带有明确半选的时候,通过节点自身的checked和valueMap的状态能都得到选中信息 |
|
check = ((tempCheck || checked) && !half) || has(valueMap, current); |
|
} |
|
|
|
return [check, halfCheck]; |
|
}; |
|
const getResult = (parentValues, checkState) => { |
|
let valueMap = {}; |
|
// if (judgeState(parentValues, selectedValues, checkState)) { |
|
const isAllSelected = (selected, parents) => { |
|
if (isEmpty(selected)) { |
|
return true; |
|
} |
|
|
|
if (this._getChildCount(parents) !== size(selected)) { |
|
return false; |
|
} |
|
|
|
return every(selected, value => isAllSelected(selected[value], concat(parents, value))); |
|
}; |
|
|
|
function dealWithSelectedValue(parentValues, selectedValues) { |
|
const valueMap = {}, |
|
parents = (parentValues || []).slice(0); |
|
each(parentValues, (i, v) => { |
|
parents.push(v); |
|
|
|
selectedValues = selectedValues[v] || {}; |
|
}); |
|
each(selectedValues, (value, obj) => { |
|
const currentParents = concat(parents, value); |
|
|
|
if (isNull(obj)) { |
|
valueMap[value] = [0, 0]; |
|
|
|
return; |
|
} |
|
if (isEmpty(obj)) { |
|
valueMap[value] = [2, 0]; |
|
|
|
return; |
|
} |
|
const nextNames = {}; |
|
each(obj, (t, o) => { |
|
if (isNull(o) || isEmpty(o)) { |
|
nextNames[t] = true; |
|
} else { |
|
isAllSelected(o, concat(currentParents, [t])) && (nextNames[t] = true); |
|
} |
|
}); |
|
// valueMap的数组第一个参数为不选: 0, 半选: 1, 全选:2, 第二个参数为改节点下选中的子节点个数(子节点全选或者不存在) |
|
valueMap[value] = [1, size(nextNames)]; |
|
}); |
|
|
|
return valueMap; |
|
} |
|
valueMap = dealWithSelectedValue(parentValues, selectedValues); |
|
// } |
|
const nodes = this._getChildren(parentValues); |
|
for (let i = (times - 1) * this._const.perPage; nodes[i] && i < times * this._const.perPage; i++) { |
|
const state = getCheckState(nodes[i].value, parentValues, valueMap, checkState); |
|
const openState = o.open || nodes[i].open; |
|
result.push({ |
|
id: nodes[i].id, |
|
pId: nodes[i].pId, |
|
value: nodes[i].value, |
|
text: nodes[i].text, |
|
times: 1, |
|
isParent: nodes[i].isParent || nodes[i].getChildrenLength() > 0, |
|
checked: state[0], |
|
half: state[1], |
|
halfCheck: openState ? false : state[1], |
|
open: openState, |
|
disabled: nodes[i].disabled, |
|
title: nodes[i].title || nodes[i].text, |
|
warningTitle: nodes[i].warningTitle, |
|
iconCls: nodes[i].iconCls, |
|
}); |
|
if (openState) { |
|
getResult(parentValues.concat([nodes[i].value]), { checked: state[0], half: state[1] }); |
|
} |
|
} |
|
}; |
|
|
|
getResult(parentValues, checkState); |
|
|
|
// 如果指定节点全部打开 |
|
// if (o.open) { |
|
// var allNodes = []; |
|
// // 获取所有节点 |
|
// each(nodes, function (idx, node) { |
|
// allNodes = concat(allNodes, self._getAllChildren(parentValues.concat([node.value]))); |
|
// }); |
|
// var lastFind; |
|
// each(allNodes, function (idx, node) { |
|
// var valueMap = dealWithSelectedValue(node.parentValues, selectedValues); |
|
// // REPORT-24409 fix: 设置节点全部展开,添加的节点没有给状态 |
|
// var parentCheckState = {}; |
|
// var find = find(result, function (idx, pNode) { |
|
// return pNode.id === node.pId; |
|
// }); |
|
// if (find) { |
|
// parentCheckState.checked = find.halfCheck ? false : find.checked; |
|
// parentCheckState.half = find.halfCheck; |
|
// // 默认展开也需要重置父节点的halfCheck |
|
// if (isNotNull(lastFind) && (lastFind !== find || allNodes.length - 1 === idx)) { |
|
// lastFind.half = lastFind.halfCheck; |
|
// lastFind.halfCheck = false; |
|
// } |
|
// } |
|
// lastFind = find; |
|
// var state = getCheckState(node.value, node.parentValues, valueMap, parentCheckState); |
|
// result.push({ |
|
// id: node.id, |
|
// pId: node.pId, |
|
// value: node.value, |
|
// text: node.text, |
|
// times: 1, |
|
// isParent: node.getChildrenLength() > 0, |
|
// checked: state[0], |
|
// halfCheck: state[1], |
|
// open: true, |
|
// disabled: node.disabled, |
|
// title: node.title || node.text, |
|
// warningTitle: node.warningTitle, |
|
// }); |
|
// }); |
|
// } |
|
|
|
// 深层嵌套的比较麻烦,这边先实现的是在根节点添加 |
|
if (parentValues.length === 0 && times === 1) { |
|
result = concat(this._getAddedValueNode(parentValues, selectedValues), result); |
|
} |
|
nextTick(() => { |
|
callback({ |
|
items: result, |
|
hasNext: this._getChildren(parentValues).length > times * this._const.perPage, |
|
}); |
|
}); |
|
|
|
// function judgeState(parentValues, selected_value, checkState) { |
|
// var checked = checkState.checked, half = checkState.half; |
|
// if (parentValues.length > 0 && !checked) { |
|
// return false; |
|
// } |
|
// return (parentValues.length === 0 || (checked && half) && !isEmpty(selected_value)); |
|
// } |
|
} |
|
|
|
_getAddedValueNode(parentValues, selectedValues) { |
|
const nodes = this._getChildren(parentValues); |
|
|
|
return map(difference(keys(selectedValues), map(nodes, "value")), (idx, v) => { |
|
return { |
|
id: UUID(), |
|
pId: nodes.length > 0 ? nodes[0].pId : UUID(), |
|
value: v, |
|
text: v, |
|
times: 1, |
|
isParent: false, |
|
checked: true, |
|
halfCheck: false, |
|
}; |
|
}); |
|
} |
|
|
|
_getNode(selectedValues, parentValues) { |
|
let pNode = selectedValues; |
|
for (let i = 0, len = parentValues.length; i < len; i++) { |
|
if (pNode == null) { |
|
return null; |
|
} |
|
pNode = pNode[parentValues[i]]; |
|
} |
|
|
|
return pNode; |
|
} |
|
|
|
_deleteNode(selectedValues, values) { |
|
let name = values[values.length - 1]; |
|
let p = values.slice(0, values.length - 1); |
|
let pNode = this._getNode(selectedValues, p); |
|
if (pNode != null && pNode[name]) { |
|
delete pNode[name]; |
|
// 递归删掉空父节点 |
|
while (p.length > 0 && isEmpty(pNode)) { |
|
name = p[p.length - 1]; |
|
p = p.slice(0, p.length - 1); |
|
pNode = this._getNode(selectedValues, p); |
|
if (pNode != null) { |
|
delete pNode[name]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
_buildTree(jo, values) { |
|
let t = jo; |
|
each(values, (i, v) => { |
|
if (!has(t, v)) { |
|
t[v] = {}; |
|
} |
|
t = t[v]; |
|
}); |
|
} |
|
|
|
_isMatch(parentValues, value, keyword) { |
|
const o = this.options; |
|
const node = this._getTreeNode(parentValues, value); |
|
if (!node) { |
|
return false; |
|
} |
|
const find = Func.getSearchResult([node.text || node.value], keyword); |
|
if (o.allowSearchValue && node.value) { |
|
const valueFind = Func.getSearchResult([node.value], keyword); |
|
|
|
return ( |
|
valueFind.find.length > 0 || valueFind.match.length > 0 || find.find.length > 0 || find.match.length > 0 |
|
); |
|
} |
|
|
|
return find.find.length > 0 || find.match.length > 0; |
|
} |
|
|
|
_getTreeNode(parentValues, v) { |
|
let findParentNode; |
|
let index = 0; |
|
let currentParent = this.tree.getRoot(); |
|
this.tree.traverse(node => { |
|
if (this.tree.isRoot(node)) { |
|
return; |
|
} |
|
if (index > parentValues.length) { |
|
return false; |
|
} |
|
|
|
/** |
|
* 一个树结构。要找root_1_3的子节点 |
|
* {root: { 1: {1: {}, 2: {}, 3: {}}, 3: {1: {}, 2: {}} } } |
|
* 当遍历到root_1节点时,index++,而下一个节点root_3时,符合下面的if逻辑,这样找到的节点就是root_3节点了,需要加步判断是否是root_1的子节点 |
|
*/ |
|
if (index === parentValues.length && node.value === v) { |
|
if (node.getParent() !== currentParent) { |
|
return; |
|
} |
|
|
|
findParentNode = node; |
|
|
|
return false; |
|
} |
|
if (node.value === parentValues[index] && node.getParent() === currentParent) { |
|
index++; |
|
currentParent = node; |
|
|
|
return; |
|
} |
|
|
|
return true; |
|
}); |
|
|
|
return findParentNode; |
|
} |
|
|
|
_getChildren(parentValues) { |
|
let parent; |
|
if (parentValues.length > 0) { |
|
const value = last(parentValues); |
|
parent = this._getTreeNode(parentValues.slice(0, parentValues.length - 1), value); |
|
} else { |
|
parent = this.tree.getRoot(); |
|
} |
|
|
|
return parent ? parent.getChildren() : []; |
|
} |
|
|
|
_getAllChildren(parentValues) { |
|
const children = this._getChildren(parentValues); |
|
let nodes = [].concat(children); |
|
each(nodes, (idx, node) => { |
|
node.parentValues = parentValues; |
|
}); |
|
let queue = map(children, (idx, node) => { |
|
return { |
|
parentValues, |
|
value: node.value, |
|
}; |
|
}); |
|
while (isNotEmptyArray(queue)) { |
|
const node = queue.shift(); |
|
const pValues = node.parentValues.concat(node.value); |
|
const childNodes = this._getChildren(pValues); |
|
each(childNodes, (idx, node) => { |
|
node.parentValues = pValues; |
|
}); |
|
queue = queue.concat(childNodes); |
|
nodes = nodes.concat(childNodes); |
|
} |
|
|
|
return nodes; |
|
} |
|
|
|
_getChildCount(parentValues) { |
|
return this._getChildren(parentValues).length; |
|
} |
|
|
|
assertSelectedValue(selectedValues, items = []) { |
|
if (isPlainObject(selectedValues)) { |
|
return selectedValues; |
|
} |
|
|
|
const tree = Tree.transformToTreeFormat(items); |
|
const value2ParentMap = {}; |
|
Tree.traversal(tree, (index, node, pNode) => { |
|
value2ParentMap[node.value] = pNode; |
|
}); |
|
|
|
const result = {}; |
|
each(selectedValues, (index, value) => { |
|
let curr = value; |
|
const parentPath = []; |
|
while (curr) { |
|
parentPath.unshift(curr); |
|
curr = value2ParentMap[curr]?.value; |
|
} |
|
each(parentPath, index => { |
|
if (isNull(get(result, parentPath.slice(0, index + 1)))) { |
|
set(result, parentPath.slice(0, index + 1), {}); |
|
} |
|
}); |
|
// 执行完一条路径,check一下 |
|
const lengths = size(get(result, parentPath.slice(0, -1))); |
|
if (lengths === value2ParentMap[value]?.children?.length) { |
|
set(result, parentPath.slice(0, -1), {}); |
|
} |
|
}); |
|
|
|
return result; |
|
} |
|
|
|
buildCompleteTree(selectedValues) { |
|
const result = {}; |
|
|
|
const fill = (parentValues, node, selected, r) => { |
|
if (selected === null || isEmpty(selected)) { |
|
each(node.getChildren(), (i, child) => { |
|
const newParents = clone(parentValues); |
|
newParents.push(child.value); |
|
r[child.value] = {}; |
|
fill(newParents, child, null, r[child.value]); |
|
}); |
|
|
|
return; |
|
} |
|
each(selected, k => { |
|
const node = this._getTreeNode(parentValues, k); |
|
const newParents = clone(parentValues); |
|
newParents.push(node.value); |
|
r[k] = {}; |
|
fill(newParents, node, selected[k], r[k]); |
|
}); |
|
} |
|
|
|
if (selectedValues !== null && !isEmpty(selectedValues)) { |
|
fill([], this.tree.getRoot(), selectedValues, result); |
|
} |
|
|
|
return result; |
|
} |
|
}
|
|
|