码迷,mamicode.com
首页 > Web开发 > 详细

AngularJS学习笔记(一)

时间:2015-07-23 19:58:59      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:angularjs

前言

几个月之前了解过一点Angular,主要是通过phonecat应用程序了解一些入门东西,但是当被问及什么是Angular或者你对Angular的理解时,只记得一个MVVM双向数据绑定,显然这是不能令人满意的。现在重新来过吧。
ps:该文档只是见证自己学习Angular的过程。所用版本为1.4.3。另外向大家推荐个windows下各 API离线查找工具Velocity,官网:http://velocity.silverlakesoftware.com/,真的不是一般地好用。

AngularJS简介

AngularJS是一个为动态WEB应用设计的结构框架,创新点在于,利用数据绑定依赖注入,使你不用再写大量的代码。AngularJS的一些出众之处在于:

  • 构建一个CRUD应用可能用到的全部内容包括:数据绑定、基本模板标识符、表单验证、路由、深度链接、组件重用、依赖注入
  • 测试方面包括:单元测试、端到端测试、模拟和自动化则是框架
  • 具有目录布局和测试脚本的种子应用作为起点

而Angular信奉的是,当组建视图同时又要写软件逻辑时,声明式的代码会比命令式的代码好的多。

基本概念

首先举个例子:

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <title>myApp</title>
    <style>
        #test {
            width: 100px;
            height: 100px;
            background-color: red;
            border: 1px solid #ccc;
        }
    </style>
    <script src="js/jquery-1.7.1.js"></script>
    <script src="js/angular.min.js"></script>
</head>
<body ng-controller="myCtrl">
    <div id="test" ng-click="click()">Come ON!</div>
    <p>{{width}} * {{height}}</p>
    <p>Width: <input type="text" ng-model="width"/></p>
    <p>Height: <input type="text" ng-model="height"/></p>
    <script>
        "use strict";
        var myApp = angular.module("myApp", []);
        myApp.controller(‘myCtrl‘, [‘$scope‘, function($scope) {
            var oDiv = angular.element(‘#test‘);
            $scope.width = oDiv.width();
            $scope.height = oDiv.height();

            $scope.click = function() {
                $scope.width = parseInt($scope.width) + 10;
                $scope.height = parseInt($scope.height) + 10;
            };

            $scope.$watch(‘width‘, function(to, from) {
                oDiv.width(to);
            });

            $scope.$watch(‘height‘, function(to, from) {
                oDiv.height(to);
            });
        }]);
    </script>
</body>
</html>

从代码中可以看出,在HTML中引入了一些以ng开头的标记,这些就是angular(一下简称ng)声明式的代码。

  • 使用ng-app声明ng的初始化工作,同时定义了ng应用的作用域,ng的初始化一般绑定在DOMContentLoaded事件中,可以使用angular.bootstap(element,modules,config);手动引导ng应用程序。
  • ng-controller声明控制器,控制器里可以写相应的代码逻辑,修改相应的$scope,进行model到view的数据绑定工作。
  • ng-click绑定click事件,事件在controller中声明,绑定到$scope.
  • ng模板解析:默认使用{{}}
  • ng-model:ng的双向数据绑定声明

依赖注入

ng通过在函数参数中做手脚来完成“依赖声明”的功能。[‘$scope‘, ‘dep1‘, ‘dep2‘, function($scope, dep1, dep2) {}]像AMD声明一样,该函数依赖于$scope,dep1,dep2,然后依次作为参数传递进去。也可以使用另外一种声明方式,利用ng中的函数隐藏属性$inject(不推荐):

var MyController = function($scope, greeter) {}
MyController.$inject = [‘$scope‘, ‘greeter‘];
someModule.controller(‘MyController‘, MyController);

在处理时,ng通过函数对象的toString()方法将该函数定义的代码转为字符串表现形式,然后利用正则表达式过滤出相应的参数,通过参数名获取资源,最后把资源作为参数调用定义的函数。
因此最好采用上述两种方法来显示声明所需的依赖,防止代码压缩过程中压缩相应的函数参数而报错。

作用域

每个ng应用都有一个根作用域($rootScope),有多个子作用域。因为一些指令会声明新的子作用域,这些作用域的结构关系和其绑定到相应的DOM结构是对应的。
属性查找:当ng查找某个模板中的某个数据时,会按照作用域链一直向上查找,直至找到或查至根作用域$rootrScope为至(独立作用域除外)。

<body ng-controller="parentCtrl">
    <div ng-controller="childCtrl">
        <p>hello {{name || "world"}}!</p>
    </div>
</body>

事件冒泡:我们可以在scopes上模拟DOM事件类型的事件冒泡,该事件可以被广播到子作用域或者触发到父作用域。

<div ng-controller="EventController">
    Root scope <tt>MyEvent</tt> count: {{count}}
    <ul>
        <li ng-repeat="i in [1]" ng-controller="EventController">
            <button ng-click="$emit(‘MyEvent‘)">$emit(‘MyEvent‘)</button>
            <button ng-click="$broadcast(‘MyEvent‘)">$broadcast(‘MyEvent‘)</button>
            <br>
            Middle scope <tt>MyEvent</tt> count: {{count}}
            <ul>
                <li ng-repeat="item in [1, 2]" ng-controller="EventController">
                    Leaf scope <tt>MyEvent</tt> count: {{count}}
                </li>
            </ul>
        </li>
    </ul>
</div>
    <script>
        angular.module(‘myApp‘, [])
                .controller(‘EventController‘, [‘$scope‘, function($scope) {
                    $scope.count = 0;
                    $scope.$on(‘MyEvent‘, function() {
                        $scope.count++;
                    });
                }]);
    </script>

另外,我们还可以调用$watch()检测某个属性改变。

模板与数据绑定

ng中的主要特点之一就是双向数据绑定。

数据->模板:

可以直接使用ng的默认模板标记{{name}},则可以直接绑定一个作用域内的变量。当然也可以更改使用自定义的标记//name//

<script>
    myApp.config(function($interpolateProvider) {
        $interpolateProvider.startSymbol(‘//‘);
        $interpolateProvider.endSymbol(‘//‘);
    });
</script>

模板:

ng有自己的一套强大的模板命令:
1.ng-include直接引入模板内容
<div ng-include="‘template.html‘"></div>
ng-include内容必须是字符串,需多加个引号。
template.html主要有两种定义方式:
(1)script标签中定义:type属性值为“text/ng-template”,id为ng-include属性值

<script type="text/ng-template" id="template.html">
   <p>This is the content of the template</p>
</script>

(2)外部html文件中定义:该html文件名为ng-include的属性值

Content of template.html

2.内容控制
(1)ng-cloak
该指令绑定内容不显示,直到ng解析相关指令后才显示解析后的内容,避免相关内容解析前后出现跳转现象。
(2)ng-show/ng-hide
根据表达式的值来改变绑定该命令的DOM的display属性,相关CSS类已经在ng文件中提前定义,且使用了!important提升权重。

点我: <input type="checkbox" ng-model="checked">
<div>
    显示:
    <div ng-show="checked">
        选中时显示。
    </div>
</div>
<div>
    隐藏:
    <div ng-hide="checked">
        选中时隐藏。
    </div>
</div>

(3)ng-if
不同于ng-show改变DOM的display属性来显示隐藏节点,ng-if根据表达式的布尔值判断:false则从文档中移除该DOM节点,true则向文档中添加该DOM节点。
(4)ng-switch
根据值的匹配情况来显示相应的节点:
ng-init直接在模板中进行赋值,与作用域无关。

<div ng-init="a=1">
        <div ng-switch on="a">
            <div ng-switch-when="1">1</div>
            <div ng-switch-when="2">2</div>
            <div ng-switch-default>other</div>
        </div>
    </div>

(5)ng-repeat
遍历对象或数组:

<div ng-init="list = [{name: ‘AAA‘}, {name: ‘BBB‘}, {name: ‘CCC‘}]">
        <ul ng-repeat="member in list">
            <li>{{$index}}</li>
            <li>{{$first}}</li>
            <li>{{$middle}}</li>
            <li>{{$last}}</li>
            <li>{{$odd}}</li>
            <li>{{$even}}</li>
            <li>{{member.name}}</li>
        </ul>
    </div>

额外变量:
$index 当前索引
$first 是否为首元素
$middle 是否为中间元素
$last 是否为尾元素
$odd 当前索引是否为奇数
$even 当前索引是否为偶数
遍历对象使用(key, value) in obj.
另外,默认待遍历的数组中不能有重复值,因为数组元素与相应的DOM元素是一对一的关系。如需要使用:

<ul ng-repeat="n in [10,10,10,10] track by $index">
        {{n}}
    </ul>

(6)其它

  • ng-src src属性
  • ng-href href属性
  • ng-checked 选中状态
  • ng-selected 被选择状态
  • ng-disabled 禁用状态
  • ng-multiple 多选状态
  • ng-readonly 只读状态

3.样式
(1)ng-style
使用对象字面量的形式来赋值:

<input type="button" value="set color" ng-click="myStyle={color:‘red‘}">
<input type="button" value="set background" ng-click="myStyle={‘background-color‘:‘blue‘}">
<input type="button" value="clear" ng-click="myStyle={}">
<br/>
<span ng-style="myStyle">Sample Text</span>
<pre>myStyle={{myStyle}}</pre>

(2)ng-class
直接通过字符串绑定CSS中预定义的类

<!doctype html>
<html lang="en" ng-app>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="js/angular.min.js"></script>
    <style>
        .strike {
            text-decoration: line-through;
        }
        .bold {
            font-weight: bold;
        }
        .red {
            color: red;
        }
    </style>
</head>
<body>
<p ng-class="style">Using String Syntax</p>
<input type="text" ng-model="style"
       placeholder="Type: bold strike red" aria-label="Type: bold strike red">
</body>
</html>

4.事件绑定:
模板中的事件绑定预定义了一些常用的事件绑定指令,可以直接传递相关的处理程序且直观地在DOM中声明。

  • ng-blur
  • ng-change
  • ng-click
  • ng-dblclick
  • ng-focus
  • ng-keydown
  • ng-keypress
  • ng-keyup
  • ng-mousedown
  • ng-mouseenter
  • ng-mouseleave
  • ng-mousemove
  • ng-mouseover
  • ng-mouseup
    使用$event可以给相应的事件处理函数传递事件对象本身
<input ng-keyup="show($event)">
    <p>event keyCode: {{ keyCode }}</p>
    <p>event altKey: {{ altKey }}</p>
    <script>
        angular.module(‘myApp‘,[])
                .controller(‘myCtrl‘, function($scope) {
                    $scope.show = function($event) {
                        $scope.keyCode = $event.keyCode;
                        $scope.altKey = $event.altKey;
                    }
                });
    </script>

5.表单
HTML中form是一个核心控件,是网页与用户进行交流的主要方式之一。而ng对form进行了封装”ng-form”,区别是HTML中的form不能嵌套,而ng-form可以嵌套。而ng-form的目的就是为了统一控制,而不是为了取代form标签。

form中的一些控件可以预先通过ng-controller中的$scope绑定一些状态。

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="js/angular.min.js"></script>
</head>
<script>
    angular.module(‘myApp‘, [])
            .controller(‘FormController‘, [‘$scope‘, function($scope) {
                $scope.userType = ‘guest‘;
            }]);
</script>
<style>
    .my-form {
        -webkit-transition:all linear 0.5s;
        transition:all linear 0.5s;
        background: transparent;
    }
    .my-form.ng-invalid {
        background: red;
    }
</style>
<body>
<form name="myForm" ng-controller="FormController" class="my-form">
    userType: <input name="input" ng-model="userType" required >
    <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
    age: <input name="input_b" ng-model="age" required pattern="\d+">
    <span class="error" ng-show="myForm.input_b.$error.required">Required!</span>
    <span class="error" ng-show="myForm.input_b.$error.pattern">Pattern!</span><br>
    <code>userType = {{userType}}</code><br>
    <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
    <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
    <code>myForm.$valid = {{myForm.$valid}}</code><br>
    <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
</form>
</body>
</html>

ng给form提前定义了一些CSS类:

  • ng-valid 当表单验证全部通过时使用
  • ng-invalid 表单中有未通过验证的控件时使用
  • ng-pristine 当表单为被修改之前使用
  • ng-dirty 当表单被修改之后使用
  • ng-submitted 当表单被提交之后使用

form对象有一些属性:

  • $pristine 表单是否未被动过
  • $dirty 表单是否被动过
  • $valid 表单是否通过验证
  • $invalid 表单是否未通过验证
  • $error 表单的错误对象

其中$error是一个hash对象,引用表单控件中未通过验证的键值对。属性是验证失败信息,值是对应的实例列表。另外,该失败对象是按一定的验证逻辑所取,例如上例中的age输入框,先判定required,在判定pattern。$error对象可能的属性有:email/max/maxlength/min/minlength/number/pattern/required/url/date/time/week/month.
同时,我们也能在form中的具体某个input框中查看相应的错误,格式为formName.inputName.$error.但是,input控件的相关属性是ng-required/ng-pattern等,经测试发现,仅ng-maxlength,ng-max,ng-min与HTML5中相关属性有区别:ng-maxlength=’12’可以输入超过12个字符,但超过相关属性为true,而maxlength则最多只能输入12个字符,超过默认丢弃,相关错误属性永不为true.
(1)checkbox/radio
可以为checkbox分别绑定ng-true-value/ng-false-value的值,为radio绑定value值。而这些值与视图中的相关控件是否选中相对应。

checkbox: <input type="checkbox" name="box" ng-model="check" ng-true-value="‘China‘" ng-false-value="‘Beijing‘" />
    <span>check = {{check}}</span>
    <hr />
    radio: <input type="radio" name="checkRadio" ng-model="checkRadio" value="China" />
    radio: <input type="radio" name="checkRadio" ng-model="checkRadio" value="Beijing" /><br>
    <span>radio = {{checkRadio}}</span>

(2)textarea
包含input框中相应属性,仅多了ng-trim指令。
(3)select
select控件中,有个用于呈现下拉选项的指令ng-options主要用于select模板中绑定的是非字符串。主要用法见下例:

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="js/angular.min.js"></script>
</head>
<body>
    <div ng-controller="myCtrl">
        <h3>参数是数组情况下:</h3>
        <label>Color (可以指定value为null的option)
            <select ng-model="myColor1" ng-options="color.name for color in colors">
                <option value="">——Choose Color——</option>
            </select>
            <span>选择颜色:{{myColor1}}</span>
        </label><br />

        <label>Color(区分显示和值)
            <select ng-model="myColor2" ng-options="color.name as color.shade for color in colors">
            </select>
            <span>选择颜色:{{myColor2}}</span>
        </label><br />

        <label>Color(以shade值分组)
            <select ng-model="myColor3" ng-options="color.name group by color.shade for color in colors">
            </select>
            <span>选择颜色:{{myColor3}}</span>
        </label><br />

        <label>Color (以shade分组,且有附加条件)
            <select ng-model="myColor4" ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
            </select>
            <span>选择颜色:{{myColor4}}</span>
        </label>

        <h3>参数是对象情况下:</h3>
        <label>Color (以shade分组,且有附加条件)
            <select ng-model="ordinalNumber1" ng-options="value.name  for (key, value) in ordinal">
            </select>
            <span>所选列:{{ordinalNumber1}}</span>
        </label><br />

        <label>Color(区分显示和值)
            <select ng-model="ordinalNumber2" ng-options="value.value as value.name for (key, value) in ordinal">
            </select>
            <span>所选列:{{ordinalNumber2}}</span>
        </label><br />

        <label>Color(以shade值分组)
            <select ng-model="ordinalNumber3" ng-options="value.name group by value.group for (key, value) in ordinal">
            </select>
            <span>所选列:{{ordinalNumber3}}</span>
        </label><br />

        <label>Color (以shade分组,且有附加条件)
            <select ng-model="ordinalNumber4" ng-options="value.value group by value.group disable when value.notAnOption for (key, value) in ordinal">
            </select>
            <span>所选列:{{ordinalNumber4}}</span>
        </label>
    </div>

    <script>
        angular.module(‘myApp‘, []).controller(‘myCtrl‘, [‘$scope‘, function($scope) {
            $scope.colors = [
                {name:‘black‘, shade:‘dark‘},
                {name:‘white‘, shade:‘light‘, notAnOption: true},
                {name:‘red‘, shade:‘dark‘},
                {name:‘blue‘, shade:‘dark‘, notAnOption: true},
                {name:‘yellow‘, shade:‘light‘, notAnOption: false}
            ];

            $scope.ordinal = {
                first: {
                    name: "1st",
                    value: "NO.1",
                    group: "1-3"
                },
                second: {
                    name: "2nd",
                    value: "NO.2",
                    group: "1-3",
                    notAnOption: true
                },
                third: {
                    name: "3nd",
                    value: "NO.3",
                    group: "1-3",
                    notAnOption: true
                },
                fourth: {
                    name: "4th",
                    value: "NO.4",
                    group: "4-5"
                },
                fifth: {
                    name: "5th",
                    value: "NO.5",
                    group: "4-5",
                    notAnOption: false
                }
            };
        }]);
    </script>
</body>
</html>

通常情况下,ng-repeat也可用在option元素中,但ng-repeat会为每个遍历的实例创造一个新的作用域,而使用ng-options则具有节省内存,响应更快捷的优点。

模板->数据:

ng中模板到数据的绑定主要是通过ng-model来实现。

<p>hello {{name || "World"}}!</p>
<hr/>
<input type="text" ng-model="name"/>

当我们在input框中输入内容,会发现模板中内容也相应改变。实际上,ng中的双向数据绑定主要是通过ng-model来实现。

过滤

ng默认提供了一些可以直接使用的过滤指令

  • currency 货币过滤指令
  • date 时间过滤指令
  • filter 数组过滤指令
  • json 将js对象转换为json字符串
  • limitTo 截取字符串/数组/数字的一部分
  • lowercase 字符串转小写
  • number 格式化数字
  • orderBy 数组排序
  • uppercase 字符串转大写
  • linky 找出文本输入中的连接然后格式化

模板中使用

我们可以在模板中使用过滤命令,使用方法类似与linux系统中的管道命令{{expression | filter1:arg1:arg2:... | filter2:arg}}即我们可以在一个语句后面使用多个过滤命令,依次用”|”分割开即可,也可以在一个过滤命令后面传递多个参数,依次用“:”分割开即可。

<span>{{1437272917693 | date:"MM/dd/yyyy ‘at‘ h:mma"}}</span><br>
<span>{{ ["AAAAA","AAAA","AAA","AA","A","BBB","BB"] | filter:‘A‘ | limitTo: 2 }}</span><br>

JS中使用

JS中使用过滤命令有两种方式:
(1)<filterName>Filter方式:
在依赖声明中明确声明需要使用filterName的过滤指令,然后就可以在内部向该指令函数传递参数,返回处理后的数据:

angular.module(‘myApp‘, [])
.controller(‘filterCtrl‘, [‘filterFilter‘, ‘dateFilter‘, ‘numberFilter‘, ‘currencyFilter‘, ‘$scope‘,
            function(myFilter, dateFilter, numberFilter, currencyFilter, $scope) {
                $scope.array = contacts;
                $scope.filteredArray = myFilter(this.array, ‘a‘);
                $scope.date = dateFilter(1437272917693, ‘mediumDate‘);
                $scope.number = numberFilter(1437272917693,2);
                $scope.money = currencyFilter(100.1234,‘¥‘,2);
            }]);

(2)$filter方式
依赖声明中引入$filter,然后我们就可以在内部向该函数传入我们想要使用的过滤指令名,返回对应的过滤指令函数。较方式1简单:

angular.module(‘myApp‘, [])
                .controller(‘myCtrl‘, [‘$scope‘, ‘$filter‘, function($scope, $filter) {
                    $scope.filteredText = $filter(‘uppercase‘)(‘China‘);
                    $scope.number = $filter(‘number‘)(123456789.1123);
                }]);

自定义过滤命令

如果感觉ng默认提供的几种过滤指令满足不了需求,我们可以自定义指令。通过你的module中的filter工厂函数就可以自定义一个新的指令。

angular.module(‘myApp‘, [])
            .filter(‘reverse‘, function() {
                return function(input, uppercase) {
                    input = input || ‘‘;
                    var out = "";
                    for (var i = 0; i < input.length; i++) {
                        out = input.charAt(i) + out;
                    }
                    // 可传入参数uppercase
                    if (uppercase) {
                        out = out.toUpperCase();
                    }
                    return out;
                };
            })

向filter工厂函数传入命令名,初始化函数,返回一个过滤函数,过滤函数第一个参数是待过滤的内容,其余参数依次输入。仅当该过滤指令的输入内容改变时,ng才会执行该指令。
另外,自定义的过滤函数应该全部是无状态的,那些有状态的指令无法被ng优化,经常导致表现问题。而如果真的需要维持状态的过滤命令,可指定该过滤函数的$stateful属性,这样每次模板中内容改变该过滤函数都会执行一次。

angular.module(‘myStatefulFilterApp‘, [])
.filter(‘decorate‘, [‘decoration‘, function(decoration) {

  function decorateFilter(input) {
    return decoration.symbol + input + decoration.symbol;
  }
  decorateFilter.$stateful = true;

  return decorateFilter;
}])

另外,自定义的过滤函数只能在模板中使用。

AJAX

与其它框架一样,ng也提供了类似的一套AJAX封装,内部通过XMLHttpRequest对象或JSONP方式。

$HTTP

$HTTP提供基本的操作服务,传入一个config配置对象,设置一些参数,返回一个可以注册成功、失败回调函数的promise对象。$http常用的配置有:

  • method 请求方法
  • url 请求路径
  • params GET请求的参数
  • data 请求报文(POST请求的参数)
  • headers 定义请求报头
  • cache GET请求的缓存
  • timeout 过期时间
  • responseType 响应类型

另外对于一些请求方式,有一些简写:

  • $http.get(url, [config])
  • $http.delete(url, [config])
  • $http.head(url, [config])
  • $http.jsonp(url, [config])
  • $http.post(url, data, [config])
  • $http.put(url, data, [config])
$http({method: $scope.method, url: $scope.url}).
        success(function(data, status) {
          $scope.status = status;
          $scope.data = data;
        }).
        error(function(data, status) {
          $scope.data = data || "Request failed";
          $scope.status = status;
      });

$http属性:

  • pendingRequests 当前请求队列状态,主要用于测试
  • defaults 请求的全局配置

异步回调

ng的异步回调函数服务$q为AJAX的异步回调提供服务。
(1)$q用法

  • $q(resolveFn, errorFn) 注册并返回一个promise对象
  • $q.defer() 返回一个deferred实例
  • $q.reject(reason) 包装一个错误
  • $q.when(value) 返回一个promise对象
  • $q.resolve(value) 与when方法一样,为了与ES6保持一致性
  • $q.all(promises) 将多个promise对象合并成一个promise对象

(2)deferred对象:通过$q.defer()构建

  • resolve(value) 成功回调
  • reject(reason) 失败回调
  • notify(value) 更新promise的执行状态
  • promise属性 返回一个promise对象

(3)promise

  • then(successCallback, errorCallback, notifyCallback) 分别注册成功,失败,通知的回调函数
  • catch(errorCallback) 相当于then(null, errorCallback) 注册失败回调函数
  • finally(callback, notifyCallback)
angular.module(‘myApp‘, []).controller(‘myCtrl‘, [‘$q‘, function($q) {
            function success(message) {
                console.log("OK! " + message);
            }

            function error(message) {
                console.log("error! " + message);
            }

            function notify(message) {
                console.log(‘notify! ‘ + message);
            }
            var defer = $q.defer(),
                    promise = defer.promise;
            promise.then(success, error, notify);

//            defer.resolve(‘resolve‘);
//            defer.reject(‘reject‘);
            defer.notify(‘notify‘);

其它部分,明天继续。。。

版权声明:本文为博主原创文章,未经博主允许不得转载。

AngularJS学习笔记(一)

标签:angularjs

原文地址:http://blog.csdn.net/xqg666666/article/details/46989629

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!