iView‐Tree - daniel-qa/Vue GitHub Wiki
- 初始問題
問題:previewTree 初始值為 null,Tree 組件嘗試讀取 null.length 導致錯誤
解決:將初始值改為空陣列 [],Tree 組件可以正確處理空陣列
previewTree: [],
-
node-key="key"
node-key="key" 是 Tree 用來「唯一識別每一個節點」的主鍵。
沒有它,所有勾選、展開、設定 checkedKeys 的行為都會變得不可靠。
node-key="key" 到底在幹嘛?
在 iView / View UI 的 裡:
<Tree
:data="treeData"
show-checkbox
node-key="key"
/>
這行的意思是:
「請用每個 node 裡的 key 欄位,當作這個節點的唯一 ID」
- 載入後再執行
iView 的 Modal 有 @on-open / @on-visible-change 事件,可以保證 Modal 打開後再操作 Tree。
<i-modal :visible.sync="showEditModal" @on-visible-change="handleModalVisibleChange">
<i-tree ref="editModalTree" :data="treeData" />
</i-modal>
handleModalVisibleChange(visible) {
if (visible) {
this.$nextTick(() => {
this.$refs.editModalTree.setCheckedKeys(shareVolumeIds);
});
}
}
- 取得所有的 key
const tree = this.$refs.editModalTree;
const allKeys = tree.data.flatMap(function getKeys(node) {
return [node.key].concat(node.children ? node.children.flatMap(getKeys) : []);
});
- 延遲 50 毫秒後執行
setTimeout(() => {
this.$refs.editModalTree.setCheckedKeys(shareVolumeIds);
}, 50);
// 科目節點
{
title: '數學',
key: subject.id, // 👈 就是 subjectId
subjectId: subject.id,
children: [
{
title: '第一冊',
key: volume.id, // 👈 就是 volumeId
volumeId: volume.id
}
]
}
iView Tree 的 setCheckedKeys 只認 key,沒有父子層的概念
在 key 剛好等於 subjectId , 可以直接指定 shareSubjects 陣列
this.$refs.editModalTree.setCheckedKeys(shareSubjects );
但後續要另外處理實際鉤選的部分( UI 只會顯示子項目鉤選,但資料層面要另外處理)
要加 ref,editModalTree 才能操作
<Tree ref="editModalTree"
:data="subjectTreeData"
show-checkbox
:check-strictly="false"
@on-check-change="handleTreeCheckChange"
style="margin-left: 20px;">
</Tree>
@on-check-change 有鉤選時,觸發的事件
handleTreeCheckChange(checkedNodes, node)
第二個參數 node 裡面有一個非常關鍵的屬性:
👉 node.children
如果有內容 → 父節點
如果沒有 → 葉子節點(子節點)
checkedNodes : 是全部的鉤選狀態
node : 是當下被鉤選的節點 (選中或取消)
checked 是否勾选 (父,子元件的狀態,都是獨立的)
學科固定展開、點擊後展開冊別、每個節點都可勾選;支援父子連動
expand: false // 預設收起,true 則展開
<template>
<div style="padding: 20px;">
<h3>學科與冊別選擇</h3>
<!-- 樹狀選單 -->
<Tree
:data="subjectTreeData"
show-checkbox
@on-check-change="updateSelections"
/>
<!-- 已選擇項目 -->
<div style="margin-top: 15px; padding: 10px; background: #f5f5f5;">
<strong>已選擇項目:</strong>
<div v-if="selectedItems.length === 0" style="color: #999; margin-top: 5px;">
尚未選擇任何項目
</div>
<div v-else style="margin-top: 5px;">
<Tag
v-for="item in selectedItems"
:key="item.key"
closable
@on-close="removeItem(item)"
style="margin: 2px;"
>
{{ item.title }}
</Tag>
</div>
</div>
<!-- 操作按鈕 -->
<div style="margin-top: 20px;">
<Button type="default" @click="clearAll">清除所有選擇</Button>
<Button type="success" @click="getSelected">獲取選中資料</Button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
subjectTreeData: [
{
title: '國語文',
key: 'chinese',
expand: true,
children: [
{ title: '第一冊', key: 'chinese-1' },
{ title: '第二冊', key: 'chinese-2' },
{ title: '第三冊', key: 'chinese-3' }
]
},
{
title: '數學',
key: 'math',
expand: true,
children: [
{ title: '第一冊', key: 'math-1' },
{ title: '第二冊', key: 'math-2' },
{ title: '第三冊', key: 'math-3' }
]
},
{
title: '英語',
key: 'english',
expand: true,
children: [
{ title: '第一冊', key: 'english-1' },
{ title: '第二冊', key: 'english-2' }
]
}
],
selectedItems: []
};
},
methods: {
// 重新整理已選項目
updateSelections(checkedNodes) {
const list = [];
const walk = node => {
if (node.checked) list.push({ key: node.key, title: node.title, isParent: !!node.children });
if (node.children) node.children.forEach(walk);
};
checkedNodes.forEach(walk);
this.selectedItems = list;
},
// 移除 Tag 時,同步取消樹狀選取
removeItem(item) {
const uncheck = nodes => {
nodes.forEach(n => {
if (n.key === item.key) n.checked = false;
if (n.children) uncheck(n.children);
});
};
uncheck(this.subjectTreeData);
this.selectedItems = this.selectedItems.filter(x => x.key !== item.key);
this.$Message.info(`已移除 ${item.title}`);
},
// 清空所有勾選
clearAll() {
const clear = nodes => {
nodes.forEach(n => {
n.checked = false;
if (n.children) clear(n.children);
});
};
clear(this.subjectTreeData);
this.selectedItems = [];
this.$Message.info('已清除所有選擇');
},
// 取得已選項目
getSelected() {
const result = {
subjects: this.selectedItems.filter(i => i.isParent),
volumes: this.selectedItems.filter(i => !i.isParent)
};
console.log('選中的資料:', result);
this.$Message.success(`學科 ${result.subjects.length},冊別 ${result.volumes.length}`);
return result;
}
}
};
</script>