php+ajax 实现无限树列表
首先介绍我实现的是xhprof插件的日志转为无限树状图,先看效果图:
废话不多说,直接看代码:(辛辛苦苦敲了好久才搞定,逻辑比较多,新手多揣摩)
控制器:
1 <?php
2
3 namespace Admin\Controller;
4
7
8 class XhprofController extends AdminbaseController
9 {
10 function _initialize()
11 {
12 parent::_initialize();
13 }
14
15 //获取当前文件目录
16 public function index()
17 {
18 $file = scandir("./public");
19 $str = ".xhprof";
20 foreach ($file as $value) {
21 if (preg_replace("/($str)/", "", $value) != $value) {
22 $dir[] = $value;
23 }
24 }
25 // $count = count($dir);
26 // $page = $this->page($count, 20);
27 // $this -> assign( "Page", $page->show( 'Admin' ) );
28 $this->assign("dir", $dir);
29 $this->display();
30 }
31
32 //获取适当的结构
33 public function tree($file_name)
34 {
35 $file_path = "./public/xhprof/$file_name";
36 $file_name = str_replace('.', '', $file_name);
37 if (F($file_name)) {
38 $mainTree = F($file_name);
39 } else {
40 if (file_exists($file_path)) {
41 $fp = fopen($file_path, "r");
42 $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
43 $array = json_decode($str, true);
44 $array_keys = array_keys($array);
45 //按照调用者整理数据
46 $calls = [];
47 foreach ($array_keys as $array_key) {
48 $caller = explode("==>", $array_key)[0];
49 $beCall = explode("==>", $array_key)[1];
50 if (!$calls[$caller]) {
51 $calls[$caller] = [
52 "caller" => $caller,
53 "beCalls" => []
54 ];
55 }
56 $status = 0;//是否有子目录
57 foreach ($array_keys as $keyv) {
58 if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") {
59 $status = 1;
60 }
61 }
62 if ($beCall !== null) {
63 $calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]);
64 }
65 }
66 foreach ($calls as &$v) {
67 $tmpAll = 0;
68 foreach ($v['beCalls'] as $vv) {
69 $tmpAll += $vv['data']['wt'];
70 }
71 foreach ($v['beCalls'] as &$vv1) {
72 $vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2);
73 $vv1['wtp'] = $vv1['data']['wt'];
74 }
75 }
76 $mainTree = $calls;
77 //缓存$mainTree
78 F($file_name, $mainTree);
79 } else {
80 $mainTree = "";
81 }
82 }
83 array_multisort(array_column($mainTree["main()"]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree["main()"]["beCalls"], "name"), SORT_DESC, $mainTree["main()"]["beCalls"]);
84 $this->assign("main", $array["main()"]);
85 $this->assign("mainTree", $mainTree["main()"]);
86 $this->assign("level", 1);
87 $this->assign("file_name", $file_name);
88 $this->assign("file_path", $file_path);
89 $this->display();
90 }
91
92 public function ajax()
93 {
94 $note = htmlspecialchars_decode(I("post.note"));
95 $file_name = htmlspecialchars_decode(I("post.file_name"));
96 $file_path = htmlspecialchars_decode(I("post.file_path"));
97 //处理$mainTree
98 if (F($file_name)) {
99 $mainTree = F($file_name);
100 } else {
101 if (file_exists($file_path)) {
102 $fp = fopen($file_path, "r");
103 $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
104 $array = json_decode($str, true);
105 $array_keys = array_keys($array);
106 //按照调用者整理数据
107 $calls = [];
108 foreach ($array_keys as $array_key) {
109 $caller = explode("==>", $array_key)[0];
110 $beCall = explode("==>", $array_key)[1];
111 if (!$calls[$caller]) {
112 $calls[$caller] = [
113 "caller" => $caller,
114 "beCalls" => []
115 ];
116 }
117 $status = 0;//是否有子目录
118 foreach ($array_keys as $keyv) {
119 if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") {
120 $status = 1;
121 }
122 }
123 if ($beCall !== null) {
124 $calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]);
125 }
126 }
127 foreach ($calls as &$v) {
128 $tmpAll = 0;
129 foreach ($v['beCalls'] as $vv) {
130 $tmpAll += $vv['data']['wt'];
131 }
132 foreach ($v['beCalls'] as &$vv1) {
133 $vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2);
134 $vv1['wtp'] = $vv1['data']['wt'];
135 }
136 }
137 $mainTree = $calls;
138 //缓存$mainTree
139 F($file_name, $mainTree);
140 } else {
141 $mainTree = "";
142 }
143 }
144 array_multisort(array_column($mainTree[$note]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree[$note]["beCalls"], "name"), SORT_DESC, $mainTree[$note]["beCalls"]);
145 $this->ajaxReturn($mainTree[$note]);
146 }
147
148
149 }
veiw界面:
1 </head> 2 <body> 3 <div class="wrap"> 4 <ul class="nav nav-tabs"> 5 <li class="active"><a href="">{:L('ADMIN_XHPROF')}</a></li> 6 </ul> 7 <form method="post" class="js-ajax-form" action="{:U('AdminTerm/listorders')}"> 8 <table class="table table-hover table-bordered table-list"> 9 <thead> 10 <tr> 11 <th width="2">ID</th> 12 <th width="50">{:L('FILE_NAME')}</th> 13 </tr> 14 </thead> 15 <tbody> 16 <volist name="dir" id="vo" key="k"> 17 <tr> 18 <th width="2">{$k}</th> 19 <th width="50"><a href="{:U('admin/xhprof/tree',array('file_name'=>$vo))}">{$vo}</a></th> 20 </tr> 21 </volist> 22 </tbody> 23 <tfoot> 24 <tr> 25 <th width="2">ID</th> 26 <th width="50">{:L('FILE_NAME')}</th> 27 </tr> 28 </tfoot> 29 </table> 30 <div class="pagination">{$Page}</div> 31 </form> 32 </div> 33 <script src="__PUBLIC__/js/common.js"></script> 34 </body> 35 </html>
1 <html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
4 <title>Xhprof数据分析</title>
5 <link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/bootstrap.min.css"/>
6 <link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/xhprof.css"/>
7 <script type="text/javascript" src="__PUBLIC__/js/jquery.js"></script>
8 </head>
9 <body>
10 <div class="tree well">
11 <ul>
12 <li>
13 <span class="main">
14 <i class='icon-minus'> </i> <b class="name">main()</b>
15 第<b> 1 </b>级   
16 ct:<b class="bw">{$main.ct}</b>
17 wt:<b class="red" >{$main.wt} (100%)</b>
18 cpu:<b class="bw">{$main.cpu}</b>
19 mu:<b class="bw">{$main.mu}</b>
20 pmu:<b class="bw">{$main.pmu}</b>
21 </span>
22 <foreach name="mainTree['beCalls']" item="vo">
23 <ul>
24 <li class='parent_li'>
25 <span title='Collapse this branch' note="{$vo.name}" level="2" file_name={$file_name} file_path={$file_path}>
26 <if condition="$vo['status']==1">
27 <i class="icon-plus"></i>
28 <else/>
29 <b style='width:14px;display: inline-block;'></b>
30 </if>
31 <div style="display: inline-block;">
32 <b class="name" title={$vo.name}>{$vo.name}</b>
33 第<b> 2 </b>级   
34 ct:<b class="ct">{$vo.data.ct}</b>
35 <if condition="$vo['data']['wtp'] gt 10">
36 wt:<b class="red">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
37 <else/>
38 wt:<b class="wt" style="display: inline-block;min-width: 150px;">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
39 </if>
40 cpu:<b class="bw">{$vo.data.cpu}</b>
41 mu:<b class="bw">{$vo.data.mu}</b>
42 pmu:<b class="bw">{$vo.data.pmu}</b>
43 </div>
44 </span>
45 </li>
46 </ul>
47 </foreach>
48 </li>
49 </ul>
50 </div>
51 </body>
52 <script>
53 $(function () {
54 $('.tree li:has(ul)').addClass('parent_li').find(' > span').attr('title', 'Collapse this branch');
55 $('body').on('click', "span", function (e) {
56 e.stopPropagation();
57 if ($(this).attr('is_click') === 1) {
58 return false;
59 }
60 $(this).attr('is_click', 1);
61 var children = $(this).parent('li.parent_li').find(' > ul > li');
62 if (children.is(":visible")) {
63 children.hide('fast');
64 $(this).attr('title', 'Expand this branch').find(' > i').addClass('icon-plus').removeClass('icon-minus');
65 $(this).attr('is_click', 0);
66 } else {
67 children.show('fast');
68 $(this).attr('title', 'Collapse this branch').find(' > i').addClass('icon-minus').removeClass('icon-plus');
69 if ($(this).hasClass('main')) {
70 $(this).attr('is_click', 0);
71 return;
72 }
73 if (!$(this).siblings('.child').length) {
74 do_ajax($(this));
75 } else {
76 $(this).attr('is_click', 0);
77 }
78 }
79 });
80
81 function do_ajax(e) {
82 var note_js = e.attr('note');
83 var level_js = e.attr('level');
84 var file_name_js = e.attr('file_name');
85 var file_path_js = e.attr('file_path');
86 $.ajax({
87 type: "post",
88 cache: false,
89 dataType: "json",
90 url: "{:U('Admin/Xhprof/ajax')}",
91 data: {
92 note: note_js,
93 level: level_js,
94 file_name: file_name_js,
95 file_path: file_path_js
96 },
97 success: function (data) {
98 if (data.beCalls !== null) {
99 var str = "";
100 for (var i = 0; i < data.beCalls.length; i++) {
101 str += "<ul class='child'>";
102 str += "<li class='parent_li'>";
103 str += "<span title='Collapse this branch' class='load' note=" + data.beCalls[i].name + " level= " + eval(Number(level_js) + 1) + " file_name= " + file_name_js + " file_path= " + file_path_js + ">";
104 if (data.beCalls[i].status == 1) {
105 str += "<i class='icon-plus' ></i>";
106 } else {
107 str += "<b style='width:14px;display: inline-block;'> </b>";
108 }
109 str += "<b class='name' title="+ data.beCalls[i].name + ">" + data.beCalls[i].name + '</b>第<b> ' + eval(Number(level_js) + 1) + "</b> 级   ct:<b class='ct'>" + data.beCalls[i].data.ct + "</b> wt:<b class= " + (data.beCalls[i].data.wtp < 10 ? 'wt' : 'red') + " >" + data.beCalls[i].data.wt + "(" + data.beCalls[i].data.wtp + "%)</b> cpu:<b class='bw' >" + data.beCalls[i].data.cpu + "</b> mu:<b class='bw'>" + data.beCalls[i].data.mu + "</b> pmu:<b class='bw'>" + data.beCalls[i].data.pmu + "</b></span>";
110 str += '</li>';
111 str += '</ul>';
112 }
113 e.siblings('.child').remove();
114 e.after(str);
115 e.attr('is_click', 0);
116 }
117 },
118 error: function () {
119 }
120 })
121 }
122 });
123 </script>
124 </html>