一、說明
當有一個樹形結構的數據有非常多個節點的時候,一次性加載所有節點會顯得過於臃腫,可能會對性能造成影響,正好Ant Design 的樹(Tree)組件支持異步加載,於是我就想把異步加載封裝爲一個組件,可以減少接口數據返回,點擊展開節點,動態加載數據。非常好用!
二、前端實現
需要接收一些值用來數據的初始化和格式化
const props = defineProps({
//替換treeNode 中 children
fieldNamesChildren: { type: String, default: 'children' },
//替換treeNode 中 title
fieldNamesTitle: { type: String, default: 'title' },
//替換treeNode 中 key
fieldNamesKey: { type: String, default: 'id' },
//初始數據
treeData: { type: Function, required: true },
//父Id字段
parentIdColumn: { type: String, default: 'parentId' },
//默認頂級父Id值
topValue: { default: 0 }
})
以組織樹爲例,需要異步加載的時候,由tree
組件換成lazyTree
組件就行
<lazy-tree field-names-title="name" :treeData="loadTreeData" @select="treeSelect"></lazy-tree>
默認需要傳treeData屬性,也就是去請求後端去獲取數據的方法,這個方法需要有一個參數parameter,當展開節點的時候會請求這個方法並帶上父ID去查詢。
const loadTreeData = (parameter) => {
return genTestApi.orgTree(parameter).then((res) => {
return res
})
}
首次加載組件會調用一次獲取節點數據方法。
onMounted(() => {
let parameter = {} //查詢參數
parameter[props.parentIdColumn] = props.topValue //默認從0開始
loadTreeData(parameter) //獲取數據
})
點擊某個節點會根據節點的key對應的字段,獲取下級節點。並添加到節點數據中。
let parameter = {} //查詢參數
parameter[props.parentIdColumn] = treeNode[props.fieldNamesKey] //獲取當前節點的id
const result = props.treeData(parameter) //調用父組件傳遞的方法
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then((data) => {
setTimeout(() => {
treeNode.dataRef[props.fieldNamesChildren] = data
treeData.value = [...treeData.value]
resolve()
}, 500)
})
}
三、後端實現
以組織樹爲例,原來的tree方法需要增加父ID
參數來根據父ID獲取下級節點
/// <summary>
/// 組織樹查詢參數
/// 懶加載用
/// </summary>
public class SysOrgTreeInput
{
/// <summary>
/// 父Id
/// </summary>
public long? ParentId { get; set; }
}
原來的tree方法也要改一改,如果選擇器ID不爲空則表示是懶加載,只加載子節點。
/// <inheritdoc />
public async Task<List<SysOrg>> Tree(List<long> orgIds = null, SysOrgTreeInput treeInput = null)
{
long parentId = SimpleAdminConst.Zero;//父級ID
//獲取所有組織
var sysOrgs = await GetListAsync();
if (orgIds != null)
sysOrgs = GetParentListByIds(sysOrgs, orgIds);//如果組織ID不爲空則獲取組織ID列表的所有父節點
//如果選擇器ID不爲空則表示是懶加載,只加載子節點
if (treeInput != null && treeInput.ParentId != null)
{
parentId = treeInput.ParentId.Value;
sysOrgs = GetSysOrgChildenLazy(sysOrgs, treeInput.ParentId.Value);//獲取懶加載下級
}
sysOrgs = sysOrgs.OrderBy(it => it.SortCode).ToList();//排序
//構建組織樹
var result = ConstrucOrgTrees(sysOrgs, parentId);
return result;
}
/// <summary>
/// 獲取組織下級(懶加載)
/// </summary>
/// <param name="orgList"></param>
/// <param name="parentId"></param>
/// <returns></returns>
public List<SysOrg> GetSysOrgChildenLazy(List<SysOrg> orgList, long parentId)
{
//找下級組織ID列表
var orgs = orgList.Where(it => it.ParentId == parentId).ToList();
if (orgs.Count > 0)//如果數量大於0
{
var data = new List<SysOrg>();
foreach (var item in orgs)//遍歷組織
{
var childen = orgList.Where(it => it.ParentId == item.Id).ToList();//獲取子節點
//遍歷子節點
childen.ForEach(it =>
{
if (!orgList.Any(org => org.ParentId == it.Id)) it.IsLeaf = true;//如果沒有下級,則設置爲葉子節點
});
data.AddRange(childen);//添加子節點);
data.Add(item);//添加到列表
}
return data;//返回結果
}
return new List<SysOrg>();
}
需要在組織實體中加個IsLeaf
字段判斷是不是葉子節點,如果IsLeaf
是true就表示沒有子節點了,前端樹控件也不會顯示小箭頭。
/// <summary>
/// 設置爲葉子節點(設置了loadData時有效)
/// </summary>
[SugarColumn(IsIgnore = true)]
public bool? IsLeaf { get; set; }
四、效果
最後實現效果如下,這裏一次請求加載2級節點。