import React from 'react'; import * as Tools from 'jad-tool'; type OptionType = { value: string | number, name?: string, fullName?: (string)[], leaf: number, // 从 0 开始 parent?: OptionType, children?: OptionType[], checked: boolean, indeterminate: boolean, }; type PropsKeyValue = { propPValue: string, propValue: string, propName: string, } export function initOptions(initValue: (string | number)[], initOptions: OptionType[]) { if (!initOptions) return console.warn(`MultiCascaderPanel initOptions has some problems`) const { propValue, propName } = this.keyValue; const { optionsFlat } = this.state; const { selectedMap, options } = getSelectedMapOptions.bind(this)(initValue, initOptions, propValue); const newValues = selectedMap.map(item => item[propValue]); const newOptionsFlat = options ? transform2FlatData(options, propName) : optionsFlat; return { selectedMap, options, optionsFlat: newOptionsFlat, selectedValue: newValues } } // 无论 showCheckedStrategy 策略是什么,都只展示 initValue 中的选项,因为一般 selectedMap,其实对应的也是接口接受的入参 需要传的 // 获取初始的 selectedMap、options walk export function getSelectedMapOptions(value: (string | number)[], options: OptionType[]) { if (!options || !options.length) { console.warn(`MultiCascaderPanel getSelectedMapOptions options has some problems`) return { options: [], selectedMap: [] }; } if (!value || !value.length) { return { options: options, selectedMap: [] };; } const { showCheckedStrategy } = this.props; // 定制回填方式 const { propValue } = this.keyValue; // 定制回填方式 let newOptions = options; // clone 一份 let newSelectedMap = []; function walk(walkValue, walkOptions: OptionType[]) { return walkOptions.map((item: OptionType, index) => { if (Tools.isExist(item[propValue]) && walkValue.includes(item[propValue])) { //TODO onCheckHigh2Low(true, item); onCheckLow2High(true, item); // 如果是 所有选中的节点都展示,那么 该层及其子层们都要查一遍 if (showCheckedStrategy === 'SHOW_ALL' && item['children'] && item['children'].length) { const newChildren = walk(walkValue, item['children']); const newItem = item; newItem['children'] = newChildren; newSelectedMap[newSelectedMap.length] = newItem; return newItem; } else { newSelectedMap[newSelectedMap.length] = item; return item; }; } else if (Tools.isExist(item[propValue]) && !walkValue.includes(item[propValue]) && item['children'] && item['children'].length) { const newChildren = walk(walkValue, item['children']); let newChildrenCheeckedLength = 0; let newChildrenIndeterminateLength = 0; newChildren.forEach(child => { if(child['checked']) ++newChildrenCheeckedLength; if(child['indeterminate']) ++newChildrenIndeterminateLength; }); const newItem = item; newItem['checked'] = newChildrenCheeckedLength && newChildrenCheeckedLength === newChildren.length ? true : false ; newItem['indeterminate'] = newChildrenCheeckedLength && newChildrenCheeckedLength < newChildren.length || newChildrenIndeterminateLength ? true : false; newItem['children'] = newChildren; return newItem; } else if (Tools.isExist(item[propValue]) && !walkValue.includes(item[propValue])) { return item } else console.log(`MultiCascaderPanel selectedMapOptions walk walkOptions item(${index}) has some problems`) }) } newOptions = walk(value, newOptions); if (value.length !== newSelectedMap.length) console.warn(`MultiCascaderPanel selectedMapOptions walk value.length !== newSelectedMap.length`) return { options: newOptions, selectedMap: newSelectedMap }; } // 从低到高 export function onCheckLow2High(checked: boolean, record: OptionType) { record['checked'] = checked; if (!record['parent']) { return } else { let parentChildren = record['parent']['children']; let flag = false; if (parentChildren.every(item => item.checked)) { flag = true; } if (!flag && parentChildren.some(item => item.checked || item.indeterminate)) { record['parent']['indeterminate'] = true; } else { record['parent']['indeterminate'] = false; } record['parent']['checked'] = flag; onCheckLow2High(flag, record['parent']) }; } // 从高到低 export function onCheckHigh2Low(checked: boolean, record: OptionType) { record['checked'] = checked; record['indeterminate'] = false; // 从高到低,indeterminate其实可直接设置为 checked 的值,不需要遍历children 的 checked if (record['children'] && record['children'].length) { record['children'].forEach(child => { onCheckHigh2Low(checked, child) }) } } // 给 record 新增属性 fullName 代表完整层级 的 name export function getFullName(record: OptionType, propName: string) { return record['fullName'] ? record['fullName'] : (record['parent'] ? [...record['parent']['fullName'], record[propName]] : [record[propName]]); } export function transform2FlatData(treeData: OptionType[], propName?) { if (!treeData.length) return []; let platData = []; const tree2Flat = (data) => data.map(e => { platData[platData.length] = e; if (!e['fullName'] && propName) { e['fullName'] = getFullName(e, propName) } if (e['children'] && e['children'].length) { tree2Flat(e['children']) } }); tree2Flat(treeData).filter(e => e); return platData }; export const transform2TreeData = (platData, keyValue: PropsKeyValue) => { const { propPValue, propValue, propName } = keyValue; if (!platData.length) return []; const plat2Tree = (parentData: OptionType[], excptParentData: OptionType[], leaf: number, parent?: OptionType) => { return parentData.map(e => { const hasChildrenData = excptParentData.filter(d => d[propPValue] == e[propValue]); e['leaf'] = leaf; e['checked'] = false; e['indeterminate'] = false; if (e && parent) e['parent'] = parent; if (e && !e['fullName']) { e['fullName'] = getFullName(e, propName) } if (hasChildrenData && hasChildrenData.length) e['children'] = plat2Tree(hasChildrenData, excptParentData, leaf + 1, e); return e; }).filter(e => e); } return plat2Tree(platData.filter(e => e[propPValue] == 0), platData.filter(e => e[propPValue] != 0), 0); };