上文讲了 loadTable ,表格统计。表格统计的数据虽然在微观上较为准确,但不能从宏观上看到访客的趋势和比重,于是我制作了 loadChart 图表统计。



图表插件:echart.js

​chart.js 部分代码,用于发送请求和接受数据

(function(){
    "use strict";

    var init = function(){
        initPage();
        bindEvent();
        $("#vistor").click();
    },
    initPage = function(){
         $("#vistorChart").css("height", "500px");
    },
    bindEvent = function(){
        $("#btnGroup button").click(function(){
            $("#btnGroup button").removeClass("active");
            $(this).addClass("active");
        });
        $("#vistor").click(function(){
            loadChart('vistor');
        });
        $("#link").click(function(){
            loadChart('link');
        });
        $("#system").click(function(){
            loadChart('system');
        });
        $("#browser").click(function(){
            loadChart('browser');
        });
    },
    loadChart = function(type){
        $('#vistorChart').removeAttr('_echarts_instance_').empty();
        var myChart = echarts.init($("#vistorChart")[0]);
        myChart.showLoading();
        $.ajax({
            url: '__APP__/Vistor/loadChart',
            data: {
                type: type
            },
            type: 'POST',
            cache: false,
            dataType: 'JSON',
            beforeSend: function(){},
            complete: function(){}
        }).done(function(data){
            myChart.hideLoading();
            myChart.setOption(data);
        }).fail(function(){
                swal({
                type: 'error',
                title: '加载失败',
                allowOutsideClick: false,
                allowEnterKey: false,
                confirmButtonText: '确认',
                animation: false,
                customClass: 'animated wobble'
            });
        });
    };

    $(function(){
        init();
    });
})();

loadChart 部分,用于处理请求,组装数据,并返回:

public function loadChart($type = 'vistor') { $Vistor = D('vistor'); $Dic = D('dictionary'); $vistorShow = array('all' => '总访问量', 'vistor' => '访客', 'Googlebot' => '谷歌蜘蛛', 'bingbot' => '必应蜘蛛', 'Baiduspider' => '百度蜘蛛', 'AhrefsBot' => 'Ahrefs蜘蛛', 'MJ12bot' => 'MJ12蜘蛛', 'others' => '其他流量'); $linkShow = array('all' => '访问总量', 'vistor' => '访客', 'spider' => '爬虫'); $systemShow = array('Windows 10', 'Windows 8', 'Windows 7', 'Windows XP', 'Windows Vista', 'Windows NT', 'Linux', 'MacOS', 'Android', 'iPhone', 'spider', 'sitemap', 'others'); $browserShow = array('Chrome', 'Firefox', 'Safari', 'IE', 'Opera', 'Edge', 'Maxthon', 'spider', 'sitemap', 'others'); switch ($type){ case 'vistor': $countGroupDate = array();//临时存放数据,计算其余类型 $vistorCountGroupDate = array_column($Vistor->getCountGroupDate('all'), 'num');//总量线 $data['title'] = array('left' => '2%', 'text' => '访客类型统计图', 'subtext' => C('WUGN.WEB_SITE')); $data['tooltip'] = array('trigger' => 'axis'); $data['legend'] = array('data' => array_values($vistorShow)); $data['toolbox'] = array('right' => '2%', 'feature' => array('dataZoom' => array('yAxisIndex' => 'none'), 'restore' => array(), 'dataView' => array(), 'saveAsImage' => array())); $data['dataZoom'] = array(array('startValue' => '2018-09-20'), array('type' => 'inside')); $data['xAxis'] = array('type' => 'category', 'boundaryGap' => false, 'data' => array_column($Vistor->getCountGroupDate('all'), 'ddate')); $data['yAxis'] = array('type' => 'value'); $data['series'] = array(); foreach($vistorShow as $k=>$v) { if($k != 'others'){ $temp = array_column($Vistor->getCountGroupDate($k), 'num');//本条线 array_push($data['series'], array('name' => $v, 'type' => 'line', 'data' => $temp)); foreach($temp as $i=>$j){ $countGroupDate[$i] += $j; } } } foreach($countGroupDate as $k=>&$v){ $v = $vistorCountGroupDate[$k] * 2 - $v; } array_push($data['series'], array('name' => '其他流量', 'type' => 'line', 'data' => $countGroupDate)); break; case 'link': $data['title'] = array('left' => '5%', 'text' => '受访页面统计图', 'subtext' => C('WUGN.WEB_SITE')); $data['tooltip'] = array('trigger' => 'axis', 'axisPointer' => array('type' => 'cross')); $data['legend'] = array('data' => array_values($linkShow)); $data['toolbox'] = array('right' => '5%', 'feature' => array('dataView' => array('readOnly' => true), 'magicType' => array('type' => array('line', 'bar')), 'restore' => array(), 'saveAsImage' => array())); $data['xAxis'] = array('type' => 'category', 'axisPointer' => array('type' => 'shadow'), 'data' => array_column($Dic->getDictionary('vistor_link'), 'code_note')); $data['yAxis'] = array('type' => 'value'); $data['series'] = array(); foreach($linkShow as $k=>$v){ $temp = array_column($Vistor->getCountGroupLink($k), 'num'); array_push($data['series'], array('name' => $v, 'type' => ($k=='all'?'line':'bar'), 'data' => $temp)); } break; case 'system': $data['title'] = array('left' => '5%', 'text' => '操作系统统计图', 'subtext' => C('WUGN.WEB_SITE')); $data['tooltip'] = array('trigger' => 'item', 'formatter' => '{a}<br />{b}:{c}({d}%)'); $data['legend'] = array('orient' => 'vertical', 'right' => '5%', 'data' => $systemShow); $data['toolbox'] = array('right' => '5%', 'bottom' => '0', 'feature' => array('dataView' => array('readOnly' => true), 'restore' => array(), 'saveAsImage' => array())); $data['series'] = array('name' => '操作系统', 'type' => 'pie', 'itemStyle' => array('emphasis' => array('shadowBlur' => '10', 'shadowOffsetX' => '0', 'shadowColor' => 'rgba(0, 0, 0, 0.5)')), 'data' => array()); foreach($Vistor->getSystemCount($systemShow) as $k=>$v){ array_push($data['series']['data'], array('value' => $v, 'name' => $k)); } break; case 'browser': $countGroupBrowser = 0;//临时存放数据,计算其余类型 $data['title'] = array('left' => '5%', 'text' => '用户客户端统计图', 'subtext' => C('WUGN.WEB_SITE')); $data['tooltip'] = array('trigger' => 'item', 'formatter' => '{a}<br />{b}:{c}({d}%)'); $data['legend'] = array('orient' => 'vertical', 'right' => '5%', 'data' => $browserShow); $data['toolbox'] = array('right' => '5%', 'bottom' => '0', 'feature' => array('dataView' => array('readOnly' => true), 'restore' => array(), 'saveAsImage' => array())); $data['series'] = array('name' => '客户端', 'type' => 'pie', 'itemStyle' => array('emphasis' => array('shadowBlur' => '10', 'shadowOffsetX' => '0', 'shadowColor' => 'rgba(0, 0, 0, 0.5)')), 'data' => array()); foreach($Vistor->getBrowserCount($browserShow) as $k=>$v){ array_push($data['series']['data'], array('value' => $v, 'name' => $k)); } break; } $this->ajaxReturn($data); }

$vistorShow $linkShow $systemShow $browserShow 分别定义要展示的数据内容。

代码逻辑比较简单,不懂的地方请咨询站长。

此外, 模型层如下:

public function getCountGroupDate($type){
    $sql = "SELECT DATE(dday) AS ddate, COUNT(*) - 1 AS num
            FROM(
            SELECT datelist AS dday FROM wu_calendar WHERE DATE(datelist) BETWEEN '2018-08-12' AND CURDATE()
            UNION ALL
            SELECT tm FROM wu_vistor WHERE (";
    switch ($type){
    case 'all':
        $sql .= "1=1";
        break;
    case 'vistor':
        $sql .= "ag NOT LIKE '%spider%' AND ag NOT LIKE '%bot%' AND ag NOT LIKE '%sitemap%' AND ag NOT LIKE '%parser%'";
        break;
    case 'sitemap':
        $sql .= "ag LIKE '%sitemap%' OR ag LIKE '%parser%'";
        break;
    default:
        $sql .= "(ag LIKE '%spider%' OR ag LIKE '%bot%') AND ag LIKE '%{$type}%'";
    }
    $sql .= ") AND (DATE(tm) BETWEEN '2018-08-12' AND CURDATE())
            )T GROUP BY ddate ORDER BY ddate";
    return $this->query($sql);
}

public function getCountGroupLink($type){
    $Dic = D('dictionary');
    $linkCode = array_column($Dic->getDictionary('vistor_link'), 'code_value');

    switch ($type){
    case 'all':
        $where['ag'] = array('neq','null');
        break;
    case 'vistor':
        $where['ag'] = array('notlike',array('%spider%','%bot%'),'AND');
        break;
    case 'spider':
        $where['ag'] = array('like',array('%spider%','%bot%'),'OR');
        break;
    }
    
    $data = array();
    foreach($linkCode as $v){
        $where['lk'] = array('like', explode(',', $v), 'or');
        array_push($data, $this->field('count(id) as num')->where($where)->find());
    }
    return $data;
}

public function getCountLike($type){
    $where['ag'] = array('like', "%{$type}%");
    $data = $this->field('count(id) as num')->where($where)->find();
    return $data['num'];
}

public function getSystemCount($systemShow){
    $where['ag'] = array('notlike', '%bot%');
    $agent = $this->field('ag')->where($where)->select();
    $data = get_os_count($agent, $systemShow);
    return $data;
}

public function getBrowserCount($browserShow){
    $where['ag'] = array('notlike', '%bot%');
    $agent = $this->field('ag')->where($where)->select();
    $data = get_br_count($agent, $browserShow);
    return $data;
}

模型层中写的 SQL 不常见,(如:getCountGroupDate的查询方法)情参考本站其他文章。

get_os_count($agent, $systemShow) 函数和 get_br_count($agent, $browserShow) 用于统计数量,传入agent(array) 和要展示的内容,返回数量。这里只列出 get_br_count($agent, $browserShow) 以供参考:

function get_br_count($agent, $browserShow) {
    foreach($agent as $v){
        $v = $v['ag'];
        if (preg_match('/sitemap/i', $v) || preg_match('/Parser/i', $v)) {
            $data['sitemap'] += 1;
        } else if (preg_match('/spider/i', $v) || preg_match('/bot/i', $v)) {
            $data['spider'] += 1;
        } else if (stripos($v, 'Firefox/')) {
            $data['Firefox'] += 1;
        } else if (stripos($v, 'Maxthon')) {
            $data['Maxthon'] += 1;
        } else if (stripos($v, 'MSIE')) {
            $data['IE'] += 1;
        } else if (stripos($v, 'OPR')) {
            $data['Opera'] += 1;
        } else if (stripos($v, 'Edge')) {
            $data['Edge'] += 1;
        } else if (stripos($v, 'Chrome')) {
            $data['Chrome'] += 1;
        } else if (stripos($v, 'Safari')) {
            $data['Safari'] += 1;
        } else if (stripos($v, 'rv:') && stripos($v, 'Gecko')) {
            $data['IE'] += 1;
        } else {
            $others += 1;
        }
    }
    arsort($data);//根据值降序排序
    foreach($data as $k=>&$v){
        if(!in_array($k, $browserShow)){
            $others += $v;
            unset($data[$k]);
        }
    }
    $data['others'] = $others;
    return $data;
}