This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch update-antdpro
in repository https://gitbox.apache.org/repos/asf/incubator-skywalking-ui.git


The following commit(s) were added to refs/heads/update-antdpro by this push:
     new 651f29c  Add routes
651f29c is described below

commit 651f29c7eaef2e2fe781d3eb34192bc76f40b77c
Author: hanahmily <[email protected]>
AuthorDate: Mon Feb 26 11:55:57 2018 +0800

    Add routes
---
 src/routes/Alarm/Alarm.js                          | 132 ++++++
 src/routes/Alarm/Alarm.less                        |  50 +++
 src/routes/Application/Application.js              | 121 +++++
 .../Monitor.less => Application/Application.less}  |   5 +
 src/routes/Dashboard/Analysis.js                   | 488 ---------------------
 src/routes/Dashboard/Analysis.less                 | 170 -------
 src/routes/Dashboard/Dashboard.js                  | 137 ++++++
 .../Dashboard/{Monitor.less => Dashboard.less}     |   5 +
 src/routes/Dashboard/Monitor.js                    | 171 --------
 src/routes/Dashboard/Workplace.js                  | 275 ------------
 src/routes/Dashboard/Workplace.less                | 233 ----------
 src/routes/Server/Server.js                        | 193 ++++++++
 src/routes/Service/Service.js                      | 139 ++++++
 src/routes/Service/Service.less                    |   0
 src/routes/Topology/Topology.js                    |  37 ++
 src/routes/Trace/Trace.js                          | 184 ++++++++
 src/routes/Trace/Trace.less                        |  48 ++
 17 files changed, 1051 insertions(+), 1337 deletions(-)

diff --git a/src/routes/Alarm/Alarm.js b/src/routes/Alarm/Alarm.js
new file mode 100644
index 0000000..1ab3f8b
--- /dev/null
+++ b/src/routes/Alarm/Alarm.js
@@ -0,0 +1,132 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Card, Input, Tabs, List, Avatar } from 'antd';
+import { Panel } from '../../components/Page';
+import styles from './Alarm.less';
+
+const { Search } = Input;
+const { TabPane } = Tabs;
+const defaultPaging = {
+  pageNum: 1,
+  pageSize: 10,
+  needTotal: true,
+};
+
+@connect(state => ({
+  alarm: state.alarm,
+  globalVariables: state.global.globalVariables,
+  loading: state.loading.models.alarm,
+}))
+export default class Alarm extends PureComponent {
+  componentDidMount() {
+    const { alarm: { variables: { values } } } = this.props;
+    if (!values.alarmType) {
+      this.props.dispatch({
+        type: 'alarm/saveVariables',
+        payload: { values: {
+          alarmType: 'APPLICATION',
+          paging: defaultPaging,
+        } },
+      });
+    }
+  }
+  handleSearch = (keyword) => {
+    this.props.dispatch({
+      type: 'alarm/saveVariables',
+      payload: { values: {
+        keyword,
+        paging: defaultPaging,
+      } },
+    });
+  }
+  handlePageChange = (pag) => {
+    this.props.dispatch({
+      type: 'alarm/saveVariables',
+      payload: { values: {
+        paging: {
+          pageNum: pag,
+          pageSize: 10,
+          needTotal: true,
+        },
+      } },
+    });
+  }
+  changeAlarmType = (alarmType) => {
+    this.props.dispatch({
+      type: 'alarm/saveVariables',
+      payload: { values: {
+        alarmType,
+        paging: defaultPaging,
+      } },
+    });
+  }
+  handleChange = (variables) => {
+    const type = variables.alarmType.charAt(0) + 
variables.alarmType.slice(1).toLowerCase();
+    this.props.dispatch({
+      type: 'alarm/fetchData',
+      payload: { variables, reducer: `save${type}AlarmList` },
+    });
+  }
+  renderList = ({ items, total }) => {
+    const { alarm: { variables: { values: { paging = defaultPaging } } }, 
loading } = this.props;
+    const pagination = {
+      pageSize: paging.pageSize,
+      current: paging.pageNum,
+      total,
+      onChange: this.handlePageChange,
+    };
+    return (
+      <List
+        className="demo-loadmore-list"
+        loading={loading}
+        itemLayout="horizontal"
+        dataSource={items}
+        pagination={pagination}
+        renderItem={item => (
+          <List.Item>
+            <List.Item.Meta
+              avatar={
+                <Avatar
+                  style={item.causeType === 'LOW_SUCCESS_RATE' ? { 
backgroundColor: '#e68a00' } : { backgroundColor: '#b32400' }}
+                  icon={item.causeType === 'LOW_SUCCESS_RATE' ? 
'clock-circle-o' : 'close'}
+                />}
+              title={item.title}
+              description={item.content}
+            />
+            <div>{item.startTime}</div>
+          </List.Item>
+        )}
+      />);
+  }
+  render() {
+    const extraContent = (
+      <div className={styles.extraContent}>
+        <Search
+          className={styles.extraContentSearch}
+          placeholder="Search..."
+          onSearch={this.handleSearch}
+        />
+      </div>
+    );
+    const { alarm: { variables: { values }, data } } = this.props;
+    return (
+      <Panel
+        variables={values}
+        globalVariables={this.props.globalVariables}
+        onChange={this.handleChange}
+      >
+        <Card
+          title="Alarm List"
+          bordered={false}
+          extra={extraContent}
+        >
+          <Tabs activeKey={values.alarmType} onChange={this.changeAlarmType}>
+            <TabPane tab="Application" 
key="APPLICATION">{this.renderList(data.applicationAlarmList)}</TabPane>
+            <TabPane tab="Server" 
key="SERVER">{this.renderList(data.serverAlarmList)}</TabPane>
+            <TabPane tab="Service" 
key="SERVICE">{this.renderList(data.serviceAlarmList)}</TabPane>
+          </Tabs>
+        </Card>
+      </Panel>
+    );
+  }
+}
diff --git a/src/routes/Alarm/Alarm.less b/src/routes/Alarm/Alarm.less
new file mode 100644
index 0000000..1aa3571
--- /dev/null
+++ b/src/routes/Alarm/Alarm.less
@@ -0,0 +1,50 @@
+@import "~antd/lib/style/themes/default.less";
+@import "../../utils/utils.less";
+
+.tableList {
+  .tableListOperator {
+    margin-bottom: 16px;
+    button {
+      margin-right: 8px;
+    }
+  }
+}
+
+.tableListForm {
+  :global {
+    .ant-form-item {
+      margin-bottom: 24px;
+      margin-right: 0;
+      display: flex;
+      > .ant-form-item-label {
+        width: auto;
+        line-height: 32px;
+        padding-right: 8px;
+      }
+    }
+    .ant-form-item-control-wrapper {
+      flex: 1;
+    }
+  }
+  .submitButtons {
+    white-space: nowrap;
+    margin-bottom: 24px;
+  }
+}
+
+@media screen and (max-width: @screen-lg) {
+  .tableListForm :global(.ant-form-item) {
+    margin-right: 24px;
+  }
+}
+
+@media screen and (max-width: @screen-md) {
+  .tableListForm :global(.ant-form-item) {
+    margin-right: 8px;
+  }
+}
+
+.extraContentSearch {
+  margin-left: 16px;
+  width: 272px;
+}
diff --git a/src/routes/Application/Application.js 
b/src/routes/Application/Application.js
new file mode 100644
index 0000000..ab07b15
--- /dev/null
+++ b/src/routes/Application/Application.js
@@ -0,0 +1,121 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Row, Col, Select, Card, Form } from 'antd';
+import { AppTopology } from '../../components/Topology';
+import { Panel, Ranking } from '../../components/Page';
+
+const { Option } = Select;
+const { Item: FormItem } = Form;
+
+const middleColResponsiveProps = {
+  xs: 24,
+  sm: 24,
+  md: 12,
+  lg: 12,
+  xl: 12,
+  style: { marginBottom: 24, marginTop: 24 },
+};
+
+@connect(state => ({
+  application: state.application,
+  globalVariables: state.global.globalVariables,
+}))
[email protected]({
+  mapPropsToFields(props) {
+    const { variables: { values, labels } } = props.application;
+    return {
+      applicationId: Form.createFormField({
+        value: { key: values.applicationId ? values.applicationId : '', label: 
labels.applicationId ? labels.applicationId : '' },
+      }),
+    };
+  },
+})
+export default class Application extends PureComponent {
+  componentDidMount() {
+    this.props.dispatch({
+      type: 'application/initOptions',
+      payload: { variables: this.props.globalVariables },
+    });
+  }
+  componentWillUpdate(nextProps) {
+    if (nextProps.globalVariables.duration === 
this.props.globalVariables.duration) {
+      return;
+    }
+    this.props.dispatch({
+      type: 'application/initOptions',
+      payload: { variables: nextProps.globalVariables },
+    });
+  }
+  handleSelect = (selected) => {
+    this.props.dispatch({
+      type: 'application/saveVariables',
+      payload: {
+        values: { applicationId: selected.key },
+        labels: { applicationId: selected.label },
+      },
+    });
+  }
+  handleChange = (variables) => {
+    this.props.dispatch({
+      type: 'application/fetchData',
+      payload: { variables },
+    });
+  }
+  render() {
+    const { getFieldDecorator } = this.props.form;
+    const { variables: { values, options }, data } = this.props.application;
+    return (
+      <div>
+        <Form layout="inline">
+          <FormItem>
+            {getFieldDecorator('applicationId')(
+              <Select
+                showSearch
+                style={{ width: 200 }}
+                placeholder="Select a application"
+                labelInValue
+                onSelect={this.handleSelect.bind(this)}
+              >
+                {options.applicationId && options.applicationId.map((app) => {
+                    return (<Option key={app.key} 
value={app.key}>{app.label}</Option>);
+                  })}
+              </Select>
+            )}
+          </FormItem>
+        </Form>
+        <Panel
+          variables={values}
+          globalVariables={this.props.globalVariables}
+          onChange={this.handleChange}
+        >
+          <Card
+            bordered={false}
+            bodyStyle={{ padding: 0, marginTop: 24 }}
+          >
+            <AppTopology elements={data.getApplicationTopology} layout={{ 
name: 'concentric', minNodeSpacing: 200 }} />
+          </Card>
+          <Row gutter={24}>
+            <Col {...middleColResponsiveProps}>
+              <Card
+                title="Slow Service"
+                bordered={false}
+                bodyStyle={{ padding: '0px 10px' }}
+              >
+                <Ranking data={data.getSlowService} title="name" 
content="avgResponseTime" unit="ms" />
+              </Card>
+            </Col>
+            <Col {...middleColResponsiveProps}>
+              <Card
+                title="Servers Throughput"
+                bordered={false}
+                bodyStyle={{ padding: '0px 10px' }}
+              >
+                <Ranking data={data.getServerThroughput} title="name" 
content="tps" unit="t/s" />
+              </Card>
+            </Col>
+          </Row>
+        </Panel>
+      </div>
+    );
+  }
+}
diff --git a/src/routes/Dashboard/Monitor.less 
b/src/routes/Application/Application.less
similarity index 86%
copy from src/routes/Dashboard/Monitor.less
copy to src/routes/Application/Application.less
index e52dd30..8e13f67 100644
--- a/src/routes/Dashboard/Monitor.less
+++ b/src/routes/Application/Application.less
@@ -21,3 +21,8 @@
     height: auto;
   }
 }
+
+.trendText {
+  margin-left: 8px;
+  color: @heading-color;
+}
diff --git a/src/routes/Dashboard/Analysis.js b/src/routes/Dashboard/Analysis.js
deleted file mode 100644
index 2f98da8..0000000
--- a/src/routes/Dashboard/Analysis.js
+++ /dev/null
@@ -1,488 +0,0 @@
-import React, { Component } from 'react';
-import { connect } from 'dva';
-import {
-  Row,
-  Col,
-  Icon,
-  Card,
-  Tabs,
-  Table,
-  Radio,
-  DatePicker,
-  Tooltip,
-  Menu,
-  Dropdown,
-} from 'antd';
-import numeral from 'numeral';
-import {
-  ChartCard,
-  yuan,
-  MiniArea,
-  MiniBar,
-  MiniProgress,
-  Field,
-  Bar,
-  Pie,
-  TimelineChart,
-} from '../../components/Charts';
-import Trend from '../../components/Trend';
-import NumberInfo from '../../components/NumberInfo';
-import { getTimeDistance } from '../../utils/utils';
-
-import styles from './Analysis.less';
-
-const { TabPane } = Tabs;
-const { RangePicker } = DatePicker;
-
-const rankingListData = [];
-for (let i = 0; i < 7; i += 1) {
-  rankingListData.push({
-    title: `工专路 ${i} 号店`,
-    total: 323234,
-  });
-}
-
-@connect(({ chart, loading }) => ({
-  chart,
-  loading: loading.effects['chart/fetch'],
-}))
-export default class Analysis extends Component {
-  state = {
-    salesType: 'all',
-    currentTabKey: '',
-    rangePickerValue: getTimeDistance('year'),
-  };
-
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'chart/fetch',
-    });
-  }
-
-  componentWillUnmount() {
-    const { dispatch } = this.props;
-    dispatch({
-      type: 'chart/clear',
-    });
-  }
-
-  handleChangeSalesType = (e) => {
-    this.setState({
-      salesType: e.target.value,
-    });
-  };
-
-  handleTabChange = (key) => {
-    this.setState({
-      currentTabKey: key,
-    });
-  };
-
-  handleRangePickerChange = (rangePickerValue) => {
-    this.setState({
-      rangePickerValue,
-    });
-
-    this.props.dispatch({
-      type: 'chart/fetchSalesData',
-    });
-  };
-
-  selectDate = (type) => {
-    this.setState({
-      rangePickerValue: getTimeDistance(type),
-    });
-
-    this.props.dispatch({
-      type: 'chart/fetchSalesData',
-    });
-  };
-
-  isActive(type) {
-    const { rangePickerValue } = this.state;
-    const value = getTimeDistance(type);
-    if (!rangePickerValue[0] || !rangePickerValue[1]) {
-      return;
-    }
-    if (
-      rangePickerValue[0].isSame(value[0], 'day') &&
-      rangePickerValue[1].isSame(value[1], 'day')
-    ) {
-      return styles.currentDate;
-    }
-  }
-
-  render() {
-    const { rangePickerValue, salesType, currentTabKey } = this.state;
-    const { chart, loading } = this.props;
-    const {
-      visitData,
-      visitData2,
-      salesData,
-      searchData,
-      offlineData,
-      offlineChartData,
-      salesTypeData,
-      salesTypeDataOnline,
-      salesTypeDataOffline,
-    } = chart;
-
-    const salesPieData =
-      salesType === 'all'
-        ? salesTypeData
-        : salesType === 'online' ? salesTypeDataOnline : salesTypeDataOffline;
-
-    const menu = (
-      <Menu>
-        <Menu.Item>操作一</Menu.Item>
-        <Menu.Item>操作二</Menu.Item>
-      </Menu>
-    );
-
-    const iconGroup = (
-      <span className={styles.iconGroup}>
-        <Dropdown overlay={menu} placement="bottomRight">
-          <Icon type="ellipsis" />
-        </Dropdown>
-      </span>
-    );
-
-    const salesExtra = (
-      <div className={styles.salesExtraWrap}>
-        <div className={styles.salesExtra}>
-          <a className={this.isActive('today')} onClick={() => 
this.selectDate('today')}>
-            今日
-          </a>
-          <a className={this.isActive('week')} onClick={() => 
this.selectDate('week')}>
-            本周
-          </a>
-          <a className={this.isActive('month')} onClick={() => 
this.selectDate('month')}>
-            本月
-          </a>
-          <a className={this.isActive('year')} onClick={() => 
this.selectDate('year')}>
-            全年
-          </a>
-        </div>
-        <RangePicker
-          value={rangePickerValue}
-          onChange={this.handleRangePickerChange}
-          style={{ width: 256 }}
-        />
-      </div>
-    );
-
-    const columns = [
-      {
-        title: '排名',
-        dataIndex: 'index',
-        key: 'index',
-      },
-      {
-        title: '搜索关键词',
-        dataIndex: 'keyword',
-        key: 'keyword',
-        render: text => <a href="/">{text}</a>,
-      },
-      {
-        title: '用户数',
-        dataIndex: 'count',
-        key: 'count',
-        sorter: (a, b) => a.count - b.count,
-        className: styles.alignRight,
-      },
-      {
-        title: '周涨幅',
-        dataIndex: 'range',
-        key: 'range',
-        sorter: (a, b) => a.range - b.range,
-        render: (text, record) => (
-          <Trend flag={record.status === 1 ? 'down' : 'up'}>
-            <span style={{ marginRight: 4 }}>{text}%</span>
-          </Trend>
-        ),
-        align: 'right',
-      },
-    ];
-
-    const activeKey = currentTabKey || (offlineData[0] && offlineData[0].name);
-
-    const CustomTab = ({ data, currentTabKey: currentKey }) => (
-      <Row gutter={8} style={{ width: 138, margin: '8px 0' }}>
-        <Col span={12}>
-          <NumberInfo
-            title={data.name}
-            subTitle="转化率"
-            gap={2}
-            total={`${data.cvr * 100}%`}
-            theme={currentKey !== data.name && 'light'}
-          />
-        </Col>
-        <Col span={12} style={{ paddingTop: 36 }}>
-          <Pie
-            animate={false}
-            color={currentKey !== data.name && '#BDE4FF'}
-            inner={0.55}
-            tooltip={false}
-            margin={[0, 0, 0, 0]}
-            percent={data.cvr * 100}
-            height={64}
-          />
-        </Col>
-      </Row>
-    );
-
-    const topColResponsiveProps = {
-      xs: 24,
-      sm: 12,
-      md: 12,
-      lg: 12,
-      xl: 6,
-      style: { marginBottom: 24 },
-    };
-
-    return (
-      <div>
-        <Row gutter={24}>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="总销售额"
-              action={
-                <Tooltip title="指标说明">
-                  <Icon type="info-circle-o" />
-                </Tooltip>
-              }
-              total={yuan(126560)}
-              footer={<Field label="日均销售额" 
value={`¥${numeral(12423).format('0,0')}`} />}
-              contentHeight={46}
-            >
-              <Trend flag="up" style={{ marginRight: 16 }}>
-                周同比<span className={styles.trendText}>12%</span>
-              </Trend>
-              <Trend flag="down">
-                日环比<span className={styles.trendText}>11%</span>
-              </Trend>
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="访问量"
-              action={
-                <Tooltip title="指标说明">
-                  <Icon type="info-circle-o" />
-                </Tooltip>
-              }
-              total={numeral(8846).format('0,0')}
-              footer={<Field label="日访问量" value={numeral(1234).format('0,0')} 
/>}
-              contentHeight={46}
-            >
-              <MiniArea color="#975FE4" data={visitData} />
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="支付笔数"
-              action={
-                <Tooltip title="指标说明">
-                  <Icon type="info-circle-o" />
-                </Tooltip>
-              }
-              total={numeral(6560).format('0,0')}
-              footer={<Field label="转化率" value="60%" />}
-              contentHeight={46}
-            >
-              <MiniBar data={visitData} />
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="运营活动效果"
-              action={
-                <Tooltip title="指标说明">
-                  <Icon type="info-circle-o" />
-                </Tooltip>
-              }
-              total="78%"
-              footer={
-                <div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
-                  <Trend flag="up" style={{ marginRight: 16 }}>
-                    周同比<span className={styles.trendText}>12%</span>
-                  </Trend>
-                  <Trend flag="down">
-                    日环比<span className={styles.trendText}>11%</span>
-                  </Trend>
-                </div>
-              }
-              contentHeight={46}
-            >
-              <MiniProgress percent={78} strokeWidth={8} target={80} 
color="#13C2C2" />
-            </ChartCard>
-          </Col>
-        </Row>
-
-        <Card loading={loading} bordered={false} bodyStyle={{ padding: 0 }}>
-          <div className={styles.salesCard}>
-            <Tabs tabBarExtraContent={salesExtra} size="large" tabBarStyle={{ 
marginBottom: 24 }}>
-              <TabPane tab="销售额" key="sales">
-                <Row>
-                  <Col xl={16} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesBar}>
-                      <Bar height={295} title="销售额趋势" data={salesData} />
-                    </div>
-                  </Col>
-                  <Col xl={8} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesRank}>
-                      <h4 className={styles.rankingTitle}>门店销售额排名</h4>
-                      <ul className={styles.rankingList}>
-                        {rankingListData.map((item, i) => (
-                          <li key={item.title}>
-                            <span className={i < 3 ? styles.active : ''}>{i + 
1}</span>
-                            <span>{item.title}</span>
-                            <span>{numeral(item.total).format('0,0')}</span>
-                          </li>
-                        ))}
-                      </ul>
-                    </div>
-                  </Col>
-                </Row>
-              </TabPane>
-              <TabPane tab="访问量" key="views">
-                <Row>
-                  <Col xl={16} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesBar}>
-                      <Bar height={292} title="访问量趋势" data={salesData} />
-                    </div>
-                  </Col>
-                  <Col xl={8} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesRank}>
-                      <h4 className={styles.rankingTitle}>门店访问量排名</h4>
-                      <ul className={styles.rankingList}>
-                        {rankingListData.map((item, i) => (
-                          <li key={item.title}>
-                            <span className={i < 3 ? styles.active : ''}>{i + 
1}</span>
-                            <span>{item.title}</span>
-                            <span>{numeral(item.total).format('0,0')}</span>
-                          </li>
-                        ))}
-                      </ul>
-                    </div>
-                  </Col>
-                </Row>
-              </TabPane>
-            </Tabs>
-          </div>
-        </Card>
-
-        <Row gutter={24}>
-          <Col xl={12} lg={24} md={24} sm={24} xs={24}>
-            <Card
-              loading={loading}
-              bordered={false}
-              title="线上热门搜索"
-              extra={iconGroup}
-              style={{ marginTop: 24 }}
-            >
-              <Row gutter={68}>
-                <Col sm={12} xs={24} style={{ marginBottom: 24 }}>
-                  <NumberInfo
-                    subTitle={
-                      <span>
-                        搜索用户数
-                        <Tooltip title="指标文案">
-                          <Icon style={{ marginLeft: 8 }} type="info-circle-o" 
/>
-                        </Tooltip>
-                      </span>
-                    }
-                    gap={8}
-                    total={numeral(12321).format('0,0')}
-                    status="up"
-                    subTotal={17.1}
-                  />
-                  <MiniArea line height={45} data={visitData2} />
-                </Col>
-                <Col sm={12} xs={24} style={{ marginBottom: 24 }}>
-                  <NumberInfo
-                    subTitle="人均搜索次数"
-                    total={2.7}
-                    status="down"
-                    subTotal={26.2}
-                    gap={8}
-                  />
-                  <MiniArea line height={45} data={visitData2} />
-                </Col>
-              </Row>
-              <Table
-                rowKey={record => record.index}
-                size="small"
-                columns={columns}
-                dataSource={searchData}
-                pagination={{
-                  style: { marginBottom: 0 },
-                  pageSize: 5,
-                }}
-              />
-            </Card>
-          </Col>
-          <Col xl={12} lg={24} md={24} sm={24} xs={24}>
-            <Card
-              loading={loading}
-              className={styles.salesCard}
-              bordered={false}
-              title="销售额类别占比"
-              bodyStyle={{ padding: 24 }}
-              extra={
-                <div className={styles.salesCardExtra}>
-                  {iconGroup}
-                  <div className={styles.salesTypeRadio}>
-                    <Radio.Group value={salesType} 
onChange={this.handleChangeSalesType}>
-                      <Radio.Button value="all">全部渠道</Radio.Button>
-                      <Radio.Button value="online">线上</Radio.Button>
-                      <Radio.Button value="offline">门店</Radio.Button>
-                    </Radio.Group>
-                  </div>
-                </div>
-              }
-              style={{ marginTop: 24, minHeight: 509 }}
-            >
-              <h4 style={{ marginTop: 8, marginBottom: 32 }}>销售额</h4>
-              <Pie
-                hasLegend
-                subTitle="销售额"
-                total={yuan(salesPieData.reduce((pre, now) => now.y + pre, 0))}
-                data={salesPieData}
-                valueFormat={val => yuan(val)}
-                height={248}
-                lineWidth={4}
-              />
-            </Card>
-          </Col>
-        </Row>
-
-        <Card
-          loading={loading}
-          className={styles.offlineCard}
-          bordered={false}
-          bodyStyle={{ padding: '0 0 32px 0' }}
-          style={{ marginTop: 32 }}
-        >
-          <Tabs activeKey={activeKey} onChange={this.handleTabChange}>
-            {offlineData.map(shop => (
-              <TabPane tab={<CustomTab data={shop} currentTabKey={activeKey} 
/>} key={shop.name}>
-                <div style={{ padding: '0 24px' }}>
-                  <TimelineChart
-                    height={400}
-                    data={offlineChartData}
-                    titleMap={{ y1: '客流量', y2: '支付笔数' }}
-                  />
-                </div>
-              </TabPane>
-            ))}
-          </Tabs>
-        </Card>
-      </div>
-    );
-  }
-}
diff --git a/src/routes/Dashboard/Analysis.less 
b/src/routes/Dashboard/Analysis.less
deleted file mode 100644
index 38682c3..0000000
--- a/src/routes/Dashboard/Analysis.less
+++ /dev/null
@@ -1,170 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
-
-.iconGroup {
-  i {
-    transition: color 0.32s;
-    color: @text-color-secondary;
-    cursor: pointer;
-    margin-left: 16px;
-    &:hover {
-      color: @text-color;
-    }
-  }
-}
-
-.rankingList {
-  margin: 25px 0 0;
-  padding: 0;
-  list-style: none;
-  li {
-    .clearfix();
-    margin-top: 16px;
-    span {
-      color: @text-color;
-      font-size: 14px;
-      line-height: 22px;
-    }
-    span:first-child {
-      background-color: @background-color-base;
-      border-radius: 20px;
-      display: inline-block;
-      font-size: 12px;
-      font-weight: 600;
-      margin-right: 24px;
-      height: 20px;
-      line-height: 20px;
-      width: 20px;
-      text-align: center;
-    }
-    span.active {
-      //background-color: @primary-color;
-      background-color: #314659;
-      color: #fff;
-    }
-    span:last-child {
-      float: right;
-    }
-  }
-}
-
-.salesExtra {
-  display: inline-block;
-  margin-right: 24px;
-  a {
-    color: @text-color;
-    margin-left: 24px;
-    &:hover {
-      color: @primary-color;
-    }
-    &.currentDate {
-      color: @primary-color;
-    }
-  }
-}
-
-.salesCard {
-  .salesBar {
-    padding: 0 0 32px 32px;
-  }
-  .salesRank {
-    padding: 0 32px 32px 72px;
-  }
-  :global {
-    .ant-tabs-bar {
-      padding-left: 16px;
-      .ant-tabs-nav .ant-tabs-tab {
-        padding-top: 16px;
-        padding-bottom: 14px;
-        line-height: 24px;
-      }
-    }
-    .ant-tabs-extra-content {
-      padding-right: 24px;
-      line-height: 55px;
-    }
-    .ant-card-head {
-      position: relative;
-    }
-  }
-}
-
-.salesCardExtra {
-  height: 68px;
-}
-
-.salesTypeRadio {
-  position: absolute;
-  left: 24px;
-  bottom: 15px;
-}
-
-.offlineCard {
-  :global {
-    .ant-tabs-ink-bar {
-      bottom: auto;
-    }
-    .ant-tabs-bar {
-      border-bottom: none;
-    }
-    .ant-tabs-nav-container-scrolling {
-      padding-left: 40px;
-      padding-right: 40px;
-    }
-    .ant-tabs-tab-prev-icon:before {
-      position: relative;
-      left: 6px;
-    }
-    .ant-tabs-tab-next-icon:before {
-      position: relative;
-      right: 6px;
-    }
-  }
-
-  :global(.ant-tabs-tab-active) h4 {
-    color: @primary-color;
-  }
-}
-
-.trendText {
-  margin-left: 8px;
-  color: @heading-color;
-}
-
-@media screen and (max-width: @screen-lg) {
-  .salesExtra {
-    display: none;
-  }
-
-  .rankingList {
-    li {
-      span:first-child {
-        margin-right: 8px;
-      }
-    }
-  }
-}
-
-@media screen and (max-width: @screen-md) {
-  .rankingTitle {
-    margin-top: 16px;
-  }
-
-  .salesCard .salesBar {
-    padding: 16px;
-  }
-}
-
-@media screen and (max-width: @screen-sm) {
-  .salesExtraWrap {
-    display: none;
-  }
-
-  .salesCard {
-    :global {
-      .ant-tabs-content {
-        padding-top: 30px;
-      }
-    }
-  }
-}
diff --git a/src/routes/Dashboard/Dashboard.js 
b/src/routes/Dashboard/Dashboard.js
new file mode 100644
index 0000000..c27079d
--- /dev/null
+++ b/src/routes/Dashboard/Dashboard.js
@@ -0,0 +1,137 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Row, Col, Card } from 'antd';
+import {
+  ChartCard, Pie, MiniArea, Field,
+} from '../../components/Charts';
+import { timeRange } from '../../utils/utils';
+import { Panel, Ranking } from '../../components/Page';
+
+@connect(state => ({
+  dashboard: state.dashboard,
+  duration: state.global.duration,
+  globalVariables: state.global.globalVariables,
+}))
+export default class Dashboard extends PureComponent {
+  handleDurationChange = (variables) => {
+    this.props.dispatch({
+      type: 'dashboard/fetchData',
+      payload: { variables },
+    });
+  }
+  render() {
+    const { data } = this.props.dashboard;
+    const visitData = [];
+    const { numOfAlarmRate } = data.getAlarmTrend;
+    const accuracy = 100;
+    let avg = 0;
+    let max = 0;
+    let min = 0;
+    if (numOfAlarmRate && numOfAlarmRate.length > 0) {
+      timeRange(this.props.duration).forEach((v, i) => {
+        visitData.push({ x: v, y: numOfAlarmRate[i] / accuracy });
+      });
+      avg = numOfAlarmRate.reduce((acc, curr) => acc + curr) / 
numOfAlarmRate.length / accuracy;
+      max = numOfAlarmRate.reduce((acc, curr) => { return acc < curr ? curr : 
acc; }) / accuracy;
+      min = numOfAlarmRate.reduce((acc, curr) => { return acc > curr ? curr : 
acc; }) / accuracy;
+    }
+    return (
+      <Panel globalVariables={this.props.globalVariables} 
onChange={this.handleDurationChange}>
+        <Row gutter={24}>
+          <Col xs={24} sm={24} md={12} lg={6} xl={6}>
+            <ChartCard
+              title="App"
+              avatar={<img style={{ width: 56, height: 56 }} src="app.svg" 
alt="app" />}
+              total={data.getClusterBrief.numOfApplication}
+            />
+          </Col>
+          <Col xs={24} sm={24} md={12} lg={6} xl={6}>
+            <ChartCard
+              title="Service"
+              avatar={<img style={{ width: 56, height: 56 }} src="service.svg" 
alt="service" />}
+              total={data.getClusterBrief.numOfService}
+            />
+          </Col>
+          <Col xs={24} sm={24} md={12} lg={6} xl={6}>
+            <ChartCard
+              title="Store"
+              avatar={<img style={{ width: 48, height: 56 }} 
src="database.svg" alt="database" />}
+              total={data.getClusterBrief.numOfDatabase
+                + data.getClusterBrief.numOfCache}
+            />
+          </Col>
+          <Col xs={24} sm={24} md={12} lg={6} xl={6}>
+            <ChartCard
+              title="MQ"
+              avatar={<img style={{ width: 56, height: 56 }} src="redis.svg" 
alt="redis" />}
+              total={data.getClusterBrief.numOfMQ}
+            />
+          </Col>
+        </Row>
+        <Row gutter={24}>
+          <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 24 
}}>
+            <ChartCard
+              title="Avg Application Alarm"
+              avatar={<img style={{ width: 56, height: 56 }} src="alert.svg" 
alt="app" />}
+              total={`${avg.toFixed(2)}%`}
+              footer={<div><Field label="Max" value={`${max}%`} /> <Field 
label="Min" value={`${min}%`} /></div>}
+            >
+              <MiniArea
+                animate={false}
+                color="#D87093"
+                borderColor="#B22222"
+                line="true"
+                height={143}
+                data={visitData}
+                yAxis={{
+                  formatter(val) {
+                      return `${val} %`;
+                  },
+                }}
+              />
+            </ChartCard>
+          </Col>
+          <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 24 
}}>
+            <Card
+              bordered={false}
+              bodyStyle={{ padding: 0 }}
+            >
+              <Pie
+                animate={false}
+                hasLegend
+                title="Database"
+                subTitle="Total"
+                total={data.getConjecturalApps.apps
+                  .reduce((pre, now) => now.num + pre, 0)}
+                data={data.getConjecturalApps.apps
+                  .map((v) => { return { x: v.name, y: v.num }; })}
+                height={300}
+                lineWidth={4}
+              />
+            </Card>
+          </Col>
+        </Row>
+        <Row gutter={24}>
+          <Col xs={24} sm={24} md={24} lg={16} xl={16} style={{ marginTop: 24 
}}>
+            <Card
+              title="Slow Service"
+              bordered={false}
+              bodyStyle={{ padding: '0px 10px' }}
+            >
+              <Ranking data={data.getTopNSlowService} title="name" 
content="avgResponseTime" unit="ms" />
+            </Card>
+          </Col>
+          <Col xs={24} sm={24} md={24} lg={8} xl={8} style={{ marginTop: 24 }}>
+            <Card
+              title="Application Throughput"
+              bordered={false}
+              bodyStyle={{ padding: '0px 10px' }}
+            >
+              <Ranking data={data.getTopNApplicationThroughput} 
title="applicationCode" content="tps" unit="t/s" />
+            </Card>
+          </Col>
+        </Row>
+      </Panel>
+    );
+  }
+}
diff --git a/src/routes/Dashboard/Monitor.less 
b/src/routes/Dashboard/Dashboard.less
similarity index 86%
rename from src/routes/Dashboard/Monitor.less
rename to src/routes/Dashboard/Dashboard.less
index e52dd30..8e13f67 100644
--- a/src/routes/Dashboard/Monitor.less
+++ b/src/routes/Dashboard/Dashboard.less
@@ -21,3 +21,8 @@
     height: auto;
   }
 }
+
+.trendText {
+  margin-left: 8px;
+  color: @heading-color;
+}
diff --git a/src/routes/Dashboard/Monitor.js b/src/routes/Dashboard/Monitor.js
deleted file mode 100644
index 02b1ff8..0000000
--- a/src/routes/Dashboard/Monitor.js
+++ /dev/null
@@ -1,171 +0,0 @@
-import React, { PureComponent } from 'react';
-import { connect } from 'dva';
-import { Row, Col, Card, Tooltip } from 'antd';
-import numeral from 'numeral';
-import Authorized from '../../utils/Authorized';
-import { Pie, WaterWave, Gauge, TagCloud } from '../../components/Charts';
-import NumberInfo from '../../components/NumberInfo';
-import CountDown from '../../components/CountDown';
-import ActiveChart from '../../components/ActiveChart';
-import styles from './Monitor.less';
-
-const { Secured } = Authorized;
-
-const targetTime = new Date().getTime() + 3900000;
-
-// use permission as a parameter
-const havePermissionAsync = new Promise((resolve) => {
-  // Call resolve on behalf of passed
-  setTimeout(() => resolve(), 1000);
-});
-@Secured(havePermissionAsync)
-@connect(({ monitor, loading }) => ({
-  monitor,
-  loading: loading.models.monitor,
-}))
-export default class Monitor extends PureComponent {
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'monitor/fetchTags',
-    });
-  }
-
-  render() {
-    const { monitor, loading } = this.props;
-    const { tags } = monitor;
-
-    return (
-      <div>
-        <Row gutter={24}>
-          <Col xl={18} lg={24} md={24} sm={24} xs={24} style={{ marginBottom: 
24 }}>
-            <Card title="活动实时交易情况" bordered={false}>
-              <Row>
-                <Col md={6} sm={12} xs={24}>
-                  <NumberInfo
-                    subTitle="今日交易总额"
-                    suffix="元"
-                    total={numeral(124543233).format('0,0')}
-                  />
-                </Col>
-                <Col md={6} sm={12} xs={24}>
-                  <NumberInfo
-                    subTitle="销售目标完成率"
-                    total="92%"
-                  />
-                </Col>
-                <Col md={6} sm={12} xs={24}>
-                  <NumberInfo subTitle="活动剩余时间" total={<CountDown 
target={targetTime} />} />
-                </Col>
-                <Col md={6} sm={12} xs={24}>
-                  <NumberInfo
-                    subTitle="每秒交易总额"
-                    suffix="元"
-                    total={numeral(234).format('0,0')}
-                  />
-                </Col>
-              </Row>
-              <div className={styles.mapChart}>
-                <Tooltip title="等待后期实现">
-                  <img 
src="https://gw.alipayobjects.com/zos/rmsportal/HBWnDEUXCnGnGrRfrpKa.png"; 
alt="map" />
-                </Tooltip>
-              </div>
-            </Card>
-          </Col>
-          <Col xl={6} lg={24} md={24} sm={24} xs={24}>
-            <Card title="活动情况预测" style={{ marginBottom: 24 }} bordered={false}>
-              <ActiveChart />
-            </Card>
-            <Card
-              title="券核效率"
-              style={{ marginBottom: 24 }}
-              bodyStyle={{ textAlign: 'center' }}
-              bordered={false}
-            >
-              <Gauge
-                format={(val) => {
-                  switch (parseInt(val, 10)) {
-                    case 20:
-                      return '差';
-                    case 40:
-                      return '中';
-                    case 60:
-                      return '良';
-                    case 80:
-                      return '优';
-                    default:
-                      return '';
-                  }
-                }}
-                title="跳出率"
-                height={180}
-                percent={87}
-              />
-            </Card>
-          </Col>
-        </Row>
-        <Row gutter={24}>
-          <Col xl={12} lg={24} sm={24} xs={24}>
-            <Card
-              title="各品类占比"
-              style={{ marginBottom: 24 }}
-              bordered={false}
-              className={styles.pieCard}
-            >
-              <Row style={{ padding: '16px 0' }}>
-                <Col span={8}>
-                  <Pie
-                    animate={false}
-                    percent={28}
-                    subTitle="中式快餐"
-                    total="28%"
-                    height={128}
-                    lineWidth={2}
-                  />
-                </Col>
-                <Col span={8}>
-                  <Pie
-                    animate={false}
-                    color="#5DDECF"
-                    percent={22}
-                    subTitle="西餐"
-                    total="22%"
-                    height={128}
-                    lineWidth={2}
-                  />
-                </Col>
-                <Col span={8}>
-                  <Pie
-                    animate={false}
-                    color="#2FC25B"
-                    percent={32}
-                    subTitle="火锅"
-                    total="32%"
-                    height={128}
-                    lineWidth={2}
-                  />
-                </Col>
-              </Row>
-            </Card>
-          </Col>
-          <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
-            <Card title="热门搜索" loading={loading} bordered={false} bodyStyle={{ 
overflow: 'hidden' }}>
-              <TagCloud
-                data={tags}
-                height={161}
-              />
-            </Card>
-          </Col>
-          <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
-            <Card title="资源剩余" bodyStyle={{ textAlign: 'center', fontSize: 0 
}} bordered={false}>
-              <WaterWave
-                height={161}
-                title="补贴资金剩余"
-                percent={34}
-              />
-            </Card>
-          </Col>
-        </Row>
-      </div>
-    );
-  }
-}
diff --git a/src/routes/Dashboard/Workplace.js 
b/src/routes/Dashboard/Workplace.js
deleted file mode 100644
index 9159cbf..0000000
--- a/src/routes/Dashboard/Workplace.js
+++ /dev/null
@@ -1,275 +0,0 @@
-import React, { PureComponent } from 'react';
-import moment from 'moment';
-import { connect } from 'dva';
-import { Link } from 'dva/router';
-import { Row, Col, Card, List, Avatar } from 'antd';
-
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import EditableLinkGroup from '../../components/EditableLinkGroup';
-import { Radar } from '../../components/Charts';
-
-import styles from './Workplace.less';
-
-const links = [
-  {
-    title: '操作一',
-    href: '',
-  },
-  {
-    title: '操作二',
-    href: '',
-  },
-  {
-    title: '操作三',
-    href: '',
-  },
-  {
-    title: '操作四',
-    href: '',
-  },
-  {
-    title: '操作五',
-    href: '',
-  },
-  {
-    title: '操作六',
-    href: '',
-  },
-];
-
-const members = [
-  {
-    id: 'members-1',
-    title: '科学搬砖组',
-    logo: 
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
-    link: '',
-  },
-  {
-    id: 'members-2',
-    title: '程序员日常',
-    logo: 
'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png',
-    link: '',
-  },
-  {
-    id: 'members-3',
-    title: '设计天团',
-    logo: 
'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png',
-    link: '',
-  },
-  {
-    id: 'members-4',
-    title: '中二少女团',
-    logo: 
'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png',
-    link: '',
-  },
-  {
-    id: 'members-5',
-    title: '骗你学计算机',
-    logo: 
'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png',
-    link: '',
-  },
-];
-
-@connect(({ project, activities, chart, loading }) => ({
-  project,
-  activities,
-  chart,
-  projectLoading: loading.effects['project/fetchNotice'],
-  activitiesLoading: loading.effects['activities/fetchList'],
-}))
-export default class Workplace extends PureComponent {
-  componentDidMount() {
-    const { dispatch } = this.props;
-    dispatch({
-      type: 'project/fetchNotice',
-    });
-    dispatch({
-      type: 'activities/fetchList',
-    });
-    dispatch({
-      type: 'chart/fetch',
-    });
-  }
-
-  componentWillUnmount() {
-    const { dispatch } = this.props;
-    dispatch({
-      type: 'chart/clear',
-    });
-  }
-
-  renderActivities() {
-    const {
-      activities: { list },
-    } = this.props;
-    return list.map((item) => {
-      const events = item.template.split(/@\{([^{}]*)\}/gi).map((key) => {
-        if (item[key]) {
-          return <a href={item[key].link} 
key={item[key].name}>{item[key].name}</a>;
-        }
-        return key;
-      });
-      return (
-        <List.Item key={item.id}>
-          <List.Item.Meta
-            avatar={<Avatar src={item.user.avatar} />}
-            title={
-              <span>
-                <a className={styles.username}>{item.user.name}</a>
-                &nbsp;
-                <span className={styles.event}>{events}</span>
-              </span>
-            }
-            description={
-              <span className={styles.datetime} title={item.updatedAt}>
-                {moment(item.updatedAt).fromNow()}
-              </span>
-            }
-          />
-        </List.Item>
-      );
-    });
-  }
-
-  render() {
-    const {
-      project: { notice },
-      projectLoading,
-      activitiesLoading,
-      chart: { radarData },
-    } = this.props;
-
-    const pageHeaderContent = (
-      <div className={styles.pageHeaderContent}>
-        <div className={styles.avatar}>
-          <Avatar size="large" 
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"; />
-        </div>
-        <div className={styles.content}>
-          <div className={styles.contentTitle}>早安,曲丽丽,祝你开心每一天!</div>
-          <div>交互专家 | 蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED</div>
-        </div>
-      </div>
-    );
-
-    const extraContent = (
-      <div className={styles.extraContent}>
-        <div className={styles.statItem}>
-          <p>项目数</p>
-          <p>56</p>
-        </div>
-        <div className={styles.statItem}>
-          <p>团队内排名</p>
-          <p>8<span> / 24</span></p>
-        </div>
-        <div className={styles.statItem}>
-          <p>项目访问</p>
-          <p>2,223</p>
-        </div>
-      </div>
-    );
-
-    return (
-      <PageHeaderLayout
-        content={pageHeaderContent}
-        extraContent={extraContent}
-      >
-        <Row gutter={24}>
-          <Col xl={16} lg={24} md={24} sm={24} xs={24}>
-            <Card
-              className={styles.projectList}
-              style={{ marginBottom: 24 }}
-              title="进行中的项目"
-              bordered={false}
-              extra={<Link to="/">全部项目</Link>}
-              loading={projectLoading}
-              bodyStyle={{ padding: 0 }}
-            >
-              {
-                notice.map(item => (
-                  <Card.Grid className={styles.projectGrid} key={item.id}>
-                    <Card bodyStyle={{ padding: 0 }} bordered={false}>
-                      <Card.Meta
-                        title={(
-                          <div className={styles.cardTitle}>
-                            <Avatar size="small" src={item.logo} />
-                            <Link to={item.href}>{item.title}</Link>
-                          </div>
-                        )}
-                        description={item.description}
-                      />
-                      <div className={styles.projectItemContent}>
-                        <Link to={item.memberLink}>{item.member || ''}</Link>
-                        {item.updatedAt && (
-                          <span className={styles.datetime} 
title={item.updatedAt}>
-                            {moment(item.updatedAt).fromNow()}
-                          </span>
-                        )}
-                      </div>
-                    </Card>
-                  </Card.Grid>
-                ))
-              }
-            </Card>
-            <Card
-              bodyStyle={{ padding: 0 }}
-              bordered={false}
-              className={styles.activeCard}
-              title="动态"
-              loading={activitiesLoading}
-            >
-              <List loading={activitiesLoading} size="large">
-                <div className={styles.activitiesList}>
-                  {this.renderActivities()}
-                </div>
-              </List>
-            </Card>
-          </Col>
-          <Col xl={8} lg={24} md={24} sm={24} xs={24}>
-            <Card
-              style={{ marginBottom: 24 }}
-              title="快速开始 / 便捷导航"
-              bordered={false}
-              bodyStyle={{ padding: 0 }}
-            >
-              <EditableLinkGroup
-                onAdd={() => {}}
-                links={links}
-                linkElement={Link}
-              />
-            </Card>
-            <Card
-              style={{ marginBottom: 24 }}
-              bordered={false}
-              title="XX 指数"
-              loading={radarData.length === 0}
-            >
-              <div className={styles.chart}>
-                <Radar hasLegend height={343} data={radarData} />
-              </div>
-            </Card>
-            <Card
-              bodyStyle={{ paddingTop: 12, paddingBottom: 12 }}
-              bordered={false}
-              title="团队"
-            >
-              <div className={styles.members}>
-                <Row gutter={48}>
-                  {
-                    members.map(item => (
-                      <Col span={12} key={`members-item-${item.id}`}>
-                        <Link to={item.link}>
-                          <Avatar src={item.logo} size="small" />
-                          <span className={styles.member}>{item.title}</span>
-                        </Link>
-                      </Col>
-                    ))
-                  }
-                </Row>
-              </div>
-            </Card>
-          </Col>
-        </Row>
-      </PageHeaderLayout>
-    );
-  }
-}
diff --git a/src/routes/Dashboard/Workplace.less 
b/src/routes/Dashboard/Workplace.less
deleted file mode 100644
index 4c87529..0000000
--- a/src/routes/Dashboard/Workplace.less
+++ /dev/null
@@ -1,233 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
-
-.activitiesList {
-  padding: 0 24px 8px 24px;
-  .username {
-    color: @text-color;
-  }
-  .event {
-    font-weight: normal;
-  }
-}
-
-.pageHeaderContent {
-  display: flex;
-  .avatar {
-    flex: 0 1 72px;
-    margin-bottom: 8px;
-    & > span {
-      border-radius: 72px;
-      display: block;
-      width: 72px;
-      height: 72px;
-    }
-  }
-  .content {
-    position: relative;
-    top: 4px;
-    margin-left: 24px;
-    flex: 1 1 auto;
-    color: @text-color-secondary;
-    line-height: 22px;
-    .contentTitle {
-      font-size: 20px;
-      line-height: 28px;
-      font-weight: 500;
-      color: @heading-color;
-      margin-bottom: 12px;
-    }
-  }
-}
-
-.extraContent {
-  .clearfix();
-  float: right;
-  white-space: nowrap;
-  .statItem {
-    padding: 0 32px;
-    position: relative;
-    display: inline-block;
-    > p:first-child {
-      color: @text-color-secondary;
-      font-size: @font-size-base;
-      line-height: 22px;
-      margin-bottom: 4px;
-    }
-    > p {
-      color: @heading-color;
-      font-size: 30px;
-      line-height: 38px;
-      margin: 0;
-      > span {
-        color: @text-color-secondary;
-        font-size: 20px;
-      }
-    }
-    &:after {
-      background-color: @border-color-split;
-      position: absolute;
-      top: 8px;
-      right: 0;
-      width: 1px;
-      height: 40px;
-      content: '';
-    }
-    &:last-child {
-      padding-right: 0;
-      &:after {
-        display: none;
-      }
-    }
-  }
-}
-
-.members {
-  a {
-    display: block;
-    margin: 12px 0;
-    line-height: 24px;
-    height: 24px;
-    .textOverflow();
-    .member {
-      font-size: @font-size-base;
-      color: @text-color;
-      line-height: 24px;
-      max-width: 100px;
-      vertical-align: top;
-      margin-left: 12px;
-      transition: all .3s;
-      display: inline-block;
-      .textOverflow();
-    }
-    &:hover {
-      span {
-        color: @primary-color;
-      }
-    }
-  }
-}
-
-.projectList {
-  :global {
-    .ant-card-meta-description {
-      color: @text-color-secondary;
-      height: 44px;
-      line-height: 22px;
-      overflow: hidden;
-    }
-  }
-  .cardTitle {
-    font-size: 0;
-    a {
-      color: @heading-color;
-      margin-left: 12px;
-      line-height: 24px;
-      height: 24px;
-      display: inline-block;
-      vertical-align: top;
-      font-size: @font-size-base;
-      &:hover {
-        color: @primary-color;
-      }
-    }
-  }
-  .projectGrid {
-    width: 33.33%;
-  }
-  .projectItemContent {
-    display: flex;
-    margin-top: 8px;
-    overflow: hidden;
-    font-size: 12px;
-    height: 20px;
-    line-height: 20px;
-    .textOverflow();
-    a {
-      color: @text-color-secondary;
-      display: inline-block;
-      flex: 1 1 0;
-      .textOverflow();
-      &:hover {
-        color: @primary-color;
-      }
-    }
-    .datetime {
-      color: @disabled-color;
-      flex: 0 0 auto;
-      float: right;
-    }
-  }
-}
-
-.datetime {
-  color: @disabled-color;
-}
-
-@media screen and (max-width: @screen-xl)  and (min-width: @screen-lg) {
-  .activeCard {
-    margin-bottom: 24px;
-  }
-  .members {
-    margin-bottom: 0;
-  }
-  .extraContent {
-    margin-left: -44px;
-    .statItem {
-      padding: 0 16px;
-    }
-  }
-}
-
-@media screen and (max-width: @screen-lg) {
-  .activeCard {
-    margin-bottom: 24px;
-  }
-  .members {
-    margin-bottom: 0;
-  }
-  .extraContent {
-    float: none;
-    margin-right: 0;
-    .statItem {
-      padding: 0 16px;
-      text-align: left;
-      &:after {
-        display: none;
-      }
-    }
-  }
-}
-
-@media screen and (max-width: @screen-md) {
-  .extraContent {
-    margin-left: -16px;
-  }
-  .projectList {
-    .projectGrid {
-      width: 50%;
-    }
-  }
-}
-
-@media screen and (max-width: @screen-sm) {
-  .pageHeaderContent {
-    display: block;
-    .content {
-      margin-left: 0;
-    }
-  }
-  .extraContent {
-    .statItem {
-      float: none;
-    }
-  }
-}
-
-@media screen and (max-width: @screen-xs) {
-  .projectList {
-    .projectGrid {
-      width: 100%;
-    }
-  }
-}
diff --git a/src/routes/Server/Server.js b/src/routes/Server/Server.js
new file mode 100644
index 0000000..835f05b
--- /dev/null
+++ b/src/routes/Server/Server.js
@@ -0,0 +1,193 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Row, Col, Card, Form } from 'antd';
+import {
+  ChartCard, MiniArea, MiniBar, Line, Area, StackBar,
+} from '../../components/Charts';
+import DescriptionList from '../../components/DescriptionList';
+import { timeRange } from '../../utils/utils';
+import { Panel, Search } from '../../components/Page';
+
+const { Description } = DescriptionList;
+const { Item: FormItem } = Form;
+
+@connect(state => ({
+  server: state.server,
+  duration: state.global.duration,
+  globalVariables: state.global.globalVariables,
+}))
[email protected]({
+  mapPropsToFields(props) {
+    const { variables: { values, labels } } = props.server;
+    return {
+      serverId: Form.createFormField({
+        value: { key: values.serverId ? values.serverId : '', label: 
labels.serverId ? labels.serverId : '' },
+      }),
+    };
+  },
+})
+export default class Server extends PureComponent {
+  handleSelect = (selected) => {
+    this.props.dispatch({
+      type: 'server/save',
+      payload: {
+        variables: {
+          values: { serverId: selected.key },
+          labels: { serverId: selected.label },
+        },
+        data: {
+          serverInfo: selected,
+        },
+      },
+    });
+  }
+  handleChange = (variables) => {
+    this.props.dispatch({
+      type: 'server/fetchData',
+      payload: { variables },
+    });
+  }
+  avg = list => (list.length > 0 ?
+    (list.reduce((acc, curr) => acc + curr) / list.length).toFixed(2) : 0)
+  render() {
+    const { getFieldDecorator } = this.props.form;
+    const { variables: { values }, data } = this.props.server;
+    const { serverInfo, getServerResponseTimeTrend, getServerTPSTrend,
+      getCPUTrend, getMemoryTrend, getGCTrend } = data;
+    const timeRangeArray = timeRange(this.props.duration);
+    return (
+      <div>
+        <Form layout="inline">
+          <FormItem>
+            {getFieldDecorator('serverId')(
+              <Search
+                placeholder="Select a server"
+                onSelect={this.handleSelect.bind(this)}
+                url="/server/search"
+                variables={{ duration: this.props.globalVariables.duration }}
+                query={`
+                  query SearchServer($keyword: String!, $duration: Duration!) {
+                    searchServer(keyword: $keyword, duration: $duration) {
+                      key: id
+                      label: name
+                      host
+                      pid
+                      ipv4
+                    }
+                  }
+                `}
+              />
+            )}
+          </FormItem>
+        </Form>
+        <Panel
+          variables={values}
+          globalVariables={this.props.globalVariables}
+          onChange={this.handleChange}
+        >
+          <Card title="Info" style={{ marginTop: 24 }} bordered={false}>
+            <DescriptionList>
+              <Description term="OS">{serverInfo.label}</Description>
+              <Description term="Host Name">{serverInfo.host}</Description>
+              <Description term="Process Id">{serverInfo.pid}</Description>
+              <Description term="IPv4">{serverInfo.ipv4 ? 
serverInfo.ipv4.join() : ''}</Description>
+            </DescriptionList>
+          </Card>
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 
24 }}>
+              <ChartCard
+                title="Avg Response Time"
+                total={`${this.avg(getServerResponseTimeTrend.trendList)} ms`}
+              >
+                <MiniArea
+                  animate={false}
+                  color="#975FE4"
+                  height={46}
+                  data={getServerResponseTimeTrend.trendList
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v }; })}
+                />
+              </ChartCard>
+            </Col>
+            <Col xs={24} sm={24} md={24} lg={12} xl={12} style={{ marginTop: 
24 }}>
+              <ChartCard
+                title="Avg TPS"
+                total={`${this.avg(getServerTPSTrend.trendList)} ms`}
+              >
+                <MiniBar
+                  animate={false}
+                  height={46}
+                  data={getServerTPSTrend.trendList
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v }; })}
+                />
+              </ChartCard>
+            </Col>
+          </Row>
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: 
24 }}>
+              <Card
+                title="CPU"
+                bordered={false}
+                bodyStyle={{ padding: 0 }}
+              >
+                <Line
+                  height={250}
+                  data={getCPUTrend.cost
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v }; })}
+                />
+              </Card>
+            </Col>
+          </Row>
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={12} lg={12} xl={12} style={{ marginTop: 
24 }}>
+              <Card
+                title="Heap"
+                bordered={false}
+                bodyStyle={{ padding: 0 }}
+              >
+                <Area
+                  height={250}
+                  data={getMemoryTrend.heap
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 
'value' }))
+                    .concat(getMemoryTrend.maxHeap
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 'limit 
' })))}
+                />
+              </Card>
+            </Col>
+            <Col xs={24} sm={24} md={12} lg={12} xl={12} style={{ marginTop: 
24 }}>
+              <Card
+                title="No-Heap"
+                bordered={false}
+                bodyStyle={{ padding: 0 }}
+              >
+                <Area
+                  height={250}
+                  data={getMemoryTrend.noheap
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 
'value' }))
+                    .concat(getMemoryTrend.maxNoheap
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 'limit 
' })))}
+                />
+              </Card>
+            </Col>
+          </Row>
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: 
24 }}>
+              <Card
+                title="GC"
+                bordered={false}
+                bodyStyle={{ padding: 0 }}
+              >
+                <StackBar
+                  height={250}
+                  data={getGCTrend.oldGC
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 
'oldGC' }))
+                    .concat(getGCTrend.youngGC
+                    .map((v, i) => ({ x: timeRangeArray[i], y: v, type: 
'youngGC' })))}
+                />
+              </Card>
+            </Col>
+          </Row>
+        </Panel>
+      </div>
+    );
+  }
+}
diff --git a/src/routes/Service/Service.js b/src/routes/Service/Service.js
new file mode 100644
index 0000000..4abb882
--- /dev/null
+++ b/src/routes/Service/Service.js
@@ -0,0 +1,139 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Row, Col, Card, Form } from 'antd';
+import {
+  ChartCard, MiniArea, MiniBar,
+} from '../../components/Charts';
+import { timeRange } from '../../utils/utils';
+import { ServiceTopology } from '../../components/Topology';
+import { Panel, Search } from '../../components/Page';
+
+const { Item: FormItem } = Form;
+
+@connect(state => ({
+  service: state.service,
+  duration: state.global.duration,
+  globalVariables: state.global.globalVariables,
+}))
[email protected]({
+  mapPropsToFields(props) {
+    const { variables: { values, labels } } = props.service;
+    return {
+      serviceId: Form.createFormField({
+        value: { key: values.serviceId ? values.serviceId : '', label: 
labels.serviceId ? labels.serviceId : '' },
+      }),
+    };
+  },
+})
+export default class Service extends PureComponent {
+  handleSelect = (selected) => {
+    this.props.dispatch({
+      type: 'service/save',
+      payload: {
+        variables: {
+          values: { serviceId: selected.key },
+          labels: { serviceId: selected.label },
+        },
+        data: {
+          serviceInfo: selected,
+        },
+      },
+    });
+  }
+  handleChange = (variables) => {
+    this.props.dispatch({
+      type: 'service/fetchData',
+      payload: { variables },
+    });
+  }
+  avg = list => (list.length > 0 ?
+    (list.reduce((acc, curr) => acc + curr) / list.length).toFixed(2) : 0)
+  render() {
+    const { getFieldDecorator } = this.props.form;
+    const { variables: { values }, data } = this.props.service;
+    const { getServiceResponseTimeTrend, getServiceTPSTrend,
+      getServiceSLATrend, getServiceTopology } = data;
+    const timeRangeArray = timeRange(this.props.duration);
+    return (
+      <div>
+        <Form layout="inline">
+          <FormItem>
+            {getFieldDecorator('serviceId')(
+              <Search
+                placeholder="Search a service"
+                onSelect={this.handleSelect.bind(this)}
+                url="/service/search"
+                query={`
+                  query SearchService($keyword: String!) {
+                    searchService(keyword: $keyword, topN: 10) {
+                      key: id
+                      label: name
+                    }
+                  }
+                `}
+              />
+            )}
+          </FormItem>
+        </Form>
+        <Panel
+          variables={values}
+          globalVariables={this.props.globalVariables}
+          onChange={this.handleChange}
+        >
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={24} lg={8} xl={8} style={{ marginTop: 24 
}}>
+              <ChartCard
+                title="Avg Throughout"
+                total={`${this.avg(getServiceTPSTrend.trendList)}`}
+              >
+                <MiniArea
+                  animate={false}
+                  color="#975FE4"
+                  height={46}
+                  data={getServiceTPSTrend.trendList
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v }; })}
+                />
+              </ChartCard>
+            </Col>
+            <Col xs={24} sm={24} md={24} lg={8} xl={8} style={{ marginTop: 24 
}}>
+              <ChartCard
+                title="Avg Response Time"
+                total={`${this.avg(getServiceResponseTimeTrend.trendList)} ms`}
+              >
+                <MiniArea
+                  animate={false}
+                  height={46}
+                  data={getServiceResponseTimeTrend.trendList
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v }; })}
+                />
+              </ChartCard>
+            </Col>
+            <Col xs={24} sm={24} md={24} lg={8} xl={8} style={{ marginTop: 24 
}}>
+              <ChartCard
+                title="Avg SLA"
+                total={`${this.avg(getServiceSLATrend.trendList) / 100} %`}
+              >
+                <MiniBar
+                  animate={false}
+                  height={46}
+                  data={getServiceSLATrend.trendList
+                    .map((v, i) => { return { x: timeRangeArray[i], y: v / 100 
}; })}
+                />
+              </ChartCard>
+            </Col>
+          </Row>
+          <Row gutter={24}>
+            <Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: 
24 }}>
+              <Card
+                bordered={false}
+                bodyStyle={{ padding: 0 }}
+              >
+                <ServiceTopology elements={getServiceTopology} layout={{ name: 
'concentric', minNodeSpacing: 200 }} />
+              </Card>
+            </Col>
+          </Row>
+        </Panel>
+      </div>
+    );
+  }
+}
diff --git a/src/routes/Service/Service.less b/src/routes/Service/Service.less
new file mode 100644
index 0000000..e69de29
diff --git a/src/routes/Topology/Topology.js b/src/routes/Topology/Topology.js
new file mode 100644
index 0000000..b234ce8
--- /dev/null
+++ b/src/routes/Topology/Topology.js
@@ -0,0 +1,37 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { ChartCard } from '../../components/Charts';
+import { AppTopology } from '../../components/Topology';
+import { Panel } from '../../components/Page';
+
+@connect(state => ({
+  topology: state.topology,
+  duration: state.global.duration,
+  globalVariables: state.global.globalVariables,
+}))
+export default class Topology extends PureComponent {
+  static defaultProps = {
+    graphHeight: 600,
+  };
+  handleChange = (variables) => {
+    this.props.dispatch({
+      type: 'topology/fetchData',
+      payload: { variables },
+    });
+  }
+  render() {
+    const { data } = this.props.topology;
+    return (
+      <Panel globalVariables={this.props.globalVariables} 
onChange={this.handleChange}>
+        <ChartCard
+          title="Topolgy Map"
+        >
+          <AppTopology
+            height={this.props.graphHeight}
+            elements={data.getClusterTopology}
+          />
+        </ChartCard>
+      </Panel>
+    );
+  }
+}
diff --git a/src/routes/Trace/Trace.js b/src/routes/Trace/Trace.js
new file mode 100644
index 0000000..0730ed9
--- /dev/null
+++ b/src/routes/Trace/Trace.js
@@ -0,0 +1,184 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Row, Col, Form, Input, Select, Button, Card, InputNumber } from 
'antd';
+import TraceTable from '../../components/TraceTable';
+import { Panel } from '../../components/Page';
+import styles from './Trace.less';
+
+const { Option } = Select;
+const FormItem = Form.Item;
+
+
+@connect(state => ({
+  trace: state.trace,
+  duration: state.global.duration,
+  loading: state.loading.models.trace,
+  globalVariables: state.global.globalVariables,
+}))
[email protected]({
+  mapPropsToFields(props) {
+    const { variables: { values } } = props.trace;
+    const result = {};
+    Object.keys(values).forEach((_) => {
+      result[_] = Form.createFormField({
+        value: values[_],
+      });
+    });
+    return result;
+  },
+})
+export default class Trace extends PureComponent {
+  componentDidMount() {
+    this.props.dispatch({
+      type: 'trace/initOptions',
+      payload: { variables: this.props.globalVariables },
+    });
+  }
+  componentWillUpdate(nextProps) {
+    if (nextProps.globalVariables.duration === 
this.props.globalVariables.duration) {
+      return;
+    }
+    this.props.dispatch({
+      type: 'trace/initOptions',
+      payload: { variables: nextProps.globalVariables },
+    });
+  }
+  handleChange = (variables) => {
+    const filteredVariables = { ...variables };
+    filteredVariables.queryDuration = filteredVariables.duration;
+    delete filteredVariables.duration;
+    if (filteredVariables.applicationId && 
!Array.isArray(filteredVariables.applicationId)) {
+      filteredVariables.applicationId = [filteredVariables.applicationId];
+    }
+    this.props.dispatch({
+      type: 'trace/fetchData',
+      payload: { variables: { condition: filteredVariables } },
+    });
+  }
+  handleSearch = (e) => {
+    if (e) {
+      e.preventDefault();
+    }
+    const { dispatch, form, globalVariables: { duration } } = this.props;
+
+    form.validateFields((err, fieldsValue) => {
+      if (err) return;
+      dispatch({
+        type: 'trace/saveVariables',
+        payload: {
+          values: {
+            ...fieldsValue,
+            queryDuration: duration,
+            paging: {
+              pageNum: 1,
+              pageSize: 10,
+              needTotal: true,
+            },
+          },
+        },
+      });
+    });
+  }
+  handleTableChange = (pagination) => {
+    const { dispatch, globalVariables: { duration },
+      trace: { variables: { values } } } = this.props;
+    dispatch({
+      type: 'trace/saveVariables',
+      payload: {
+        values: {
+          ...values,
+          queryDuration: duration,
+          paging: {
+            pageNum: pagination.current,
+            pageSize: pagination.pageSize,
+            needTotal: true,
+          },
+        },
+      },
+    });
+  }
+  handleTableExpand = (expanded, record) => {
+    const { dispatch } = this.props;
+    if (expanded && !record.spans) {
+      dispatch({
+        type: 'trace/fetchSpans',
+        payload: { variables: { traceId: record.traceId } },
+      });
+    }
+  }
+  renderForm() {
+    const { getFieldDecorator } = this.props.form;
+    const { trace: { variables: { options } } } = this.props;
+    return (
+      <Form onSubmit={this.handleSearch} layout="inline">
+        <Row gutter={{ md: 8, lg: 8, xl: 8 }}>
+          <Col xl={4} sm={24}>
+            <FormItem label="Application">
+              {getFieldDecorator('applicationId')(
+                <Select placeholder="No application" style={{ width: '100%' }}>
+                  {options.applicationId && options.applicationId.map((app) => 
{
+                      return (<Option value={app.key}>{app.label}</Option>);
+                    })}
+                </Select>
+              )}
+            </FormItem>
+          </Col>
+          <Col xl={4} sm={24}>
+            <FormItem label="TraceId">
+              {getFieldDecorator('traceId')(
+                <Input placeholder="Input trace id" />
+              )}
+            </FormItem>
+          </Col>
+          <Col xl={6} sm={24}>
+            <FormItem label="OperationName">
+              {getFieldDecorator('operationName')(
+                <Input placeholder="Input operation name" />
+              )}
+            </FormItem>
+          </Col>
+          <Col xl={6} sm={24}>
+            <FormItem label="DurationRange">
+              {getFieldDecorator('minTraceDuration')(
+                <InputNumber style={{ width: '40%' }} />
+              )}~
+              {getFieldDecorator('maxTraceDuration')(
+                <InputNumber style={{ width: '40%' }} />
+              )}
+            </FormItem>
+          </Col>
+          <Col xl={4} sm={24}>
+            <span className={styles.submitButtons}>
+              <Button type="primary" htmlType="submit">Search</Button>
+            </span>
+          </Col>
+        </Row>
+      </Form>
+    );
+  }
+  render() {
+    const { trace: { variables: { values }, data: { queryBasicTraces } }, 
loading } = this.props;
+    return (
+      <Card bordered={false}>
+        <div className={styles.tableList}>
+          <div className={styles.tableListForm}>
+            {this.renderForm()}
+          </div>
+          <Panel
+            variables={values}
+            globalVariables={this.props.globalVariables}
+            onChange={this.handleChange}
+          >
+            <TraceTable
+              loading={loading}
+              data={queryBasicTraces.traces}
+              pagination={{ ...values.paging, total: queryBasicTraces.total }}
+              onChange={this.handleTableChange}
+              onExpand={this.handleTableExpand}
+            />
+          </Panel>
+        </div>
+      </Card>
+    );
+  }
+}
diff --git a/src/routes/Trace/Trace.less b/src/routes/Trace/Trace.less
new file mode 100644
index 0000000..5920cd1
--- /dev/null
+++ b/src/routes/Trace/Trace.less
@@ -0,0 +1,48 @@
+@import "~antd/lib/style/themes/default.less";
+@import "../../utils/utils.less";
+
+.tableList {
+  .tableListOperator {
+    margin-bottom: 16px;
+    button {
+      margin-right: 8px;
+    }
+  }
+}
+
+.tableListForm {
+  :global {
+    .ant-form-item {
+      margin-bottom: 24px;
+      margin-right: 0;
+      display: flex;
+      > .ant-form-item-label {
+        width: auto;
+        line-height: 32px;
+        padding-right: 8px;
+      }
+      .ant-form-item-control {
+        line-height: 32px;
+      }
+    }
+    .ant-form-item-control-wrapper {
+      flex: 1;
+    }
+  }
+  .submitButtons {
+    white-space: nowrap;
+    margin-bottom: 24px;
+  }
+}
+
+@media screen and (max-width: @screen-lg) {
+  .tableListForm :global(.ant-form-item) {
+    margin-right: 24px;
+  }
+}
+
+@media screen and (max-width: @screen-md) {
+  .tableListForm :global(.ant-form-item) {
+    margin-right: 8px;
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to