505 lines
17 KiB
PHP
505 lines
17 KiB
PHP
<?php
|
|
|
|
namespace app\common\utils;
|
|
|
|
class Tree
|
|
{
|
|
// /**
|
|
// * 递归构建树形结构
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @param int $level 当前层级
|
|
// * @return array
|
|
// */
|
|
// public static function build(array $data, int $parentId = 0, string $pidField = 'parent_id', string $idField = 'id', int $level = 0): array
|
|
// {
|
|
// $tree = [];
|
|
|
|
// foreach ($data as $item) {
|
|
// if ($item[$pidField] == $parentId) {
|
|
// $item['level'] = $level;
|
|
// $children = static::build($data, $item[$idField], $pidField, $idField, $level + 1);
|
|
|
|
// if (!empty($children)) {
|
|
// $item['children'] = $children;
|
|
// }
|
|
|
|
// $tree[] = $item;
|
|
// }
|
|
// }
|
|
|
|
// return $tree;
|
|
// }
|
|
|
|
// /**
|
|
// * 使用引用方式构建树形结构(性能更好)
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @return array
|
|
// */
|
|
// public static function buildWithReference(array $data, string $pidField = 'parent_id', string $idField = 'id'): array
|
|
// {
|
|
// $items = [];
|
|
// $tree = [];
|
|
|
|
// // 建立以id为索引的数组
|
|
// foreach ($data as $item) {
|
|
// $item['level'] = 0;
|
|
// $items[$item[$idField]] = $item;
|
|
// }
|
|
|
|
// // 构建树形结构
|
|
// foreach ($items as $id => $item) {
|
|
// if ($item[$pidField] == 0) {
|
|
// $tree[] = &$items[$id];
|
|
// } else {
|
|
// if (isset($items[$item[$pidField]])) {
|
|
// $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
|
|
|
|
// if (!isset($items[$item[$pidField]]['children'])) {
|
|
// $items[$item[$pidField]]['children'] = [];
|
|
// }
|
|
|
|
// $items[$item[$pidField]]['children'][] = &$items[$id];
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// return $tree;
|
|
// }
|
|
|
|
// /**
|
|
// * 将树形结构扁平化为列表(带层级前缀)
|
|
// *
|
|
// * @param array $tree 树形结构数据
|
|
// * @param string $prefix 前缀字符
|
|
// * @param string $field 显示字段名
|
|
// * @param int $level 层级
|
|
// * @return array
|
|
// */
|
|
// public static function flattenWithPrefix(array $tree, string $prefix = '--', string $field = 'name', int $level = 0): array
|
|
// {
|
|
// $result = [];
|
|
|
|
// foreach ($tree as $item) {
|
|
// $item[$field . '_with_prefix'] = str_repeat($prefix, $item['level']) . $item[$field];
|
|
// $result[] = $item;
|
|
|
|
// if (isset($item['children']) && !empty($item['children'])) {
|
|
// $children = static::flattenWithPrefix($item['children'], $prefix, $field, $item['level'] + 1);
|
|
// $result = array_merge($result, $children);
|
|
// }
|
|
// }
|
|
|
|
// return $result;
|
|
// }
|
|
|
|
// /**
|
|
// * 根据父ID查找所有子节点(包括子孙节点)
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @return array
|
|
// */
|
|
// public static function findChildren(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
|
|
// {
|
|
// $children = [];
|
|
|
|
// foreach ($data as $item) {
|
|
// if ($item[$pidField] == $parentId) {
|
|
// $children[] = $item;
|
|
// $grandChildren = static::findChildren($data, $item[$idField], $pidField, $idField);
|
|
// $children = array_merge($children, $grandChildren);
|
|
// }
|
|
// }
|
|
|
|
// return $children;
|
|
// }
|
|
|
|
// /**
|
|
// * 获取指定节点的所有父级节点
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $id 当前节点ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @return array
|
|
// */
|
|
// public static function getParents(array $data, int $id, string $pidField = 'parent_id', string $idField = 'id'): array
|
|
// {
|
|
// $parents = [];
|
|
|
|
// foreach ($data as $item) {
|
|
// if ($item[$idField] == $id) {
|
|
// if ($item[$pidField] != 0) {
|
|
// $parents[] = $item;
|
|
// $parentParents = static::getParents($data, $item[$pidField], $pidField, $idField);
|
|
// $parents = array_merge($parents, $parentParents);
|
|
// }
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// return $parents;
|
|
// }
|
|
|
|
// /**
|
|
// * 获取指定节点的子孙节点ID列表
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @return array
|
|
// */
|
|
// public static function getChildrenIds(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
|
|
// {
|
|
// $ids = [];
|
|
// $children = static::findChildren($data, $parentId, $pidField, $idField);
|
|
|
|
// foreach ($children as $child) {
|
|
// $ids[] = $child[$idField];
|
|
// }
|
|
|
|
// return $ids;
|
|
// }
|
|
|
|
// /**
|
|
// * 检查是否有子节点
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @return bool
|
|
// */
|
|
// public static function hasChildren(array $data, int $parentId, string $pidField = 'parent_id'): bool
|
|
// {
|
|
// foreach ($data as $item) {
|
|
// if ($item[$pidField] == $parentId) {
|
|
// return true;
|
|
// }
|
|
// }
|
|
|
|
// return false;
|
|
// }
|
|
|
|
|
|
// /**
|
|
// * 构建带有权限列表的资源树(权限不显示在树结构中)
|
|
// *
|
|
// * @param array $data 资源数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @param string $typeField 类型字段名
|
|
// * @param string $permissionType 权限类型标识
|
|
// * @param int $level 当前层级
|
|
// * @return array
|
|
// */
|
|
// public static function buildWithAuthList(
|
|
// array $data,
|
|
// int $parentId = 0,
|
|
// string $pidField = 'parent_id',
|
|
// string $idField = 'id',
|
|
// string $typeField = 'type',
|
|
// string $permissionType = 'permission',
|
|
// int $level = 0
|
|
// ): array {
|
|
// $tree = [];
|
|
|
|
// foreach ($data as $item) {
|
|
// // 只处理指定父级的非权限节点
|
|
// if ($item[$pidField] == $parentId && $item[$typeField] != $permissionType) {
|
|
// $item['level'] = $level;
|
|
|
|
// // 递归处理子节点(非权限类型)
|
|
// $children = static::buildWithAuthList($data, $item[$idField], $pidField, $idField, $typeField, $permissionType, $level + 1);
|
|
|
|
// if (!empty($children)) {
|
|
// $item['children'] = $children;
|
|
// }
|
|
|
|
// // 收集当前节点下的所有权限
|
|
// $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
|
|
|
|
// $tree[] = $item;
|
|
// }
|
|
// }
|
|
|
|
// return $tree;
|
|
// }
|
|
|
|
// /**
|
|
// * 收集指定节点下的直接权限项(只收集直接子节点中的权限)
|
|
// *
|
|
// * @param array $data 数据列表
|
|
// * @param int $parentId 父级ID
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $typeField 类型字段名
|
|
// * @param string $permissionType 权限类型标识
|
|
// * @return array
|
|
// */
|
|
// public static function collectDirectPermissions1(
|
|
// array $data,
|
|
// int $parentId,
|
|
// string $pidField = 'parent_id',
|
|
// string $typeField = 'type',
|
|
// string $permissionType = 'permission'
|
|
// ): array {
|
|
// $permissions = [];
|
|
|
|
// foreach ($data as $item) {
|
|
// // 收集直接子节点中的权限类型
|
|
// if ($item[$pidField] == $parentId && $item[$typeField] == $permissionType) {
|
|
// $permissions[] = $item;
|
|
// }
|
|
// }
|
|
|
|
// return $permissions;
|
|
// }
|
|
|
|
// /**
|
|
// * 使用引用方式构建带有权限列表的资源树(权限不显示在树结构中)
|
|
// *
|
|
// * @param array $data 资源数据列表
|
|
// * @param string $pidField 父级字段名
|
|
// * @param string $idField ID字段名
|
|
// * @param string $typeField 类型字段名
|
|
// * @param string $permissionType 权限类型标识
|
|
// * @return array
|
|
// */
|
|
// public static function buildWithAuthListAndReference(
|
|
// array $data,
|
|
// string $pidField = 'parent_id',
|
|
// string $idField = 'id',
|
|
// string $typeField = 'type',
|
|
// string $permissionType = 'permission'
|
|
// ): array {
|
|
// $items = [];
|
|
// $tree = [];
|
|
|
|
// // 建立以id为索引的数组(只包含非权限节点)
|
|
// foreach ($data as $item) {
|
|
// if ($item[$typeField] != $permissionType) {
|
|
// $item['level'] = 0;
|
|
// $items[$item[$idField]] = $item;
|
|
// }
|
|
// }
|
|
|
|
// // 构建树形结构
|
|
// foreach ($items as $id => $item) {
|
|
// if ($item[$pidField] == 0) {
|
|
// $tree[] = &$items[$id];
|
|
// } else {
|
|
// if (isset($items[$item[$pidField]])) {
|
|
// $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
|
|
|
|
// if (!isset($items[$item[$pidField]]['children'])) {
|
|
// $items[$item[$pidField]]['children'] = [];
|
|
// }
|
|
|
|
// $items[$item[$pidField]]['children'][] = &$items[$id];
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// // 为每个节点添加权限列表
|
|
// foreach ($items as &$item) {
|
|
// $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
|
|
// }
|
|
|
|
// return $tree;
|
|
// }
|
|
|
|
|
|
|
|
// 8.13 add
|
|
/**
|
|
* 构建前端需要的菜单树结构
|
|
*
|
|
* @param array $data 服务端菜单数据
|
|
* @param int $parentId 父级ID
|
|
* @return array
|
|
*/
|
|
public static function buildMenuTree(array $data, int $parentId = 0): array
|
|
{
|
|
$tree = [];
|
|
|
|
foreach ($data as $item) {
|
|
// 只处理非权限类型的节点
|
|
if ($item['parent_id'] == $parentId && $item['type'] != 'permission') {
|
|
// 转换数据结构以匹配前端需要的格式
|
|
$menuItem = [
|
|
'id' => $item['id'],
|
|
'path' => $item['url'] ?? '',
|
|
'name' => $item['name'],
|
|
'component' => $item['url'] ?? '',
|
|
'meta' => [
|
|
'title' => $item['name'],
|
|
'icon' => $item['icon'] ?? '',
|
|
'isHide' => !empty($item['is_hide']),
|
|
'isHideTab' => !empty($item['is_hide_tab']),
|
|
'keepAlive' => !empty($item['keep_alive']),
|
|
]
|
|
];
|
|
|
|
// 收集当前节点下的所有权限
|
|
$permissions = static::collectDirectPermissions($data, $item['id']);
|
|
if (!empty($permissions)) {
|
|
$authList = [];
|
|
foreach ($permissions as $permission) {
|
|
$authList[] = [
|
|
'id' => $permission['id'],
|
|
'title' => $permission['name'] ?? '',
|
|
'authMark' => $permission['code'] ?? ''
|
|
];
|
|
}
|
|
$menuItem['meta']['authList'] = $authList;
|
|
}
|
|
|
|
// 将服务端额外字段放入meta中
|
|
$extraFields = static::getExtraFields($item);
|
|
if (!empty($extraFields)) {
|
|
$menuItem['meta'] = array_merge($menuItem['meta'], $extraFields);
|
|
}
|
|
|
|
// 递归处理子菜单
|
|
$children = static::buildMenuTree($data, $item['id']);
|
|
if (!empty($children)) {
|
|
$menuItem['children'] = $children;
|
|
}
|
|
|
|
$tree[] = $menuItem;
|
|
}
|
|
}
|
|
|
|
return $tree;
|
|
}
|
|
|
|
/**
|
|
* 使用引用方式构建前端需要的菜单树结构(性能更好)
|
|
*
|
|
* @param array $data 服务端菜单数据
|
|
* @return array
|
|
*/
|
|
public static function buildMenuTreeWithReference(array $data): array
|
|
{
|
|
$items = [];
|
|
$tree = [];
|
|
|
|
// 过滤出菜单类型的节点并转换数据结构
|
|
$menuData = array_filter($data, function($item) {
|
|
return ($item['type'] ?? '') != 'permission';
|
|
});
|
|
|
|
// 建立以id为索引的数组并转换数据结构
|
|
foreach ($menuData as $item) {
|
|
$items[$item['id']] = [
|
|
'id' => $item['id'],
|
|
'path' => $item['url'] ?? '',
|
|
'name' => $item['name'],
|
|
'component' => $item['url'] ?? '',
|
|
'meta' => [
|
|
'title' => $item['name'],
|
|
'icon' => $item['icon'] ?? '',
|
|
'isHide' => !empty($item['is_hide']),
|
|
'isHideTab' => !empty($item['is_hide_tab']),
|
|
'keepAlive' => !empty($item['keep_alive']),
|
|
]
|
|
];
|
|
|
|
// 将服务端额外字段放入meta中
|
|
$extraFields = static::getExtraFields($item);
|
|
if (!empty($extraFields)) {
|
|
$items[$item['id']]['meta'] = array_merge($items[$item['id']]['meta'], $extraFields);
|
|
}
|
|
}
|
|
|
|
// 构建树形结构
|
|
foreach ($menuData as $item) {
|
|
if ($item['parent_id'] == 0) {
|
|
$tree[] = &$items[$item['id']];
|
|
} else {
|
|
if (isset($items[$item['parent_id']])) {
|
|
if (!isset($items[$item['parent_id']]['children'])) {
|
|
$items[$item['parent_id']]['children'] = [];
|
|
}
|
|
$items[$item['parent_id']]['children'][] = &$items[$item['id']];
|
|
}
|
|
}
|
|
}
|
|
|
|
// 为每个节点添加权限列表
|
|
foreach ($items as &$item) {
|
|
$parentId = $item['id'];
|
|
$permissions = static::collectDirectPermissions($data, $parentId);
|
|
if (!empty($permissions)) {
|
|
$authList = [];
|
|
foreach ($permissions as $permission) {
|
|
$authList[] = [
|
|
'id' => $permission['id'],
|
|
'title' => $permission['name'] ?? '',
|
|
'authMark' => $permission['code'] ?? ''
|
|
];
|
|
}
|
|
$item['meta']['authList'] = $authList;
|
|
}
|
|
}
|
|
|
|
return $tree;
|
|
}
|
|
|
|
/**
|
|
* 收集指定节点下的直接权限项(只收集直接子节点中的权限)
|
|
*
|
|
* @param array $data 数据列表
|
|
* @param int $parentId 父级ID
|
|
* @return array
|
|
*/
|
|
public static function collectDirectPermissions(array $data, int $parentId): array
|
|
{
|
|
$permissions = [];
|
|
|
|
foreach ($data as $item) {
|
|
// 收集直接子节点中的权限类型
|
|
if ($item['parent_id'] == $parentId && ($item['type'] ?? '') == 'permission') {
|
|
$permissions[] = $item;
|
|
}
|
|
}
|
|
|
|
return $permissions;
|
|
}
|
|
|
|
/**
|
|
* 获取需要放入meta的额外字段
|
|
*
|
|
* @param array $item 菜单项
|
|
* @return array
|
|
*/
|
|
public static function getExtraFields(array $item): array
|
|
{
|
|
$extraFields = [];
|
|
$standardFields = [
|
|
'id', 'name', 'url', 'icon', 'parent_id', 'type', 'is_hide',
|
|
'is_hide_tab', 'keep_alive', 'code', 'level'
|
|
];
|
|
|
|
foreach ($item as $key => $value) {
|
|
// 如果不是标准字段且不是auth_list,则放入extraFields
|
|
if (!in_array($key, $standardFields) && $key !== 'auth_list') {
|
|
// 转换下划线命名到驼峰命名
|
|
$camelKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
|
|
$extraFields[$camelKey] = $value;
|
|
}
|
|
}
|
|
|
|
return $extraFields;
|
|
}
|
|
} |