鸿蒙ArkUI实现部门树列表
开发ERP系统的时候经常会用到部门树列表,页面主要由搜索框、顶部部门面包屑、多层级部门列表组成,部门列表项由不么名称和下一级右边箭头组成,点击部门名称区域可以回传部门数据到上一个页面,点击下一级箭头按钮可以展示下一级部门列表,同时将父级部门添加到顶部面包屑。
1、加载部门数数据
部门树数据由多个部门信息对象组成,部门对象中包含下级部门数组,里面可能会嵌套多个子级部门对象,这里用的json数据如下:
[
{
"DepartCode": 0,
"DepartNo": "",
"FirstName": "",
"DepartName": "所有部门",
"SystemTag": 0,
"DepartType": 0,
"DepartStatus": 0,
"DepartLevel": 0,
"DepartStatusName": "",
"DepartmentListZtree": [
{
"DepartCode": 50001,
"DepartNo": "002",
"FirstName": "测试大区",
"DepartName": "测试大区",
"SystemTag": 1,
"DepartType": 1,
"DepartStatus": 1,
"DepartLevel": 1,
"DepartStatusName": "",
"DepartmentListZtree": []
}
]
}
]
2、自定义顶部输入框
顶部输入框由Stack组件包裹TextInput输入框组件和Image图片组件组成。
/**
* 构建搜索框
*/
@Builder
BuildSearch() {
Stack() {
TextInput({ text: this.depWord, placeholder: "搜索部门" })
.height(36)
.placeholderFont({ size: 14 })
.placeholderColor($r("app.color.text_auxiliary"))
.backgroundColor($r("app.color.tag_default"))
.borderRadius(4)
.padding({ left: 36 })
.onChange((value) => {
this.depWord = value
})
Image($r("app.media.icon_search")).width(14).height(14).margin({ left: 12 })
}.align(Alignment.Start)
}
3、自定义部门面包屑
部门面包屑由Scroll组件包裹Row组件,当有选择多个层级,超出一屏时,可以滑动展示,Row组件中包裹部门名称Text和右箭头图标Image组件,点击部门名称,部门列表展示当前点击部门的所有子部门数据,具体代码如下:
/**
* 构建顶部部门面包屑
*/
@Builder
BuildTopDep() {
Scroll(this.topScroller) {
Row() {
ForEach(this.topDeps, (item: DepartBean, index) => {
Text(item.DepartName).fontSize(14).fontColor($r("app.color.main_color"))
.onClick(() => {
this.topDeps.splice(index + 1, this.topDeps.length - index - 1)
this.listDeps = item.DepartmentListZtree ?? []
})
if (index < this.topDeps.length - 1) {
Image($r("app.media.icon_back"))
.width(12).height(12).margin({ left: 4, right: 4 })
.rotate({ angle: 180 })
}
})
}
}.scrollable(ScrollDirection.Horizontal) //水平方向滚动
.scrollBar(BarState.Off)
.margin({ top: 12 })
}
4、绘制部门列表
部门列表的每一项由部门名称和下一级按钮组成,点击部门名称区域会将当前部门传回到上一页面中,点击下一级按钮会展示下一级不么列数据,代码如下:
//构建部门列表
@Builder
BuildListDep() {
List({ space: 1, scroller: this.listScroller }) {
ForEach(this.listDeps, (item: DepartBean, index) => {
ListItem() {
Row() {
Text(item.DepartName).fontSize(14).fontColor($r("app.color.text_two")).layoutWeight(1)
//如果存在子部门就展示右箭头
if (item.DepartmentListZtree?.length ?? 0 > 0) {
Image($r("app.media.icon_back"))
.width(12).height(12).margin({ left: 4, right: 4 })
.rotate({ angle: 180 })
.margin({right: 12})
.onClick(()=>{
//点击跳转到下一级部门
this.topDeps.push(item)
this.listDeps = item.DepartmentListZtree ?? []
this.topScroller.scrollPage({ next: true }) //面包屑滚动到最右边
})
}
}
.margin({ left: 12 })
}
.backgroundColor(Color.White)
.height(50)
.width("100%")
.align(Alignment.Start)
.onClick(() => {
//点击将选中的部门信息返回给上一个页面
router.back({
url:"",
params:{
depBean:item
}
})
})
})
}.layoutWeight(1)
.scrollBar(BarState.Off)
.margin({ top: 8 })
}
5、部门数据回传上一页
点击部门名称后,调用router.back方法,返回到上一页,将部门数据通过params回传到上一页中,注意url是必传参数,这里返回上一页,可以直接传空字符。
//点击将选中的部门信息返回给上一个页面
router.back({
url:"",
params:{
depBean:item
}
})
6、接收页面回传数据
在页面生命周期onPageShow方法中接收回传数据,通过router.getParams()可以拿到回传参数,params['depBean']可以拿到部门对象数据,depBean对应的回传参数的Key.
onPageShow(): void {
let params=router.getParams() as Record<string, DepartBean>
if (params) {
this.depBean=params['depBean']
}
}
完整代码如下:
import { CommApiService } from "../../http/CommApiService";
import { DepartBean } from "../../model/DepartBean";
import { TitleBar } from "../../view/TitleBar";
import { router } from "@kit.ArkUI";
import { createWaterMarkView } from "../../view/WaterMarkView";
@Entry
@Component
export struct SelectDepPage {
departments: Array<DepartBean> = [] //保存所有的层级部门列表数据
@State depWord: string = "" // 搜索部门名称
@State topDeps: Array<DepartBean> = [] // 顶级部门
private topScroller: Scroller = new Scroller(); // 创建一个顶部面包屑滚动控制器
private listScroller: Scroller = new Scroller(); // 创建一个部门列表滚动控制器
@State listDeps: Array<DepartBean> = [] // 当前显示的部门列表数据
aboutToAppear(): void {
CommApiService.getInstance().httpGetDepartmentViewTree(list => {
this.departments = list
this.topDeps.push(list[0])
this.listDeps = list[0].DepartmentListZtree ?? [] //默认添加所有部门数据
})
}
build() {
Column() {
TitleBar({
backShow: true,
title: "选择部门",
backCallBack: () => {
router.back()
}
})
Column() {
//搜索框
this.BuildSearch()
//顶部部门面包屑
this.BuildTopDep()
}.backgroundColor(Color.White)
.alignItems(HorizontalAlign.Start)
.padding({ left: 12, right: 12, bottom: 12 })
//部门列表
this.BuildListDep()
}.overlay(createWaterMarkView()) //页面添加水印
}
/**
* 构建搜索框
*/
@Builder
BuildSearch() {
Stack() {
TextInput({ text: this.depWord, placeholder: "搜索部门" })
.height(36)
.placeholderFont({ size: 14 })
.placeholderColor($r("app.color.text_auxiliary"))
.backgroundColor($r("app.color.tag_default"))
.borderRadius(4)
.padding({ left: 36 })
.onChange((value) => {
this.depWord = value
})
Image($r("app.media.icon_search")).width(14).height(14).margin({ left: 12 })
}.align(Alignment.Start)
}
/**
* 构建顶部部门面包屑
*/
@Builder
BuildTopDep() {
Scroll(this.topScroller) {
Row() {
ForEach(this.topDeps, (item: DepartBean, index) => {
Text(item.DepartName).fontSize(14).fontColor($r("app.color.main_color"))
.onClick(() => {
this.topDeps.splice(index + 1, this.topDeps.length - index - 1)
this.listDeps = item.DepartmentListZtree ?? []
})
if (index < this.topDeps.length - 1) {
Image($r("app.media.icon_back"))
.width(12).height(12).margin({ left: 4, right: 4 })
.rotate({ angle: 180 })
}
})
}
}.scrollable(ScrollDirection.Horizontal) //水平方向滚动
.scrollBar(BarState.Off)
.margin({ top: 12 })
}
//构建部门列表
@Builder
BuildListDep() {
List({ space: 1, scroller: this.listScroller }) {
ForEach(this.listDeps, (item: DepartBean, index) => {
ListItem() {
Row() {
Text(item.DepartName).fontSize(14).fontColor($r("app.color.text_two")).layoutWeight(1)
//如果存在子部门就展示右箭头
if (item.DepartmentListZtree?.length ?? 0 > 0) {
Image($r("app.media.icon_back"))
.width(12).height(12).margin({ left: 4, right: 4 })
.rotate({ angle: 180 })
.margin({right: 12})
.onClick(()=>{
//点击跳转到下一级部门
this.topDeps.push(item)
this.listDeps = item.DepartmentListZtree ?? []
this.topScroller.scrollPage({ next: true }) //面包屑滚动到最右边
})
}
}
.margin({ left: 12 })
}
.backgroundColor(Color.White)
.height(50)
.width("100%")
.align(Alignment.Start)
.onClick(() => {
//点击将选中的部门信息返回给上一个页面
router.back({
url:"",
params:{
depBean:item
}
})
})
})
}.layoutWeight(1)
.scrollBar(BarState.Off)
.margin({ top: 8 })
}
}