Merge branch 'master' of github.com:didi/nightingale

master
710leo 5 years ago
commit 59f2bb003b

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
{"version":3,"file":"index-520b59d8379e5c351e38.js","sources":["webpack:///index-520b59d8379e5c351e38.js"],"mappings":"AAAA;;;;;;;AAuhZA","sourceRoot":""}

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
{"version":3,"file":"index-5c8cbf958683e20086f5.js","sources":["webpack:///index-5c8cbf958683e20086f5.js"],"mappings":"AAAA;;;;;;;AAmsYA","sourceRoot":""}

@ -1 +1 @@
<!doctype html><html><head><meta charset="UTF-8"><title>Nightingale</title><link rel="shortcut icon" href="/favicon.ico"><link href="/index-5c8cbf958683e20086f5.css" rel="stylesheet"></head><body><div id="react-content"></div><script src="/lib-033bee8514de110e36ef.dll.js"></script><script src="/index-5c8cbf958683e20086f5.js"></script></body></html>
<!doctype html><html><head><meta charset="UTF-8"><title>Nightingale</title><link rel="shortcut icon" href="/favicon.ico"><link href="/index-520b59d8379e5c351e38.css" rel="stylesheet"></head><body><div id="react-content"></div><script src="/lib-033bee8514de110e36ef.dll.js"></script><script src="/index-520b59d8379e5c351e38.js"></script></body></html>

@ -32,11 +32,13 @@ func consume(event *model.Event) {
return
}
// 配置了升级策略,但不代表每个事件都要升级,比如判断时间是否到了升级条件
if event.NeedUpgrade == 1 {
event.RealUpgrade = needUpgrade(event)
}
if event.RealUpgrade {
// 确实需要升级的话,事件级别要改成升级之后的级别
if err := updatePriority(event); err != nil {
return
}
@ -57,6 +59,7 @@ func consume(event *model.Event) {
SetEventStatus(event, model.STATUS_CALLBACK)
}
// 如果需要升级,需要在这个方法里把升级策略里配置的升级人员也解析出来
if err := fillRecvs(event); err != nil {
return
}
@ -210,7 +213,7 @@ func isInConverge(event *model.Event) bool {
}
if cnt >= convergeMaxCounts {
logger.Infof("converge max counts: %c reached, current: %v, event hashid: %v", convergeMaxCounts, cnt, event.HashId)
logger.Infof("converge max counts: %d reached, current: %v, event hashid: %v", convergeMaxCounts, cnt, event.HashId)
return true
}
@ -220,7 +223,7 @@ func isInConverge(event *model.Event) bool {
// 三种情况,不需要升级报警
// 1认领的报警不需要升级
// 2忽略的报警不需要升级
// 3屏蔽的报警不需要升级
// 3屏蔽的报警不需要升级,屏蔽判断在前面已经有了处理,这个方法不用关注
func needUpgrade(event *model.Event) bool {
alertUpgradeKey := PrefixAlertUpgrade + fmt.Sprint(event.HashId)
eventAlertKey := PrefixAlertTime + fmt.Sprint(event.HashId)

@ -79,7 +79,7 @@ func popEvent(queues []interface{}) (*model.Event, bool) {
// 可能endpoint挪了节点
endpoint, err := model.EndpointGet("ident", event.Endpoint)
if err != nil {
logger.Errorf("get host_id failed, event: %+v, err: %v", event, err)
logger.Errorf("model.EndpointGet fail, event: %+v, err: %v", event, err)
return nil, true
}

@ -2313,6 +2313,43 @@
"resize-detector": "^0.2.0"
}
},
"@formatjs/intl-displaynames": {
"version": "1.2.2",
"resolved": "http://registry.npm.xiaojukeji.com/@formatjs/intl-displaynames/download/@formatjs/intl-displaynames-1.2.2.tgz",
"integrity": "sha1-0PDt662WZgGW0oL2CaEWmoiifUo=",
"requires": {
"@formatjs/intl-utils": "^2.2.0"
}
},
"@formatjs/intl-listformat": {
"version": "1.4.2",
"resolved": "http://registry.npm.xiaojukeji.com/@formatjs/intl-listformat/download/@formatjs/intl-listformat-1.4.2.tgz",
"integrity": "sha1-7YJQEH6E+qn+n6/YYCAi6NnWoXk=",
"requires": {
"@formatjs/intl-utils": "^2.2.0"
}
},
"@formatjs/intl-relativetimeformat": {
"version": "4.5.10",
"resolved": "http://registry.npm.xiaojukeji.com/@formatjs/intl-relativetimeformat/download/@formatjs/intl-relativetimeformat-4.5.10.tgz",
"integrity": "sha1-XCN3XaQ2bo43Y8kAJwcR1CoPT7c=",
"requires": {
"@formatjs/intl-utils": "^2.2.0"
}
},
"@formatjs/intl-unified-numberformat": {
"version": "3.3.0",
"resolved": "http://registry.npm.xiaojukeji.com/@formatjs/intl-unified-numberformat/download/@formatjs/intl-unified-numberformat-3.3.0.tgz",
"integrity": "sha1-BpI0apzUMquyzZtoed3WWSWFZBo=",
"requires": {
"@formatjs/intl-utils": "^2.2.0"
}
},
"@formatjs/intl-utils": {
"version": "2.2.0",
"resolved": "http://registry.npm.xiaojukeji.com/@formatjs/intl-utils/download/@formatjs/intl-utils-2.2.0.tgz",
"integrity": "sha1-um4S/mT/f9FgvjkgB8R9JLeuXHU="
},
"@hot-loader/react-dom": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.8.6.tgz",
@ -2402,6 +2439,11 @@
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/invariant": {
"version": "2.2.31",
"resolved": "http://registry.npm.xiaojukeji.com/@types/invariant/download/@types/invariant-2.2.31.tgz",
"integrity": "sha1-RETAMATyFSidvKOFZThDQxfdKLI="
},
"@types/lodash": {
"version": "4.14.149",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
@ -7952,6 +7994,28 @@
"ipaddr.js": "^1.9.0"
}
},
"intl-format-cache": {
"version": "4.2.22",
"resolved": "http://registry.npm.xiaojukeji.com/intl-format-cache/download/intl-format-cache-4.2.22.tgz",
"integrity": "sha1-tafory9Dnq/r0MQOP+bNilTnnR0="
},
"intl-messageformat": {
"version": "8.2.3",
"resolved": "http://registry.npm.xiaojukeji.com/intl-messageformat/download/intl-messageformat-8.2.3.tgz",
"integrity": "sha1-CQ6T8uX347l8fOmpTfqZLz0OjE8=",
"requires": {
"intl-format-cache": "^4.2.22",
"intl-messageformat-parser": "^4.1.1"
}
},
"intl-messageformat-parser": {
"version": "4.1.1",
"resolved": "http://registry.npm.xiaojukeji.com/intl-messageformat-parser/download/intl-messageformat-parser-4.1.1.tgz",
"integrity": "sha1-M6OsGFSoua3Bjfxz2wGKv5G+TDI=",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.3.0"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -11421,6 +11485,40 @@
"source-map": "^0.7.3"
}
},
"react-intl": {
"version": "4.2.2",
"resolved": "http://registry.npm.xiaojukeji.com/react-intl/download/react-intl-4.2.2.tgz",
"integrity": "sha1-VLQGXHTXd8RtnURZASSqkm39nCE=",
"requires": {
"@formatjs/intl-displaynames": "^1.2.2",
"@formatjs/intl-listformat": "^1.4.2",
"@formatjs/intl-relativetimeformat": "^4.5.10",
"@formatjs/intl-unified-numberformat": "^3.3.0",
"@formatjs/intl-utils": "^2.2.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/invariant": "^2.2.31",
"hoist-non-react-statics": "^3.3.2",
"intl-format-cache": "^4.2.22",
"intl-messageformat": "^8.2.3",
"intl-messageformat-parser": "^4.1.1",
"shallow-equal": "^1.2.1"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "http://registry.npm.xiaojukeji.com/hoist-non-react-statics/download/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=",
"requires": {
"react-is": "^16.7.0"
}
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "http://registry.npm.xiaojukeji.com/shallow-equal/download/shallow-equal-1.2.1.tgz",
"integrity": "sha1-TBar+lYEOqINBQMk76aJQLDaedo="
}
}
},
"react-is": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",

@ -38,6 +38,7 @@
"react-dom": "^16.8.6",
"react-highlight": "^0.12.0",
"react-hot-loader": "^4.8.7",
"react-intl": "^4.2.2",
"react-router-dom": "4.x",
"react-sortable-hoc": "^1.8.3",
"react-syntax-highlighter": "^7.0.4",

@ -4,3 +4,5 @@ declare module 'd3';
declare module 'd3-scale-chromatic';
declare module '@d3-charts/ts-graph';
declare module 'react-sortable-hoc';
declare module 'rc-calendar';
declare module 'rc-calendar/lib/locale/en_US';

@ -1,9 +1,16 @@
import React from 'react';
import React, { useState } from 'react';
import { HashRouter, Switch, Route, Redirect } from 'react-router-dom';
import { hot } from 'react-hot-loader/root';
import { ConfigProvider } from 'antd';
import antdZhCN from 'antd/lib/locale/zh_CN';
import antdEnUS from 'antd/lib/locale/en_US';
import { IntlProvider } from 'react-intl';
import _ from 'lodash';
import { Page403, Page404 } from '@cpts/Exception';
import { Login, Register, PrivateRoute } from '@cpts/Auth';
import Layout from './components/Layout';
import Layout from '@cpts/Layout';
import intlZhCN from './locales/zh';
import intlEnUS from './locales/en';
import Monitor from './pages/Monitor';
import ServiceTree from './pages/ServiceTree';
import User from './pages/User';
@ -13,87 +20,120 @@ interface Props {
habitsId: string;
}
interface LocaleMap {
[index: string]: any,
}
const localeMap: LocaleMap = {
zh: {
antd: antdZhCN,
intl: 'zh',
intlMessages: intlZhCN,
},
en: {
antd: antdEnUS,
intl: 'en',
intlMessages: intlEnUS,
},
};
const defaultLanguage = window.localStorage.getItem('language') || navigator.language.substr(0, 2);
function App({ habitsId }: Props) {
const [language, setLanguage] = useState(defaultLanguage);
const intlMessages = _.get(localeMap[language], 'intlMessages', intlZhCN);
const menuConf = [
{
name: '监控对象',
name: intlMessages['menu.endpoints'],
path: 'sTree',
icon: 'cluster',
children: [
{
name: '全部对象',
name: intlMessages['menu.endpoints.all'],
path: 'endpointMgmt',
}, {
name: '节点下对象',
name: intlMessages['menu.endpoints.node'],
path: 'endpoints',
}, {
name: '树节点管理',
name: intlMessages['menu.endpoints.node.manage'],
path: 'node',
},
],
}, {
name: '监控报警',
name: intlMessages['menu.monitor'],
path: 'monitor',
icon: 'icon-speed-fast',
children: [
{
name: '监控看图',
name: intlMessages['menu.monitor.dashboard'],
path: 'dashboard',
}, {
name: '监控大盘',
name: intlMessages['menu.monitor.screen'],
path: 'screen',
}, {
name: '报警策略',
name: intlMessages['menu.monitor.strategy'],
path: 'strategy',
}, {
name: '报警历史',
name: intlMessages['menu.monitor.history'],
path: 'history',
}, {
name: '报警屏蔽',
name: intlMessages['menu.monitor.silence'],
path: 'silence',
}, {
name: '采集配置',
name: intlMessages['menu.monitor.collect'],
path: 'collect',
},
],
}, {
name: '用户管理',
name: intlMessages['menu.users'],
path: 'user',
icon: 'icon-users2',
children: [
{
name: '用户管理',
name: intlMessages['menu.users.users'],
path: 'list',
}, {
name: '团队管理',
name: intlMessages['menu.users.teams'],
path: 'team',
},
],
},
];
return (
<HashRouter>
<Switch>
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route path="/403" component={Page403} />
<Route path="/404" component={Page404} />
<Layout
appName=""
menuConf={menuConf}
habitsId={habitsId}
>
<IntlProvider
locale={_.get(localeMap[language], 'intl', 'zh')}
messages={intlMessages}
>
<ConfigProvider locale={_.get(localeMap[language], 'antd', antdZhCN)}>
<HashRouter>
<Switch>
<Route exact path="/" render={() => <Redirect to="/sTree" />} />
<PrivateRoute path="/monitor" component={Monitor} />
<PrivateRoute path="/sTree" component={ServiceTree} />
<PrivateRoute path="/user" component={User} />
<PrivateRoute path="/profile" component={Profile} />
<Route render={() => <Redirect to="/404" />} />
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route path="/403" component={Page403} />
<Route path="/404" component={Page404} />
<Layout
appName=""
menuConf={menuConf}
habitsId={habitsId}
language={language}
onLanguageChange={(newLanguage) => {
setLanguage(newLanguage);
window.localStorage.setItem('language', newLanguage);
}}
>
<Switch>
<Route exact path="/" render={() => <Redirect to="/sTree" />} />
<PrivateRoute path="/monitor" component={Monitor} />
<PrivateRoute path="/sTree" component={ServiceTree} />
<PrivateRoute path="/user" component={User} />
<PrivateRoute path="/profile" component={Profile} />
<Route render={() => <Redirect to="/404" />} />
</Switch>
</Layout>
</Switch>
</Layout>
</Switch>
</HashRouter>
</HashRouter>
</ConfigProvider>
</IntlProvider>
);
}

@ -4,13 +4,14 @@ import { Card, Form, Input, Icon, Button, Checkbox } from 'antd';
import { FormProps } from 'antd/lib/form';
import queryString from 'query-string';
import _ from 'lodash';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { appname } from '@common/config';
import auth from './auth';
import './style.less';
const FormItem = Form.Item;
class Login extends Component<RouteComponentProps & FormProps> {
class Login extends Component<RouteComponentProps & FormProps & WrappedComponentProps> {
handleSubmit = (e: FormEvent) => {
e.preventDefault();
const { history, location } = this.props;
@ -47,6 +48,7 @@ class Login extends Component<RouteComponentProps & FormProps> {
const { history } = this.props;
const { getFieldDecorator } = this.props.form!;
const isAuthenticated = auth.getIsAuthenticated();
const { formatMessage } = this.props.intl;
if (isAuthenticated) {
history.push({
@ -58,20 +60,20 @@ class Login extends Component<RouteComponentProps & FormProps> {
<div className={prefixCls}>
<div className={`${prefixCls}-main`}>
<Card>
<div className={`${prefixCls}-title`}></div>
<div className={`${prefixCls}-title`}>{formatMessage({ id: 'login.title' })}</div>
<Form onSubmit={this.handleSubmit}>
<FormItem>
{getFieldDecorator('username', {
rules: [{ required: true, message: '请输入你的用户名!' }],
rules: [{ required: true }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="用户名" />,
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder={formatMessage({ id: 'user.username' })} />,
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: '请输入你的密码!' }],
rules: [{ required: true }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="密码" />,
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder={formatMessage({ id: 'user.password' })} />,
)}
</FormItem>
<FormItem>
@ -79,10 +81,10 @@ class Login extends Component<RouteComponentProps & FormProps> {
valuePropName: 'checked',
initialValue: false,
})(
<Checkbox>使LDAP</Checkbox>,
<Checkbox>{formatMessage({ id: 'login.ldap' })}</Checkbox>,
)}
<Button type="primary" htmlType="submit" className={`${prefixCls}-submitBtn`}>
{formatMessage({ id: 'form.login' })}
</Button>
</FormItem>
</Form>
@ -93,4 +95,4 @@ class Login extends Component<RouteComponentProps & FormProps> {
}
}
export default Form.create()(Login);
export default injectIntl(Form.create()(Login));

@ -2,17 +2,19 @@ import React, { Component, FormEvent } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Card, Button, message } from 'antd';
import queryString from 'query-string';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import ProfileForm from '@cpts/ProfileForm';
import request from '@common/request';
import api from '@common/api';
import { appname } from '@common/config';
import './style.less';
class Register extends Component<RouteComponentProps> {
class Register extends Component<RouteComponentProps & WrappedComponentProps> {
profileForm: any; // TODO useRef
handleSubmit = (e: FormEvent) => {
e.preventDefault();
const { location, history } = this.props;
const { formatMessage } = this.props.intl;
const query = queryString.parse(location.search);
this.profileForm.validateFields((err: any, values: any) => {
if (!err) {
@ -23,7 +25,7 @@ class Register extends Component<RouteComponentProps> {
token: query.token,
}),
}).then(() => {
message.success('注册成功!');
message.success(formatMessage({ id: 'msg.submit.success' }));
history.push({
pathname: '/',
});
@ -34,19 +36,20 @@ class Register extends Component<RouteComponentProps> {
render() {
const prefixCls = `${appname}-register`;
const { formatMessage } = this.props.intl;
return (
<div className={prefixCls}>
<div className={`${prefixCls}-main`}>
<Card>
<div className={`${prefixCls}-title`}></div>
<div className={`${prefixCls}-title`}>{formatMessage({ id: 'register' })}</div>
<ProfileForm type="register" ref={(ref: any) => { this.profileForm = ref; }} />
<Button
type="primary"
className={`${prefixCls}-submitBtn`}
onClick={this.handleSubmit}
>
{formatMessage({ id: 'register' })}
</Button>
</Card>
</div>
@ -55,4 +58,4 @@ class Register extends Component<RouteComponentProps> {
}
}
export default Register;
export default injectIntl(Register);

@ -1,123 +0,0 @@
import React, { Component } from 'react';
import { Table } from 'antd';
import _ from 'lodash';
import queryString from 'query-string';
import api from '@common/api';
import * as config from '@common/config';
import request from '@common/request';
import './style.less';
export default class BaseComponent extends Component {
constructor(props) {
super(props);
this.api = api;
this.config = config;
this.prefixCls = config.appname;
this.request = request;
this.otherParamsKey = [];
this.state = {
loading: false,
pagination: {
current: 1,
pageSize: 10,
showSizeChanger: true,
},
data: [],
searchValue: '',
};
}
handleSearchChange = (value) => {
this.setState({ searchValue: value }, () => {
this.reload({
query: value,
}, true);
});
}
handleTableChange = (pagination) => {
const { pagination: paginationState } = this.state;
const pager = {
...paginationState,
current: pagination.current,
pageSize: pagination.pageSize,
};
this.setState({ pagination: pager }, () => {
this.reload({
limit: pagination.pageSize,
page: pagination.current,
});
});
}
reload(params) {
this.fetchData(params);
}
fetchData(newParams = {}, backFirstPage = false) {
const url = this.getFetchDataUrl();
if (!url) return;
const othenParams = _.pick(this.state, this.otherParamsKey);
const { pagination, searchValue } = this.state;
const params = {
limit: pagination.pageSize,
p: backFirstPage ? 1 : pagination.current,
query: searchValue,
...othenParams,
...newParams,
};
this.setState({ loading: true });
// TODO: Method 'fetchData' expected no return value.
// eslint-disable-next-line consistent-return
return this.request(`${url}?${queryString(params)}`).then((res) => {
const newPagination = {
...pagination,
current: backFirstPage ? 1 : pagination.current,
total: res.total,
};
let data = [];
if (_.isArray(res.list)) {
data = res.list;
} else if (_.isArray(res)) {
data = res;
}
this.setState({
data,
pagination: newPagination,
});
return data;
}).finally(() => {
this.setState({ loading: false });
});
}
renderTable(params) {
const { loading, pagination, data } = this.state;
return (
<Table
rowKey="id"
size="small"
loading={loading}
pagination={{
...pagination,
showTotal: total => `${total} 条数据`,
pageSizeOptions: config.defaultPageSizeOptions,
onChange: () => {
if (this.handlePaginationChange) this.handlePaginationChange();
},
}}
rowClassName={(record, index) => {
if (index % 2 === 1) {
return 'table-row-bg';
}
return '';
}}
dataSource={data}
onChange={this.handleTableChange}
{...params}
/>
);
}
}

@ -1,3 +0,0 @@
.table-row-bg {
background: #f9f9f9;
}

@ -134,17 +134,14 @@ export default class DateInput extends Component<any, any> {
...locale,
}}
selectedValue={selectedValue}
onOk={(mDate) => {
onOk={(mDate: any) => {
onChange(mDate.toDate());
this.closePopover();
}}
onClear={() => {
this.closePopover();
}}
// onChange={(mDate) => {
// this.setState({ tempSelectedValue: mDate.format(momentFormat) });
// }}
onSelect={(mDate) => {
onSelect={(mDate: any) => {
if (mDate && mDate.format() !== 'Invalid date') {
this.setState({ tempSelectedValue: mDate.format(momentFormat) });
}

@ -3,6 +3,7 @@ import { Modal, Form, Input, Radio } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
import ModalControl from '@cpts/ModalControl';
import { FormattedMessage } from 'react-intl';
interface Props {
field: string,
@ -18,7 +19,7 @@ const FormItem = Form.Item;
const RadioGroup = Radio.Group;
class BatchSearch extends Component<Props & FormProps> {
static defaultProps = {
static defaultProps: any = {
field: 'ident',
batch: '',
title: '',
@ -54,17 +55,17 @@ class BatchSearch extends Component<Props & FormProps> {
onCancel={this.handleCancel}
>
<Form layout="vertical">
<FormItem label="过滤字段">
<FormItem label={<FormattedMessage id="endpoints.batch.filter.key" />}>
{getFieldDecorator('field', {
initialValue: field,
})(
<RadioGroup>
<Radio value="ident"></Radio>
<Radio value="alias"></Radio>
<Radio value="ident"><FormattedMessage id="endpoints.ident" /></Radio>
<Radio value="alias"><FormattedMessage id="endpoints.alias" /></Radio>
</RadioGroup>,
)}
</FormItem>
<FormItem label="过滤值">
<FormItem label={<FormattedMessage id="endpoints.batch.filter.value" />}>
{getFieldDecorator('batch', {
initialValue: _.replace(batch, /,/g, '\n'),
})(

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Dropdown, Menu, Modal, Input, Icon, message } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import clipboard from '@common/clipboard';
import request from '@common/request';
import api from '@common/api';
@ -14,16 +15,10 @@ interface Props {
hasSelected: boolean;
}
export default class CopyTitle extends Component<Props> {
class CopyTitle extends Component<Props> {
static contextTypes = {
getSelectedNode: PropTypes.func,
};
static propTypes = {
data: PropTypes.array,
selected: PropTypes.array,
dataIndex: PropTypes.string.isRequired,
hasSelected: PropTypes.bool,
intl: PropTypes.any,
};
static defaultProps = {
@ -33,7 +28,7 @@ export default class CopyTitle extends Component<Props> {
};
handleCopyBtnClick = async (dataIndex: string, copyType: string) => {
const { getSelectedNode } = this.context;
const { getSelectedNode, intl } = this.context;
const { data, selected } = this.props;
let tobeCopy = [];
@ -47,13 +42,14 @@ export default class CopyTitle extends Component<Props> {
}
tobeCopy = _.map(allData, item => item[dataIndex]);
} else if (copyType === 'currentPage') {
console.log('dataIndex', dataIndex);
tobeCopy = _.map(data, item => item[dataIndex]);
} else if (copyType === 'selected') {
tobeCopy = _.map(selected, item => item[dataIndex]);
}
if (_.isEmpty(tobeCopy)) {
message.warning('复制的对象为空');
message.warning(intl.formatMessage({ id: 'endpoints.copy.empty' }));
return;
}
@ -61,10 +57,14 @@ export default class CopyTitle extends Component<Props> {
const copySucceeded = clipboard(tobeCopyStr);
if (copySucceeded) {
message.success(`复制成功${tobeCopy.length}条记录!`);
if (intl.locale === 'zh') {
message.success(`复制成功${tobeCopy.length}条记录`);
} else if (intl.locale === 'en') {
message.success(`Successful copy ${tobeCopy.length} items`);
}
} else {
Modal.warning({
title: '复制失败,请手动复制',
title: intl.formatMessage({ id: 'endpoints.copy.error' }),
content: <Input.TextArea defaultValue={tobeCopyStr} />,
});
}
@ -81,13 +81,19 @@ export default class CopyTitle extends Component<Props> {
overlay={
<Menu>
<Menu.Item>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'selected')}></a>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'selected')}>
<FormattedMessage id="endpoints.copy.selected" />
</a>
</Menu.Item>
<Menu.Item>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'currentPage')}></a>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'currentPage')}>
<FormattedMessage id="endpoints.copy.currentPage" />
</a>
</Menu.Item>
<Menu.Item>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'all')}></a>
<a onClick={() => this.handleCopyBtnClick(dataIndex, 'all')}>
<FormattedMessage id="endpoints.copy.all" />
</a>
</Menu.Item>
</Menu>
}
@ -112,3 +118,5 @@ export default class CopyTitle extends Component<Props> {
);
}
}
export default CopyTitle;

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Modal, Form, Input, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
import { injectIntl, FormattedMessage, WrappedComponentProps } from 'react-intl';
import ModalControl from '@cpts/ModalControl';
import { Endpoint } from '@interface';
import request from '@common/request';
@ -19,7 +20,7 @@ interface Props {
const FormItem = Form.Item;
class SingleEdit extends Component<FormProps & Props> {
class SingleEdit extends Component<FormProps & Props & WrappedComponentProps> {
static defaultProps = {
title: '',
visible: true,
@ -29,7 +30,6 @@ class SingleEdit extends Component<FormProps & Props> {
};
handleOk = () => {
const { title } = this.props;
this.props.form!.validateFields((err, values) => {
if (!err) {
request(`${api.endpoint}/${values.id}`, {
@ -38,7 +38,7 @@ class SingleEdit extends Component<FormProps & Props> {
alias: values.alias,
}),
}).then(() => {
message.success(`${title}成功`);
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
this.props.onOk();
this.props.destroy();
});
@ -68,10 +68,10 @@ class SingleEdit extends Component<FormProps & Props> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="标识">
<FormItem label={<FormattedMessage id="endpoints.ident" />}>
<span className="ant-form-text">{data.ident}</span>
</FormItem>
<FormItem label="别名">
<FormItem label={<FormattedMessage id="endpoints.alias" />}>
{getFieldDecorator('alias', {
initialValue: data.alias,
})(
@ -84,4 +84,4 @@ class SingleEdit extends Component<FormProps & Props> {
}
}
export default ModalControl(Form.create()(SingleEdit));
export default ModalControl(Form.create()(injectIntl(SingleEdit)));

@ -4,6 +4,7 @@ import { Link } from 'react-router-dom';
import { Row, Col, Input, Button, Dropdown, Menu, Checkbox, Icon } from 'antd';
import { ColumnProps } from 'antd/lib/table';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import FetchTable from '@cpts/FetchTable';
import request from '@common/request';
import api from '@common/api';
@ -35,6 +36,7 @@ interface State {
class index extends Component<Props, State> {
static contextTypes = {
habitsId: PropTypes.string,
intl: PropTypes.any,
};
static defaultProps = {
@ -55,7 +57,8 @@ class index extends Component<Props, State> {
handelBatchSearchBtnClick = () => {
BatchSearch({
title: '批量过滤',
title: this.context.intl.formatMessage({ id: 'endpoints.batch.filter' }),
language: this.context.intl.locale,
field: this.state.field,
batch: this.state.batch,
onOk: (field: string, batch: string) => {
@ -127,7 +130,7 @@ class index extends Component<Props, State> {
data={_.get(this.fetchtable, 'state.data')}
selected={this.state.selectedRows}
>
<FormattedMessage id="endpoints.ident" />
</CopyTitle>
),
dataIndex: 'ident',
@ -149,11 +152,11 @@ class index extends Component<Props, State> {
);
},
}, {
title: '别名',
title: <FormattedMessage id="endpoints.alias" />,
dataIndex: 'alias',
}, {
title: '操作',
width: 100,
title: <FormattedMessage id="table.operations" />,
width: 150,
render: (_text, record) => {
return this.props.renderOper(record);
},
@ -161,7 +164,7 @@ class index extends Component<Props, State> {
];
if (displayBindNode) {
fullColumns.splice(2, 0, {
title: '挂载节点',
title: <FormattedMessage id="endpoints.nodes" />,
dataIndex: 'nodes',
render(text) {
return (
@ -195,7 +198,7 @@ class index extends Component<Props, State> {
searchValue: value,
});
}}
placeholder="快速过滤"
placeholder="Search"
/>
<Button
className="ml10"
@ -203,7 +206,7 @@ class index extends Component<Props, State> {
icon={batch ? 'check-circle' : ''}
onClick={this.handelBatchSearchBtnClick}
>
<FormattedMessage id="endpoints.batch.filter" />
</Button>
<Checkbox
className="ml10"
@ -215,7 +218,7 @@ class index extends Component<Props, State> {
});
}}
>
<FormattedMessage id="node.display.path" />
</Checkbox>
</Col>
<Col span={8} className="textAlignRight">
@ -223,13 +226,15 @@ class index extends Component<Props, State> {
overlay={
<Menu>
<Menu.Item>
<a onClick={() => { this.props.exportEndpoints(_.get(this.fetchtable, 'state.data')); }}> Excel</a>
<a onClick={() => { this.props.exportEndpoints(_.get(this.fetchtable, 'state.data')); }}>
<FormattedMessage id="endpoints.export.excel" />
</a>
</Menu.Item>
{this.props.renderBatchOper(this.state.selectedIdents)}
</Menu>
}
>
<Button icon="down"></Button>
<Button icon="down"><FormattedMessage id="table.batch.operations" /></Button>
</Dropdown>
</Col>
</Row>

@ -166,7 +166,7 @@ export default class FetchTable extends Component<Props, State> {
pagination={{
...this.state.pagination,
showTotal: (total) => {
return `${total} 条数据`;
return `Total ${total} items`;
},
pageSizeOptions: config.defaultPageSizeOptions,
}}

@ -28,15 +28,15 @@ export default class Info extends Component<Props> {
return (
<ul className="graph-info" key={groupName}>
<li>
<span className="graph-info-key">:</span>
<span className="graph-info-key">Metric:</span>
<span className="graph-info-value">{groupName}</span>
</li>
<li>
<span className="graph-info-key">:</span>
<span className="graph-info-key">Step:</span>
<span className="graph-info-value">{firstItem.step ? `${firstItem.step} s` : '无'}</span>
</li>
<li>
<span className="graph-info-key">:</span>
<span className="graph-info-key">Time:</span>
<span className="graph-info-value">
{moment(Number(start)).format(config.timeFormatMap.moment)}
<span> - </span>
@ -46,7 +46,7 @@ export default class Info extends Component<Props> {
{
unit ?
<li>
<span className="graph-info-key">:</span>
<span className="graph-info-key">Unit:</span>
<span className="graph-info-value">{unit}</span>
</li> : null
}
@ -61,7 +61,6 @@ export default class Info extends Component<Props> {
<Popover
trigger="click"
content={this.getContent()}
title="详情"
placement="topLeft"
>
{this.props.children}

@ -91,7 +91,7 @@ export default class Legend extends Component<Props, State> {
const copySucceeded = clipboard(currentCounter);
if (!copySucceeded) {
Modal.info({
title: '复制失败,请手动选择复制',
title: 'Copy failed, please manually select copy',
content: (
<p>{currentCounter}</p>
),
@ -132,17 +132,17 @@ export default class Legend extends Component<Props, State> {
const firstData = data[0];
const columns: ColumnProps<LegendDataItem>[] = [
{
title: <span> 线({data.length}) </span>,
title: <span> Series({data.length}) </span>,
dataIndex: 'tags',
filterDropdown: (
<div className="custom-filter-dropdown">
<Input
placeholder="请输入曲线名称"
placeholder="Input serie name"
value={searchText}
onChange={this.handleInputChange}
onPressEnter={this.handleSearch}
/>
<Button type="primary" onClick={this.handleSearch}></Button>
<Button type="primary" onClick={this.handleSearch}>Search</Button>
</div>
),
filterDropdownVisible: this.state.filterDropdownVisible,
@ -224,7 +224,7 @@ export default class Legend extends Component<Props, State> {
if (_.get(firstData, 'isSameMetric') === false) {
columns.unshift({
title: '指标',
title: 'Metric',
dataIndex: 'metric',
width: 60,
});
@ -247,7 +247,7 @@ export default class Legend extends Component<Props, State> {
<ContextMenu visible={this.state.contextMenuVisiable} left={this.state.contextMenuLeft} top={this.state.contextMenuTop}>
<ul className="ant-dropdown-menu ant-dropdown-menu-vertical ant-dropdown-menu-light ant-dropdown-menu-root">
<li className="ant-dropdown-menu-item">
<a onClick={this.handleCopyCounter}> counter</a>
<a onClick={this.handleCopyCounter}>copy counter</a>
</li>
</ul>
</ContextMenu>

@ -191,19 +191,19 @@ export default class Graph extends Component<Props, State> {
let errorText = e.err;
if (e.statusText === 'error') {
errorText = '网络已断开,请检查网络';
errorText = 'The network has been disconnected, please check the network';
} else if (e.statusText === 'Not Found') {
errorText = '404 Not Found,请联系管理员';
errorText = '404 Not Found';
} else if (e.responseJSON) {
errorText = _.get(e.responseJSON, 'msg', e.responseText);
if (!errorText || e.status === 500) {
errorText = '数据加载异常,请刷新重新加载';
errorText = 'Data loading exception, please refresh and reload';
}
// request entity too large
if (e.status === 413) {
errorText = '请求条件过大,请减少条件';
errorText = 'Request condition is too large, please reduce the condition';
}
}
@ -215,17 +215,17 @@ export default class Graph extends Component<Props, State> {
checkEndpointCounters(endpointCounters: CounterInterface[], countersMaxLength: number) {
let errorText: any = '';
if (!_.get(endpointCounters, 'length', 0)) {
errorText = '暂无数据';
errorText = 'No data';
}
if (endpointCounters.length > countersMaxLength) {
errorText = (
<span className="counters-maxLength">
线
Too many seriesCurrent
{endpointCounters.length}
cap
{countersMaxLength}
线
Please reduce the number of series
</span>
);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save