美文网首页
2019-03-03

2019-03-03

作者: hc3001 | 来源:发表于2019-12-09 16:19 被阅读0次

    需求介绍:

    1. 系统权限管理,数据系统管理,各个系统权限联通。
    2. 可以对不同用户设置角色,赋予不同权限,显示看到不同的页面。
    3. 需要在不改代码的前提下,增加外部链接菜单页面。
    4. 决策系统数据系统部分迁移到天眼。
    

    项目命名

    项目命名为bigdata-admin-vue

    技术栈

    1. vue-cli3;
    2. vue-router、vuex、element、axios;
    3. 权限基于RBAC权限管理模型,即用户→角色→权限管理模型;
    

    系统目录

    [站外图片上传中...(image-87a9ff-1575879535075)]

    1路由控制和菜单显示

    vue权限路由实现方式:

    * 视图(View) 使用xtpl模板引擎
    
    ##路由解析,根据URL寻找对应的控制器和行为
    
    ```javascript
    app.post('/template|/templatePC|/user-defined|/user-defined-pc',control.postRander);  //post入口,页面编辑、报表、热图、abtest报表页面渲染
    app.get('/modules/*/index',control.moduleRander); //用于本地单个无线模块开发调试
    app.get('/modules_pc/*/index',control.moduleRander);//用于本地单个pc模块开发调试
    app.get('/template/design(_second)?|design_pc(_second)?',control.pagePreview); // 所有iframe的入口,预览
    app.get(/personalized$|personalized\/[^\/]*?$/,control.personalized); //个性化
    app.get('/AB_RedirectServer?',control.abtest); //ab
    
    //保存页面请求
    app.post('/savePage|savePagePc/index',control.pageShow);
    
    //js combo
    app.get('/jscombo/',combo.jsCombo);
    
    app.get('*',control.errorPage);
    

    3.渲染部分:

    编辑页渲染父级通过调用function dataSend(tag,viewModule,callback) 里的ajax将数据发送给cms postDataProcessing函数处理,

    returnData = self.dataRender(data,equipment,xtplPath+data.template_file+'.xtpl',moduleName);
    if(data.flagForPage == 0 || data.mustDocumentWrite == true){
        xtpl.renderFile('./views/'+xtplPath+data.template_file+'.xtpl',returnData,function(error,content){
            res.writeHead(200, {"Content-Type": "text/plain"});
            res.write(content);
        });
    }else{
        res.send(returnData);
    }
    

    iframe在展示页面之初,默认模板是index.xtpl,如果要使用用户自定义的xtpl模板,因为页面结构是默认的index.xtpl的结构,需要对页面进行重新写入。

    1. 通过dataRender将处理好的返回数据,赋值给returnData

    2. if语句里的代码对模板进行的渲染,得到的数据是字符串,并且通过res.write将数据返回个浏览器对应的浏览器部分代码处理如下

    document.open(); //打开文档流
    document.write(data); //将默认的iframe里页面内容替换
    document.close(); //关闭文档流
    

    3. else语句里将returnData直接返回给浏览器

    对应代码处理如下:

    $('#css-box').html(data.css);
    $('#components-box').html(data.xtplHTml);
    $("#body-content-box").attr({
        "data-page-style": data.style,
        "data-is-publish": data.isModule,
        "ppcc-id": data.pageId_peopleId_childPageId_childPeopleId,
        "idsite": data.idsite,
        "page-id": (data['digital'] ? data['digital'].page_group_id : null) || data.id,
        "is-personalized": data.isPersonalized,
        "people-id": data.peopleId,
        "data-view-module": (data.viewModule ? data.viewModule : "")
    });
    company_id = !!data.tenant_id ? data.tenant_id : (!!data.company_id ? data.company_id : 0);
    //设置页面背景
    (function () {
        var style = $('body').attr('data-page-style');
        if (style != '') {
            $('body').css('background', style);
        }
    })();
    $(getIframe()).attr("isSuccess", "true");
    $('#components-box').append("<script>" + data.jsContainer + "</\script>");
    if ($(".module_bottom-nav").length === 0) {
        $("body").css("padding", 0);
    }
    if (data.config.length > 0 || data.show_user_defined) {
        $(".prompts").hide();
    } else {
        $(".prompts").show();
    }
    if (tag.c_data && tag.r_data) {
        reportClickData(tag);
    }
    

    ('#css-box').html(data.css);iframe里的css样式替换 \('#components-box').html(data.xtplHTml);将iframe里的html替换
    $('#components-box').append("<script>" + data.jsContainer + "</script>");将iframe里的js替换

    4.个性化

    个性化是一个比较复杂的系统,但是cms只做了渲染,复杂的逻辑都在我们系统编辑部分做了处理,个性化页面没有生成静态的文件,通过接口获取数据,并有dataRender渲染展示页面

    5.Abtest

    函数abtest对其做的处理
    目前有两种情况is_out 为 0 和is_out 为 1
    为0的时候表示内部页面,也就是我们站点生成的页面,在调用dataRender方法做处理展示页面
    为1的时候表示外部页面,也就是客户的页面,直接res.write渲染

    6.页面生成机制

    当页面保存的时候触发,将接收的数据里的配置信息解析,查找对应的xtpl模板,
    在dist查找对应模块css和js并导入数据,生成个一个字符串的页面数据\

    1. pageSave函数执行;
    2. saveToMakeFile普通页面保存,就生成一个html,以页面id为命名
    3. makeImageFiles函数来生成图片文件
    4. separateFiles函数做文件分离处理,将html、css、js、图片剥离开,变成单个文件
    5. getlistening函数将监听文件添加到页面
    6. tongjiFn函数设置统计参数

    7.日志管理:

    require log4js

    log4js.configure({
      appenders: [
        { type: 'console' }, //控制台输出
        {
          type: 'dateFile', //文件输出
          filename: './log/',  //文件保存在log目录下
          maxLogSize: 20480,
          backups:3,
          category: 'log_date',
          alwaysIncludePattern: true,
          pattern: "yyyy_MM_dd.log"  //文件命名规范
        }
      ],
      replaceConsole: true
    });
    var logger = log4js.getLogger('log_date');
    logger.setLevel(log4js.levels.INFO);
    

    8.pm2多进程服务

    pm2 是一个带有负载均衡功能的Node应用的进程管理器,把独立代码利用全部的服务器上的所有CPU,并保证进程永远都存活,0秒的重载;
    线上服务在运行的时候会出现异常,虽然代码里已经用try catch 来捕获异常,但是还是会有一些情况会导致服务崩溃,pm2开启多进程,可以保证代码运行稳定
    

    9.Redis

    只要用于用户预览的时候数据存储,在用户点击预览的时候将保存在redis中,以时间戳和随机6位随机数字为key存储数据,用户可以通过手机扫描二维码访问到页面,pc预览页面可以通过新窗口打开,目前页面预览有效时间设置为20分钟,预览的时候通过key来读取数据数据
    

    10.监听部分:listening

    1. 监听包括piwik和sdk,piwik已不再使用。页面滚动手机pv数据、页面到达率数据,点击搜集点击数据,热图数据;
    2. 页面编辑状态下,预览状态下不会添加监听代码,发布的页面、ab页面、个性化页面会引入监听代码,
    

    11.打包工具:gulp

    1. watch监听了模块里的js和css文件,当模块里的js或css做修改时,自动打包压缩到dist目录下
    2. gulp bulid可以生成打包后的代码,用于部署客户服务器上

    12.模板引擎:xtpl

    1. xtpl是淘宝kissy架构里的模板渲染引擎,介绍:https://github.com/xtemplate/xtemplate/blob/master/docs/tutorial/introduce.md
    2. 在系统开发之初,我是想用ejs作为渲染模板引擎,由于项目的时间紧迫,我对xtpl比较熟悉些,经过商议还是决定用xtpl。
    3. xtpl的语法功能并不强,但也是可以满足我们的渲染需求,目前在项目里xtpl运行稳定。
      npm包链接:https://www.npmjs.com/package/xtpl
      gitbub链接:https://github.com/xtemplate/xtemplate

    13.模块(组件)

    1. 页面是由一个或者多个模块构成的,模块是构成页面基础单位
    2. 模块js代码基础框架
    (function() {  //闭包函数自执行
        function Mod() { //函数定义 Mod构造函数
            this.init.apply(this, arguments);
        }
        Mod.prototype = { //原型链
            init: function(container) {
                var self = this;
                self._node = $(container); // 模块容器
                self.data = self._node.find('.J-dataJson').html(); // 模块数据
                self.data = JSON.parse(self.data);
            }
        };
        new Mod("#clickplus-module-box"); //don't modify this line of code
        // new一个构造函数
    })();
    

    基础框架不允许改动,当页面里有多个相同模块的时候,构造函数就发挥作用了,我会new多个Mod
    new Mod("#clickplus-module-1496965951574")
    new Mod("#clickplus-module-1496965951575")
    .......
    后面跟上模块创建的时间戳,保证唯一性,这样就避免了重复写入相同的模块代码,减少页面的代码量

    1. 模块xtpl
      精简代码示例:
    <textarea class="J-dataJson" style="display: none">{{newTextarea}}</textarea>
    <div class="area-choose">
        <div class="main" style="{{#if(config.data[0].height)}}margin-bottom:{{config.data[0].height/64}}rem;{{/if}}">
        </div>
    </div>
    

    当你页面里需要cms返回的模块数据时可以添加textarea的代码
    class="main"也是固定的

    1. 模块样式
      精简代码示例:
    @r: 64rem;
    .area-choose {
       min-width: 320px;
          width: 640/@r;
          margin: 0 auto;
          .main {
              width: 640/@r;
              min-width: 320px;
          }
    }
    

    @r: 64rem;作为页面适配基础单位
    用实际的尺寸除以@r得到适配的rem尺寸,就可以适配各个手机


    14.nginx应用

    主要做了8080端口的代理,转发到80,设置了cms的

    server { 
        listen 80; 
        server_name node.clickplus.cn; 
        server_name cms.clickplus.cn; 
        server_name cms-test.clickplus.cn; 
        location / { 
            proxy_pass http://127.0.0.1:8080; 
            fastcgi_connect_timeout 60;           
            fastcgi_send_timeout 180; 
            fastcgi_read_timeout 180;
            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 16k;
            client_body_buffer_size 4096k;
        }
    } 
    

    15.报表部分

    1. 先前版本是将报表的渲染处理放在controller.js里的,带来一个很大的问题就是,当切换有效点击或者无效点击、选着日期等操作的时候其实要重新渲染整个页面
    2. 现在的逻辑是页面只渲染一次,做切换数据的操作时页面不再刷新,只将数据刷新
      footSettings.xtpl文件里:
      reportClickData、
      reportCalculate、
      reportForNav、
      reportForNavGoods、
      reportForGoods
      函数对报表的数据做了处理

    16.页面适配原理

    对font-size进行了重新定义,以rem作为一个单位,64为基数,

    !function(e) {
        function t() {
            var t = n.clientWidth,
                r = "}";
            !navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i) && t > 1024 && (t = 640, r = ";max-width:" + t + "px;margin-right:auto!important;margin-left:auto!important;}"), e.rem = t / 10, /ZTE U930_TD/.test(navigator.userAgent) && (e.rem = 1.13 * e.rem), /Android\s+4\.4\.4;\s+M351\s/.test(navigator.userAgent) && (e.rem = e.rem / 1.05), i.innerHTML = "html{font-size:" + e.rem + "px!important;}body{font-size:" + 12 * (t / 320) + "px" + r
        }
        var n = document.documentElement,
            i = document.createElement("style");
        n.firstElementChild.appendChild(i), e.addEventListener("resize", function() {
            t()
        }, !1), e.addEventListener("pageshow", function(e) {
            e.persisted && t()
        }, !1), t()
    }(window);
    

    17.调试

    1. 可以通过webstorm来调试
    2. 可用node debug index.js 打debugger断点
    3. 基于Chrome浏览器的调试器 npm install -g node-inspector 导入安装路径到环境变量。 node-inspector 默认情况下node-inspector的端口是8080,可以通过参数--web-port=[port]来设置端口。 node-debug --web-port=8000 index.js启动程序 node --debug-brk

    18.环境安装

    1. 在cms根目录里执行npm install,安装依赖包
    2. 安装全局pm2模块 sudo npm install -g pm2 用于开启多进程,保证node服务不停止工作,线上环境一点要安装并执行 pm2 start index.js -i 4
    3. 安装全局gulp模块 sudo npm install -g gulp
    4. 安装全局nginx sudo npm install -g nginx 用于将8080端口发送到80端口
    5. 打开sudo gedit /etc/nginx/etc/nginx ,找到http,在里面加上如下代码,server_name 为你本地host设置的127.0.0.1对应的名称 ,将8080的端口请求指向80
    server { 
        listen 80; 
        server_name node.clickplus.cn; 
        server_name cms.clickplus.cn; 
        server_name cms-test.clickplus.cn; 
        location / { 
            proxy_pass http://127.0.0.1:8080; 
        } 
    }
    

    nginx的buffer设定 开发中可能会发现post请求传输问题net::ERR_INCOMPLETE_CHUNKED_ENCODING 请在server添加buffer设定,并且设置gzip为on;

    server { 
        listen 80; 
        server_name node.clickplus.cn; 
        server_name cms.clickplus.cn; 
        server_name cms-test.clickplus.cn; 
        location / { 
            proxy_pass http://127.0.0.1:8080; 
            fastcgi_connect_timeout 60;           
            fastcgi_send_timeout 180; 
            fastcgi_read_timeout 180;
            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 16k;
            client_body_buffer_size 4096k;
        }
    } 
    

    19.数据结构

    模块数据以对象数组形式存放在在config里,动态的配置信息存放在模块的config里

    1.普通页面数据结构

    {
        "app_url": null,    //页面app链接
        "body_foot": null,  //用户自定尾部
        "body_head": "",    //用户自定头部
        "cdn_url": "https://huodong.clickplus.cn",  //cdn地址,用户域名
        "children": null,
        "component_num": null,
        "config": [  //存放页面模板的数据
            {
                "id": 546,
                "img_path": "http://huodong.clickplus.cn/image/tuwenrequ.jpg",
                "title": "图文",
                "stick": false,
                "basic": 1,
                "timeIndex": 1496288530354,
                "create_time": 1477459545000,
                "device": 0,
                "path": "/modules/area-choose-text",
                "config": { //动态配置信息
                    "data": [
                        {
                            "wrapHeight": 640,
                            "wrapWidth": 640,
                            "image": "http://huodong.clickplus.cn/image/hjhg.jpg",
                            "iHeight": 640,
                            "iWidth": 640
                        }
                    ],
                    "areaData": []
                },
                "tenant_id": 3,
                "type": 12
            }
        ],
        "create_time": 1495789835000,
        "custom_body": "<script src=\"//res.wx.qq.com/open/js/jweixin-1.2.0.js\"></script>\n<script type=\"text/javascript\">\nvar shareURL = window.location.href;\nvar sTitle = document.title;\nvar sDesc = document.getElementsByTagName('meta')['description'].content;\nvar imgUrl = $(\"#body-content-box > div:nth-child(1) > img\")[0].src;\n$.ajax({\n    url: '//weixintest.clickplus.cn/sign/0',\n    type: 'get',\n    dataType: 'jsonp',\n    async:false,\n    data: {\n        url: location.href.split('#')[0]\n    },\n    success: function(data) {\n        wx.config({\n            debug: false,\n            appId: data.appid,\n            timestamp: data.timestamp,\n            nonceStr: data.nonceStr,\n            signature: data.signature,\n            jsApiList: [\n                // 所有要调用的 API 都要加到这个列表中\n                'onMenuShareTimeline',\n                'onMenuShareAppMessage'\n            ]\n        });\n        wx.ready(function(){\n             wx.onMenuShareTimeline({\n                title: sTitle, // 分享标题\n                desc: sDesc, // 分享描述\n                link: shareURL, // 分享链接\n                imgUrl: imgUrl, // 分享图标\n                success: function () {\n                },\n                cancel: function () {\n                }\n            });\n            wx.onMenuShareAppMessage({\n                title: sTitle, // 分享标题\n                desc: sDesc, // 分享描述\n                link: shareURL, // 分享链接\n                imgUrl: imgUrl, // 分享图标\n                type: '', // 分享类型,music、video或link,不填默认为link\n                dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空\n                success: function () {\n                },\n                cancel: function () {\n                }\n            }); \n            wx.error(function(res){\n                console.log(\"errorMSG-1:\"+res);\n            });\n        })\n    },\n    error:function(e){\n        console.log(e)\n    }\n});\n</script>",
        "custom_head": "",
        "data_collection_type": 1,
        "default_page_id": null,
        "description": "adadasddasd",
        "device": 0,
        "file_name": "zhuxiyu_1028",
        "hav_app": 0,
        "id": 5228,
        "idsite": 31,
        "im_position": "0",
        "im_text": "",
        "is_deleted": null,
        "is_group": 0,
        "keywords": null,
        "pageMainWidth": "auto",
        "path": null,
        "relation": null,
        "release_time": null,
        "setting": null,
        "share_img": "https://huodong.clickplus.cn/image/1496891043699.png",
        "site_type": 1,
        "smart_component_setting": 0,
        "stat_position": "0",
        "stat_text": "<script>\nvar _hmt = _hmt || [];\n(function() {\n  var hm = document.createElement(\"script\");\n  hm.src = \"https://hm.baidu.com/hm.js?b5e1a9760cd46dbf098ad3e8db6f15a2\";\n  var s = document.getElementsByTagName(\"script\")[0]; \n  s.parentNode.insertBefore(hm, s);\n})();\n</script>",
        "status": 1,
        "style": "#eeeeee",
        "templateSettingItem": null,
        "template_file": "index",
        "template_id": 0,
        "tenant_id": 3,
        "tenant_logo": "https://huodong.clickplus.cn/image/1495612416087.jpg",
        "thumbnail": "http://huodong.clickplus.cn/file/1496909741687.jpg",
        "title": "zhuxiyu_1028",
        "type": 0,
        "type_name": null,
        "update_time": 1496909741000,
        "url": "https://huodong.clickplus.cn/html/zhuxiyu_1028.html",
        "wx_app_id": "",
        "only": "149691787773263078",
        "isEdit": true,
        "mustDocumentWrite": false,
        "flagForPage": "2"
    }
    

    2.个性化页面数据结构

    {
        "config": [  //组件数据
            {
                "basic": 1,
                "config": {
                    "data": [
                        {
                            "image": "http://huodong.clickplus.cn/image/hjhg.jpg",
                            "wrapHeight": 640,
                            "wrapWidth": 640,
                            "iWidth": 640,
                            "iHeight": 640
                        }
                    ],
                    "areaData": []
                },
                "create_time": 1477459545000,
                "device": 0,
                "id": 546,
                "img_path": "http://huodong.clickplus.cn/image/tuwenrequ.jpg",
                "path": "/modules/area-choose-text",
                "stick": false,
                "tenant_id": 0,
                "title": "图文",
                "type": 12,
                "timeIndex": 1496918752643,
                "fromComponent": "component1496918752643",
                "fromText": "default1496918752643"
            }
        ],
        "id": "4454",
        "img_path": "",
        "path": "",
        "tenant_id": 3,
        "title": "",
        "style": "#eeeeee",
        "company_id": "3",
        "isEdit": true,
        "template_file": "index",
        "device": 0,
        "only": "149691875264302242",
        "mustDocumentWrite": false,
        "flagForPage": "2"
    }
    

    参考文献

    pm2:https://www.douban.com/note/314200231/
    《深入浅出 node.js》作者:朴灵

    相关文章

      网友评论

          本文标题:2019-03-03

          本文链接:https://www.haomeiwen.com/subject/hhfpuqtx.html