Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
wanchun committed Dec 28, 2021
2 parents f560281 + 77c2223 commit e5c0d4a
Show file tree
Hide file tree
Showing 10 changed files with 489 additions and 144 deletions.
17 changes: 2 additions & 15 deletions components/cascader-panel/cascaderPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from './const';
import usePanel from './usePanel';
import CascaderMenu from './menu';
import PROPS from './props';
const prefixCls = getPrefixCls('cascader-panel');
Expand All @@ -28,21 +29,7 @@ export default defineComponent({
CascaderMenu,
},
props: {
currentValue: [Number, String, Array, Object],
options: {
type: Array,
default: () => [],
},
multiple: Boolean,
nodeConfig: {
type: Object,
default: () => {},
},
renderLabel: Function,
handleUpdateSelectedNodes: {
type: Function,
required: true,
},
...PROPS,
},
emits: ['expandChange', 'checkChange', 'close'],
setup(props, { emit, slots }) {
Expand Down
6 changes: 6 additions & 0 deletions components/cascader-panel/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ export const EVENT_CODE = {
DOWN: 'ArrowDown', // 40
ESC: 'Escape',
};

export const CHECK_STRATEGY = {
ALL: 'all',
PARENT: 'parent',
CHILD: 'child',
};
23 changes: 11 additions & 12 deletions components/cascader-panel/nodeContent.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, h, inject } from 'vue';
import { computed, defineComponent, inject } from 'vue';
import getPrefixCls from '../_util/getPrefixCls';
import { CASCADER_PANEL_INJECTION_KEY } from './const';

Expand All @@ -18,16 +18,15 @@ export default defineComponent({
const { data, label } = node;
const { renderLabelFn } = panel;

return () =>
h(
'span',
{
class: {
[prefixCls]: true,
'is-multiple': panel.multiple,
},
},
renderLabelFn ? renderLabelFn({ node, data }) : label,
);
const classes = computed(() => ({
[prefixCls]: true,
'is-multiple': panel.multiple,
}));

return () => (
<span class={classes.value}>
{renderLabelFn ? renderLabelFn({ node, data }) : label}
</span>
);
},
});
33 changes: 33 additions & 0 deletions components/cascader-panel/props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { CHECK_STRATEGY } from './const';

const PROPS = {
currentValue: [Number, String, Array, Object],
options: {
type: Array,
default: () => [],
},
multiple: Boolean,
nodeConfig: {
type: Object,
default: () => {},
},
renderLabel: Function,
handleUpdateSelectedNodes: Function,
showAllLevels: {
type: Boolean,
default: true,
},
separator: {
type: String,
default: ' / ',
},
checkStrictly: {
type: String,
default: CHECK_STRATEGY.CHILD,
validator(value) {
return Object.values(CHECK_STRATEGY).includes(value);
},
},
};

export default PROPS;
13 changes: 6 additions & 7 deletions components/cascader-panel/useNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function setNodeElem(node, elem) {
node.elem = elem;
}

function useSelectedNodes(config, props, nodes) {
function useSelectedNodes(config, props, allNodes) {
const selectedNodes = computed(() => {
const { emitPath } = config.value;
const { currentValue, multiple } = props;
Expand All @@ -66,7 +66,7 @@ function useSelectedNodes(config, props, nodes) {
currentValue,
);

const node = getNodeByValue(nodes.value, nodeValue);
const node = getNodeByValue(allNodes.value, nodeValue);

if (node) {
currentSelectedNodes.push(node);
Expand All @@ -79,10 +79,9 @@ function useSelectedNodes(config, props, nodes) {
emitPath,
currentValue,
);
nodeValues.forEach((nodeValue) => {
const node = getNodeByValue(nodes.value, nodeValue);
if (node) {
currentSelectedNodes.push(node);
allNodes.value.forEach((item) => {
if (nodeValues.includes(item.value)) {
currentSelectedNodes.push(item);
}
});
}
Expand All @@ -101,7 +100,7 @@ export default (config, props) => {
props,
);

const { selectedNodes } = useSelectedNodes(config, props, nodes);
const { selectedNodes } = useSelectedNodes(config, props, allNodes);

return {
nodes,
Expand Down
82 changes: 62 additions & 20 deletions components/cascader-panel/usePanel.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { ref, watch } from 'vue';
import { flatNodes } from '../_util/utils';
import { EVENT_CODE, EXPAND_TRIGGER } from './const';
import { CHECK_STRATEGY, EVENT_CODE, EXPAND_TRIGGER } from './const';
import useNode from './useNode';
import {
updateParentNodesCheckState,
updateChildNodesCheckState,
getValueByOption,
getNodeSibling,
focusNodeElem,
getMenuNodeByElem,
getMenuIndexByElem,
checkNodeElem,
generateId,
getCheckNodesByLeafCheckNodes,
} from './utils';

function useUpdateNodes(props, selectedNodes, allNodes) {
Expand All @@ -25,15 +27,22 @@ function useUpdateNodes(props, selectedNodes, allNodes) {
const doUpdateNodes = () => {
clearCheckedNodes();

const { multiple, handleUpdateSelectedNodes } = props;
const { multiple, handleUpdateSelectedNodes, checkStrictly } = props;

selectedNodes.value.forEach((node) => {
node.checked = true;
});

/**
* 多选情况,更新节点选中状态
* 1. 根据选中节点,更新父节点的中间状态和选中状态
* 1. 若为 checkStrictly = parent 情况,则需要更新子节点的选中状态
*/
if (multiple) {
// 更新中间状态
updateParentNodesCheckState(selectedNodes.value);
if (checkStrictly === CHECK_STRATEGY.PARENT) {
updateChildNodesCheckState(selectedNodes.value);
}
}

handleUpdateSelectedNodes(selectedNodes.value);
Expand Down Expand Up @@ -92,7 +101,14 @@ function useExpandNode(menus, emit, updateMenus) {
};
}

function useCheckChange(config, props, emit, selectedNodes, leafNodes) {
function useCheckChange(
config,
props,
emit,
selectedNodes,
leafNodes,
allNodes,
) {
const handleCheckChange = (node, checked) => {
if (node.checked === checked) return;
const { multiple } = props;
Expand All @@ -104,33 +120,58 @@ function useCheckChange(config, props, emit, selectedNodes, leafNodes) {
} else {
/**
* 多选
* 1. 解析得到节点值的列表(目前为最后一级节点的值的列表)
* 2. 解析得到当前节点下所有子孙节点的值的列表
* 3. 若为选中情况,则遍历子孙节点值,判断当前值是否存在,若不存在,则插入
* 4. 若为取消选中情况,则遍历子孙节点值,判断当前值是否存在,若存在,则删除
* 解析得到叶子节点列表
* 1. 解析得到当前节点下所有叶子节点的值的列表
* 2. 若为选中情况,则遍历子孙节点值,判断当前值是否存在,若不存在,则插入
* 3. 若为取消选中情况,则遍历子孙节点值,判断当前值是否存在,若存在,则删除
*/
const nodeValues = selectedNodes.value.map((item) => item.value);

// 因为 selectedNodes 可能父子节点都有的情况,所以需要做下去重处理
const leafNodeValues = flatNodes(selectedNodes.value, true).reduce(
(prev, cur) =>
prev.includes(cur.value) ? prev : [...prev, cur.value],
[],
);
// 获取当前节点的子孙节点信息
const checkNodes = flatNodes([node], true);
checkNodes.forEach((item) => {
if (checked) {
if (!nodeValues.includes(item.value)) {
nodeValues.push(item.value);
if (!leafNodeValues.includes(item.value)) {
leafNodeValues.push(item.value);
}
} else if (nodeValues.includes(item.value)) {
nodeValues.splice(nodeValues.indexOf(item.value), 1);
} else if (leafNodeValues.includes(item.value)) {
leafNodeValues.splice(
leafNodeValues.indexOf(item.value),
1,
);
}
});

// 目前值为最后一级节点值,所以仅需过滤子节点即可
const filterNodes = leafNodes.value.filter((item) =>
nodeValues.includes(item.value),
const sortLeafNodes = leafNodes.value.filter((item) =>
leafNodeValues.includes(item.value),
);
const sortValues = filterNodes.map((item) =>
getValueByOption(config.value, item),
);
emit('checkChange', sortValues);

let checkValues = [];

if (props.checkStrictly === CHECK_STRATEGY.CHILD) {
const sortLeafValues = sortLeafNodes.map((item) =>
getValueByOption(config.value, item),
);
checkValues = sortLeafValues;
} else {
const checkParentNodes = getCheckNodesByLeafCheckNodes(
sortLeafNodes,
allNodes.value,
props.checkStrictly,
);
const checkParentValus = checkParentNodes.map((item) =>
getValueByOption(config.value, item),
);

checkValues = checkParentValus;
}

emit('checkChange', checkValues);
}
};

Expand Down Expand Up @@ -239,6 +280,7 @@ export default (config, props, emit) => {
emit,
selectedNodes,
leafNodes,
allNodes,
);

const { handleKeyDown } = useKeyDown(config, emit, menus);
Expand Down
Loading

0 comments on commit e5c0d4a

Please sign in to comment.