Tree.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. namespace app\common\library;
  3. use think\facade\Config;
  4. class Tree
  5. {
  6. protected static $instance;
  7. //默认配置
  8. protected $config = [];
  9. public $options = [];
  10. /**
  11. * 生成树型结构所需要的2维数组
  12. * @var array
  13. */
  14. public $arr = [];
  15. /**
  16. * 生成树型结构所需修饰符号,可以换成图片
  17. * @var array
  18. */
  19. public $icon = array('│', '├', '└');
  20. public $nbsp = "&nbsp;";
  21. public $pidname = 'pid';
  22. public function __construct($options = [])
  23. {
  24. if ($config = Config::get('tree')) {
  25. $this->options = array_merge($this->config, $config);
  26. }
  27. $this->options = array_merge($this->config, $options);
  28. }
  29. /**
  30. * 初始化
  31. * @access public
  32. * @param array $options 参数
  33. * @return Tree
  34. */
  35. public static function instance($options = [])
  36. {
  37. if (is_null(self::$instance)) {
  38. self::$instance = new static($options);
  39. }
  40. return self::$instance;
  41. }
  42. /**
  43. * 初始化方法
  44. * @param array $arr 2维数组,例如:
  45. * array(
  46. * 1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
  47. * 2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
  48. * 3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
  49. * 4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
  50. * 5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
  51. * 6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
  52. * 7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
  53. * )
  54. * @param string $pidname 父字段名称
  55. * @param string $nbsp 空格占位符
  56. * @return Tree
  57. */
  58. public function init($arr = [], $pidname = null, $nbsp = null)
  59. {
  60. $this->arr = $arr;
  61. if (!is_null($pidname)) {
  62. $this->pidname = $pidname;
  63. }
  64. if (!is_null($nbsp)) {
  65. $this->nbsp = $nbsp;
  66. }
  67. return $this;
  68. }
  69. /**
  70. * 得到子级数组
  71. * @param int
  72. * @return array
  73. */
  74. public function getChild($myid)
  75. {
  76. $newarr = [];
  77. foreach ($this->arr as $value) {
  78. if (!isset($value['id'])) {
  79. continue;
  80. }
  81. if ($value[$this->pidname] == $myid) {
  82. $newarr[$value['id']] = $value;
  83. }
  84. }
  85. return $newarr;
  86. }
  87. /**
  88. * 读取指定节点的所有孩子节点
  89. * @param int $myid 节点ID
  90. * @param boolean $withself 是否包含自身
  91. * @return array
  92. */
  93. public function getChildren($myid, $withself = false)
  94. {
  95. $newarr = [];
  96. foreach ($this->arr as $value) {
  97. if (!isset($value['id'])) {
  98. continue;
  99. }
  100. if ((string)$value[$this->pidname] == (string)$myid) {
  101. $newarr[] = $value;
  102. $newarr = array_merge($newarr, $this->getChildren($value['id']));
  103. } elseif ($withself && (string)$value['id'] == (string)$myid) {
  104. $newarr[] = $value;
  105. }
  106. }
  107. return $newarr;
  108. }
  109. /**
  110. * 读取指定节点的所有孩子节点ID
  111. * @param int $myid 节点ID
  112. * @param boolean $withself 是否包含自身
  113. * @return array
  114. */
  115. public function getChildrenIds($myid, $withself = false)
  116. {
  117. $childrenlist = $this->getChildren($myid, $withself);
  118. $childrenids = [];
  119. foreach ($childrenlist as $k => $v) {
  120. $childrenids[] = $v['id'];
  121. }
  122. return $childrenids;
  123. }
  124. /**
  125. * 得到当前位置父辈数组
  126. * @param int
  127. * @return array
  128. */
  129. public function getParent($myid)
  130. {
  131. $pid = 0;
  132. $newarr = [];
  133. foreach ($this->arr as $value) {
  134. if (!isset($value['id'])) {
  135. continue;
  136. }
  137. if ($value['id'] == $myid) {
  138. $pid = $value[$this->pidname];
  139. break;
  140. }
  141. }
  142. if ($pid) {
  143. foreach ($this->arr as $value) {
  144. if ($value['id'] == $pid) {
  145. $newarr[] = $value;
  146. break;
  147. }
  148. }
  149. }
  150. return $newarr;
  151. }
  152. /**
  153. * 得到当前位置所有父辈数组
  154. * @param int
  155. * @param bool $withself 是否包含自己
  156. * @return array
  157. */
  158. public function getParents($myid, $withself = false)
  159. {
  160. $pid = 0;
  161. $newarr = [];
  162. foreach ($this->arr as $value) {
  163. if (!isset($value['id'])) {
  164. continue;
  165. }
  166. if ($value['id'] == $myid) {
  167. if ($withself) {
  168. $newarr[] = $value;
  169. }
  170. $pid = $value[$this->pidname];
  171. break;
  172. }
  173. }
  174. if ($pid) {
  175. $arr = $this->getParents($pid, true);
  176. $newarr = array_merge($arr, $newarr);
  177. }
  178. return $newarr;
  179. }
  180. /**
  181. * 读取指定节点所有父类节点ID
  182. * @param int $myid
  183. * @param boolean $withself
  184. * @return array
  185. */
  186. public function getParentsIds($myid, $withself = false)
  187. {
  188. $parentlist = $this->getParents($myid, $withself);
  189. $parentsids = [];
  190. foreach ($parentlist as $k => $v) {
  191. $parentsids[] = $v['id'];
  192. }
  193. return $parentsids;
  194. }
  195. /**
  196. * 树型结构Option
  197. * @param int $myid 表示获得这个ID下的所有子级
  198. * @param string $itemtpl 条目模板 如:"<option value=@id @selected @disabled>@spacer@name</option>"
  199. * @param mixed $selectedids 被选中的ID,比如在做树型下拉框的时候需要用到
  200. * @param mixed $disabledids 被禁用的ID,比如在做树型下拉框的时候需要用到
  201. * @param string $itemprefix 每一项前缀
  202. * @param string $toptpl 顶级栏目的模板
  203. * @return string
  204. */
  205. public function getTree($myid, $itemtpl = "<option value=@id @selected @disabled>@spacer@name</option>", $selectedids = '', $disabledids = '', $itemprefix = '', $toptpl = '')
  206. {
  207. $ret = '';
  208. $number = 1;
  209. $childs = $this->getChild($myid);
  210. if ($childs) {
  211. $total = count($childs);
  212. foreach ($childs as $value) {
  213. $id = $value['id'];
  214. $j = $k = '';
  215. if ($number == $total) {
  216. $j .= $this->icon[2];
  217. $k = $itemprefix ? $this->nbsp : '';
  218. } else {
  219. $j .= $this->icon[1];
  220. $k = $itemprefix ? $this->icon[0] : '';
  221. }
  222. $spacer = $itemprefix ? $itemprefix . $j : '';
  223. $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
  224. $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
  225. $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
  226. $value = array_combine(array_map(function ($k) {
  227. return '@' . $k;
  228. }, array_keys($value)), $value);
  229. $nstr = strtr((($value["@{$this->pidname}"] == 0 || $this->getChild($id)) && $toptpl ? $toptpl : $itemtpl), $value);
  230. $ret .= $nstr;
  231. $ret .= $this->getTree($id, $itemtpl, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp, $toptpl);
  232. $number++;
  233. }
  234. }
  235. return $ret;
  236. }
  237. /**
  238. * 树型结构UL
  239. * @param int $myid 表示获得这个ID下的所有子级
  240. * @param string $itemtpl 条目模板 如:"<li value=@id @selected @disabled>@name @childlist</li>"
  241. * @param string $selectedids 选中的ID
  242. * @param string $disabledids 禁用的ID
  243. * @param string $wraptag 子列表包裹标签
  244. * @param string $wrapattr 子列表包裹属性
  245. * @return string
  246. */
  247. public function getTreeUl($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '')
  248. {
  249. $str = '';
  250. $childs = $this->getChild($myid);
  251. if ($childs) {
  252. foreach ($childs as $value) {
  253. $id = $value['id'];
  254. unset($value['child']);
  255. $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
  256. $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
  257. $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
  258. $value = array_combine(array_map(function ($k) {
  259. return '@' . $k;
  260. }, array_keys($value)), $value);
  261. $nstr = strtr($itemtpl, $value);
  262. $childdata = $this->getTreeUl($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr);
  263. $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
  264. $str .= strtr($nstr, array('@childlist' => $childlist));
  265. }
  266. }
  267. return $str;
  268. }
  269. /**
  270. * 菜单数据
  271. * @param int $myid
  272. * @param string $itemtpl
  273. * @param mixed $selectedids
  274. * @param mixed $disabledids
  275. * @param string $wraptag
  276. * @param string $wrapattr
  277. * @param int $deeplevel
  278. * @return string
  279. */
  280. public function getTreeMenu($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '', $deeplevel = 0)
  281. {
  282. $str = '';
  283. $childs = $this->getChild($myid);
  284. if ($childs) {
  285. foreach ($childs as $value) {
  286. $id = $value['id'];
  287. unset($value['child']);
  288. $selected = in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
  289. $disabled = in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
  290. $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
  291. $value = array_combine(array_map(function ($k) {
  292. return '@' . $k;
  293. }, array_keys($value)), $value);
  294. $bakvalue = array_intersect_key($value, array_flip(['@url', '@caret', '@class']));
  295. $value = array_diff_key($value, $bakvalue);
  296. $nstr = strtr($itemtpl, $value);
  297. $value = array_merge($value, $bakvalue);
  298. $childdata = $this->getTreeMenu($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr, $deeplevel + 1);
  299. $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
  300. $childlist = strtr($childlist, array('@class' => $childdata ? 'last' : ''));
  301. $value = array(
  302. '@childlist' => $childlist,
  303. '@url' => $childdata || !isset($value['@url']) ? "javascript:;" : $value['@url'],
  304. '@addtabs' => $childdata || !isset($value['@url']) ? "" : (stripos($value['@url'], "?") !== false ? "&" : "?") . "ref=addtabs",
  305. '@caret' => ($childdata && (!isset($value['@badge']) || !$value['@badge']) ? '<i class="fa fa-angle-left"></i>' : ''),
  306. '@badge' => isset($value['@badge']) ? $value['@badge'] : '',
  307. '@class' => ($selected ? ' active' : '') . ($disabled ? ' disabled' : '') . ($childdata ? ' treeview' . (config('fastadmin.show_submenu') ? ' treeview-open' : '') : ''),
  308. );
  309. $str .= strtr($nstr, $value);
  310. }
  311. }
  312. return $str;
  313. }
  314. /**
  315. * 特殊
  316. * @param integer $myid 要查询的ID
  317. * @param string $itemtpl1 第一种HTML代码方式
  318. * @param string $itemtpl2 第二种HTML代码方式
  319. * @param mixed $selectedids 默认选中
  320. * @param mixed $disabledids 禁用
  321. * @param string $itemprefix 前缀
  322. * @return string
  323. */
  324. public function getTreeSpecial($myid, $itemtpl1, $itemtpl2, $selectedids = 0, $disabledids = 0, $itemprefix = '')
  325. {
  326. $ret = '';
  327. $number = 1;
  328. $childs = $this->getChild($myid);
  329. if ($childs) {
  330. $total = count($childs);
  331. foreach ($childs as $id => $value) {
  332. $j = $k = '';
  333. if ($number == $total) {
  334. $j .= $this->icon[2];
  335. $k = $itemprefix ? $this->nbsp : '';
  336. } else {
  337. $j .= $this->icon[1];
  338. $k = $itemprefix ? $this->icon[0] : '';
  339. }
  340. $spacer = $itemprefix ? $itemprefix . $j : '';
  341. $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
  342. $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
  343. $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
  344. $value = array_combine(array_map(function ($k) {
  345. return '@' . $k;
  346. }, array_keys($value)), $value);
  347. $nstr = strtr(!isset($value['@disabled']) || !$value['@disabled'] ? $itemtpl1 : $itemtpl2, $value);
  348. $ret .= $nstr;
  349. $ret .= $this->getTreeSpecial($id, $itemtpl1, $itemtpl2, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp);
  350. $number++;
  351. }
  352. }
  353. return $ret;
  354. }
  355. /**
  356. *
  357. * 获取树状数组
  358. * @param string $myid 要查询的ID
  359. * @param string $itemprefix 前缀
  360. * @return array
  361. */
  362. public function getTreeArray($myid, $itemprefix = '')
  363. {
  364. $childs = $this->getChild($myid);
  365. $n = 0;
  366. $data = [];
  367. $number = 1;
  368. if ($childs) {
  369. $total = count($childs);
  370. foreach ($childs as $id => $value) {
  371. if ($number == $total) {
  372. $k = $itemprefix ? $this->nbsp : '';
  373. } else {
  374. $k = $itemprefix ? $this->icon[0] : '';
  375. }
  376. $data[$n] = $value;
  377. $data[$n]['childlist'] = $this->getTreeArray($id, $itemprefix . $k . $this->nbsp);
  378. $n++;
  379. $number++;
  380. }
  381. }
  382. return $data;
  383. }
  384. /**
  385. * 将getTreeArray的结果返回为二维数组
  386. * @param array $data
  387. * @param string $field
  388. * @return array
  389. */
  390. public function getTreeList($data = [])
  391. {
  392. $arr = [];
  393. foreach ($data as $k => $v) {
  394. $childlist = isset($v['childlist']) ? $v['childlist'] : [];
  395. unset($v['childlist']);
  396. $v['haschild'] = $childlist ? 1 : 0;
  397. if ($v['id']) {
  398. $arr[] = $v;
  399. }
  400. if ($childlist) {
  401. $arr = array_merge($arr, $this->getTreeList($childlist));
  402. }
  403. }
  404. return $arr;
  405. }
  406. }