Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b9b53c0

Browse files
feat: add maintainCheckOrder prop
1 parent 7c0323a commit b9b53c0

File tree

6 files changed

+254
-6
lines changed

6 files changed

+254
-6
lines changed

‎examples/Drop.vue‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@
5858
</VTreeDrop>
5959
选中的值:{{ value2 }}
6060
</div>
61+
<div style="width: 200px">
62+
<p>多选(保持顺序):</p>
63+
<VTreeDrop
64+
v-model="orderValue"
65+
:data="data"
66+
checkable
67+
clearable
68+
drop-placeholder="请选择"
69+
:placement="placement"
70+
:dropdown-min-width="300"
71+
dropdown-width-fixed
72+
maintain-check-order
73+
@checked-change="handleCheckedChange"
74+
/>
75+
<div>顺序值:{{ orderValue }}</div>
76+
</div>
6177
</div>
6278
</template>
6379

@@ -91,6 +107,7 @@ export default defineComponent({
91107
const data = ref(genData().data)
92108
const value = ref('2')
93109
const value2 = ref('2')
110+
const orderValue = ref([])
94111
const placement = ref<PlacementType>('bottom-start')
95112
function handleCheckedChange() {
96113
console.log('checked-change')
@@ -108,6 +125,7 @@ export default defineComponent({
108125
data,
109126
value,
110127
value2,
128+
orderValue,
111129
placement,
112130
handleCheckedChange,
113131
handleSelectedChange,

‎site/api/vtree.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
| nodeIndent | 子节点缩进 | `number` | 20 |
4141
| renderNodeAmount | 渲染节点数量,可见节点数大于此值且高度超过(容器可视高度能容纳节点数 + bufferNodeAmount)则不会渲染所有可见节点 | `number` | 100 |
4242
| bufferNodeAmount | 当滚动到视野外的节点个数大于此值时刷新渲染节点 | `number` | 20 |
43+
| maintainCheckOrder `4.2.0` | 多选时保持选中的顺序 | `boolean` | false |
4344

4445
## VTree Events
4546

‎site/en/api/vtree.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
| nodeIndent | Child node indent | `number` | 20 |
4141
| renderNodeAmount | Node amount to render. Not all visible nodes will be rendered when they are more than this prop and the height is more than (node amount the container clientHeight can hold + bufferNodeAmount) | `number` | 100 |
4242
| bufferNodeAmount | Refresh render nodes when scrolled node amount is more than this prop | `number` | 20 |
43+
| maintainCheckOrder `4.2.0` | Maintain check order in multiple select mode | `boolean` | false |
4344

4445
## VTree Events
4546

‎src/components/Tree.vue‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ export interface TreeProps {
226226
227227
/** 当滚动到视野外的节点个数大于此值时刷新渲染节点 */
228228
bufferNodeAmount?: number,
229+
230+
/**
231+
* 多选时是否保持选中顺序
232+
*/
233+
maintainCheckOrder?: boolean,
229234
}
230235
231236
export const DEFAULT_TREE_PROPS = {
@@ -258,6 +263,7 @@ export const DEFAULT_TREE_PROPS = {
258263
nodeIndent: 20,
259264
renderNodeAmount: 100,
260265
bufferNodeAmount: 20,
266+
maintainCheckOrder: false,
261267
}
262268
</script>
263269

@@ -314,7 +320,8 @@ const getInitialNonReactiveValues = (): INonReactiveData => {
314320
cascade: props.cascade,
315321
defaultExpandAll: props.defaultExpandAll,
316322
load: props.load,
317-
expandOnFilter: props.expandOnFilter
323+
expandOnFilter: props.expandOnFilter,
324+
maintainCheckOrder: props.maintainCheckOrder,
318325
}),
319326
blockNodes: [] as TreeNode[]
320327
}

‎src/store/tree-store.ts‎

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface ITreeStoreOptions {
1414
defaultExpandAll?: boolean
1515
load?: Function
1616
expandOnFilter?: boolean
17+
maintainCheckOrder?: boolean
1718
}
1819

1920
interface IMapData {
@@ -46,6 +47,9 @@ export default class TreeStore extends TreeEventTarget {
4647
/** 当前单选选中节点 key */
4748
private currentSelectedKey: TreeNodeKeyType | null = null
4849

50+
/** 多选选中节点的顺序 */
51+
private checkedNodesOrder: TreeNodeKeyType[] = []
52+
4953
//#endregion Properties
5054

5155
constructor(private readonly options: ITreeStoreOptions) {
@@ -74,6 +78,8 @@ export default class TreeStore extends TreeEventTarget {
7478
for (let key in this.mapData) delete this.mapData[key]
7579
// 扁平化之前清空单选选中,如果 value 有值,则是 selectableUnloadKey 有值,会重新设置 currentSelectedKey ;多选选中没有存储在 store 中,因此不必事先清空。
7680
this.currentSelectedKey = null
81+
// 清空选中顺序
82+
this.checkedNodesOrder = []
7783
// 扁平化节点数据
7884
this.flatData = this.flattenData(this.data)
7985
// 更新未载入多选选中节点
@@ -122,6 +128,20 @@ export default class TreeStore extends TreeEventTarget {
122128
node.checked = value
123129
}
124130

131+
// 更新选中顺序
132+
if (value) {
133+
// 如果节点被选中,将其添加到顺序数组的末尾
134+
if (!this.checkedNodesOrder.includes(key)) {
135+
this.checkedNodesOrder.push(key)
136+
}
137+
} else {
138+
// 如果节点被取消选中,从顺序数组中移除
139+
const index = this.checkedNodesOrder.indexOf(key)
140+
if (index !== -1) {
141+
this.checkedNodesOrder.splice(index, 1)
142+
}
143+
}
144+
125145
if (triggerEvent) {
126146
if (node.checked) {
127147
this.emit('check', node)
@@ -168,6 +188,8 @@ export default class TreeStore extends TreeEventTarget {
168188
triggerEvent: boolean = true,
169189
triggerDataChange: boolean = true
170190
): void {
191+
// 清空选中顺序
192+
this.checkedNodesOrder = []
171193
keys.forEach(key => {
172194
this.setChecked(key, value, false, false)
173195
})
@@ -222,6 +244,8 @@ export default class TreeStore extends TreeEventTarget {
222244
})
223245
// 清空未加载多选选中节点
224246
this.unloadCheckedKeys = []
247+
// 清空选中顺序
248+
this.checkedNodesOrder = []
225249

226250
this.triggerCheckedChange(triggerEvent, triggerDataChange)
227251
}
@@ -605,8 +629,10 @@ export default class TreeStore extends TreeEventTarget {
605629
/**
606630
* 获取多选选中节点
607631
* @param ignoreMode 忽略模式,可选择忽略父节点或子节点,默认值是 VTree 的 ignoreMode Prop
632+
* @param maintainCheckOrder 是否保持选中顺序,默认为 false 以保持向后兼容性
608633
*/
609-
getCheckedNodes(ignoreMode = this.options.ignoreMode): TreeNode[] {
634+
getCheckedNodes(ignoreMode = this.options.ignoreMode, maintainCheckOrder = this.options.maintainCheckOrder): TreeNode[] {
635+
let checkedNodes: TreeNode[]
610636
if (ignoreMode === ignoreEnum.children) {
611637
const result: TreeNode[] = []
612638
const traversal = (nodes: TreeNode[]) => {
@@ -619,22 +645,36 @@ export default class TreeStore extends TreeEventTarget {
619645
})
620646
}
621647
traversal(this.data)
622-
return result
648+
checkedNodes= result
623649
} else {
624-
return this.flatData.filter(node => {
650+
checkedNodes= this.flatData.filter(node => {
625651
if (ignoreMode === ignoreEnum.parents)
626652
return node.checked && node.isLeaf
627653
return node.checked
628654
})
629655
}
656+
657+
// 只有在需要保持选中顺序时才进行排序
658+
if (maintainCheckOrder) {
659+
return checkedNodes.sort((a, b) => {
660+
const aIndex = this.checkedNodesOrder.indexOf(a[this.options.keyField])
661+
const bIndex = this.checkedNodesOrder.indexOf(b[this.options.keyField])
662+
if (aIndex === -1) return 1
663+
if (bIndex === -1) return -1
664+
return aIndex - bIndex
665+
})
666+
}
667+
668+
return checkedNodes
630669
}
631670

632671
/**
633672
* 获取多选选中的节点 key ,包括未加载的 key
634673
* @param ignoreMode 忽略模式,同 `getCheckedNodes`
674+
* @param maintainCheckOrder 是否保持选中顺序,默认为 false 以保持向后兼容性
635675
*/
636-
getCheckedKeys(ignoreMode = this.options.ignoreMode): TreeNodeKeyType[] {
637-
return this.getCheckedNodes(ignoreMode)
676+
getCheckedKeys(ignoreMode = this.options.ignoreMode,maintainCheckOrder=this.options.maintainCheckOrder): TreeNodeKeyType[] {
677+
return this.getCheckedNodes(ignoreMode,maintainCheckOrder)
638678
.map(checkedNodes => checkedNodes[this.options.keyField])
639679
.concat(this.unloadCheckedKeys)
640680
}
@@ -1223,6 +1263,19 @@ export default class TreeStore extends TreeEventTarget {
12231263

12241264
//#region Check nodes
12251265

1266+
/**
1267+
* 递归收集节点及其所有子节点的 key,父节点在前,子节点在后
1268+
*/
1269+
private collectAllKeys(node: TreeNode): TreeNodeKeyType[] {
1270+
const keys: TreeNodeKeyType[] = [node[this.options.keyField]]
1271+
if (node.children && node.children.length) {
1272+
node.children.forEach(child => {
1273+
keys.push(...this.collectAllKeys(child))
1274+
})
1275+
}
1276+
return keys
1277+
}
1278+
12261279
/**
12271280
* 向下勾选/取消勾选节点,包括自身
12281281
* @param node 需要向下勾选的节点
@@ -1234,6 +1287,21 @@ export default class TreeStore extends TreeEventTarget {
12341287
value: boolean,
12351288
filtering: boolean = false
12361289
): void {
1290+
// cascade 模式下,父节点整体操作 checkedNodesOrder
1291+
if (this.options.cascade && !node._parent) {
1292+
const keys = this.collectAllKeys(node)
1293+
if (value) {
1294+
// 整体插入末尾,去重
1295+
keys.forEach(key => {
1296+
if (!this.checkedNodesOrder.includes(key)) {
1297+
this.checkedNodesOrder.push(key)
1298+
}
1299+
})
1300+
} else {
1301+
// 整体移除
1302+
this.checkedNodesOrder = this.checkedNodesOrder.filter(key => !keys.includes(key))
1303+
}
1304+
}
12371305
node.children.forEach(child => {
12381306
this.checkNodeDownward(child, value, filtering)
12391307
})
@@ -1248,6 +1316,21 @@ export default class TreeStore extends TreeEventTarget {
12481316
return
12491317
node.checked = value
12501318
node.indeterminate = false
1319+
1320+
// 非 cascade 或子节点单独勾选时,单独插入末尾,去重
1321+
if (!this.options.cascade || node._parent) {
1322+
const key = node[this.options.keyField]
1323+
if (value) {
1324+
if (!this.checkedNodesOrder.includes(key)) {
1325+
this.checkedNodesOrder.push(key)
1326+
}
1327+
} else {
1328+
const index = this.checkedNodesOrder.indexOf(key)
1329+
if (index !== -1) {
1330+
this.checkedNodesOrder.splice(index, 1)
1331+
}
1332+
}
1333+
}
12511334
}
12521335
} else {
12531336
this.checkParentNode(node)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /