在开发项目的时候,有些常用的功能封装到一个类里。

以后要用的话,直接导入过来就可以用了,有一些方法是从网站复制过来的,有些方法是网上复制过来,然后自己修改了一下,标记一下吧!

    /**
     * 一些共用方法
     * @class Utility
     */
angular.module('LHB')
 .service('Utility', ['$rootScope', '$timeout', '$interval', "XiaotuniVersion", function ($rootScope, $timeout, $interval, XiaotuniVersion) {  //应用范围:service,controller,run,factory         var _TempSaveContent = {};
        var __key = "Xiaotuni@pz!365_com";         /**
         * 设置内容,这里主要是用来存放临时数据的。
         * @method _SetContent
         * @param key  键值,用于下次的时候获取内容用的。其实就是 _TempSaveContent的属性名称。
         * @param content 要存储的内容
         * @param isSaveLocalStorage 是否保存到本地存储里面
         * @param IsUser 根据用户uid 来获取缓存里的数据。
         * @private
         */
        var _SetContent = function (key, content, isSaveLocalStorage, IsUser) {
            try {
                if (isSaveLocalStorage) {
                    var __Content = content;
                    if (IsUser) {
                        var __CheckIn = _TempSaveContent[_Const.API_CheckIn];
                        if (null !== __CheckIn && typeof  __CheckIn !== "undefined") {
                            __Content = {};
                            __Content[__CheckIn.uid] = content;
                        }
                    }
                    __Content = JSON.stringify(__Content);
                    //__content = CryptoJS.AES.encrypt(__Content, __key);
                    window.localStorage.setItem(key, __Content);
                }
                _TempSaveContent[key] = content;
            }
            catch (ex) {
                console.log(ex);
            }
        }         /**
         * 删除指定字段值。
         * @method __RemoveContent
         * @param key
         * @return {null}
         * @private
         */
        var __RemoveContent = function (key, IsRemoveLocalStorage) {
            try {
                if (null === key || typeof key === 'undefined') {
                    return;
                }
                if (_TempSaveContent.hasOwnProperty(key)) {
                    delete _TempSaveContent[key];
                    //_TempSaveContent[key] = null;
                }
                if (IsRemoveLocalStorage) {
                    window.localStorage.removeItem(key);
                }
            }
            catch (ex) {
                __PrintLog(ex.toString());
            }
        }         /**
         * 获取内容,
         * @method _GetContent
         * @param key 健名称。其实就是 _TempSaveContent的属性名称。
         * @return {*} 返回内容
         * @private
         */
        var _GetContent = function (key, IsUser) {
            try {
                var __Content = null;
                if (_TempSaveContent.hasOwnProperty(key)) {
                    __Content = _TempSaveContent[key];
                    return __Content;
                }
                if (null === __Content || typeof __Content === "undefined") {
                    var _value = window.localStorage.getItem(key);
                    if (null != _value && '' !== _value && typeof _value !== 'undefined') {
                        /*  // Decrypt  解密
                         var bytes = CryptoJS.AES.decrypt(_value, __key);
                         _value = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
                         _TempSaveContent[key] = _value;
                         */
                        var __JSONValue = JSON.parse(_value);
                        _TempSaveContent[key] = JSON.parse(_value);
                        if (IsUser) {
                            if (_TempSaveContent.hasOwnProperty(_Const.API_CheckIn)) {
                                var __CheckInfo = _TempSaveContent[_Const.API_CheckIn];
                                if (__JSONValue.hasOwnProperty(__CheckInfo.uid)) {
                                    _TempSaveContent[key] = __JSONValue[__CheckInfo.uid];
                                }
                                else {
                                    _TempSaveContent[key] = null;
                                }
                            }
                            else {
                                _TempSaveContent[key] = null;
                            }
                        }
                        __Content = _TempSaveContent[key];
                    }
                }                 return __Content;
            }
            catch (ex) {
                console.log(ex);
                return null;
            }
        }         /**
         * 清空本地存储
         * @method __ClearLocalStorage
         * @private
         */
        var __ClearLocalStorage = function () {
            try {
                __RemoveContent(_Const.AddressInfo, true);
                __RemoveContent(_Const.CacheUserCartGoods, true);
                __RemoveContent(_Const.CacheUserDefaultAddress, true);
                __RemoveContent(_Const.CacheUserHistorySearch, true);
                __RemoveContent(_Const.CacheUserLatelySelectAddress, true);
                __RemoveContent(_Const.AddressManagerSelectAddress, true);
            }
            catch (ex) {
                __PrintLog(ex.toString());
            }
        };         /**
         * 清空当前页面缓存数据
         * @method __ClearCacheData
         * @private
         */
        var __ClearCacheData = function () {
            _TempSaveContent = {};
        };         /**
         * 获取对像属性值
         * @method __GetObjectPropertyValue
         * @param obj 对象
         * @param key 属性
         * @param IsReturnNull 默认(null)是返回 null,否则返回 ''
         * @return {*}
         * @private
         */
        var __GetObjectPropertyValue = function (obj, key, IsReturnNull) {
            if (null === obj || typeof obj === 'undefined' || '' === obj
                || null == key || typeof key === 'undefined' || '' === key) {
                return IsReturnNull === false ? '' : null;
            }
            if (obj.hasOwnProperty(key)) {
                var __value = obj[key];
                return obj[key]
            }
            return IsReturnNull === false ? '' : null;
        }         /**
         * 触发事件
         * @method __Emit
         * @param eventName 事件名称
         * @param {object} param1 参数1
         * @param {object} param2 参数2
         * @param {object} param3 参数3
         * @param {object} param4 参数4
         * @param {object} param5 参数5
         * @param {object} param6 参数6
         * @param {object} param7 参数7
         * @private
         */
        var __Emit = function (eventName, param1, param2, param3, param4, param5, param6, param7) {
            $rootScope.$emit(eventName, param1, param2, param3, param4, param5, param6, param7);
        }         /**
         * 广播事件
         * @method __Broadcast
         * @param eventName 广播事件名称
         * @param {object} param1 参数1
         * @param {object} param2 参数2
         * @param {object} param3 参数3
         * @param {object} param4 参数4
         * @param {object} param5 参数5
         * @param {object} param6 参数6
         * @param {object} param7 参数7
         * @private
         */
        var __Broadcast = function (eventName, param1, param2, param3, param4, param5, param6, param7) {
            $rootScope.$broadcast(eventName, param1, param2, param3, param4, param5, param6, param7);
        }         /**
         * 弹出对话框
         * @method __Alert
         * @param {object} param 参数
         * @example
         *  Utility.Alert({title: '购物袋', template: '请选择要结算的商品'});
         * @constructor
         */
        var __Alert = function (param) {
            $rootScope.$emit(_Const.Alert, param);
        };         /**
         * 打印输出日志
         * @method __PrintLog
         * @param {object} args 内容
         * @private
         */
        var __PrintLog = function (args) {
            try {
                var __callmethod = '';
                try {
                    throw new Error();
                } catch (e) {
                    //console.log(e.stack);
                    __callmethod = e.stack.replace(/Error\n/).split(/\n/)[1].replace(/^\s+|\s+$/, "");
                }                 var _curDate = new Date();
                var _aa = _curDate.toLocaleDateString() + " " + _curDate.toLocaleTimeString() + "." + _curDate.getMilliseconds();
                console.log("--begin->", _aa, ' call method :', __callmethod);
                var __content = "";
                if (angular.isObject(args) || angular.isArray(args)) {
                    __content = JSON.stringify(args);
                }
                else {
                    __content = args;
                }
                console.log(__content)
            }
            catch (ex) {
                console.log('---------输出日志,传入的内容传为JSON出现在异常--------------');
                console.log(ex);
                console.log('---------输出日志,内容为下--------------');
                console.log(args);
            }
        };         /**
         * 判断输入的是否是手机号
         * @method __PhonePattern
         * @param {number} phone 手机号
         * @return {boolean} true 成功;false 失败
         * @example
         *  Utility.PhonePattern('13000100100');
         * @private
         */
        var __PhonePattern = function (phone) {
            var ex = /^(13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9])\d{8}$/;
            return ex.test(phone);
        }         /**
         * 密码验证
         * @method __PasswordPattern
         * @param {string} password 密码
         * @return {boolean} true 成功;false 失败
         * @private
         */
        var __PasswordPattern = function (password) {
            //test("/^[_0-9a-z]{6,16}$/i");
            var ex = /^[_0-9a-zA-Z]{6,25}$/;
            return ex.test(password);
        }         /**
         * 是否含有中文(也包含日文和韩文)
         * @method __IsChineseChar
         * @param {string} str 要判断的内容
         * @return {boolean} true:成功;false:失败.
         * @private
         */
        var __IsChineseChar = function (str) {
            //var reg = !/^[\u4E00-\u9FA5]/g;    //\uF900-\uFA2D
            //return !/^[\u4e00-\u9fa5]+$/gi.test(str);//.test(str);             var regu = "^[\u4e00-\u9fa5]+$";
            var re = new RegExp(regu);
            return re.test(str);
        }         /**
         * 同理,是否含有全角符号的函数
         * @method __IsFullWidthChar
         * @param {string} str 要判断的内容
         * @return {boolean}  true:成功;false:失败.
         * @private
         */
        var __IsFullWidthChar = function (str) {
            var reg = /[\uFF00-\uFFEF]/;
            return reg.test(str);
        }         /**
         * 常量
         * @class _Const
         * @type {{}}
         * @private
         */
        var _Const = {
            FromPageUrl: "XiaotuniFromPageName",//
            IsFirstStartApp: 'XiaotuniIsFirstStartApp', //是否是第一次启动APP
            LoadingHide: 'XiaotuniLoadingHide',      //隐藏加载
            Loading: 'XiaotuniLoading',    //显示加载
            LoadingDefined: "XiaotuniLoadingDefined",
            Alert: 'XiaotuniAlert',        //弹出对话框
            HomePage: 'tab.home.goods',        //跳到首页
            GoBack: 'XiaotuniGoBack',      //返回
            GoToPage: 'XiaotuniGoToPage',  //页面跳转
            GoHome: 'XiaotuniGoHome',      //转到tab的首页上去,
            ToPage: "XiaotuniToPage",      //这个是
            FromWhereEnterSearch: "XiaotuniFromWhereEnterSearch",//从哪个界面里进入搜索界面的。             CacheUserCartGoods: "XiaotuniCacheUserCartGoods", //用于临时保存当前购物袋里goodsId,amount。这主要用于首页,商品搜索页时显示数量用的。
            CacheUserHistorySearch: "XiaotuniCacheUserHistorySearch",//保存当前历史搜索记录
            CacheUserDefaultAddress: "XiaotuniCacheUserDefaultAddress",//用户默认地址
            CacheUserLatelySelectAddress: "XiaotuniCacheUserLatelySelectAddress",    //保存用户最近选中的地址。
            CartListShippingModel: 'XiaotuniCartListShippingModel',   //购物袋配送方式
            GifiCertificate: 'XiaotuniGifiCertificate',                //优惠券
            GoodsListSelect: 'XiaotuniGoodsListSelectIndex',          //当前选中商品标签搜索
            AddressInfo: 'XiaotuniAddressInfo',                         //地址信息
            AddressManagerSelectAddress: 'XiaotuniAddressManagerSelectAddress',//订单界面,当前选中的地址
            AddressManagerAddressList: 'XiaotuniAddressManagerAddressList',    //地址管理,所有地址信息
            PositionInfo: 'XiaotuniSavePositionInfo',                         //位置信息
            OrderDetail: 'XiaotuniOrderDetail',              //订单详情
            OrderStatusParam: "XiaotuniOrderStatusParam",//订单状态参数
            API_Locate: 'XiaotuniApiLocateResult',                            //本地位置定位
            API_CheckIn: 'XiaotuniApiCheckinResult',                          //登录
            API_Coupon: 'XiaotuniApiCoupon',                                   //优惠券
            API_Build: 'XiaotuniApiBuild',                                     //生成订单
            API_Order_Detail: 'XiaotuniApiOrderDetail',                       //订单明细
            API_Order_Total: 'XiaotuniApiOrderTotal',                         //订单统计
            API_Order_Status: 'XiaotuniApiOrderStatus',                       //订单统计
            API_TrolleySettle: 'XiaotuniApiTrolleySettle',                     //购物袋结算
            API_TrollerDelete: 'XiaotuniApiTrollerDelete',                    //删除购物袋
            API_TrollerDeleteError: 'XiaotuniApiTrollerDeleteError',         //删除购物袋
            API_TrollerDeleteComplete: 'XiaotuniApiTrollerDeleteComplete',  //删除购物袋
            API_Goods_Detail: 'XiaotuniApiGoodsDetail',                       //商品明细
            API_Address_Delete: 'XiaotuniApiAddressDelete',
            API_Version: "XiaotuniApiVersion",//版本信息             GetLocation: 'onXiaotuniGetLocation',                             //获取定位
            GetLocationComplete: 'onXiaotuniGetLocationComplete',           //定位完成
            GetLocationCompleteNotFound: "onXiaotuniGetLocationCompleteNotFound", //没有定位到商铺
            GetLocationError: 'onXiaotuniGetLocationError',                  //定位出错
            OnBarcodeScanner: 'onXiaotuniBarcodeScanner',                   //条码扫描
            OnActionSheet: "onXiaotuniActionSheet",     //弹出内容出来。
            OnGetUpdateTabsIconStatus: "onXiaotuniGetUpdateTabsIconStatus",//更新状态图标
            OnGetUpdateTabsIconStatusComplete: "onXiaotuniUpdateTabsIconStatusComplete",//更新状态图标
            OnImageLoadingComplete: "onXiaotuniImageLoadingComplete",//图片加截完成。
            OnUpdateSettle: "onXiaotuniUpdateSettle",
            OnEnterSearch: "onXiaotuniEnterSearch",//点击搜索的时候事件。
            OnOrderConfirm: "onXiaotuniOrderConfirm",//订单确认
            OnOrderCancel: "onXiaotuniOrderCancel",//订单取消
            OnGoodsDetailImage: "onXiaotuniGoodsDetailImage",//商品明细详情里的图片。
            OnOrderTotal: "onXiaotuniOrderTotal",//订单统计总数
            OnTralley: "onXiaotuniTralley",//
            OnVersion: "onXiaotuniVersion",//版本信息
            OnCloseInAppBrowser: "onXiaotuniCloseInAppBrowser",//关闭浏览器窗体
        }       
        /**
         * 对Date的扩展,将 Date 转化为指定格式的String
         * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
         * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
         * @method __FormatDate
         * @param fmt
         * @param date
         * @return {*}
         * @example
         *  Utility.FormatDate("yyyy-MM-dd hh:mm:ss.S",new Date());
         * @constructor
         */
        var __FormatDate = function (fmt, date) {
            var __this = new Date();
            if (null !== date) {
                if (angular.isDate(date)) {
                    __this = date;
                }
                else {
                    try {
                        __this = new Date(date);
                    }
                    catch (ex) {
                        __this = new Date();
                    }
                }
            }
            var o = {
                "M+": __this.getMonth() + 1, //月份
                "d+": __this.getDate(), //日
                "H+": __this.getHours(), //小时
                "m+": __this.getMinutes(), //分
                "s+": __this.getSeconds(), //秒
                "q+": Math.floor((__this.getMonth() + 3) / 3), //季度
                "S": __this.getMilliseconds() //毫秒
            };
            if (/(y+)/.test(fmt)) {
                var a = /(y+)/.test(fmt);
                var fmt1 = fmt.replace(RegExp.$1, (__this.getFullYear() + "").substr(4 - RegExp.$1.length));
                fmt = fmt1;
            }             for (var k in o) {
                if (new RegExp("(" + k + ")").test(fmt)) {
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                }
            }
            return fmt;
        }         /**
         * 判断是第一次启动。位置是否已经定位了。
         * @method __JudgeIsFirstStartAndLocate
         * @private
         */
        var __JudgeIsFirstStartAndLocate = function () {
            var _IsFirstStartApp = _GetContent(_Const.IsFirstStartApp);
            if (null === _IsFirstStartApp) {
                __Emit(_Const.ToPage, "startapp");
                return;
            }
            var __location = _GetContent(_Const.API_Locate);
            if (null === __location) {
                __Emit(_Const.ToPage, "location");
                return;
            }
            return false;
        }         /**
         * 解析URL地址
         * @method __ParseURL
         *@param {string} url 完整的URL地址
         *@return {object} 自定义的对象
         *@example
         *  用法示例:var myURL = parseURL('http://abc.com:8080/dir/index.html?id=255&m=hello#top');
         * myURL.file='index.html'
         * myURL.hash= 'top'
         * myURL.host= 'abc.com'
         * myURL.query= '?id=255&m=hello'
         * myURL.params= Object = { id: 255, m: hello }
         * myURL.path= '/dir/index.html'
         * myURL.segments= Array = ['dir', 'index.html']
         * myURL.port= '8080'
         * yURL.protocol= 'http'
         * myURL.source= 'http://abc.com:8080/dir/index.html?id=255&m=hello#top'
         */
        var __ParseURL = function (url) {
            var a = document.createElement('a');
            a.href = url;
            return {
                source: url,
                protocol: a.protocol.replace(':', ''),
                host: a.hostname,
                port: a.port,
                query: a.search,
                params: (function () {
                    var ret = {},
                        seg = a.search.replace(/^\?/, '').split('&'),
                        len = seg.length, i = 0, s;
                    for (; i < len; i++) {
                        if (!seg[i]) {
                            continue;
                        }
                        s = seg[i].split('=');
                        ret[s[0]] = s[1];
                    }
                    return ret;
                })(),
                file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
                hash: a.hash.replace('#', ''),
                path: a.pathname.replace(/^([^\/])/, '/$1'),
                relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
                segments: a.pathname.replace(/^\//, '').split('/')
            };
        }         var _Browser = {
            versions: function () {
                var u = navigator.userAgent, app = navigator.appVersion;
                return {
                    trident: u.indexOf('Trident') > -1, //IE内核
                    presto: u.indexOf('Presto') > -1, //opera内核
                    webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
                    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
                    mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
                    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
                    android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
                    iPhone: u.indexOf('iPhone') > -1, //是否为iPhone或者QQHD浏览器
                    iPad: u.indexOf('iPad') > -1, //是否iPad
                    webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
                    weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
                    qq: u.match(/\sQQ/i) == " qq" //是否QQ
                };
            }(),
            language: (navigator.browserLanguage || navigator.language).toLowerCase()
        }         var _UtilityService = {
            IsChineseChar: __IsChineseChar,//是否包含汉字
            IsFullWidthChar: __IsFullWidthChar,//是否包含全角符
            Emit: __Emit,            //事件
            Broadcast: __Broadcast,  //广播事件
            Alert: __Alert,          //弹出对话框
            PrintLog: __PrintLog,    //打印输入日志
            ConstItem: _Const,      //常量
            GetContent: _GetContent, //获取内容
            SetContent: _SetContent, //保存内容
            RemoveContent: __RemoveContent,//删除不要的内容
            TokenApi: _TokenApi,    //访问这些接口的时候要加token。
            ClearCacheData: __ClearCacheData,            //清空缓存数据
            ClearLocalStorage: __ClearLocalStorage,      //清空本地存储数据
            GetObjectPropertyValue: __GetObjectPropertyValue, //获取对象值
            PhonePattern: __PhonePattern,      //判断是否是手机号。
            PasswordPattern: __PasswordPattern,  //密码验证
            FormatDate: __FormatDate,   //格式化日期
            JudgeIsFirstStartAndLocate: __JudgeIsFirstStartAndLocate,//判断是第一启动和位置定位
            ParseURL: __ParseURL,
            XiaotuniVersion: __XiaotuniVersion,
        };         return _UtilityService;
    }])

ionic 开发当中,有一些常用的方法。的更多相关文章

  1. odoo开发笔记--模型中常用的方法

    create方法在数据表中插入一条记录(或新建一个对象的resource)格式:def create(self,cr,uid,vals,context={})参数:vals:待新建记录的字段值,是一个 ...

  2. Cordova/Ionic开发的Android APP启用Chrome Inspect调试的方法

    Cordova/Ionic开发的Android APP,需要启用WebView的调试模式,才可以在Chrome浏览器中输入chrome://Inspect,然后使用大家熟悉的开发者工具进行调试.不启用 ...

  3. 介绍web开发中实现会话跟踪的常用技术方法

    由于http是无状态的协议,这种特性严重阻碍了客户端与服务器进行动态交互,例如购物车程序,客户在购物车中添加了商品,服务器如何知道购物车已有的物品呢?为了支持客户端与服务器之间的交互,为了弥补http ...

  4. Ionic开发实战

    转自:http://blog.csdn.net/i348018533/article/details/47258449/ 折磨的两个月!Ionic从零单排,到项目发布!遇到了很多问题但都一一解决了,此 ...

  5. jQuery 常用核心方法

    jQuery 常用核心方法 .each() 遍历一个jQuery对象,为每个匹配元素执行一个函数 $('p').each(function(idx,node){ $(node).text(idx + ...

  6. 关于ionic开发中遇到的坑与总结

    这次是第二次使用ionic开发混合app,今天算是对这个框架做一个总结,基础的我就不再重复了,网上都有教程.我就说说自己的心得和遇见的各种坑, 之后会陆续补充,想到什么说什么吧. 1.关于ionic效 ...

  7. 分享 Ionic 开发 Hybrid App 中遇到的问题以及后期发布 iOS/Android 的方方面面

    此篇文章主要整理了最近在使用 Ionic 开发 Hybrid App 过程中遇到的一些疑难点以及后期发布生成 iOS 和 Android 版本过程中的种种问题. 文章目录 Ionic 简介和项目需求介 ...

  8. Android应用开发SharedPreferences存储数据的使用方法

    Android应用开发SharedPreferences存储数据的使用方法 SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的 ...

  9. jQuery操作Table tr td常用的方法

    虽然现在DIV+CSS进行页的布局大行其道,但是很多地方使用table还是有很多优势,用table展示数据是比较方便的,下面汇总了jQuery操作Table tr td常用的方法,熟记这些操作技巧,下 ...

随机推荐

  1. Regularization and model selection

    Suppose we are trying select among several different models for a learning problem.For instance, we ...

  2. UltraEdit 回车符替换空格

    查找和替换    输入 ^r^n   替换为:(空格)

  3. 使用Jenkins远程部署war包到tomcat container

    Jenkins首先使用maven将源代码进行编译打包,之后需要将war包传送到tomcat服务器上进行部署. 来看一下Jenkins的基本配置,首先需要安装插件"Deploy to cont ...

  4. WINDOWS权限大牛们,请进

    大家好, 我遇到一个问题,我的一台windows7去访问另一个电脑的共享,输入账号密码后,老是说密码不正确.而其他电脑去访问共享,密码账号密码后都OK 我想知道原因是什么?

  5. 关于使用idea的一些小技巧

    1:idea与git同步以后查看修改变化: file --setting--versioncontorller

  6. angular 响应式表单

  7. Permission denied: .gvfs

    $ sudo umount /home/william/.gvfs $ rm -rf ~/.gvfs/ Reference: (Permission denied: .gvfs)[https://an ...

  8. opencv.js小案例

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. loj #2116. 「HNOI2015」开店

    #2116. 「HNOI2015」开店 题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的想法当然非 ...

  10. GitHub 十大 CI 工具

    简评:GitHub 上最受欢迎的 CI 工具. 持续集成(Continuous integration)指的是,频繁地(一天多次)将代码集成到主干. 持续集成工具让产品可以快速迭代,同时还能保持高质量 ...