Flutter & OpenHarmony OA系统组织架构树组件开发指南

前言
组织架构是企业管理的基础,OA系统中的组织架构树组件用于展示公司的部门层级关系和人员分布。一个优秀的组织架构树组件需要支持多级展开收起、节点搜索、人员选择等功能,同时要保证在大数据量下的渲染性能。本文将详细介绍如何使用Flutter和OpenHarmony开发一个功能完善的组织架构树组件,帮助企业用户快速了解公司结构和查找同事。
组件设计思路
组织架构树采用递归结构设计,每个节点可以是部门或人员。部门节点可以包含子部门和人员,支持展开收起操作。组件需要支持单选和多选两种模式,单选模式用于选择审批人,多选模式用于群发通知或会议邀请。在视觉设计上,不同层级使用缩进区分,部门和人员使用不同图标标识。
Flutter端实现
定义组织架构节点数据模型:
class OrgNode {
final String id;
final String name;
final String type; // department or employee
final String? avatar;
final List<OrgNode> children;
bool isExpanded;
OrgNode({
required this.id,
required this.name,
required this.type,
this.avatar,
this.children = const [],
this.isExpanded = false,
});
}
数据模型采用递归结构,children字段存储子节点列表。type字段区分部门和人员,avatar字段存储人员头像。isExpanded用于控制节点的展开收起状态,设为非final以便在运行时修改。
组织架构树组件的基础结构:
class OrgTreeWidget extends StatefulWidget {
final OrgNode rootNode;
final bool multiSelect;
final Function(List<OrgNode>) onSelectionChanged;
const OrgTreeWidget({
Key? key,
required this.rootNode,
this.multiSelect = false,
required this.onSelectionChanged,
}) : super(key: key);
State<OrgTreeWidget> createState() => _OrgTreeWidgetState();
}
组件接收根节点数据和选择模式配置,onSelectionChanged回调在选择变化时触发,返回当前选中的节点列表。multiSelect参数控制单选或多选模式,默认为单选。
状态类中的选中节点管理:
class _OrgTreeWidgetState extends State<OrgTreeWidget> {
final Set<String> _selectedIds = {};
void _toggleSelection(OrgNode node) {
setState(() {
if (widget.multiSelect) {
if (_selectedIds.contains(node.id)) {
_selectedIds.remove(node.id);
} else {
_selectedIds.add(node.id);
}
} else {
_selectedIds.clear();
_selectedIds.add(node.id);
}
});
widget.onSelectionChanged(_getSelectedNodes());
}
}
使用Set存储选中节点的ID,Set的特性保证了ID的唯一性。单选模式下先清空再添加,多选模式下切换选中状态。_getSelectedNodes方法根据ID列表获取对应的节点对象。
递归构建树节点的方法:
Widget _buildTreeNode(OrgNode node, int level) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () {
if (node.type == 'department') {
setState(() => node.isExpanded = !node.isExpanded);
} else {
_toggleSelection(node);
}
},
child: Padding(
padding: EdgeInsets.only(left: level * 24.0),
child: _buildNodeContent(node),
),
),
if (node.isExpanded)
...node.children.map((child) => _buildTreeNode(child, level + 1)),
],
);
}
递归方法接收节点和层级参数,层级用于计算缩进距离。点击部门节点切换展开状态,点击人员节点切换选中状态。使用展开运算符将子节点列表展开到Column的children中。
节点内容的构建:
Widget _buildNodeContent(OrgNode node) {
final isSelected = _selectedIds.contains(node.id);
return Row(
children: [
if (node.type == 'department')
Icon(
node.isExpanded ? Icons.folder_open : Icons.folder,
color: Colors.amber,
)
else
CircleAvatar(
radius: 16,
backgroundImage: node.avatar != null
? NetworkImage(node.avatar!)
: null,
child: node.avatar == null ? Text(node.name[0]) : null,
),
SizedBox(width: 8),
Expanded(child: Text(node.name)),
if (node.type == 'employee')
Checkbox(
value: isSelected,
onChanged: (_) => _toggleSelection(node),
),
],
);
}
节点内容根据类型显示不同的图标,部门显示文件夹图标,人员显示头像。人员节点右侧显示复选框用于选择操作。CircleAvatar在没有头像时显示姓名首字母作为占位。
OpenHarmony鸿蒙端实现
定义组织架构节点接口:
interface OrgNode {
id: string
name: string
type: 'department' | 'employee'
avatar?: string
children: OrgNode[]
isExpanded: boolean
}
TypeScript的interface支持递归定义,children字段类型为OrgNode数组。type使用联合类型限定只能是department或employee两个值,提供了更好的类型安全。
组织架构树组件的基础结构:
@Component
struct OrgTree {
@Prop rootNode: OrgNode
@Prop multiSelect: boolean = false
@State selectedIds: Set<string> = new Set()
private onSelectionChange: (nodes: OrgNode[]) => void = () => {}
}
使用@State管理选中ID集合,Set类型保证ID唯一性。@Prop接收根节点和选择模式配置,回调函数用于通知父组件选择变化。
递归构建树节点:
@Builder
TreeNode(node: OrgNode, level: number) {
Column() {
Row() {
if (node.type === 'department') {
Image(node.isExpanded ? $r('app.media.folder_open') : $r('app.media.folder'))
.width(24)
.height(24)
} else {
Image(node.avatar || $r('app.media.default_avatar'))
.width(32)
.height(32)
.borderRadius(16)
}
Text(node.name)
.fontSize(14)
.margin({ left: 8 })
.layoutWeight(1)
if (node.type === 'employee') {
Checkbox({ name: node.id })
.select(this.selectedIds.has(node.id))
.onChange((value: boolean) => {
this.toggleSelection(node)
})
}
}
.width('100%')
.padding({ left: level * 24, top: 12, bottom: 12, right: 16 })
.onClick(() => this.handleNodeClick(node))
if (node.isExpanded && node.children.length > 0) {
ForEach(node.children, (child: OrgNode) => {
this.TreeNode(child, level + 1)
})
}
}
}
鸿蒙的@Builder方法支持递归调用,通过ForEach渲染子节点。padding的left值根据层级动态计算实现缩进效果。Checkbox组件的select属性绑定选中状态,onChange处理选择变化。
节点点击处理逻辑:
private handleNodeClick(node: OrgNode) {
if (node.type === 'department') {
node.isExpanded = !node.isExpanded
} else {
this.toggleSelection(node)
}
}
private toggleSelection(node: OrgNode) {
if (this.multiSelect) {
if (this.selectedIds.has(node.id)) {
this.selectedIds.delete(node.id)
} else {
this.selectedIds.add(node.id)
}
} else {
this.selectedIds.clear()
this.selectedIds.add(node.id)
}
this.onSelectionChange(this.getSelectedNodes())
}
点击处理逻辑与Flutter版本类似,部门节点切换展开状态,人员节点切换选中状态。Set的has、add、delete方法用于管理选中ID集合,clear方法在单选模式下清空之前的选择。
搜索过滤功能:
@State searchKeyword: string = ''
@Builder
SearchBar() {
Search({ placeholder: '搜索部门或员工' })
.width('100%')
.height(40)
.margin({ bottom: 12 })
.onChange((value: string) => {
this.searchKeyword = value
})
}
private filterNodes(node: OrgNode): OrgNode | null {
if (node.name.includes(this.searchKeyword)) {
return node
}
const filteredChildren = node.children
.map(child => this.filterNodes(child))
.filter(child => child !== null) as OrgNode[]
if (filteredChildren.length > 0) {
return { ...node, children: filteredChildren, isExpanded: true }
}
return null
}
搜索功能通过递归过滤实现,如果节点名称匹配关键词则保留,如果子节点中有匹配的也保留父节点。过滤后的节点自动展开,方便用户查看搜索结果。这种递归过滤算法保持了树的层级结构。
总结
本文详细介绍了Flutter和OpenHarmony平台上组织架构树组件的开发方法。组织架构树是OA系统中的基础组件,广泛应用于人员选择、部门浏览等场景。递归结构的设计使组件能够处理任意深度的组织层级,搜索过滤功能帮助用户快速定位目标节点。两个平台的实现思路相似,都采用了递归渲染和状态管理的方式。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)