http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
----------------------------------------------------------------------
diff --git 
a/rocketmq-console-ng/src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
 
b/rocketmq-console-ng/src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
new file mode 100644
index 0000000..857a7fa
--- /dev/null
+++ 
b/rocketmq-console-ng/src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.rocketmq.console.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
+import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("unchecked")
+public class JsonUtil {
+
+    private static Logger logger = LoggerFactory.getLogger(JsonUtil.class);
+    private static ObjectMapper objectMapper = new ObjectMapper();
+
+    private JsonUtil() {
+    }
+
+    static {
+        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, 
false);
+        
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
+        
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, 
true);
+        
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        objectMapper.setFilters(new 
SimpleFilterProvider().setFailOnUnknownId(false));
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd 
HH:mm:ss"));
+    }
+
+    public static void writeValue(Writer writer, Object obj) {
+        try {
+            objectMapper.writeValue(writer, obj);
+        }
+        catch (IOException e) {
+            Throwables.propagateIfPossible(e);
+        }
+    }
+
+    public static <T> String obj2String(T src) {
+        if (src == null) {
+            return null;
+        }
+
+        try {
+            return src instanceof String ? (String)src : 
objectMapper.writeValueAsString(src);
+        }
+        catch (Exception e) {
+            logger.error("Parse Object to String error src=" + src, e);
+            return null;
+        }
+    }
+
+    public static <T> byte[] obj2Byte(T src) {
+        if (src == null) {
+            return null;
+        }
+
+        try {
+            return src instanceof byte[] ? (byte[])src : 
objectMapper.writeValueAsBytes(src);
+        }
+        catch (Exception e) {
+            logger.error("Parse Object to byte[] error", e);
+            return null;
+        }
+    }
+
+    public static <T> T string2Obj(String str, Class<T> clazz) {
+        if (Strings.isNullOrEmpty(str) || clazz == null) {
+            return null;
+        }
+        str = escapesSpecialChar(str);
+        try {
+            return clazz.equals(String.class) ? (T)str : 
objectMapper.readValue(str, clazz);
+        }
+        catch (Exception e) {
+            logger.error("Parse String to Object error\nString: {}\nClass<T>: 
{}\nError: {}", str, clazz.getName(), e);
+            return null;
+        }
+    }
+
+    public static <T> T byte2Obj(byte[] bytes, Class<T> clazz) {
+        if (bytes == null || clazz == null) {
+            return null;
+        }
+        try {
+            return clazz.equals(byte[].class) ? (T)bytes : 
objectMapper.readValue(bytes, clazz);
+        }
+        catch (Exception e) {
+            logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: 
{}\nError: {}", bytes, clazz.getName(), e);
+            return null;
+        }
+    }
+
+    public static <T> T string2Obj(String str, TypeReference<T> typeReference) 
{
+        if (Strings.isNullOrEmpty(str) || typeReference == null) {
+            return null;
+        }
+        str = escapesSpecialChar(str);
+        try {
+            return (T)(typeReference.getType().equals(String.class) ? str : 
objectMapper.readValue(str, typeReference));
+        }
+        catch (Exception e) {
+            logger.error("Parse String to Object error\nString: 
{}\nTypeReference<T>: {}\nError: {}", str,
+                typeReference.getType(), e);
+            return null;
+        }
+    }
+
+    public static <T> T byte2Obj(byte[] bytes, TypeReference<T> typeReference) 
{
+        if (bytes == null || typeReference == null) {
+            return null;
+        }
+        try {
+            return (T)(typeReference.getType().equals(byte[].class) ? bytes : 
objectMapper.readValue(bytes,
+                typeReference));
+        }
+        catch (Exception e) {
+            logger.error("Parse byte[] to Object error\nbyte[]: 
{}\nTypeReference<T>: {}\nError: {}", bytes,
+                typeReference.getType(), e);
+            return null;
+        }
+    }
+
+    public static <T> T map2Obj(Map<String, String> map, Class<T> clazz) {
+        String str = obj2String(map);
+        return string2Obj(str, clazz);
+    }
+
+    private static String escapesSpecialChar(String str) {
+        return str.replace("\n", "\\n").replace("\r", "\\r");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/application.properties 
b/rocketmq-console-ng/src/main/resources/application.properties
new file mode 100644
index 0000000..de8eb1b
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+server.contextPath=
+server.port=8080
+#spring.application.index=true
+spring.application.name=rocketmq-console
+spring.http.encoding.charset=UTF-8
+spring.http.encoding.enabled=true
+spring.http.encoding.force=true
+logging.config=classpath:logback.xml
+#if this value is empty,use env value rocketmq.config.namesrvAddr  
NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
+rocketmq.config.namesrvAddr=
+#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be 
false.default true
+rocketmq.config.isVIPChannel=
+#rocketmq-console's data path:dashboard/monitor
+rocketmq.config.dataPath=/tmp/rocketmq-console/data
+#set it false if you don't want use dashboard.default true
+rocketmq.config.enableDashBoardCollect=true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/logback.xml
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/logback.xml 
b/rocketmq-console-ng/src/main/resources/logback.xml
new file mode 100644
index 0000000..c1fc79a
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/logback.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+               <encoder charset="UTF-8">
+                       <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p 
%m%n</pattern>
+               </encoder>
+       </appender>
+
+       <appender name="FILE"
+               class="ch.qos.logback.core.rolling.RollingFileAppender">
+               <file>${user.home}/logs/consolelogs/rocketmq-console.log</file>
+               <append>true</append>
+               <rollingPolicy 
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+                       
<fileNamePattern>${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log
+                       </fileNamePattern>
+                       <timeBasedFileNamingAndTriggeringPolicy
+                               
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                               <maxFileSize>104857600</maxFileSize>
+                       </timeBasedFileNamingAndTriggeringPolicy>
+                       <MaxHistory>10</MaxHistory>
+               </rollingPolicy>
+               <encoder>
+                       <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p 
%m%n</pattern>
+                       <charset 
class="java.nio.charset.Charset">UTF-8</charset>
+               </encoder>
+       </appender>
+
+       <root level="INFO">
+               <appender-ref ref="STDOUT" />
+               <appender-ref ref="FILE" />
+       </root>
+
+</configuration> 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/index.html
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/index.html 
b/rocketmq-console-ng/src/main/resources/static/index.html
new file mode 100644
index 0000000..745b1fa
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/index.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<html lang="en" ng-app="app">
+<head>
+    <meta charset="UTF-8">
+    <title>RocketMq-console-ng</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name='description' content=''>
+    <meta name='keywords' content=''>
+    <!--iOS -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+
+    <!-- preLoading -->
+    <link rel="stylesheet" href="style/preLoading/normalize.css">
+    <link rel="stylesheet" href="style/preLoading/main.css">
+    <script src="vendor/preLoading/modernizr-2.6.2.min.js"></script>
+    <!-- preLoading -->
+
+    <link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css">
+    <link rel="stylesheet" 
href="vendor/bootstrap-material-design/css/bootstrap-material-design.css">
+    <link rel="stylesheet" 
href="vendor/bootstrap-material-design/css/ripples.css">
+    <link rel="stylesheet" 
href="vendor/angular/notification/angular-ui-notification.css">
+    <link rel="stylesheet" href="vendor/ng-dialog/ngDialog.min.css">
+    <link rel="stylesheet" href="vendor/ng-dialog/ngDialog-theme-default.css">
+    <link rel="stylesheet" href="vendor/dropdown/jquery.dropdown.css">
+    <link rel="stylesheet" 
href="vendor/datatimepicker/bootstrap-datetimepicker.min.css">
+    <link rel="stylesheet" 
href="vendor/font-awesome-4.7.0/css/font-awesome.min.css">
+    <link rel="stylesheet" 
href="vendor/font-awesome-4.7.0/fonts/fontawesome-webfont.svg">
+    <link rel="stylesheet" type="text/css" href="vendor/chosen/chosen.css"/>
+    <link rel="stylesheet" type="text/css" 
href="vendor/chosen/chosen-spinner.css"/>
+    <link rel="stylesheet" type="text/css" 
href="vendor/angular-material/angular-material.min.css"/>
+    <link rel="stylesheet" type="text/css" href="vendor/md-tab/docs.css"/>
+    <link rel="stylesheet" href="style/animate.css">
+    <link rel="stylesheet" href="style/theme.css">
+    <link rel="stylesheet" href="style/app.css">
+
+</head>
+<body ng-controller="AppCtrl">
+<!--[if lte IE 9]>
+<script type="text/javascript">
+    location.href = 'view/pages/un_support_browser.html';
+</script>
+<![endif]-->
+
+<div id="loader-wrapper">
+    <div id="loader"></div>
+
+    <div class="loader-section section-left"></div>
+    <div class="loader-section section-right"></div>
+
+</div>
+<div ng-include="'view/layout/_header.html'"></div>
+<section ng-view></section>
+<div ng-include="'view/layout/_footer.html'"></div>
+<script type="text/javascript" 
src="vendor/jquery/jquery1.11.3.min.js"></script>
+<script type="text/javascript" 
src="vendor/bootstrap/js/bootstrap.min.js"></script>
+<script type="text/javascript" 
src="vendor/bootstrap-material-design/js/material.min.js"></script>
+<script type="text/javascript" 
src="vendor/bootstrap-material-design/js/ripples.min.js"></script>
+<script type="text/javascript" src="vendor/angular/angular.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-translate.min.js"></script>
+<script type="text/javascript"
+        
src="vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/i18n/angular-locale_zh-cn.js"></script>
+<script type="text/javascript" src="src/i18n/en.js"></script>
+<script type="text/javascript" src="src/i18n/zh.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-cookies.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-animate.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-route.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-ui-router.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-sanitize.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-aria.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-messages.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/angular-sanitize.min.js"></script>
+<script type="text/javascript" 
src="vendor/angular/notification/angular-ui-notification.js"></script>
+<script type="text/javascript" 
src="vendor/pagination/tm.pagination.js"></script>
+<script type="text/javascript" src="vendor/ng-dialog/ngDialog.min.js"></script>
+<script type="text/javascript" 
src="vendor/json-bigint/json-bigint.js"></script>
+<script type="text/javascript" 
src="vendor/dropdown/jquery.dropdown.js"></script>
+<script type="text/javascript" 
src="vendor/datatimepicker/moment.min.js"></script>
+<script type="text/javascript" 
src="vendor/datatimepicker/bootstrap-datetimepicker.min.js"></script>
+<script type="text/javascript" 
src="vendor/datatimepicker/angular-eonasdan-datetimepicker.min.js"></script>
+<script type="text/javascript" src="vendor/chosen/angular-chosen.js"></script>
+<script type="text/javascript" 
src="vendor/chosen/chosen.jquery.min.js"></script>
+<script type="text/javascript" 
src='vendor/md-tab/svg-assets-cache.js'></script>
+<script type="text/javascript" 
src='vendor/angular-material/angular-material.min.js'></script>
+<script type="text/javascript" src="vendor/echarts/echarts.min.js"></script>
+<script type="text/javascript" src="src/app.js"></script>
+<script type="text/javascript" src="src/controller.js?v=201702250025"></script>
+<script type="text/javascript" src="src/tools/tools.js"></script>
+<script type="text/javascript" src="src/cluster.js?timestamp=4"></script>
+<script type="text/javascript" src="src/topic.js"></script>
+<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
+<script type="text/javascript" src="src/producer.js"></script>
+<script type="text/javascript" src="src/message.js"></script>
+<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
+<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
+<script type="text/javascript" src="vendor/preLoading/main.js"></script>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/app.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/app.js 
b/rocketmq-console-ng/src/main/resources/static/src/app.js
new file mode 100644
index 0000000..b241010
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/app.js
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+var app = angular.module('app', [
+    'ngAnimate',
+    'ngCookies',
+    'ngRoute',
+    'ngDialog',
+    'ngMaterial',
+    'ngSanitize',
+    'material.svgAssetsCache',
+    'ui-notification',
+    'tm.pagination',
+    'ae-datetimepicker',
+    'localytics.directives',
+    'pascalprecht.translate'
+]).run(
+        ['$rootScope','$location','$cookies',
+            function ($rootScope,$location,$cookies) {
+                // var filter = function(url){
+                //     var outFilterArrs = []
+                //     outFilterArrs.push("/login");
+                //     outFilterArrs.push("/reg");
+                //     outFilterArrs.push("/logout");
+                //     outFilterArrs.push("/404");
+                //     var flag = false;
+                //     $.each(outFilterArrs,function(i,value){
+                //         if(url.indexOf(value) > -1){
+                //             flag = true;
+                //             return false;
+                //         }
+                //     });
+                //     return flag;
+                // }
+
+                // if(angular.isDefined($cookies.get("isLogin")) && 
$cookies.get("isLogin") == 'true'){
+                //     chatApi.login();
+                // }
+
+
+                $rootScope.$on('$routeChangeSuccess', function() {
+                    var pathArray = $location.url().split("/");
+                    var index = pathArray.indexOf("");
+                    if(index >= 0){
+                        pathArray.remove(index);
+                    }
+                    $rootScope.path = pathArray[0];
+
+                    //初始化material UI控件
+                    $.material.init();
+                });
+
+                $rootScope.$on('$routeChangeStart',function (evt, 
next,current) {
+                    window.clearInterval($rootScope._thread);
+                })
+            }
+        ]
+    ).animation('.view', function () {
+        return {
+            animate: function (element, className, from, to, done) {
+                //styles
+            }
+        }
+    });
+
+app.provider('getDictName', function () {
+
+    var dictList = [];
+
+    this.init = function () {
+        var url = "src/data/dict.json";//无
法使用common服务类,地址只能写死
+        var params = {};
+        $.get(url, params, function (ret) {
+            dictList = ret;
+        })
+    }
+
+    this.$get = function () {
+        return function (dictType, value) {
+            for (var i = 0; i < dictList.length; i++) {
+                var dict = dictList[i];
+                if (dict.TYPE == dictType && dict.DICT_VALUE == value) {
+                    return dict.DICT_NAME;
+                }
+            }
+        }
+    }
+})
+
+app.config(['$routeProvider', 
'$httpProvider','$cookiesProvider','getDictNameProvider','$sceProvider','$translateProvider','$mdThemingProvider',
+    function ($routeProvider, $httpProvider 
,$cookiesProvider,getDictNameProvider,$sceProvider,$translateProvider,$mdThemingProvider)
 {
+        //关闭html校验,存在安å…
¨é𐿂£ï¼Œä½†ç›®å‰æ²¡é—®é¢˜ï¼Œä½¿ç”¨ng-bind-html需要注意,防止跨站攻击
+        $sceProvider.enabled(false);
+        //前端字典项目初始化
+        getDictNameProvider.init();
+
+        //init angular
+        $mdThemingProvider.theme('default')
+            .primaryPalette('pink')
+            .accentPalette('light-blue');
+
+
+        //设置ajax默认配置
+        $.ajaxSetup({
+            type: "POST",
+            contentType: 'application/json',
+            cache:false,
+            timeout : 5000, //超时时间设置,单位毫秒
+            converters:{
+                "text json": JSONbig.parse
+            }
+        });
+
+        $httpProvider.defaults.cache = false;
+
+        $routeProvider.when('/', {
+            templateUrl: 'view/pages/index.html',
+            controller:'dashboardCtrl'
+        }).when('/cluster', {
+            templateUrl: 'view/pages/cluster.html',
+            controller:'clusterController'
+        }).when('/topic', {
+            templateUrl: 'view/pages/topic.html',
+            controller:'topicController'
+        }).when('/consumer', {
+            templateUrl: 'view/pages/consumer.html',
+            controller:'consumerController'
+        }).when('/producer', {
+            templateUrl: 'view/pages/producer.html',
+            controller:'producerController'
+        }).when('/message', {
+            templateUrl: 'view/pages/message.html',
+            controller:'messageController'
+        }).when('/ops', {
+            templateUrl: 'view/pages/ops.html',
+            controller:'opsController'
+        }).when('/404', {
+            templateUrl: '404'
+        }).otherwise('404');
+
+        $translateProvider.translations('en',en);
+        $translateProvider.translations('zh',zh);
+        $translateProvider.preferredLanguage('en');
+        $translateProvider.useCookieStorage();
+//        $translateProvider.useSanitizeValueStrategy('sanitize');
+
+    }]);
+
+app.filter('range', function() {
+    return function(input, range) {
+        var total = parseInt(range.totalPage) + 1;
+        var count = 5;
+        for (var i = range.start; i<total; i++) {
+            if(count > 0){
+                input.push(i);
+                count -- ;
+            }else {
+                break;
+            }
+        }
+        return input;
+    };
+});
+
+
+app.filter('dict',['getDictName',function(getDictName){
+    return function(value,type){
+        return getDictName(type,value);
+    }
+}])
+
+/**
+ * 数组扩展方法,移除数组中某一元素或某一段元素
+ * @param from 需要移除元素的索引开始值(只传
一个参数表示单独移除该元素)
+ * @param to 需要移除元素的索引结束值
+ * @returns {*}
+ */
+Array.prototype.remove = function(from, to) {
+    var rest = this.slice((to || from) + 1 || this.length);
+    this.length = from < 0 ? this.length + from : from;
+    return this.push.apply(this, rest);
+};
+
+/**
+ * 根据元素值查询数组中元素的索引
+ * @param val
+ * @returns {number}
+ */
+Array.prototype.indexOf = function(val) {
+    for (var i = 0; i < this.length; i++) {
+        if (this[i] == val) return i;
+    }
+    return -1;
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/cluster.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/cluster.js 
b/rocketmq-console-ng/src/main/resources/static/src/cluster.js
new file mode 100644
index 0000000..6f1baee
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/cluster.js
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+app.controller('clusterController', 
['$scope','$location','$http','Notification','remoteApi','tools', function 
($scope,$location,$http,Notification,remoteApi,tools) {
+    $scope.clusterMap = {};//cluster:brokerNameList
+    $scope.brokerMap = {};//brokerName:{id:addr}
+    $scope.brokerDetail = {};//{brokerName,id:detail}
+    $scope.clusterNames = [];
+    $scope.selectedCluster = "";
+    var callback = function (resp) {
+        if (resp.status == 0) {
+            $scope.clusterMap = resp.data.clusterInfo.clusterAddrTable;
+            $scope.brokerMap = resp.data.clusterInfo.brokerAddrTable;
+            $scope.brokerDetail = resp.data.brokerServer;
+            $.each($scope.clusterMap,function(clusterName,clusterBrokersNames){
+                $scope.clusterNames.push(clusterName);
+            });
+            if ($scope.clusterNames.length > 0) {
+                $scope.selectedCluster = $scope.clusterNames[0];
+            }
+            $scope.brokers = 
tools.generateBrokerMap($scope.brokerDetail,$scope.clusterMap,$scope.brokerMap);
+            $scope.switchCluster();
+        }else{
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    }
+
+    remoteApi.queryClusterList(callback);
+
+    $scope.switchCluster = function(){
+        $scope.instances = $scope.brokers[$scope.selectedCluster];
+    }
+
+    $scope.showDetail = function (brokerName,index) {
+        $scope.detail = $scope.brokerDetail[brokerName][index];
+        $scope.brokerName = brokerName;
+        $scope.index = index;
+        $(".brokerModal").modal();
+    }
+
+    $scope.showConfig = function (brokerAddr,brokerName,index) {
+        $scope.brokerAddr = brokerAddr;
+        $scope.brokerName = brokerName;
+        $scope.index = index;
+        $http({
+            method: "GET",
+            url: "cluster/brokerConfig.query",
+            params:{brokerAddr:brokerAddr}
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                $scope.brokerConfig = resp.data;
+                $(".configModal").modal();
+            }else{
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        })
+    }
+}])

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/consumer.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/consumer.js 
b/rocketmq-console-ng/src/main/resources/static/src/consumer.js
new file mode 100644
index 0000000..cd093d4
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/consumer.js
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var module = app;
+
+module.controller('consumerController', ['$scope', 'ngDialog', 
'$http','Notification',function ($scope, ngDialog, $http,Notification) {
+    $scope.paginationConf = {
+        currentPage: 1,
+        totalItems: 0,
+        itemsPerPage: 10,
+        pagesLength: 15,
+        perPageOptions: [10],
+        rememberPerPage: 'perPageItems',
+        onChange: function () {
+            $scope.showConsumerGroupList(this.currentPage,this.totalItems);
+        }
+    };
+    $scope.sortKey = null;
+    $scope.sortOrder=1;
+    $scope.intervalProcessSwitch = false;
+    $scope.intervalProcess = null;
+    $scope.allConsumerGrouopList = [];
+    $scope.consumerGroupShowList = [];
+    $scope.sortByKey = function (key) {
+        $scope.paginationConf.currentPage=1;
+        $scope.sortOrder = -$scope.sortOrder;
+        $scope.sortKey = key;
+        $scope.doSort();
+    };
+
+    $scope.doSort = function (){// todo  how to change this fe's code ? (it's 
dirty)
+        if($scope.sortKey == 'diffTotal'){
+            $scope.allConsumerGrouopList.sort(function(a,b) {return 
(a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? 
-$scope.sortOrder : 0);} );
+        }
+        if($scope.sortKey == 'group'){
+            $scope.allConsumerGrouopList.sort(function(a,b) {return (a.group > 
b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0);} );
+        }
+        if($scope.sortKey == 'count'){
+            $scope.allConsumerGrouopList.sort(function(a,b) {return (a.count > 
b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0);} );
+        }
+        if($scope.sortKey == 'consumeTps'){
+            $scope.allConsumerGrouopList.sort(function(a,b) {return 
(a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > 
a.consumeTps) ? -$scope.sortOrder : 0);} );
+        }
+        $scope.filterList($scope.paginationConf.currentPage)
+    };
+    $scope.refreshConsumerData = function () {
+        $http({
+            method: "GET",
+            url: "consumer/groupList.query"
+        }).success(function (resp) {
+            if(resp.status ==0){
+                $scope.allConsumerGrouopList = resp.data;
+                console.log($scope.allConsumerGrouopList);
+                console.log(JSON.stringify(resp));
+                
$scope.showConsumerGroupList($scope.paginationConf.currentPage,$scope.allConsumerGrouopList.length);
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+    $scope.monitor = function(consumerGroupName){
+        $http({
+            method: "GET",
+            url: "monitor/consumerMonitorConfigByGroupName.query",
+            params:{consumeGroupName:consumerGroupName}
+        }).success(function (resp) {
+            // if(resp.status ==0){
+                ngDialog.open({
+                    template: 'consumerMonitorDialog',
+                    controller: 'consumerMonitorDialogController',
+                    data:{consumerGroupName:consumerGroupName,data:resp.data}
+                });
+            // }else {
+            //     Notification.error({message: resp.errMsg, delay: 2000});
+            // }
+        });
+    };
+
+
+    $scope.$watch('intervalProcessSwitch', function () {
+        if ($scope.intervalProcess != null) {
+            clearInterval($scope.intervalProcess);
+            $scope.intervalProcess = null;
+        }
+        if ($scope.intervalProcessSwitch) {
+            $scope.intervalProcess = setInterval($scope.refreshConsumerData, 
10000);
+        }
+    });
+
+
+    $scope.refreshConsumerData();
+    $scope.filterStr="";
+    $scope.$watch('filterStr', function() {
+        $scope.paginationConf.currentPage=1;
+        $scope.filterList(1)
+    });
+
+    $scope.filterList = function (currentPage) {
+        var lowExceptStr =  $scope.filterStr.toLowerCase();
+        var canShowList = [];
+        $scope.allConsumerGrouopList.forEach(function(element) {
+            console.log(element)
+            if (element.group.toLowerCase().indexOf(lowExceptStr) != -1){
+                canShowList.push(element);
+            }
+        });
+        $scope.paginationConf.totalItems =canShowList.length;
+        var perPage = $scope.paginationConf.itemsPerPage;
+        var from = (currentPage - 1) * perPage;
+        var to = (from + perPage)>canShowList.length?canShowList.length:from + 
perPage;
+        $scope.consumerGroupShowList = canShowList.slice(from, to);
+    };
+
+
+    $scope.showConsumerGroupList = function (currentPage,totalItem) {
+        var perPage = $scope.paginationConf.itemsPerPage;
+        var from = (currentPage - 1) * perPage;
+        var to = (from + perPage)>totalItem?totalItem:from + perPage;
+        $scope.consumerGroupShowList = 
$scope.allConsumerGrouopList.slice(from, to);
+        $scope.paginationConf.totalItems = totalItem ;
+        console.log($scope.consumerGroupShowList)
+        console.log($scope.paginationConf.totalItems)
+        $scope.doSort()
+    };
+    $scope.openAddDialog = function () {
+        $scope.openCreateOrUpdateDialog(null);
+    };
+    $scope.openCreateOrUpdateDialog = function(request){
+        var bIsUpdate = true;
+        if(request == null){
+            request = [{
+                brokerNameList: [],
+                subscriptionGroupConfig: {
+                    groupName: "",
+                    consumeEnable: true,
+                    consumeFromMinEnable: true,
+                    consumeBroadcastEnable: true,
+                    retryQueueNums: 1,
+                    retryMaxTimes: 16,
+                    brokerId: 0,
+                    whichBrokerWhenConsumeSlowly: 1
+                }
+            }];
+            bIsUpdate = false;
+        }
+        console.log(request);
+        $http({
+            method: "GET",
+            url: "cluster/list.query"
+        }).success(function (resp) {
+            if(resp.status ==0){
+                console.log(resp);
+                ngDialog.open({
+                    template: 'consumerModifyDialog',
+                    controller: 'consumerModifyDialogController',
+                    data:{
+                        consumerRequestList:request,
+                        
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
+                        allBrokerNameList:Object.keys(resp.data.brokerServer),
+                        bIsUpdate:bIsUpdate
+                    }
+                });
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+    $scope.detail = function(consumerGroupName){
+        $http({
+            method: "GET",
+            url: "consumer/queryTopicByConsumer.query",
+            params:{consumerGroup:consumerGroupName}
+        }).success(function (resp) {
+            if(resp.status ==0){
+                console.log(resp);
+                ngDialog.open({
+                    template: 'consumerTopicViewDialog',
+                    controller: 'consumerTopicViewDialogController',
+                    data:{consumerGroupName:consumerGroupName,data:resp.data}
+                });
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+
+    $scope.client = function(consumerGroupName){
+        $http({
+            method: "GET",
+            url: "consumer/consumerConnection.query",
+            params:{consumerGroup:consumerGroupName}
+        }).success(function (resp) {
+            if(resp.status ==0){
+                console.log(resp);
+                ngDialog.open({
+                    template: 'clientInfoDialog',
+                    // controller: 'addTopicDialogController',
+                    data:{data:resp.data,consumerGroupName:consumerGroupName}
+                });
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+    $scope.updateConfigDialog = function(consumerGroupName){
+        $http({
+            method: "GET",
+            url: "consumer/examineSubscriptionGroupConfig.query",
+            params:{consumerGroup:consumerGroupName}
+        }).success(function (resp) {
+            if(resp.status ==0){
+                console.log(resp);
+                $scope.openCreateOrUpdateDialog(resp.data);
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+
+
+    };
+    $scope.delete = function(consumerGroupName){
+        $http({
+            method: "GET",
+            url: "consumer/fetchBrokerNameList.query",
+            params:{
+                consumerGroup:consumerGroupName
+            }
+        }).success(function (resp) {
+            if(resp.status ==0){
+                console.log(resp);
+
+                ngDialog.open({
+                    template: 'deleteConsumerDialog',
+                    controller: 'deleteConsumerDialogController',
+                    data:{
+                        // 
allClusterList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
+                        allBrokerNameList:resp.data,
+                        consumerGroupName:consumerGroupName
+                    }
+                });
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    }
+
+}])
+module.controller('consumerMonitorDialogController', function ($scope, 
ngDialog, $http,Notification) {
+        $scope.createOrUpdateConsumerMonitor = function () {
+            $http({
+                method: "POST",
+                url: "monitor/createOrUpdateConsumerMonitor.do",
+                params:{consumeGroupName:$scope.ngDialogData.consumerGroupName,
+                    minCount:$scope.ngDialogData.data.minCount,
+                    maxDiffTotal:$scope.ngDialogData.data.maxDiffTotal}
+            }).success(function (resp) {
+                if(resp.status ==0){
+                    Notification.info({message: "delete success!", delay: 
2000});
+                }else {
+                    Notification.error({message: resp.errMsg, delay: 2000});
+                }
+            });
+        }
+    }
+);
+
+
+module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', 
'$http','Notification',function ($scope, ngDialog, $http,Notification) {
+        $scope.selectedClusterList = [];
+        $scope.selectedBrokerNameList = [];
+        $scope.delete = function () {
+            console.log($scope.selectedClusterList);
+            console.log($scope.selectedBrokerNameList);
+            console.log($scope.ngDialogData.consumerGroupName);
+            $http({
+                method: "POST",
+                url: "consumer/deleteSubGroup.do",
+                data:{groupName:$scope.ngDialogData.consumerGroupName,
+                    brokerNameList:$scope.selectedBrokerNameList}
+            }).success(function (resp) {
+                if(resp.status ==0){
+                    Notification.info({message: "delete success!", delay: 
2000});
+                }else {
+                    Notification.error({message: resp.errMsg, delay: 2000});
+                }
+            });
+        }
+    }]
+);
+
+module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', 
'$http','Notification',function ($scope, ngDialog, $http,Notification) {
+        $scope.postConsumerRequest = function (consumerRequest) {
+            var request = JSON.parse(JSON.stringify(consumerRequest));
+            console.log(request);
+            $http({
+                method: "POST",
+                url: "consumer/createOrUpdate.do",
+                data:request
+            }).success(function (resp) {
+                if(resp.status ==0){
+                    Notification.info({message: "update success!", delay: 
2000});
+                }else {
+                    Notification.error({message: resp.errMsg, delay: 2000});
+                }
+            });
+        }
+    }]
+);
+
+module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', 
'$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
+        $scope.consumerRunningInfo = function (consumerGroup, clientId, 
jstack) {
+            $http({
+                method: "GET",
+                url: "consumer/consumerRunningInfo.query",
+                params: {
+                    consumerGroup: consumerGroup,
+                    clientId: clientId,
+                    jstack: jstack
+                }
+            }).success(function (resp) {
+                if (resp.status == 0) {
+                    ngDialog.open({
+                        template: 'consumerClientDialog',
+                        data:{consumerClientInfo:resp.data,
+                        clientId:clientId}
+                    });
+                } else {
+                    Notification.error({message: resp.errMsg, delay: 2000});
+                }
+            });
+        };
+    }]
+);
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/controller.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/controller.js 
b/rocketmq-console-ng/src/main/resources/static/src/controller.js
new file mode 100644
index 0000000..fbf041d
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/controller.js
@@ -0,0 +1,557 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+app.controller('AppCtrl', 
['$scope','$rootScope','$cookies','$location','$translate', function 
($scope,$rootScope,$cookies,$location,$translate) {
+    $scope.changeTranslate = function(langKey){
+        $translate.use(langKey);
+    }
+}]);
+
+app.controller('dashboardCtrl', 
['$scope','$rootScope','$translate','$filter','Notification','remoteApi','tools',
 function ($scope,$rootScope,$translate,$filter,Notification,remoteApi,tools) {
+
+    $scope.barChart = echarts.init(document.getElementById('main'));
+    $scope.lineChart = echarts.init(document.getElementById('line'));
+    $scope.topicBarChart = echarts.init(document.getElementById('topicBar'));
+    $scope.topicLineChart = echarts.init(document.getElementById('topicLine'));
+    $scope.timepickerOptions ={format: 'YYYY-MM-DD', showClear: true};
+    $scope.topicNames = [];
+
+    $translate('BROKER').then(function (broker) {
+        $scope.BROKER_TITLE = broker;
+        initBrokerBarChart();
+        initBrokerLineChart();
+    }, function (translationId) {
+        $scope.BROKER_TITLE = translationId;
+    });
+
+    $translate('TOPIC').then(function (topic) {
+        $scope.TOPIC_TITLE = topic;
+        initTopicBarChart();
+        initTopicLineChart();
+    }, function (translationId) {
+        $scope.TOPIC_TITLE = translationId;
+    });
+
+    var initBrokerBarChart = function(){
+        $scope.barChart.setOption({
+            title: {
+                text:$scope.BROKER_TITLE + ' TOP 10'
+            },
+            tooltip: {},
+            legend: {
+                data:['TotalMsg']
+            },
+            axisPointer : {
+                type : 'shadow'
+            },
+            xAxis: {
+                data: [],
+                axisLabel: {
+                    inside: false,
+                    textStyle: {
+                        color: '#000000'
+                    },
+                    rotate: 0,
+                    interval:0
+                },
+                axisTick: {
+                    show: true
+                },
+                axisLine: {
+                    show: true
+                },
+                z: 10
+            },
+            yAxis: {
+                type: 'value',
+                boundaryGap: [0, '100%'],
+                axisLabel: {
+                    formatter: function(value){
+                        return value.toFixed(2);
+                    }
+                },
+                splitLine: {
+                    show: true
+                }
+            },
+            series: [{
+                name: 'TotalMsg',
+                type: 'bar',
+                data: []
+            }]
+        })
+    }
+
+    var initBrokerLineChart = function(){
+        $scope.lineChart.setOption({
+            title: {
+                text: $scope.BROKER_TITLE + ' 5min trend'
+            },
+            toolbox: {
+                feature: {
+                    dataZoom: {
+                        yAxisIndex: 'none'
+                    },
+                    restore: {},
+                    saveAsImage: {}
+                }
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    animation: false
+                }
+            },
+            yAxis: {
+                type: 'value',
+                boundaryGap: [0, '80%'],
+                axisLabel: {
+                    formatter: function(value){
+                        return value.toFixed(2);
+                    }
+                },
+                splitLine: {
+                    show: true
+                }
+            },
+            dataZoom: [{
+                type: 'inside',
+                start: 90,
+                end: 100
+            }, {
+                start: 0,
+                end: 10,
+                handleIcon: 
'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z
 M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+                handleSize: '80%',
+                handleStyle: {
+                    color: '#fff',
+                    shadowBlur: 3,
+                    shadowColor: 'rgba(0, 0, 0, 0.6)',
+                    shadowOffsetX: 2,
+                    shadowOffsetY: 2
+                }
+            }],
+            legend: {
+                data: [],
+                top:30
+            },
+            xAxis: {
+                type: 'time',
+                boundaryGap: false,
+                data: []
+            },
+            series: []
+        })
+
+    }
+
+    var initTopicBarChart = function(){
+        $scope.topicBarChart.setOption({
+            title: {
+                text:$scope.TOPIC_TITLE + ' TOP 10'
+            },
+            tooltip: {},
+            legend: {
+                data:['TotalMsg']
+            },
+            axisPointer : {
+                type : 'shadow'
+            },
+            xAxis: {
+                data: [],
+                axisLabel: {
+                    inside: false,
+                    textStyle: {
+                        color: '#000000'
+                    },
+                    rotate: 0,
+                    interval:0
+                },
+                axisTick: {
+                    show: true
+                },
+                axisLine: {
+                    show: true
+                },
+                z: 10
+            },
+            yAxis: {
+                type: 'value',
+                boundaryGap: [0, '100%'],
+                axisLabel: {
+                    formatter: function(value){
+                        return value.toFixed(2);
+                    }
+                },
+                splitLine: {
+                    show: true
+                }
+            },
+            series: [{
+                name: 'TotalMsg',
+                type: 'bar',
+                data: []
+            }]
+        })
+    }
+
+    var initTopicLineChart = function(){
+        var _option = {
+            baseOption:{
+                title: {
+                    text: $scope.TOPIC_TITLE + ' 5min trend'
+                },
+                toolbox: {
+                    feature: {
+                        dataZoom: {
+                            yAxisIndex: 'none'
+                        },
+                        restore: {},
+                        saveAsImage: {}
+                    }
+                },
+                grid:{
+                    top:100
+                },
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        animation: false
+                    }
+                },
+                yAxis: {
+                    type: 'value',
+                    boundaryGap: [0, '80%'],
+                    axisLabel: {
+                        formatter: function(value){
+                            return value.toFixed(2);
+                        }
+                    },
+                    splitLine: {
+                        show: true
+                    }
+                },
+                dataZoom: [{
+                    type: 'inside',
+                    start: 90,
+                    end: 100
+                }, {
+                    start: 0,
+                    end: 10,
+                    handleIcon: 
'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z
 M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+                    handleSize: '80%',
+                    handleStyle: {
+                        color: '#fff',
+                        shadowBlur: 3,
+                        shadowColor: 'rgba(0, 0, 0, 0.6)',
+                        shadowOffsetX: 2,
+                        shadowOffsetY: 2
+                    }
+                }],
+                legend:{
+                    data:[],
+                    top:30
+                },
+                xAxis: {
+                    type: 'time',
+                    boundaryGap: false,
+                    data: []
+                },
+                series: []
+            }
+        }
+        $scope.topicLineChart.setOption(_option)
+
+    }
+
+    var getBrokerBarChartOp = function(xAxisData,data){
+        // 指定图表的配置项和数据
+        var option = {
+            xAxis: {
+                data: xAxisData,
+                axisLabel: {
+                    inside: false,
+                    textStyle: {
+                        color: '#000000'
+                    },
+                    rotate: 0,
+                    interval:0
+                },
+                axisTick: {
+                    show: true
+                },
+                axisLine: {
+                    show: true
+                },
+                z: 10
+            },
+            series: [{
+                name: 'TotalMsg',
+                type: 'bar',
+                data: data
+            }]
+        };
+
+        $scope.barChart.setOption(option);
+    }
+
+    var callback = function (resp) {
+        $scope.barChart.hideLoading();
+        if (resp.status == 0) {
+            var clusterMap = resp.data.clusterInfo.clusterAddrTable;
+            var brokerMap = resp.data.clusterInfo.brokerAddrTable;
+            var brokerDetail = resp.data.brokerServer;
+            var clusterMap = 
tools.generateBrokerMap(brokerDetail,clusterMap,brokerMap);
+            $scope.brokerArray = [];
+            $.each(clusterMap,function(clusterName,brokers){
+                $.each(brokers,function(i,broker){
+                    $scope.brokerArray.push(broker)
+                })
+            })
+
+            //sort the brokerArray
+            $scope.brokerArray.sort(function(firstBroker,lastBroker){
+                var firstTotalMsg = 
parseFloat(firstBroker.msgGetTotalTodayNow);
+                var lastTotalMsg = parseFloat(lastBroker.msgGetTotalTodayNow);
+                return lastTotalMsg-firstTotalMsg;
+            });
+
+            var xAxisData = [],
+                data = [];
+
+            $.each($scope.brokerArray,function(i,broker){
+                if(i > 9){
+                    return false;
+                }
+                xAxisData.push(broker.brokerName + ":" + broker.index);
+                data.push(broker.msgGetTotalTodayNow);
+            })
+            getBrokerBarChartOp(xAxisData,data);
+        }else{
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    }
+
+    $scope.barChart.showLoading();
+    remoteApi.queryClusterList(callback);
+
+    $scope.topicBarChart.showLoading();
+    remoteApi.queryTopicCurrentData(function(resp){
+        $scope.topicBarChart.hideLoading();
+        if (resp.status == 0) {
+            var topicList = resp.data;
+            topicList.sort(function(first,last){
+                var firstTotalMsg = parseFloat(first.split(",")[1]);
+                var lastTotalMsg = parseFloat(last.split(",")[1]);
+                return lastTotalMsg-firstTotalMsg;
+            })
+
+            var xAxisData = [];
+            var data = [];
+            $.each(topicList,function (i,currentData) {
+                var currentArray = currentData.split(",");
+                $scope.topicNames.push(currentArray[0]);
+                if(!angular.isDefined($scope.selectedTopic)){
+                    $scope.selectedTopic = currentArray[0];
+                }
+            })
+            $.each(topicList,function (i, currentData) {
+                if(i > 9){
+                    return false;
+                }
+                var currentArray = currentData.split(",");
+                xAxisData.push(currentArray[0]);
+                data.push(currentArray[1]);
+            })
+            // 指定图表的配置项和数据
+            var option = {
+                xAxis: {
+                    data: xAxisData,
+                    axisLabel: {
+                        inside: false,
+                        textStyle: {
+                            color: '#000000'
+                        },
+                        rotate: 60,
+                        interval:0
+                    },
+                    axisTick: {
+                        show: true
+                    },
+                    axisLine: {
+                        show: true
+                    },
+                    z: 10
+                },
+                series: [{
+                    name: 'TotalMsg',
+                    type: 'bar',
+                    data: data
+                }]
+            };
+            $scope.topicBarChart.setOption(option);
+            queryLineData();
+        }else{
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    })
+
+
+    var getBrokerLineChart = function(legend,data){
+        var series = [];
+        var xAxisData = [];
+        var flag = true;
+        var i = 0;
+        $.each(data,function(key,value){
+            // if(i > 9 ){
+            //     return false;
+            // }
+            var _tps = [];
+            $.each(value,function(i,tpsValue){
+                var tpsArray = tpsValue.split(",");
+                if(flag){
+                    xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
+                }
+                _tps.push(tpsArray[1]);
+            })
+            flag = false;
+            var _series = {
+                name:key,
+                type:'line',
+                smooth:true,
+                symbol: 'none',
+                sampling: 'average',
+                data: _tps
+            }
+            series.push(_series);
+            i++
+        })
+
+        var option = {
+            legend: {
+                data: legend
+            },
+            color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", 
'#EE7942'],
+            xAxis: {
+                type: 'category',
+                boundaryGap: false,
+                data: xAxisData
+            },
+            series: series
+        };
+        return option;
+    }
+
+    var getTopicLineChart = function(legend,data){
+        var series = [];
+        var xAxisData = [];
+        var flag = true;
+        var i = 0;
+        $.each(data,function(key,value){
+            var _tps = [];
+            $.each(value,function(i,tpsValue){
+                var tpsArray = tpsValue.split(",");
+                if(flag){
+                    xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
+                }
+                _tps.push(tpsArray[3]);
+            })
+            flag = false;
+            var _series = {
+                name:key,
+                type:'line',
+                smooth:true,
+                symbol: 'none',
+                sampling: 'average',
+                data: _tps
+            }
+            series.push(_series);
+            i++
+        })
+
+        var option = {
+            baseOption:{
+                legend: {
+                    data: legend
+                },
+                // color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", 
"#000000", '#EE7942'],
+                xAxis: {
+                    type: 'category',
+                    boundaryGap: false,
+                    data: xAxisData
+                },
+                series: series
+            },
+            media:[
+                {
+                    query:{},
+                    option:{
+
+                    }
+                }
+            ]
+
+        };
+        return option;
+    }
+
+
+    var queryLineData = function () {
+        var _date;
+        if($scope.date != null){
+            _date = $filter('date')($scope.date.valueOf(), "yyyy-MM-dd");
+        }else{
+            _date = $filter('date')(new Date(), "yyyy-MM-dd");
+        }
+        // $scope.lineChart.showLoading();
+        remoteApi.queryBrokerHisData(_date,function(resp){
+            // $scope.lineChart.hideLoading();
+            if (resp.status == 0) {
+                var _data = {}
+                var _xAxisData = [];
+                $.each(resp.data,function(address,values){
+                    _data[address] = values;
+                    _xAxisData.push(address);
+                })
+                
$scope.lineChart.setOption(getBrokerLineChart(_xAxisData,_data));
+            }else{
+                Notification.error({message: "" + resp.errMsg, delay: 2000});
+            }
+        })
+
+        $scope.topicLineChart.showLoading();
+        remoteApi.queryTopicHisData(_date,$scope.selectedTopic,function (resp) 
{
+            $scope.topicLineChart.hideLoading();
+            if (resp.status == 0) {
+                var _data = {};
+                _data[$scope.selectedTopic] = resp.data;
+                var _xAxisData = $scope.selectedTopic;
+                
$scope.topicLineChart.setOption(getTopicLineChart(_xAxisData,_data));
+            }else{
+                Notification.error({message: "" + resp.errMsg, delay: 2000});
+            }
+
+        })
+
+    }
+
+    //router after will clear this thread
+    $rootScope._thread = setInterval( queryLineData, 
tools.dashboardRefreshTime);
+
+
+}]);
+
+

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/data/dict.json
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/data/dict.json 
b/rocketmq-console-ng/src/main/resources/static/src/data/dict.json
new file mode 100644
index 0000000..defdab3
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/data/dict.json
@@ -0,0 +1,4 @@
+[
+  {"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"},
+  {"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"}
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/i18n/en.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/i18n/en.js 
b/rocketmq-console-ng/src/main/resources/static/src/i18n/en.js
new file mode 100644
index 0000000..d0074c2
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/i18n/en.js
@@ -0,0 +1,80 @@
+var en = {
+    "TITLE": "RocketMq-Console-Ng",
+    "CLOSE": "Close",
+    "NO": "NO.",
+    "ADDRESS": "Address",
+    "VERSION": "Version",
+    "PRO_MSG_TPS": "Produce Massage TPS",
+    "CUS_MSG_TPS": "Consumer Massage TPS",
+    "YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
+    "YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
+    "TODAY_PRO_COUNT": "Today Produce Count",
+    "TODAY_CUS_COUNT": "Today Ponsume Count",
+    "INSTANCE": "Instance",
+    "SPLIT": "Broker",
+    "CLUSTER": "Cluster",
+    "CLUSTER_DETAIL": "Cluster Detail",
+    "TOPIC": "Topic",
+    "SUBSCRIPTION_GROUP":"SubscriptionGroup",
+    "PRODUCER_GROUP":"ProducerGroup",
+    "CONSUMER":"Consumer",
+    "PRODUCER":"Producer",
+    "MESSAGE":"Message",
+    "COMMIT": "Commit",
+    "OPERATION": "Operation",
+    "ADD": "Add",
+    "UPDATE": "Update",
+    "STATUS": "Status",
+    "ROUTER": "Router",
+    "MANAGE": "Manage",
+    "CONFIG": "Config",
+    "SEND_MSG": "Send Massage",
+    "RESET_CUS_OFFSET": "Reset Consumer Offset",
+    "DELETE": "Delete",
+    "CHANGE_LANG": "ChangeLanguage",
+    "BROKER": "Broker",
+    "NORMAL": "NORMAL",
+    "RETRY": "RETRY",
+    "DLQ": "DLQ",
+    "QUANTITY":"Quantity",
+    "TYPE":"Type",
+    "MODE":"Mode",
+    "DELAY":"Delay",
+    "DASHBOARD":"Dashboard",
+    "CONSUME_DETAIL":"CONSUME DETAIL",
+    "CLIENT":"CLIENT",
+    "LAST_CONSUME_TIME":"LastConsumeTime",
+    "TIME":"Time",
+    "RESET":"RESET",
+    "DATE":"Date",
+    "NO_DATA":"NO DATA",
+    "SEARCH":"Search",
+    "BEGIN":"Begin",
+    "END":"End",
+    "TOPIC_CHANGE":"Topic Change",
+    "SEND":"Send",
+    "SUBSCRIPTION_CHANGE":"Subscription Change",
+       "QUEUE":"Queue",
+    "MIN_OFFSET":"minOffset",
+    "MAX_OFFSET":"maxOffset",
+    "LAST_UPDATE_TIME_STAMP":"lastUpdateTimeStamp",
+    "QUEUE_DATAS":"queueDatas",
+    "READ_QUEUE_NUMS":"readQueueNums",
+    "WRITE_QUEUE_NUMS":"writeQueueNums",
+    "PERM":"perm",
+    "TAG":"Tag",
+    "KEY":"Key",
+    "MESSAGE_BODY":"Message Body",
+    "TOPIC_NAME":"topicName",
+    "ORDER":"order",
+    "CONSUMER_CLIENT":"consumerClient",
+    "BROKER_OFFSET":"brokerOffset",
+    "CONSUMER_OFFSET":"consumerOffset",
+    "DIFF_TOTAL":"diffTotal",
+    "LAST_TIME_STAMP":"lastTimeStamp",
+    "RESET_OFFSET":"resetOffset",
+    "CLUSTER_NAME":"clusterName",
+    "OPS":"OPS",
+    "AUTO_REFRESH":"AUTO_REFRESH",
+    "REFRESH":"REFRESH"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/i18n/zh.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/i18n/zh.js 
b/rocketmq-console-ng/src/main/resources/static/src/i18n/zh.js
new file mode 100644
index 0000000..6cf39f0
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/i18n/zh.js
@@ -0,0 +1,80 @@
+var zh = {
+    "TITLE": "RocketMq控制台",
+    "CLOSE": "关闭",
+    "NO": "编号",
+    "ADDRESS": "地址",
+    "VERSION": "版本",
+    "PRO_MSG_TPS": "生产消息TPS",
+    "CUS_MSG_TPS": "消费消息TPS",
+    "YESTERDAY_PRO_COUNT": "昨日生产总数",
+    "YESTERDAY_CUS_COUNT": "昨日消费总数",
+    "TODAY_PRO_COUNT": "今天生产总数",
+    "TODAY_CUS_COUNT": "今天消费总数",
+    "INSTANCE": "实例",
+    "SPLIT": "分片",
+    "CLUSTER": "集群",
+    "CLUSTER_DETAIL": "集群详情",
+    "COMMIT": "提交",
+    "TOPIC": "主题",
+    "SUBSCRIPTION_GROUP":"订阅组",
+    "PRODUCER_GROUP":"生产组",
+    "CONSUMER":"消费者",
+    "PRODUCER":"生产者",
+    "MESSAGE":"消息",
+    "OPERATION": "操作",
+    "ADD": "新增",
+    "UPDATE": "更新",
+    "STATUS": "状态",
+    "ROUTER": "路由",
+    "MANAGE": "管理",
+    "CONFIG": "配置",
+    "SEND_MSG": "发送消息",
+    "RESET_CUS_OFFSET": "重置消费位点",
+    "DELETE": "删除",
+    "CHANGE_LANG": "更换语言",
+    "BROKER": "Broker",
+    "NORMAL": "普通",
+    "RETRY": "重试",
+    "DLQ": "死信",
+    "QUANTITY":"数量",
+    "TYPE":"类型",
+    "MODE":"模式",
+    "DELAY":"延迟",
+    "DASHBOARD":"驾驶舱",
+    "CONSUME_DETAIL":"消费详情",
+    "CLIENT":"终端",
+    "LAST_CONSUME_TIME":"最后消费时间",
+    "TIME":"时间点",
+    "RESET":"重置",
+    "DATE":"日期",
+    "NO_DATA":"暂无数据",
+    "SEARCH":"搜索",
+    "BEGIN":"开始",
+    "END":"结束",
+    "TOPIC_CHANGE":"修改主题",
+    "SEND":"发送",
+    "SUBSCRIPTION_CHANGE":"修改订阅",
+    "QUEUE":"队列",
+       "MIN_OFFSET":"最小位点",
+       "MAX_OFFSET":"最大位点",
+       "LAST_UPDATE_TIME_STAMP":"上次更新时间",
+    "QUEUE_DATAS":"队列信息",
+    "READ_QUEUE_NUMS":"读队列数量",
+    "WRITE_QUEUE_NUMS":"写队列数量",
+    "PERM":"perm",
+    "TAG":"标签",
+    "KEY":"值",
+    "MESSAGE_BODY":"消息主体",
+    "TOPIC_NAME":"主题名",
+    "ORDER":"顺序",
+    "CONSUMER_CLIENT":"消费者终端",
+    "BROKER_OFFSET":"代理者位点",
+    "CONSUMER_OFFSET":"消费者位点",
+    "DIFF_TOTAL":"差值",
+    "LAST_TIME_STAMP":"上次时间",
+    "RESET_OFFSET":"重置位点",
+    "CLUSTER_NAME":"集群名",
+    "OPS":"运维",
+    "AUTO_REFRESH":"自动刷新",
+    "REFRESH":"刷新"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/message.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/message.js 
b/rocketmq-console-ng/src/main/resources/static/src/message.js
new file mode 100644
index 0000000..fca286c
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/message.js
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var module = app;
+
+module.controller('messageController', ['$scope', 'ngDialog', 
'$http','Notification',function ($scope, ngDialog, $http,Notification) {
+    $scope.allTopicList = [];
+    $scope.selectedTopic =[];
+    $scope.key ="";
+    $scope.messageId ="";
+    $scope.queryMessageByTopicResult=[];
+    $scope.queryMessageByTopicAndKeyResult=[];
+    $scope.queryMessageByMessageIdResult={};
+    $http({
+        method: "GET",
+        url: "topic/list.query"
+    }).success(function (resp) {
+        if(resp.status ==0){
+            $scope.allTopicList = resp.data.topicList.sort();
+            console.log($scope.allTopicList);
+        }else {
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    });
+    $scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD 
HH:mm');
+    $scope.timepickerEnd = moment().add(1,'hour').format('YYYY-MM-DD HH:mm');
+    $scope.timepickerOptions ={format: 'YYYY-MM-DD HH:mm', showClear: true};
+
+    $scope.paginationConf = {
+        currentPage: 1,
+        totalItems: 0,
+        itemsPerPage: 20,
+        pagesLength: 15,
+        perPageOptions: [10],
+        rememberPerPage: 'perPageItems',
+        onChange: function () {
+            $scope.changeShowMessageList(this.currentPage,this.totalItems);
+        }
+    };
+
+
+    $scope.queryMessageByTopic = function () {
+        console.log($scope.selectedTopic);
+        console.log($scope.timepickerBegin)
+        console.log($scope.timepickerEnd)
+        $http({
+            method: "GET",
+            url: "message/queryMessageByTopic.query",
+            params: {
+                topic: $scope.selectedTopic,
+                begin: $scope.timepickerBegin.valueOf(),
+                end: $scope.timepickerEnd.valueOf()
+
+            }
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                console.log(resp);
+                $scope.queryMessageByTopicResult = resp.data;
+                
$scope.changeShowMessageList(1,$scope.queryMessageByTopicResult.length);
+                // todo
+                // console.log($scope.queryMessageByTopicResult);
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+
+    $scope.queryMessageByTopicAndKey = function () {
+        console.log($scope.selectedTopic);
+        console.log($scope.key);
+        $http({
+            method: "GET",
+            url: "message/queryMessageByTopicAndKey.query",
+            params: {
+                topic: $scope.selectedTopic,
+                key:$scope.key
+            }
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                console.log(resp);
+                $scope.queryMessageByTopicAndKeyResult = resp.data;
+                console.log($scope.queryMessageByTopicAndKeyResult);
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+
+    $scope.queryMessageByBrokerAndOffset = function 
(storeHost,commitLogOffset) {
+        $http({
+            method: "GET",
+            url: "message/viewMessageByBrokerAndOffset.query",
+            params: {
+                brokerHost: storeHost.address,
+                port:storeHost.port,
+                offset: commitLogOffset
+            }
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                console.log(resp);
+                ngDialog.open({
+                    template: 'messageDetailViewDialog',
+                    controller: 'messageDetailViewDialogController',
+                    data: resp.data
+                });
+            } else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+
+    $scope.queryMessageByMessageId = function (messageId,topic) {
+        $http({
+            method: "GET",
+            url: "message/viewMessage.query",
+            params: {
+                msgId: messageId,
+                topic:topic
+            }
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                console.log(resp);
+                ngDialog.open({
+                    template: 'messageDetailViewDialog',
+                    controller: 'messageDetailViewDialogController',
+                    data:resp.data
+                });
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+
+
+    $scope.changeShowMessageList = function (currentPage,totalItem) {
+        var perPage = $scope.paginationConf.itemsPerPage;
+        var from = (currentPage - 1) * perPage;
+        var to = (from + perPage)>totalItem?totalItem:from + perPage;
+        $scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, 
to);
+        $scope.paginationConf.totalItems = totalItem ;
+    };
+}]);
+
+module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', 
'$http','Notification', function ($scope, ngDialog, $http,Notification) {
+
+        $scope.resendMessage = function (msgId,topic,consumerGroup) {
+            $http({
+                method: "POST",
+                url: "message/consumeMessageDirectly.do",
+                params: {
+                    msgId: msgId,
+                    consumerGroup:consumerGroup,
+                    topic:topic
+                }
+            }).success(function (resp) {
+                if (resp.status == 0) {
+                    ngDialog.open({
+                        template: 'operationResultDialog',
+                        data:{
+                            result:resp.data
+                        }
+                    });
+                }
+                else {
+                    ngDialog.open({
+                        template: 'operationResultDialog',
+                        data:{
+                            result:resp.errMsg
+                        }
+                    });
+                }
+            });
+        };
+        $scope.showExceptionDesc = function (errmsg) {
+            if(errmsg == null){
+                errmsg = "Don't have Exception"
+            }
+            ngDialog.open({
+                template: 'operationResultDialog',
+                data:{
+                    result:errmsg
+                }
+            });
+        };
+    }]
+);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/ops.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/ops.js 
b/rocketmq-console-ng/src/main/resources/static/src/ops.js
new file mode 100644
index 0000000..312e41f
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/ops.js
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+app.controller('opsController', 
['$scope','$location','$http','Notification','remoteApi','tools', function 
($scope,$location,$http,Notification,remoteApi,tools) {
+    $scope.namesvrAddrList = [];
+    $scope.useVIPChannel = true;
+    $http({
+        method: "GET",
+        url: "ops/homePage.query"
+    }).success(function (resp) {
+        if (resp.status == 0) {
+            $scope.namesvrAddrList = resp.data.namesvrAddrList;
+            $scope.useVIPChannel = resp.data.useVIPChannel
+        }else{
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    });
+
+    $scope.updateNameSvrAddr = function () {
+        $http({
+            method: "POST",
+            url: "ops/updateNameSvrAddr.do",
+            params:{nameSvrAddrList:$scope.namesvrAddrList.join(";")}
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                Notification.info({message: "SUCCESS", delay: 2000});
+            }else{
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    };
+    $scope.updateIsVIPChannel = function () {
+        $http({
+            method: "POST",
+            url: "ops/updateIsVIPChannel.do",
+            params:{useVIPChannel:$scope.useVIPChannel}
+        }).success(function (resp) {
+            if (resp.status == 0) {
+                Notification.info({message: "SUCCESS", delay: 2000});
+            }else{
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    }
+}]);

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/producer.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/producer.js 
b/rocketmq-console-ng/src/main/resources/static/src/producer.js
new file mode 100644
index 0000000..3c039ad
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/producer.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var module = app;
+module.controller('producerController', ['$scope', 
'$http','Notification',function ($scope, $http,Notification) {
+    $scope.selectedTopic=[];
+    $scope.producerGroup="";
+    $http({
+        method: "GET",
+        url: "topic/list.query"
+    }).success(function (resp) {
+        if(resp.status ==0){
+            $scope.allTopicList = resp.data.topicList.sort();
+            console.log($scope.allTopicList);
+        }else {
+            Notification.error({message: resp.errMsg, delay: 2000});
+        }
+    });
+    $scope.queryClientByTopicAndGroup = function () {
+        $http({
+            method: "GET",
+            url: "producer/producerConnection.query",
+            params:{
+                topic:$scope.selectedTopic,
+                producerGroup:$scope.producerGroup
+            }
+        }).success(function (resp) {
+            if(resp.status ==0){
+                $scope.connectionList = resp.data.connectionSet;
+            }else {
+                Notification.error({message: resp.errMsg, delay: 2000});
+            }
+        });
+    }
+} ]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/remoteApi/remoteApi.js
----------------------------------------------------------------------
diff --git 
a/rocketmq-console-ng/src/main/resources/static/src/remoteApi/remoteApi.js 
b/rocketmq-console-ng/src/main/resources/static/src/remoteApi/remoteApi.js
new file mode 100644
index 0000000..1189771
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/remoteApi/remoteApi.js
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+app.service('remoteApi', ['$http','tools', function ($http,tools) {
+    var queryTopic = function(callback){
+        $http({
+            method: "GET",
+            url: "topic/list.query"
+        }).success(callback);
+    }
+
+    var queryClusterList = function(callback){
+        $http({
+            method: "GET",
+            url: "cluster/list.query"
+        }).success(callback);
+    }
+
+    var queryBrokerHisData = function(date,callback){
+        var url = 'dashboard/broker.query';
+        var data = {date:date};
+        var setting = {
+            type: "GET",
+            data:data,
+            timeout:15000,//data is too large,so master set time out is long 
enough
+            success:callback
+        }
+        $.ajax(url,setting)
+    }
+
+    var queryTopicHisData = function(date,topicName,callback){
+        var url =  'dashboard/topic.query';
+        var data = {date:date,topicName:topicName};
+        var setting = {
+            type: "GET",
+            data:data,
+            timeout:15000,//data is too large,so master set time out is long 
enough
+            success:callback
+        }
+        $.ajax(url,setting)
+    }
+
+    var queryTopicCurrentData = function(callback){
+        var url = 'dashboard/topicCurrent';
+        var setting = {
+            type: "GET",
+            timeout:15000,//data is too large,so master set time out is long 
enough
+            success:callback
+        }
+        $.ajax(url,setting)
+    }
+
+
+    return {
+        queryTopic:queryTopic,
+        queryClusterList:queryClusterList,
+        queryBrokerHisData:queryBrokerHisData,
+        queryTopicHisData:queryTopicHisData,
+        queryTopicCurrentData:queryTopicCurrentData
+    }
+}])
+

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq-externals/blob/4b8f1357/rocketmq-console-ng/src/main/resources/static/src/tools/tools.js
----------------------------------------------------------------------
diff --git a/rocketmq-console-ng/src/main/resources/static/src/tools/tools.js 
b/rocketmq-console-ng/src/main/resources/static/src/tools/tools.js
new file mode 100644
index 0000000..780f088
--- /dev/null
+++ b/rocketmq-console-ng/src/main/resources/static/src/tools/tools.js
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+app.service('tools', ['$http', function ($http) {
+
+    var ctx = "";
+    var dashboardRefreshTime = 5000; // todo improve. when data size is 
large,request is too slow
+
+    var generateBrokerMap = function(brokerDetail,clusterMap,brokerMap){
+        var map = {};
+        $.each(brokerDetail,function(k,v){
+            $.each(clusterMap,function (ck, cv) {
+                if(angular.isUndefined(map[ck])){
+                    map[ck] = [];
+                }
+                $.each(cv,function(cvi,cvv){
+                    if(cvv == k){
+                        var index = 0;
+                        $.each(v,function(vi,vv){
+                            vv.split = k;
+                            vv.index = index;
+                            vv.address = brokerMap[cvv].brokerAddrs[index];
+                            vv.brokerName = brokerMap[cvv].brokerName;
+                            map[ck].push(vv);
+                            index++;
+                        })
+                    }
+                })
+            })
+        })
+        return map;
+    }
+
+    var fastSort = function (arrayToSort, propertyToSortWith, sortDirection) {
+        // temporary holder of position and sort-value
+        var map = arrayToSort.map(function (e, i) {
+            if (typeof e[propertyToSortWith] === 'string') {
+                return { index: i, value: e[propertyToSortWith].toLowerCase() 
};
+            }
+            else {
+                return { index: i, value: e[propertyToSortWith] };
+            }
+
+        })
+
+        // sorting the map containing the reduced values
+        map.sort(function (a, b) {
+            if (sortDirection === "ascending") {
+                return +(a.value > b.value) || +(a.value === b.value) - 1;
+            }
+            else {
+                return +(a.value < b.value) || +(a.value === b.value) - 1;
+            }
+
+        });
+
+        // container for the resulting order
+        var result = map.map(function (e) {
+            return arrayToSort[e.index];
+        });
+        return result;
+    };
+
+    return {
+        generateBrokerMap:generateBrokerMap,
+        fastSort:fastSort,
+        ctx:ctx,
+        dashboardRefreshTime:dashboardRefreshTime
+    }
+}])
+
+
+

Reply via email to