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

This commit is contained in:
710leo 2020-04-03 15:24:00 +08:00
commit 59f2bb003b
97 changed files with 2181 additions and 1142 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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

View File

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

View File

@ -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>

View File

@ -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)

View File

@ -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
}

98
web/package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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';

View File

@ -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>
);
}

View File

@ -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));

View File

@ -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);

View File

@ -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}
/>
);
}
}

View File

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

View File

@ -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) });
}

View File

@ -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'),
})(

View File

@ -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;

View File

@ -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)));

View File

@ -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>

View File

@ -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,
}}

View File

@ -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}

View File

@ -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>

View File

@ -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>
);
}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import _ from 'lodash';
import moment from 'moment';
import { Button, Select, DatePicker } from 'antd';
@ -102,19 +103,22 @@ export default class GlobalOperationbar extends Component<Props> {
<div className="global-operationbar-warp">
{
this.props.refreshVisible ?
<Button onClick={this.handleRefresh} style={{ marginRight: 8 }}></Button> : null
<Button onClick={this.handleRefresh} style={{ marginRight: 8 }}>
<FormattedMessage id="graph.refresh" />
</Button> : null
}
<span>
<Select
style={{ width: 80 }}
value={timeVal}
onChange={this.handleTimeOptionChange}
placeholder="无"
>
{
_.map(config.time, (o) => {
return (
<Select.Option key={o.value} value={o.value}>{o.label}</Select.Option>
<Select.Option key={o.value} value={o.value}>
<FormattedMessage id={o.label} />
</Select.Option>
);
})
}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import update from 'react-addons-update';
import { Row, Col, Spin, Table, Form, Select, Input, InputNumber, Icon, TreeSelect, DatePicker } from 'antd';
@ -75,7 +76,7 @@ export default class GraphConfigForm extends Component<Props, State> {
metrics,
},
loading: false,
tableEmptyText: '暂无数据',
tableEmptyText: 'No data',
nsSearchVal: '', // 节点搜索值
counterListVisible: false,
advancedVisible: false,
@ -525,7 +526,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="节点"
label={<FormattedMessage id="graph.config.node" />}
style={{ marginBottom: 5 }}
required
>
@ -559,7 +560,7 @@ export default class GraphConfigForm extends Component<Props, State> {
readOnly
value={_.join(_.slice(selectedTagv, 0, 40), ', ')}
size="default"
placeholder="若无此tag请留空"
// placeholder="若无此tag请留空"
onClick={() => {
show(tagk);
}}
@ -585,7 +586,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="指标"
label={<FormattedMessage id="graph.config.metric" />}
style={{ marginBottom: 5 }}
required
>
@ -593,8 +594,8 @@ export default class GraphConfigForm extends Component<Props, State> {
showSearch
size="default"
style={{ width: '100%' }}
placeholder="监控项指标名, 如cpu.idle"
notFoundContent="请输入关键词过滤"
// placeholder="监控项指标名, 如cpu.idle"
// notFoundContent="请输入关键词过滤"
className="select-metric"
value={metricObj.selectedMetric}
onChange={(value: string) => this.handleMetricChange(value, currentMetric)}
@ -609,21 +610,21 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
label="聚合"
label={<FormattedMessage id="graph.config.aggr" />}
style={{ marginBottom: 0 }}
>
<Select
allowClear
size="default"
style={{ width: '100%' }}
placeholder="无"
// placeholder="无"
value={metricObj.aggrFunc}
onChange={(val: string) => this.handleAggregateChange(currentMetric, val)}
>
<Option value="sum"></Option>
<Option value="avg"></Option>
<Option value="max"></Option>
<Option value="min"></Option>
<Option value="sum"><FormattedMessage id="graph.config.aggr.sum" /></Option>
<Option value="avg"><FormattedMessage id="graph.config.aggr.avg" /></Option>
<Option value="max"><FormattedMessage id="graph.config.aggr.max" /></Option>
<Option value="min"><FormattedMessage id="graph.config.aggr.min" /></Option>
</Select>
</FormItem>
</Col>
@ -631,7 +632,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 5 }}
wrapperCol={{ span: 19 }}
label="聚合维度"
label={<FormattedMessage id="graph.config.aggr.group" />}
style={{ marginBottom: 0 }}
>
<Select
@ -639,7 +640,7 @@ export default class GraphConfigForm extends Component<Props, State> {
size="default"
style={{ width: '100%' }}
disabled={!metricObj.aggrFunc}
placeholder="无"
// placeholder="无"
value={metricObj.aggrGroup || []}
onChange={(val: string[]) => this.handleAggregateDimensionChange(currentMetric, val)}
>
@ -650,7 +651,7 @@ export default class GraphConfigForm extends Component<Props, State> {
</FormItem>
</Col>
</Row>
<FormItem
{/* <FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="采样函数"
@ -668,7 +669,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<Option value="MAX"></Option>
<Option value="MIN"></Option>
</Select>
</FormItem>
</FormItem> */}
<Tagkv
type="modal"
data={withoutEndpointTagkv}
@ -680,7 +681,7 @@ export default class GraphConfigForm extends Component<Props, State> {
readOnly
value={_.join(_.slice(selectedTagv, 0, 40), ', ')}
size="default"
placeholder="若无此tag请留空"
// placeholder="若无此tag请留空"
onClick={() => {
show(tagk);
}}
@ -706,12 +707,12 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="曲线"
label={<FormattedMessage id="graph.config.series" />}
style={{ marginBottom: 5 }}
>
<span style={{ color: '#ff7f00', paddingRight: 5 }}>
{_.get(metricObj.counterList, 'length')}
<FormattedMessage id="graph.config.series.unit" />
</span>
<a onClick={() => {
this.setState({ counterListVisible: !this.state.counterListVisible });
@ -752,7 +753,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="分类"
label={<FormattedMessage id="graph.config.cate" />}
style={{ marginBottom: 5 }}
required
>
@ -772,24 +773,24 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="标题"
label={<FormattedMessage id="graph.config.graph.title" />}
style={{ marginBottom: 5 }}
>
<Input
style={{ width: '100%' }}
value={graphConfig.title}
onChange={this.handleTitleChange}
placeholder="如果留空将会用指标名称做为标题"
placeholder="The metric name as the default title"
/>
</FormItem>
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="时间"
label={<FormattedMessage id="graph.config.time" />}
style={{ marginTop: 5, marginBottom: 0 }}
required
>
<Select placeholder="时间选择" size="default" style={
<Select size="default" style={
timeVal === 'custom' ?
{
width: 198,
@ -802,7 +803,7 @@ export default class GraphConfigForm extends Component<Props, State> {
onChange={this.handleTimeOptionChange}
>
{
_.map(config.time, o => <Option key={o.value} value={o.value}>{o.label}</Option>)
_.map(config.time, o => <Option key={o.value} value={o.value}><FormattedMessage id={o.label} /></Option>)
}
</Select>
{
@ -838,7 +839,7 @@ export default class GraphConfigForm extends Component<Props, State> {
<FormItem
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
label="阈值"
label={<FormattedMessage id="graph.config.threshold" />}
style={{ marginBottom: 5 }}
>
<InputNumber

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import update from 'react-addons-update';
import _ from 'lodash';
import moment from 'moment';
@ -188,19 +189,19 @@ export default class GraphConfigInner extends Component<Props> {
<div className="graph-config-inner">
<div className="graph-config-inner-item">
<Button size="small" type="ghost" onClick={this.refresh}>
<FormattedMessage id="graph.refresh" />
</Button>
</div>
<div className="graph-config-inner-item">
<Select
size="small"
style={{ width: 70 }}
value={timeLabel}
value={<FormattedMessage id={timeLabel} />}
onChange={this.timeOptionChange}
>
{
_.map(config.time, (o) => {
return <Option key={o.value} value={o.value}>{o.label}</Option>;
return <Option key={o.value} value={o.value}><FormattedMessage id={o.label} /></Option>;
})
}
</Select>
@ -241,7 +242,7 @@ export default class GraphConfigInner extends Component<Props> {
}
</div>
<div className="graph-config-inner-item">
<FormattedMessage id="graph.config.aggr" />
<Select
allowClear
size="small"
@ -250,17 +251,17 @@ export default class GraphConfigInner extends Component<Props> {
value={_.get(data.metrics, '[0].aggrFunc')}
onChange={this.handleAggrFuncChange}
>
<Option value="sum"></Option>
<Option value="avg"></Option>
<Option value="max"></Option>
<Option value="min"></Option>
<Option value="sum"><FormattedMessage id="graph.config.aggr.sum" /></Option>
<Option value="avg"><FormattedMessage id="graph.config.aggr.avg" /></Option>
<Option value="max"><FormattedMessage id="graph.config.aggr.max" /></Option>
<Option value="min"><FormattedMessage id="graph.config.aggr.min" /></Option>
</Select>
</div>
{
_.get(data.metrics, '[0].aggrFunc') ?
<div className="graph-config-inner-item">
<Tooltip title="按照某个 tag 聚合出多条曲线">
<span></span>
<span><FormattedMessage id="graph.config.aggr.group" /></span>
</Tooltip>
<Select
mode="multiple"
@ -276,7 +277,6 @@ export default class GraphConfigInner extends Component<Props> {
}],
});
}}
placeholder="无"
>
{
_.map(aggrGroupOptions, o => <Option key={o.value} value={o.value}>{o.label}</Option>)
@ -284,7 +284,7 @@ export default class GraphConfigInner extends Component<Props> {
</Select>
</div> : null
}
<div className="graph-config-inner-item">
{/* <div className="graph-config-inner-item">
<Select
allowClear
@ -298,7 +298,7 @@ export default class GraphConfigInner extends Component<Props> {
<Option value="MAX"></Option>
<Option value="MIN"></Option>
</Select>
</div>
</div> */}
<div className="graph-config-inner-item">
<Checkbox checked={!!data.legend} onChange={this.legendChange}>
Legend

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import update from 'react-addons-update';
import _ from 'lodash';
import { Input, Button, Modal, Popover, Switch } from 'antd';
@ -185,12 +186,12 @@ export default class Tagkv extends Component<Props, State> {
/>
<div style={{ marginTop: 10, textAlign: 'center' }}>
<Button.Group>
<Button onClick={() => this.hide(tagk)}></Button>
<Button onClick={() => this.hide(tagk)}>Cancel</Button>
<Button
type="primary"
onClick={() => this.submit(tagk)}
>
Ok
</Button>
</Button.Group>
</div>
@ -198,14 +199,14 @@ export default class Tagkv extends Component<Props, State> {
{
dynamicSwitch ?
<span>
<span> </span>
<a onClick={() => this.dynamicSelect(tagk, '=all')}></a>
<span><FormattedMessage id="select.dynamic" /> </span>
<a onClick={() => this.dynamicSelect(tagk, '=all')}><FormattedMessage id="select.all" /></a>
<span className="ant-divider" />
<Popover
trigger="click"
content={
<div style={{ width: 200 }}>
<Input placeholder="请输入关键词Enter键提交" onKeyDown={
<Input placeholder="Press enter to submit" onKeyDown={
(e: any) => {
if (e.keyCode === 13) {
this.dynamicSelect(tagk, '=+', e.target.value);
@ -213,10 +214,9 @@ export default class Tagkv extends Component<Props, State> {
}} />
</div>
}
title="包含"
getTooltipContainer={() => this.refs[`${tagk}dynamic`]}
>
<a></a>
<a><FormattedMessage id="select.include" /></a>
</Popover>
<span className="ant-divider" />
<Popover
@ -232,14 +232,13 @@ export default class Tagkv extends Component<Props, State> {
</div>
}
title="排除"
getTooltipContainer={() => this.refs[`${tagk}dynamic`]}
>
<a></a>
<a><FormattedMessage id="select.exclude" /></a>
</Popover>
</span> :
<div>
<Switch onChange={this.dynamicSwitchChange} size="small" />
<FormattedMessage id="select.dynamic" /> <Switch onChange={this.dynamicSwitchChange} size="small" />
</div>
}
</div>

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Modal, Button, message } from 'antd';
import _ from 'lodash';
@ -113,7 +114,7 @@ export default class GraphConfig extends Component {
<Modal
key={key}
width={750}
title={title}
title={<FormattedMessage id="graph.config.title" />}
destroyOnClose
visible={visible}
maskClosable={false}

View File

@ -207,7 +207,7 @@ export const time: { [index: string]: string }[] = [
label: '30天',
value: '2592000000',
}, {
label: '其它 ',
label: '其它',
value: 'custom',
},
];

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
export default function CreateIncludeNsTree(WrappedComponent: React.ComponentType, opts?: any) {
export default function CreateIncludeNsTree(WrappedComponent: any, opts?: any) {
return class HOC extends React.Component {
static contextTypes = {
nsTreeVisibleChange: PropTypes.func,

View File

@ -2,11 +2,12 @@ import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import PropTypes from 'prop-types';
import { Link, withRouter } from 'react-router-dom';
import { Layout, Dropdown, Menu, Icon } from 'antd';
import { Layout, Dropdown, Menu, Icon, Button } from 'antd';
import classNames from 'classnames';
import PubSub from 'pubsub-js';
import _ from 'lodash';
import queryString from 'query-string';
import { WrappedComponentProps, injectIntl } from 'react-intl';
import { auth } from '@cpts/Auth';
import { MenuConfItem, TreeNode } from '@interface';
import request from '@common/request';
@ -22,6 +23,8 @@ interface Props {
appName: string,
menuConf: MenuConfItem[],
children: React.ReactNode,
language: string,
onLanguageChange: (language: string) => void,
}
interface State {
@ -38,7 +41,7 @@ interface State {
const { Header, Content, Sider } = Layout;
class NILayout extends Component<Props & RouteComponentProps, State> {
class NILayout extends Component<Props & RouteComponentProps & WrappedComponentProps, State> {
static childContextTypes = {
nsTreeVisibleChange: PropTypes.func.isRequired,
getNodes: PropTypes.func.isRequired,
@ -48,9 +51,10 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
deleteSelectedNode: PropTypes.func.isRequired,
reloadNsTree: PropTypes.func.isRequired,
habitsId: PropTypes.string.isRequired,
intl: PropTypes.any.isRequired,
};
constructor(props: Props & RouteComponentProps) {
constructor(props: Props & RouteComponentProps & WrappedComponentProps) {
super(props);
let selectedNode;
try {
@ -177,6 +181,7 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
this.fetchTreeData();
},
habitsId: this.props.habitsId,
intl: this.props.intl,
};
}
@ -201,7 +206,7 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
});
return (
<Layout className={layoutCls} style={{ height: '100%' }}>
<Layout className={layoutCls}>
<Sider
className={`${prefixCls}-sider-nstree`}
width={nsTreeVisible ? 200 : 0}
@ -233,7 +238,7 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
}
render() {
const { menuConf } = this.props;
const { menuConf, language, onLanguageChange } = this.props;
const { checkAuthenticateLoading, collapsed, selectedNode, nsTreeVisible } = this.state;
const prefixCls = `${appname}-layout`;
const { dispname, isroot } = auth.getSelftProfile();
@ -296,6 +301,17 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
{nsTreeVisible ? _.get(selectedNode, 'path') : null}
</div>
<div className={`${prefixCls}-headRight`}>
<Button
style={{ margin: '0 20px' }}
size="small"
onClick={() => {
const newLanguage = language === 'zh' ? 'en' : 'zh';
onLanguageChange(newLanguage);
}}
>
{language === 'zh' ? 'English' : ''}
{language === 'en' ? '中文' : ''}
</Button>
<Dropdown placement="bottomRight" overlay={
<Menu style={{ width: 110 }}>
<Menu.Item>
@ -326,4 +342,4 @@ class NILayout extends Component<Props & RouteComponentProps, State> {
}
}
export default withRouter(NILayout);
export default injectIntl(withRouter(NILayout));

View File

@ -1,7 +1,31 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { ConfigProvider } from 'antd';
import _ from 'lodash';
import antdZhCN from 'antd/lib/locale/zh_CN';
import antdEnUS from 'antd/lib/locale/en_US';
import { IntlProvider } from 'react-intl';
import intlZhCN from '../../locales/zh';
import intlEnUS from '../../locales/en';
export default function ModalControlWrap(Component: typeof React.Component) {
interface LocaleMap {
[index: string]: any,
}
const localeMap: LocaleMap = {
zh: {
antd: antdZhCN,
intl: 'zh',
intlMessages: intlZhCN,
},
en: {
antd: antdEnUS,
intl: 'en',
intlMessages: intlEnUS,
},
};
export default function ModalControlWrap(Component: any) {
return function ModalControl(config: any) {
const div = document.createElement('div');
document.body.appendChild(div);
@ -14,7 +38,17 @@ export default function ModalControlWrap(Component: typeof React.Component) {
}
function render(props: any) {
ReactDOM.render(<Component {...props} />, div);
ReactDOM.render(
<IntlProvider
locale={_.get(localeMap[config.language], 'intl', 'zh')}
messages={_.get(localeMap[config.language], 'intlMessages', intlZhCN)}
>
<ConfigProvider locale={_.get(localeMap[config.language], 'antd', antdZhCN)}>
<Component {...props} />
</ConfigProvider>
</IntlProvider>,
div
);
}
render({ ...config, visible: true, destroy });

View File

@ -21,6 +21,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Row, Col, Input, Button, Pagination, Checkbox, Popover, Tag, message } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import './style.less';
const TextArea = Input.TextArea ? Input.TextArea : Input;
@ -134,7 +135,7 @@ class Multipicker extends Component {
if (data.length > 500) {
selectedList = selectedList.splice(0, 500);
message.warning('最多只能全选500个');
message.warning('Can only select a maximum of 500');
}
if (searchVal) {
selectedList = _.uniq(selected.concat(this.filterData()));
@ -249,12 +250,12 @@ class Multipicker extends Component {
<div className="multipicker-selected-list-box">
<Row>
<Col span={14}>
<strong>已选({selected.length})</strong>
<strong><FormattedMessage id="select.selected" />({selected.length})</strong>
<a
className="remove-all"
onClick={this.removeAll}
>
清除已选项
<FormattedMessage id="select.selected.clear" />
</a>
{
manualEntry &&
@ -278,7 +279,7 @@ class Multipicker extends Component {
}}
/>
<div style={{ marginTop: 5 }}>
<Button size="small" onClick={this.handleManualEntry}>确定</Button>
<Button size="small" onClick={this.handleManualEntry}>Ok</Button>
</div>
</div>
}
@ -290,7 +291,7 @@ class Multipicker extends Component {
this.setState({ manualVisible: !this.state.manualVisible });
}}
>
手动输入
<FormattedMessage id="select.manual.input" />
</a>
</Popover>
}
@ -301,15 +302,15 @@ class Multipicker extends Component {
<div className="multipicker-option-list-box">
<Row>
<Col span={16}>
<strong>选项({data.length})</strong>
<strong><FormattedMessage id="select.total" />({data.length})</strong>
<a
className="select-all-currentPage"
onClick={this.currentPageSelectAll}
style={{ paddingRight: 10 }}
>
全选当前页
<FormattedMessage id="select.current.page" />
</a>
<a className="select-all" onClick={this.selectAll}>全选</a>
<a className="select-all" onClick={this.selectAll}><FormattedMessage id="select.all" /></a>
</Col>
<Col span={8}>
<div className="multipicker-search">
@ -317,7 +318,7 @@ class Multipicker extends Component {
size="small"
type="text"
className="keyword"
placeholder="搜索,支持正则"
placeholder="support regular"
onChange={this.search} />
</div>
</Col>

View File

@ -1,6 +1,7 @@
import React, { Component, Fragment } from 'react';
import { Form, Input, Switch, Icon } from 'antd';
import { FormProps } from 'antd/lib/form';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { UserProfile } from '@interface';
interface Props {
@ -11,8 +12,8 @@ interface Props {
const FormItem = Form.Item;
class ProfileForm extends Component<Props & FormProps> {
static defaultProps = {
class ProfileForm extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps: any = {
type: 'post',
isrootVsible: false,
initialValue: {},
@ -33,47 +34,48 @@ class ProfileForm extends Component<Props & FormProps> {
render() {
const { type, isrootVsible, initialValue } = this.props;
const { getFieldDecorator } = this.props.form!;
const { formatMessage } = this.props.intl;
return (
<Form layout="vertical">
{
type === 'post' || type === 'register' ?
<Fragment>
<FormItem label={this.renderLabel('用户名')} required>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.username' }))} required>
{getFieldDecorator('username', {
rules: [{ required: true, message: '请输入用户名!' }],
rules: [{ required: true }],
})(
<Input placeholder="用户名" />,
<Input placeholder={formatMessage({ id: 'user.username' })} />,
)}
</FormItem>
<FormItem label={this.renderLabel('密码')} required>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.password' }))} required>
{getFieldDecorator('password', {
rules: [{ required: true, message: '请输入密码!' }],
rules: [{ required: true }],
})(
<Input type="password" placeholder="密码" />,
<Input type="password" placeholder={formatMessage({ id: 'user.password' })} />,
)}
</FormItem>
</Fragment> : null
}
<FormItem label={this.renderLabel('显示名')} required>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.dispname' }))} required>
{getFieldDecorator('dispname', {
initialValue: initialValue.dispname,
rules: [{ required: true, message: '请输入显示名!' }],
rules: [{ required: true }],
})(
<Input placeholder="显示名" />,
<Input placeholder={formatMessage({ id: 'user.dispname' })} />,
)}
</FormItem>
<FormItem label={this.renderLabel('手机')}>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.phone' }))}>
{getFieldDecorator('phone', {
initialValue: initialValue.phone,
})(
<Input placeholder="手机" style={{ width: '100%' }} />,
<Input placeholder={formatMessage({ id: 'user.phone' })} style={{ width: '100%' }} />,
)}
</FormItem>
<FormItem label={this.renderLabel('邮箱')}>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.email' }))}>
{getFieldDecorator('email', {
initialValue: initialValue.email,
})(
<Input placeholder="邮箱" />,
<Input placeholder={formatMessage({ id: 'user.email' })} />,
)}
</FormItem>
<FormItem label={this.renderLabel('im')}>
@ -85,7 +87,7 @@ class ProfileForm extends Component<Props & FormProps> {
</FormItem>
{
isrootVsible ?
<FormItem label={this.renderLabel('是否超管')}>
<FormItem label={this.renderLabel(formatMessage({ id: 'user.isroot' }))}>
{getFieldDecorator('is_root', {
valuePropName: 'checked',
initialValue: initialValue.is_root === 1,
@ -102,4 +104,4 @@ class ProfileForm extends Component<Props & FormProps> {
}
}
export default Form.create()(ProfileForm as any);
export default Form.create()(injectIntl(ProfileForm));

View File

@ -0,0 +1,22 @@
import * as React from 'react';
import { useContext } from 'react';
import { injectIntl } from 'react-intl';
export const IntlContext = React.createContext({} as any);
// turn the old context into the new context
export const InjectIntlContext = injectIntl(({ intl, children }) => (
<IntlContext.Provider value={intl}>
{ children }
</IntlContext.Provider>
));
export const getIntl = () => useContext(IntlContext);
// the format message hook
const useFormatMessage = () => {
const intl = useContext(IntlContext);
return intl.formatMessage;
};
export default useFormatMessage;

433
web/src/locales/en.ts Normal file
View File

@ -0,0 +1,433 @@
export default {
'login': 'Login',
'logout': 'Logout',
'register': 'Register',
'login.title': 'Login',
'login.ldap': 'Use LDAP',
'form.save': 'Save',
'form.create': 'Create',
'form.submit': 'Submit',
'form.delete': 'Delete',
'form.login': 'Login',
'form.goback': 'Go back',
'msg.submit.success': 'Submit successfully',
'msg.modify.success': 'Modify Successfully',
'msg.create.success': 'Create successfully',
'msg.add.success': 'Add successfully',
'msg.delete.success': 'Delete successfully',
'msg.clone.success': 'Clone successfully',
'msg.sort.success': 'Sort successfully',
'please.select.node': 'Please select the node first',
'table.nodata': 'No data',
'table.create': 'Create',
'table.operations': 'Operations',
'table.batch.operations': 'Batch Operations',
'table.detail': 'Detail',
'table.modify': 'Modify',
'table.delete': 'Delete',
'table.delete.batch': 'Batch delete',
'table.clone': 'Clone',
'table.delete.sure': 'Are you sure to delete it?',
'table.delete.there.sure': 'Are you sure to delete these?',
'table.ident': 'Ident',
'table.name': 'Name',
'table.cate': 'Cate',
'table.creator': 'Creator',
'table.lastupdated': 'Last updated',
'table.note': 'Note',
'user.create': 'Create',
'user.modify': 'Modify',
'user.username': 'Username',
'user.dispname': 'Dispname',
'user.password': 'Password',
'user.email': 'Email',
'user.phone': 'Phone',
'user.reset.password': 'Reset password',
'user.reset.password.success': 'Reset password successfully',
'user.invite': 'Invite',
'user.invite.tips': 'Click to generate a link to invite users',
'user.isroot': 'is root',
'password.old': 'Old Password',
'password.new': 'New Password',
'token.reset': 'Reset',
'token.reset.success': 'Reset successfully',
'invite.user.copy.success': 'Copy succeeded',
'invite.user.copy.faile': 'Failed, please copy manually',
'tree.select.node': 'Please choose the tree node',
'tree.search': 'Search (space division)',
'tree.node': 'Node',
'node.copy.path': 'Copy node path',
'node.copy.path.success': 'Copy succeeded',
'node.copy.path.error': 'Copy failed',
'node.create.tenant': 'Add tenant node',
'node.create': 'Add node',
'node.modify': 'Modify node',
'node.delete': 'Delete node',
'node.name': 'Name',
'node.isLeaf': 'Is leaf',
'node.cate': 'Category',
'node.color': 'Color',
'node.note': 'Note',
'node.cate.create': 'Create node category',
'node.cate.modify': 'Modify node category',
'node.display.path': 'Display node',
'node.rename': 'Rename node',
'node.rename.newname': 'node name',
'node.rename.success': 'Rename successfully',
'node.child.create': 'Create a new node',
'node.child.create.success': 'Create successfully',
'node.child.newname': 'node name',
'node.delete.success': 'Delete successfully',
'node.leaf.cannot.create': 'Leaf node cannot continue to create child node',
'根节点不能删除': 'Root node cannot delete',
'select.all': 'all',
'select.include': 'include',
'select.exclude': 'exclude',
'select.dynamic': 'dynamic value',
'select.selected': 'selected',
'select.selected.clear': 'clear',
'select.manual.input': 'manual',
'select.total': 'total',
'select.current.page': 'currentPage',
'1小时': '1hour',
'2小时': '2hours',
'6小时': '6hours',
'12小时': '12hours',
'1天': '1day',
'2天': '2days',
'7天': '7days',
'30天': '30days',
'其它': 'other',
'menu.endpoints': 'Endpoints',
'menu.endpoints.all': 'All endpoints',
'menu.endpoints.node': 'Endpoints of node',
'menu.endpoints.node.manage': 'Node manage',
'menu.monitor': 'Monitor',
'menu.monitor.dashboard': 'Dashboard',
'menu.monitor.screen': 'Screens',
'menu.monitor.strategy': 'Alarm strategies',
'menu.monitor.history': 'Alarm events',
'menu.monitor.silence': 'Alarm silences',
'menu.monitor.collect': 'Collections',
'menu.users': 'Users',
'menu.users.users': 'Users',
'menu.users.teams': 'Teams',
'endpoints.ident': 'Ident',
'endpoints.alias': 'Alias',
'endpoints.nodes': 'Nodes',
'endpoints.batch.filter': 'Batch filter',
'endpoints.batch.filter.key': 'Key',
'endpoints.batch.filter.value': 'Value',
'endpoints.export.excel': 'Export excel',
'endpoints.import': 'Import endpoints',
'endpoints.export': 'Export endpoints',
'endpoints.delete': 'Delete Endpoints',
'endpoints.bind': 'Bind Endpoints',
'endpoints.unbind': 'Unbind Endpoints',
'endpoints.copy.selected': 'Copy the selected',
'endpoints.copy.currentPage': 'Copy the current page',
'endpoints.copy.all': 'Copy all',
'endpoints.copy.empty': 'Copy the object is empty',
'endpoints.copy.error': 'Failed to copy, please manually copy',
'endpoints.import.batch.help': 'Each one is an ident::alias',
'endpoints.modify.alias': 'Modify alias',
'endpoints.bind.node': 'Node',
'endpoints.unbind.node': 'Node',
'endpoints.delete.old.bind': 'Delete old Bind',
'team.ident': 'Ident',
'team.name': 'Name',
'team.admins': 'Admins',
'team.members': 'Members',
'team.mgmt': 'Management mode',
'team.mgmt.admin': 'Admin',
'team.mgmt.member': 'Member',
'周一': 'Mon.',
'周二': 'Tue.',
'周三': 'Wed.',
'周四': 'Thu.',
'周五': 'Fri.',
'周六': 'Sat.',
'周日': 'Sun.',
'clone.to.other.node': 'Clone to other node',
'clone.to.other.node.success': 'Clone to node successfully!',
'collect.log': 'Log',
'collect.port': 'Port',
'collect.proc': 'Proc',
'collect.common.search': 'Search',
'collect.common.name': 'Name',
'collect.common.type': 'Type',
'collect.common.creator': 'Creator',
'collect.common.last_updated': 'Last updated',
'collect.common.node': 'Node',
'collect.common.step': 'Step',
'collect.common.step.unit': 'seconds',
'collect.common.note': 'Note',
'collect.log.msg.pattern.empty': 'Pattern is required',
'collect.log.msg.log.empty': 'Log is required',
'collect.log.msg.tag.maximum': 'Maximum of three',
'collect.log.ns': 'NS',
'collect.log.name': 'Name',
'collect.log.func': 'Calc func',
'collect.log.func.cnt': 'Count',
'collect.log.func.avg': 'Average',
'collect.log.func.sum': 'Sum',
'collect.log.func.max': 'Max',
'collect.log.func.min': 'Min',
'collect.log.path': 'Path',
'collect.log.path.dynamic': 'dynamic log',
'collect.log.path.dynamic.tip.1': 'The time format at the end of the log, eg.',
'collect.log.path.dynamic.tip.2': "/ cannot be included in $'{}'",
'collect.log.timeFmt': 'Time format',
'collect.log.timeFmt.help.1': 'The time format must be the same as the format in the log.',
'collect.log.timeFmt.help.2': 'Only the first match result is used.',
'collect.log.step': 'Step',
'collect.log.step.unit': 'seconds',
'collect.log.pattern': 'Pattern',
'collect.log.pattern.tip.1': 'Please Enter regular expression',
'collect.log.pattern.tip.3': 'eg. cost=(\\d+) , Take \\d+ (the default is the first bracket)',
'collect.log.tagval.placeholder': 'Not a curve value! Must be enumerable!',
'collect.log.tags.add': 'Add tag',
'collect.log.tagName.help.title': 'tagName description',
'collect.log.tagName.help.1': 'Not allowed to use host, trigger, include',
'collect.log.tagName.help.2': 'Not allowed to include = , : @',
'collect.log.tagValue.help.title': 'tagValue description',
'collect.log.tagValue.help.1': 'Must include parentheses. and the content is used as the value of tagValue, and must be enumerable.',
'collect.log.tagValue.help.2': 'Not allowed to include = , : @',
'collect.log.check': 'Check',
'collect.log.check.btn': 'Check',
'collect.log.check.btn2': 'Is there a problem with my configuration?',
'collect.log.check.help': 'Enter a complete log to be monitored, including time.',
'collect.log.check.help.tip.1': 'The correct: ',
'collect.log.check.help.tip.2': 'Output regular, tag match result complete and sub-items, and time matching results',
'collect.log.check.help.tip.3': 'The wrong: ',
'collect.log.check.help.tip.4': 'Output error message',
'collect.log.check.add.tip': 'Please check, When adding',
'collect.log.note': 'Note',
'collect.batch.import': 'Import',
'collect.batch.export': 'Export',
'collect.port.title': 'Metric',
'collect.port.name.placeholder': 'Description of the collection, such as web port',
'collect.port.pattern.msg': 'Only english, numbers, -_.',
'collect.port.port': 'Port',
'collect.port.timeout': 'Timeout',
'collect.port.timeout.unit': 'seconds',
'collect.proc.title': 'Metric',
'collect.proc.name.placeholder': 'Description of the collection, such as nginx',
'collect.proc.service.pattern.msg': 'Only english, numbers, -_.',
'collect.proc.type': 'Type',
'collect.proc.type.cmd': 'Command',
'collect.proc.type.name': 'Process Name',
'collect.proc.type.input.pattern.msg': 'Cannot contain Chinese',
'graph.subscribe': 'Subscribe',
'graph.subscribe.node': 'Node',
'graph.subscribe.screen': 'Screen',
'graph.subscribe.tag': 'Tag',
'graph.subscribe.success': 'Subscription successfully',
'graph.share': 'Share',
'graph.clear': 'Clear',
'graph.view': 'View',
'graph.save': 'Save',
'graph.machine.list.title': 'Endpoints',
'graph.machine.list.update': 'Update graphs',
'graph.metric.list.title': 'Metrics',
'graph.metric.list.search': 'Search',
'graph.metric.list.all': 'All',
'graph.refresh': 'Refresh',
'graph.config.title': 'Setting',
'graph.config.graph.title': 'title',
'graph.config.node': 'node',
'graph.config.metric': 'metric',
'graph.config.aggr': 'aggr',
'graph.config.aggr.sum': 'sum',
'graph.config.aggr.avg': 'avg',
'graph.config.aggr.max': 'max',
'graph.config.aggr.min': 'min',
'graph.config.aggr.group': 'groupBy',
'graph.config.series': 'series',
'graph.config.series.unit': 'pcs',
'graph.config.cate': 'cate',
'graph.config.time': 'time',
'graph.config.threshold': 'threshold',
'graph.config.link': 'link',
'graph.config.link.help': 'custom link',
'graph.config.chartType.targetValue': 'value',
'graph.config.chartType.current': 'current',
'graph.config.chartType.unit': 'unit',
'graph.config.chartType.subType': 'type',
'graph.config.subType.normal': 'value',
'graph.config.subType.normal.tip': '(aggr required)',
'graph.config.subType.solidGauge': 'solidGauge',
'graph.config.subType.liquidFillGauge': 'liquidFillGauge',
'graph.config.chartType.valueMap': 'mapType',
'graph.config.chartType.mapConf': 'map',
'graph.config.chartType.tableType': 'table',
'graph.config.chartType.tableType.current': 'current',
'graph.config.chartType.tableType.stats': 'stats',
'graph.config.chartType.pieType': 'type',
'graph.config.chartType.pieType.pie': 'Pie',
'graph.config.chartType.pieType.donut': 'Donut',
'graph.config.chartType.tableType.columnsKey': 'columns',
'event.tab.alert': 'Alarming',
'event.tab.all': 'History',
'event.msg.ignore.success': 'Successfully ignore',
'event.msg.claim.success': 'Successfully claim',
'event.msg.claim.all.success': 'Successfully claim all',
'event.table.time': 'Time',
'event.table.stra': 'Stra',
'event.table.node': 'Node',
'event.table.priority': 'Priority',
'event.table.notify': 'Notify result',
'event.table.ignore': 'Ignore',
'event.table.ignore.sure': 'Are you sure to ignore this alarm?',
'event.table.claim': 'Claim',
'event.table.claim.sure': 'Are you sure to claim this alarm?',
'event.table.shield': 'Shield',
'event.table.assignees': 'Assignees',
'event.table.status': 'Status',
'event.table.status.alert': 'alert',
'event.table.status.recovery': 'recovery',
'event.table.claim.all': 'Claim all',
'event.table.claim.all.sure': 'Are you sure to claim all unrecovered alarms?',
'event.table.detail.title': 'Detail',
'event.table.metric': 'Metric',
'event.table.expression': 'Expression',
'event.table.scene': 'Scene',
'event.table.scene.time': 'Time',
'event.table.scene.value': 'Value',
'screen.create': 'Add',
'screen.tag.add': 'Add tag',
'screen.tag.batch.modify': 'Batch modify',
'screen.auto.refresh': 'Auto refresh',
'screen.col': 'col',
'screen.tag.graph.add': 'Add graph',
'screen.tag.graph.add.graph': 'Graph',
'screen.tag.graph.add.number': 'Number (aggr required)',
'screen.tag.graph.add.table': 'Table',
'screen.tag.graph.add.pie': 'Pie',
'screen.tag.up': 'Up',
'screen.tag.down': 'Down',
'screen.tag.batch.modify.tag': 'Active tag',
'screen.tag.batch.modify.target.node': 'Target node',
'screen.tag.batch.modify.target.screen': 'Target screen',
'screen.graph.extraMoreList.share': 'share',
'screen.graph.extraMoreList.clone': 'clone',
'screen.graph.extraMoreList.delete': 'delete',
'screen.graph.extraMoreList.delete.sure': 'Are you sure to delete this chart?',
'silence.add': 'Add',
'silence.metric': 'Metric',
'silence.bindNode': 'Node',
'silence.time': 'Time',
'silence.cause': 'Cause',
'silence.user': 'User',
'silence.delete': 'Delete',
'silence.form.metric': 'Metric',
'silence.form.endpoints': 'Endpoints',
'silence.form.tags': 'Tags',
'silence.form.stime': 'Start time',
'silence.form.etime': 'End time',
'silence.cause.default': 'Quick shielding',
'stra.add': 'Add',
'stra.batch.import.success': 'Batch import successfully',
'stra.advanced': 'Advanced',
'stra.seconds': 's',
'stra.minutes': 'min',
'stra.name': 'Name',
'stra.priority': 'Priority',
'stra.metric': 'Metric',
'stra.notify': 'Notify',
'stra.batch.modify.excludeNs': 'Modify excluded nodes',
'stra.batch.modify.notify': 'Modify notify',
'stra.batch.cloneTo.otherNode': 'Clone to other node',
'stra.batch.delete': 'Delete',
'stra.batch.import': 'Import',
'stra.batch.export': 'Export',
'stra.node': 'Node',
'stra.node.exclude': 'Excluded nodes',
'stra.priority.1': 'P1',
'stra.priority.2': 'P2',
'stra.priority.3': 'P3',
'stra.priority.1.tip': 'P1: Phone, SMS, IM, Email',
'stra.priority.2.tip': 'P2: SMS, IM, Email',
'stra.priority.3.tip': 'P3: IM, Email',
'stra.alertDur': 'Alert duration',
'stra.trigger': 'Trigger condition',
'stra.trigger.normal': 'Normal',
'stra.trigger.and': 'And',
'stra.preview': 'Preview',
'stra.preview.duration': 'duration',
'stra.preview.all': 'each value',
'stra.preview.happen': 'value',
'stra.preview.nodata': 'no data',
'stra.preview.max': 'max',
'stra.preview.min': 'min',
'stra.preview.avg': 'avg',
'stra.preview.sum': 'sum',
'stra.preview.all.help': 'The disconnection situation is discontinuous. To increase fault tolerance, you can choose happen',
'stra.tag': 'Tag filter',
'stra.tag.add': 'Add tag filter',
'stra.tag.modify': 'Modify tag filter',
'stra.tag.include': 'include',
'stra.tag.exclude': 'exclude',
'stra.action': 'Action',
'stra.action.d1': 'in',
'stra.action.d2': 'min',
'stra.action.d3': 'maximum alarm',
'stra.action.d4': 'times',
'stra.notify.team': 'Notify teams',
'stra.notify.user': 'Notify users',
'stra.notify.msg.error': 'Must be an alarm receiver or receiving group',
'stra.notify.callback': 'Notify me of the system I developed (alarm callback, please confirm that it is an address accessible in IDC)',
'stra.recovery.dur': 'Recovery duration',
'stra.recovery.dur.help.1': 'Recovered, it will continue to observe for',
'stra.recovery.dur.help.2': 'seconds, and the recovery notification is sent only when the alarm is not triggered again.',
'stra.recovery.notify': 'Recovery notify',
'stra.recovery.notify.checkbox': 'Do not send recovery notifications',
'stra.period.time': 'Period time',
'stra.alert.upgrade': 'Alert upgrade',
'stra.alert.upgrade.checkbox': 'ON',
'stra.alert.upgrade.d1': 'duration',
'stra.alert.upgrade.d2': 'unprocessed and unrecovered continuous alarm',
'stra.alert.upgrade.d3': 'will be use',
'stra.alert.upgrade.d4': 'send to',
'api.name': 'Name',
'api.url': 'URL',
'api.viewGraph': 'Open graph',
'api.alarm': 'Setup alarm',
'api.batch.viewGraph': 'Batch open graphs',
'api.batch.alarm': 'Batch setup alarms',
'api.title': 'Metric',
'api.protocol': 'Protocol',
'api.domain': 'Domain',
'api.port': 'Port',
'api.path': 'Path',
'api.header.add': 'Add header',
'api.expected_code': 'HTTP status code',
'api.expected_string': 'Expected string',
'api.unexpected_string': 'Unexpected string',
'api.timeout': 'Timeout',
'api.interval': 'Interval',
'api.region': 'Region',
'api.comment': 'Comment',
}

435
web/src/locales/zh.ts Normal file
View File

@ -0,0 +1,435 @@
export default {
'login': '登录',
'logout': '退出登录',
'register': '注册',
'login.title': '账户登录',
'login.ldap': '使用LDAP账号登录',
'form.save': '保 存',
'form.create': '创 建',
'form.submit': '提 交',
'form.delete': '删 除',
'form.login': '登 录',
'form.goback': '返 回',
'msg.submit.success': '提交成功',
'msg.modify.success': '修改成功',
'msg.create.success': '创建成功',
'msg.add.success': '添加成功',
'msg.delete.success': '删除成功',
'msg.clone.success': '克隆成功',
'msg.sort.success': '排序成功',
'please.select.node': '请先选择左侧服务节点',
'table.nodata': '暂无数据',
'table.create': '创建',
'table.operations': '操作',
'table.batch.operations': '批量操作',
'table.detail': '详情',
'table.modify': '修改',
'table.delete': '删除',
'table.delete.batch': '批量删除',
'table.clone': '克隆',
'table.delete.sure': '确定要删除吗?',
'table.delete.there.sure': '确定要删除这些吗?',
'table.ident': '英文标识',
'table.name': '显示名',
'table.cate': '类别',
'table.creator': '创建者',
'table.lastupdated': '修改时间',
'table.note': '备注',
'user.create': '新建用户',
'user.modify': '修改用户',
'user.username': '用户名',
'user.dispname': '显示名',
'user.password': '密码',
'user.email': '邮箱',
'user.phone': '手机',
'user.reset.password': '重置密码',
'user.reset.password.success': '重置密码成功',
'user.invite': '邀请用户',
'user.invite.tips': '点击生成一个邀请用户的链接',
'user.isroot': '是否超管',
'password.old': '旧密码',
'password.new': '新密码',
'token.reset': '重置',
'token.reset.success': '重置成功',
'invite.user.copy.success': '邀请用户的链接复制成功',
'invite.user.copy.faile': '复制失败,请手动复制',
'tree.select.node': '请先选择左侧节点',
'tree.search': '搜节点(空格分割)',
'tree.node': '节点',
'node.copy.path': '拷贝路径',
'node.copy.path.success': '拷贝路径成功',
'node.copy.path.error': '拷贝路径失败',
'node.create.tenant': '添加租户节点',
'node.create': '添加节点',
'node.modify': '修改节点',
'node.delete': '删除节点',
'node.name': '节点名称',
'node.isLeaf': '是否叶子节点',
'node.cate': '类别',
'node.color': '颜色',
'node.note': '备注',
'node.cate.create': '创建节点类别',
'node.cate.modify': '修改节点类别',
'node.display.path': '显示挂载节点',
'node.rename': '节点重命名',
'node.rename.newname': '新节点名称',
'node.rename.success': '节点重命名成功!',
'node.child.create': '创建子节点',
'node.child.create.success': '创建子节点成功!',
'node.child.newname': '子节点名称',
'node.delete.success': '删除节点成功!',
'node.leaf.cannot.create': '叶子节点无法继续创建子节点',
'根节点不能删除': '根节点不能删除',
'select.all': '全选',
'select.include': '包含',
'select.exclude': '排除',
'select.dynamic': '动态值',
'select.selected': '已选',
'select.selected.clear': '清除已选项',
'select.manual.input': '手动输入',
'select.total': '选项',
'select.current.page': '全选当前页',
'1小时': '1小时',
'2小时': '2小时',
'6小时': '6小时',
'12小时': '12小时',
'1天': '1天',
'2天': '2天',
'7天': '7天',
'30天': '30天',
'其它': '其它',
'menu.endpoints': '监控对象',
'menu.endpoints.all': '全部对象',
'menu.endpoints.node': '节点对象',
'menu.endpoints.node.manage': '节点管理',
'menu.monitor': '监控报警',
'menu.monitor.dashboard': '监控看图',
'menu.monitor.screen': '监控大盘',
'menu.monitor.strategy': '报警策略',
'menu.monitor.history': '报警历史',
'menu.monitor.silence': '报警屏蔽',
'menu.monitor.collect': '采集配置',
'menu.users': '用户管理',
'menu.users.users': '用户管理',
'menu.users.teams': '团队管理',
'endpoints.ident': '标识',
'endpoints.alias': '别名',
'endpoints.nodes': '挂载节点',
'endpoints.batch.filter': '批量过滤',
'endpoints.batch.filter.key': '批量字段',
'endpoints.batch.filter.value': '批量值',
'endpoints.export.excel': '导出 Excel',
'endpoints.import': '导入 Endpoints',
'endpoints.export': '导出 Endpoints',
'endpoints.delete': '删除 Endpoints',
'endpoints.bind': '挂载 Endpoints',
'endpoints.unbind': '解载 Endpoints',
'endpoints.copy.selected': '复制已选',
'endpoints.copy.currentPage': '复制当前页',
'endpoints.copy.all': '复制所有',
'endpoints.copy.empty': '复制的对象为空',
'endpoints.copy.error': '复制失败,请手动复制',
'endpoints.import.batch.help': '每一条是 ident::alias 拼接在一起',
'endpoints.modify.alias': '改别名',
'endpoints.bind.node': '挂载的节点',
'endpoints.unbind.node': '解除挂载的节点',
'endpoints.delete.old.bind': '是否删除旧的挂载关系',
'team.ident': '英文标识',
'team.name': '中文名称',
'team.admins': '管理员',
'team.members': '普通成员',
'team.mgmt': '管理方式',
'team.mgmt.admin': '管理员管理制',
'team.mgmt.member': '成员管理制',
'周一': '周一',
'周二': '周二',
'周三': '周三',
'周四': '周四',
'周五': '周五',
'周六': '周六',
'周日': '周日',
'clone.to.other.node': '克隆到其他节点',
'clone.to.other.node.success': '克隆到节点成功成功!',
'collect.log': '日志',
'collect.port': '端口',
'collect.proc': '进程',
'collect.common.search': '搜索名称',
'collect.common.name': '采集名称',
'collect.common.type': '类型',
'collect.common.creator': '创建者',
'collect.common.last_updated': '修改时间',
'collect.common.node': '归属节点',
'collect.common.step': '采集周期',
'collect.common.step.unit': '秒',
'collect.common.note': '备注',
'collect.log.msg.pattern.empty': '匹配正则不能为空',
'collect.log.msg.log.empty': '日志不能为空',
'collect.log.msg.tag.maximum': 'tags 上限三个',
'collect.log.ns': '归属节点',
'collect.log.name': '监控指标名称',
'collect.log.func': '计算方法',
'collect.log.func.cnt': '计数:对符合规则的日志进行计数',
'collect.log.func.avg': '平均:对符合规则的日志抓取出的数字进行平均',
'collect.log.func.sum': '求和:对符合规则的日志抓取出的数字进行求和',
'collect.log.func.max': '最大值:对符合规则的日志抓取出的数字取最大值',
'collect.log.func.min': '最小值:对符合规则的日志抓取出的数字进最小值',
'collect.log.path': '日志路径',
'collect.log.path.dynamic': '动态日志',
'collect.log.path.dynamic.tip.1': '日志末尾自带时间格式,例如',
'collect.log.path.dynamic.tip.2': "$'{}' 中不能包含 /",
'collect.log.timeFmt': '时间格式',
'collect.log.timeFmt.help.1': '时间格式必须和日志中的格式一样, 否则无法采集到数据。',
'collect.log.timeFmt.help.2': '如日志中出现多段符合时间正则的, 只使用第一个匹配结果。',
'collect.log.step': '采集周期',
'collect.log.step.unit': '秒',
'collect.log.pattern': '匹配正则',
'collect.log.pattern.tip.1': '请填写正则表达式',
'collect.log.pattern.tip.2': '如计算方式选择了耗时: 必须包含括号( )',
'collect.log.pattern.tip.3': '例如 cost=(\\d+) , 则取\\d+的部分(默认以第一个括号为准)',
'collect.log.tagval.placeholder': '不是曲线值! 匹配结果必须可枚举!',
'collect.log.tags.add': '新增 tag',
'collect.log.tagName.help.title': 'tagName 填写说明',
'collect.log.tagName.help.1': '不允许包含 host、trigger、include',
'collect.log.tagName.help.2': '不允许包含如下4个特殊字符= , : @',
'collect.log.tagValue.help.title': 'tagValue 填写说明',
'collect.log.tagValue.help.1': '必须包含括号。括号中的正则内容被用作tagValue的取值必须可枚举。',
'collect.log.tagValue.help.2': '不允许包含如下4个特殊字符= , : @',
'collect.log.check': '配置验证',
'collect.log.check.btn': '验证',
'collect.log.check.btn2': '我的配置是否有问题?',
'collect.log.check.help': '请输入一行待监控的完整日志,包括时间。',
'collect.log.check.help.tip.1': '正确匹配:',
'collect.log.check.help.tip.2': '输出正则匹配结果完整式及子项输出tag正则匹配结果完整式及子项以及时间匹配结果',
'collect.log.check.help.tip.3': '错误匹配:',
'collect.log.check.help.tip.4': '输出错误信息',
'collect.log.check.add.tip': '添加采集配置的时候,请验证配置',
'collect.log.note': '备注',
'collect.batch.import': '导入采集配置',
'collect.batch.export': '导出采集配置',
'collect.port.title': '端口监控指标',
'collect.port.name.placeholder': '对采集配置的说明,例如 web端口采集',
'collect.port.pattern.msg': '只能允许填写英文、数字、中划线、下划线、点',
'collect.port.port': '端口号',
'collect.port.timeout': '连接超时',
'collect.port.timeout.unit': '秒',
'collect.proc.title': '进程采集指标',
'collect.proc.name.placeholder': '对采集配置的说明,例如 nginx进程采集',
'collect.proc.service.pattern.msg': '只能允许填写英文、数字、中划线、下划线、点',
'collect.proc.type': '采集方式',
'collect.proc.type.cmd': '命令行',
'collect.proc.type.name': '进程名',
'collect.proc.type.input.pattern.msg': '不能包含中文',
'graph.subscribe': '订阅图表',
'graph.subscribe.node': '所属节点',
'graph.subscribe.screen': '选择大盘',
'graph.subscribe.tag': '选择分类',
'graph.subscribe.success': '图表订阅成功!',
'graph.share': '分享图表',
'graph.clear': '清空图表',
'graph.view': '查看',
'graph.save': '保存',
'graph.machine.list.title': '机器列表',
'graph.machine.list.update': '更新图表',
'graph.metric.list.title': '指标列表',
'graph.metric.list.search': '搜索指标',
'graph.metric.list.all': '全部',
'graph.refresh': '刷新',
'graph.config.title': '图表配置',
'graph.config.graph.title': '标题',
'graph.config.node': '节点',
'graph.config.metric': '指标',
'graph.config.aggr': '聚合',
'graph.config.aggr.sum': '求和',
'graph.config.aggr.avg': '均值',
'graph.config.aggr.max': '最大值',
'graph.config.aggr.min': '最小值',
'graph.config.aggr.group': '聚合维度',
'graph.config.series': '曲线',
'graph.config.series.unit': '条',
'graph.config.cate': '分类',
'graph.config.time': '时间',
'graph.config.threshold': '阈值',
'graph.config.link': '下钻',
'graph.config.link.help': '自定义链接,方便跳转到更深层的大盘、临时图、报警策略等',
'graph.config.chartType.targetValue': '取值',
'graph.config.chartType.current': '当前值',
'graph.config.chartType.unit': '单位',
'graph.config.chartType.subType': '类型',
'graph.config.subType.normal': '数值',
'graph.config.subType.normal.tip': '(必须选择聚合)',
'graph.config.subType.solidGauge': '仪表盘',
'graph.config.subType.liquidFillGauge': '容量水位',
'graph.config.chartType.valueMap': '数值映射',
'graph.config.chartType.mapConf': '映射关系',
'graph.config.chartType.tableType': '表格类型',
'graph.config.chartType.tableType.current': '当前值',
'graph.config.chartType.tableType.stats': '统计值',
'graph.config.chartType.pieType': '样式',
'graph.config.chartType.pieType.pie': 'Pie',
'graph.config.chartType.pieType.donut': 'Donut',
'graph.config.chartType.tableType.columnsKey': '显示列',
'event.tab.alert': '未恢复报警',
'event.tab.all': '所有历史报警',
'event.msg.ignore.success': '忽略报警成功',
'event.msg.claim.success': '认领报警成功',
'event.msg.claim.all.success': '一键认领报警成功',
'event.table.time': '发生时间',
'event.table.stra': '策略名称',
'event.table.node': '节点',
'event.table.priority': '级别',
'event.table.notify': '通知结果',
'event.table.ignore': '忽略',
'event.table.ignore.sure': '确定要忽略这条报警吗?',
'event.table.claim': '认领',
'event.table.claim.sure': '确定要认领这条报警吗?',
'event.table.shield': '屏蔽',
'event.table.assignees': '认领人',
'event.table.status': '状态',
'event.table.status.alert': '报警',
'event.table.status.recovery': '恢复',
'event.table.claim.all': '一键认领',
'event.table.claim.all.sure': '确定认领该节点下所有未恢复的报警吗?',
'event.table.detail.title': '报警事件详情',
'event.table.metric': '指标',
'event.table.expression': '表达式',
'event.table.scene': '现场值',
'event.table.scene.time': '时间',
'event.table.scene.value': '值',
'screen.create': '创建大盘',
'screen.tag.add': '新增分类',
'screen.tag.batch.modify': '批量修改分类',
'screen.auto.refresh': '自动刷新',
'screen.col': '列',
'screen.tag.graph.add': '新增图表',
'screen.tag.graph.add.graph': '折线图',
'screen.tag.graph.add.number': '数值 (必须选择聚合方式)',
'screen.tag.graph.add.table': '表格',
'screen.tag.graph.add.pie': '扇形图',
'screen.tag.up': '上移',
'screen.tag.down': '下移',
'screen.tag.batch.modify.tag': '需要移动的分类',
'screen.tag.batch.modify.target.node': '将要移动到的节点',
'screen.tag.batch.modify.target.screen': '将要移动到的大盘',
'screen.graph.extraMoreList.share': '分享图表',
'screen.graph.extraMoreList.clone': '克隆图表',
'screen.graph.extraMoreList.delete': '删除图表',
'screen.graph.extraMoreList.delete.sure': '确定要删除这个图表吗?',
'silence.add': '新增屏蔽',
'silence.metric': '指标',
'silence.bindNode': '关联节点',
'silence.time': '屏蔽时间',
'silence.cause': '屏蔽原因',
'silence.user': '操作者',
'silence.delete': '解除',
'silence.detail.title': '屏蔽详情',
'silence.form.metric': '屏蔽指标',
'silence.form.endpoints': '屏蔽 endpoints',
'silence.form.tags': '屏蔽 tags',
'silence.form.stime': '开始时间',
'silence.form.etime': '结束时间',
'silence.cause.default': '快速屏蔽',
'stra.add': '新增报警策略',
'stra.batch.import.success': '批量导入成功',
'stra.advanced': '高级',
'stra.seconds': '秒',
'stra.minutes': '分钟',
'stra.name': '名称',
'stra.priority': '级别',
'stra.metric': '指标',
'stra.notify': '报警接收',
'stra.batch.modify.excludeNs': '修改排除节点',
'stra.batch.modify.notify': '修改报警接收组',
'stra.batch.cloneTo.otherNode': '克隆到其他节点',
'stra.batch.delete': '批量删除',
'stra.batch.import': '导入策略',
'stra.batch.export': '导出策略',
'stra.node': '生效节点',
'stra.node.exclude': '排除节点',
'stra.priority.1': '一级报警',
'stra.priority.2': '二级报警',
'stra.priority.3': '三级报警',
'stra.priority.1.tip': '一级报警:发送语音, 短信, IM, 邮件',
'stra.priority.2.tip': '二级报警:发送短信, IM, 邮件',
'stra.priority.3.tip': '三级报警发送IM邮件',
'stra.alertDur': '统计周期',
'stra.trigger': '触发条件',
'stra.trigger.normal': '常用',
'stra.trigger.and': '与条件',
'stra.preview': '预览',
'stra.preview.duration': '持续',
'stra.preview.all': '每个值',
'stra.preview.happen': '次值',
'stra.preview.nodata': '无数据上报',
'stra.preview.max': '最大值',
'stra.preview.min': '最小值',
'stra.preview.avg': '均值',
'stra.preview.sum': '求和值',
'stra.preview.all.help': '断线情况即为不连续。若要增加容错可选择happen',
'stra.tag': 'Tag 过滤',
'stra.tag.add': '添加筛选条件',
'stra.tag.modify': '修改筛选条件',
'stra.tag.include': '包含',
'stra.tag.exclude': '排除',
'stra.action': '执行动作',
'stra.action.d1': '在',
'stra.action.d2': '分钟内',
'stra.action.d3': '最多报警',
'stra.action.d4': '次',
'stra.notify.team': '报警接收团队',
'stra.notify.user': '报警接收人',
'stra.notify.msg.error': '必须存在一个报警接收人或接收组',
'stra.notify.callback': '通知我自己开发的系统(报警回调, 请确认是 IDC 内可访问的地址)',
'stra.recovery.dur': '留观时长',
'stra.recovery.dur.help.1': '告警恢复后持续观察',
'stra.recovery.dur.help.2': '秒,未再触发阈值才发送恢复通知',
'stra.recovery.notify': '静默恢复',
'stra.recovery.notify.checkbox': '不发送恢复通知',
'stra.period.time': '生效时间',
'stra.alert.upgrade': '报警升级',
'stra.alert.upgrade.checkbox': '是否启动报警升级',
'stra.alert.upgrade.d1': '持续',
'stra.alert.upgrade.d2': '未处理并且未恢复的持续报警',
'stra.alert.upgrade.d3': '将以',
'stra.alert.upgrade.d4': '发送给',
'api.name': '采集名称',
'api.url': '探测目标',
'api.viewGraph': '看图',
'api.alarm': '报警',
'api.batch.viewGraph': '批量看图',
'api.batch.alarm': '批量报警',
'api.title': '监控指标',
'api.protocol': '协议',
'api.domain': '域名',
'api.port': '端口',
'api.path': '路径',
'api.header.add': '添加 header',
'api.expected_code': '状态码',
'api.expected_string': '包含字符串',
'api.unexpected_string': '不包含字符串',
'api.timeout': '超时',
'api.interval': '采集周期',
'api.region': '探测源Region',
'api.comment': '备注',
}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, TreeSelect } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -63,7 +64,7 @@ class BatchCloneToNidModal extends Component<Props & FormProps> {
>
<Form layout="vertical">
<FormItem
label="生效节点"
label={<FormattedMessage id="collect.common.node" />}
>
{
getFieldDecorator('nid', {

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import _ from 'lodash';
import { Button, Form, Select, Input, Modal, message, Icon, Tooltip, Row, Col, TreeSelect } from 'antd';
@ -47,8 +48,8 @@ function getPureName(name: string) {
return name;
}
class CollectForm extends Component<Props, State> {
constructor(props: Props) {
class CollectForm extends Component<Props & WrappedComponentProps, State> {
constructor(props: Props & WrappedComponentProps) {
super(props);
const { params } = this.props;
this.state = {
@ -98,7 +99,7 @@ class CollectForm extends Component<Props, State> {
tags,
});
} else {
message.error('tags 上限三个');
message.error(this.props.intl.formatMessage({ id: 'collect.log.msg.tag.maximum' }));
}
}
@ -136,9 +137,9 @@ class CollectForm extends Component<Props, State> {
});
if (pattern === '') {
message.error('匹配正则不能为空');
message.error(this.props.intl.formatMessage({ id: 'collect.log.msg.pattern.empty' }));
} else if (log === '') {
message.error('log不能为空');
message.error(this.props.intl.formatMessage({ id: 'collect.log.msg.log.empty' }));
} else {
this.setState({ logChecked: true, logCheckLoading: true });
request(`${api.collect}/check`, {
@ -181,7 +182,7 @@ class CollectForm extends Component<Props, State> {
const dynamicLogReg = /\$\{[^{]+\}/;
const dynamicLogRegMatch = filePath.match(dynamicLogReg);
if (dynamicLogRegMatch && dynamicLogRegMatch.length && _.some(dynamicLogRegMatch, n => _.includes(n, '/'))) {
message.error('动态日志 ${}中不能包含/');
message.error('/ cannot be included in ${}');
return;
}
// tags 数据转换成接口需要的格式,以及验证是否包含括号
@ -190,15 +191,15 @@ class CollectForm extends Component<Props, State> {
if (tags.length) {
const TagValidateStatus = _.every(tags, (o) => {
if (o.name === '' || o.value === '') {
message.error('tagName、tagValue 值不能为空');
message.error('tagName or tagValue is required');
return false;
}
if (_.includes(reservedKws, o.name)) {
message.error('tagName 不能包含 host、trigger、include 这些是odin系统保留关键字');
message.error('Can not include the host trigger include these are the reserved keywords for the Odin');
return false;
}
if (!bracketsReg.test(o.value)) {
message.error('tagValue 必须包含括号');
message.error('tagValue must include parentheses');
return false;
}
return true;
@ -217,7 +218,7 @@ class CollectForm extends Component<Props, State> {
const { params = {} } = this.props;
if (params.action === 'add') {
if (!this.state.logChecked) {
message.error('添加采集配置的时候,请验证配置');
message.error('Verify the configuration when adding the collection configuration');
return;
}
}
@ -257,13 +258,13 @@ class CollectForm extends Component<Props, State> {
<Form layout="horizontal" onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="归属节点"
label={<FormattedMessage id="collect.common.node" />}
>
{
getFieldDecorator('nid', {
initialValue: initialValues.nid,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<TreeSelect
@ -280,16 +281,13 @@ class CollectForm extends Component<Props, State> {
)
}
</FormItem>
<FormItem {...formItemLayout} label="监控指标名称">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.name" />}>
<Input
addonBefore={params.action === 'add' || initialValues.name.indexOf('log.') === 0 ? 'log.' : null}
{...getFieldProps('name', {
initialValue: getPureName(initialValues.name),
rules: [
{
required: true,
message: '不能为空',
},
{ required: true },
nameRule,
],
})}
@ -297,7 +295,7 @@ class CollectForm extends Component<Props, State> {
style={{ width: params.action === 'add' || initialValues.name.indexOf('log.') === 0 ? 500 : 500 }}
/>
</FormItem>
<FormItem {...formItemLayout} label="计算方法">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.func" />}>
<Select
{...getFieldProps('func', {
initialValue: initialValues.func,
@ -305,28 +303,26 @@ class CollectForm extends Component<Props, State> {
rules: [
{
required: true,
message: '不能为空',
},
],
})}
size="default"
style={{ width: 500 }}
>
<Option value="cnt"></Option>
<Option value="avg"></Option>
<Option value="sum"></Option>
<Option value="max"></Option>
<Option value="min"></Option>
<Option value="cnt"><FormattedMessage id="collect.log.func.cnt" /></Option>
<Option value="avg"><FormattedMessage id="collect.log.func.avg" /></Option>
<Option value="sum"><FormattedMessage id="collect.log.func.sum" /></Option>
<Option value="max"><FormattedMessage id="collect.log.func.max" /></Option>
<Option value="min"><FormattedMessage id="collect.log.func.min" /></Option>
</Select>
</FormItem>
<FormItem {...formItemLayout} label="日志路径">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.path" />}>
<Input
{...getFieldProps('file_path', {
initialValue: initialValues.file_path,
rules: [
{
required: true,
message: '不能为空',
},
],
})}
@ -338,16 +334,16 @@ class CollectForm extends Component<Props, State> {
overlayClassName="largeTooltip"
title={
<div style={{ wordBreak: 'break-all', wordWrap: 'break-word' }}>
{'/path/access.log.${%Y%m%d%H}'}<br />
{'${}中不能包含/'}
<FormattedMessage id="collect.log.path.dynamic.tip.1" /> {'/path/access.log.${%Y%m%d%H}'}<br />
<FormattedMessage id="collect.log.path.dynamic.tip.2" />
</div>
}
>
<span> <Icon type="info-circle-o" /></span>
<span><FormattedMessage id="collect.log.path.dynamic" /> <Icon type="info-circle-o" /></span>
</Tooltip>
</span>
</FormItem>
<FormItem {...formItemLayout} label="时间格式">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.timeFmt" />}>
<div
style={{
width: 500, float: 'left', position: 'relative', zIndex: 1,
@ -359,7 +355,6 @@ class CollectForm extends Component<Props, State> {
rules: [
{
required: true,
message: '不能为空',
},
],
})}
@ -378,25 +373,25 @@ class CollectForm extends Component<Props, State> {
</Select>
</div>
<div style={{ marginLeft: 510, lineHeight: '20px' }}>
, <br />
, 使
<FormattedMessage id="collect.log.timeFmt.help.1" /><br />
<FormattedMessage id="collect.log.timeFmt.help.2" />
</div>
</FormItem>
<FormItem {...formItemLayout} label="采集周期">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.step" />}>
<Select
size="default"
style={{ width: 100 }}
{...getFieldProps('step', {
initialValue: initialValues.step,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
>
{
_.map(interval, item => <Option key={item} value={item}>{item}</Option>)
}
</Select>
</Select> <FormattedMessage id="collect.log.step.unit" />
</FormItem>
<FormItem
{...formItemLayout}
@ -404,13 +399,13 @@ class CollectForm extends Component<Props, State> {
<Tooltip
title={
<div>
<br />
如计算方式选择了耗时: 必须包含括号( )<br />
cost=(\d+) , \d+
<FormattedMessage id="collect.log.pattern.tip.1" /><br />
<FormattedMessage id="collect.log.pattern.tip.2" /><br />
<FormattedMessage id="collect.log.pattern.tip.3" />
</div>
}
>
<span> <Icon type="info-circle-o" /></span>
<span><FormattedMessage id="collect.log.pattern" /> <Icon type="info-circle-o" /></span>
</Tooltip>
}
>
@ -420,13 +415,11 @@ class CollectForm extends Component<Props, State> {
rules: [
{
required: true,
message: '不能为空',
},
],
})}
size="default"
style={{ width: 500 }}
placeholder="耗时计算:正则( )中的数值会用于计算曲线值;流量计数:每匹配到该正则,曲线值+1"
/>
</FormItem>
<FormItem {...formItemLayout} label="tags">
@ -451,7 +444,7 @@ class CollectForm extends Component<Props, State> {
<Col span={13}>
<Input
addonBefore="tagValue"
placeholder="不是曲线值! 匹配结果必须可枚举!"
placeholder={this.props.intl.formatMessage({ id: 'collect.log.tagval.placeholder' })}
value={value}
onChange={(e) => {
this.changeTag(e, index, 'value');
@ -474,25 +467,25 @@ class CollectForm extends Component<Props, State> {
size="default"
onClick={this.addTag}
>
<Icon type="plus" />tag
<Icon type="plus" /><FormattedMessage id="collect.log.tags.add" />
</Button>
</div>
<div style={{ marginLeft: 510, lineHeight: '20px' }}>
<h4>tagName填写说明</h4>
<div>1. 使hosttriggerinclude</div>
<div>2. 4= , : @</div>
<h4>tagValue填写说明</h4>
<div>1. <span style={{ color: '#B03A5B' }}></span>tagValue的取值</div>
<div>2. 4= , : @</div>
<h4><FormattedMessage id="collect.log.tagName.help.title" /></h4>
<div><FormattedMessage id="collect.log.tagName.help.1" /></div>
<div><FormattedMessage id="collect.log.tagName.help.2" /></div>
<h4><FormattedMessage id="collect.log.tagValue.help.title" /></h4>
<div><FormattedMessage id="collect.log.tagValue.help.1" /></div>
<div><FormattedMessage id="collect.log.tagValue.help.2" /></div>
</div>
</FormItem>
<FormItem {...formItemLayout} label="配置验证" required={this.state.logCheckVisible}>
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.check" />} required={this.state.logCheckVisible}>
{
this.state.logCheckVisible ?
<div>
<Input
type="textarea"
placeholder="01/Jan/2006:15:04:05 输入一段日志内容验证配置..."
placeholder="01/Jan/2006:15:04:05"
style={{ width: 500 }}
value={this.state.log}
onChange={(e) => {
@ -502,12 +495,12 @@ class CollectForm extends Component<Props, State> {
}}
/>
<span style={{ paddingLeft: 10 }}>
<FormattedMessage id="collect.log.check.help" />
<Tooltip title={
<div style={{ wordBreak: 'break-all', wordWrap: 'break-word' }}>
<br />tag正则匹配结果完整式及子项
<br />
<br />
<FormattedMessage id="collect.log.check.help.tip.1" /><br /><FormattedMessage id="collect.log.check.help.tip.2" />
<br /><FormattedMessage id="collect.log.check.help.tip.3" />
<br /><FormattedMessage id="collect.log.check.help.tip.4" />
</div>
}
>
@ -520,7 +513,7 @@ class CollectForm extends Component<Props, State> {
onClick={this.checkLog}
loading={this.state.logCheckLoading}
>
<FormattedMessage id="collect.log.check.btn" />
</Button>
</div>
</div> :
@ -533,11 +526,11 @@ class CollectForm extends Component<Props, State> {
});
}}
>
?
<FormattedMessage id="collect.log.check.btn2" />
</Button>
}
</FormItem>
<FormItem {...formItemLayout} label="备注">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.log.note" />}>
<Input
type="textarea"
placeholder=""
@ -548,22 +541,22 @@ class CollectForm extends Component<Props, State> {
/>
</FormItem>
<FormItem wrapperCol={{ offset: 6 }} style={{ marginTop: 24 }}>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}></Button>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}><FormattedMessage id="form.submit" /></Button>
<Button
style={{ marginLeft: 8 }}
>
<Link to={{ pathname: '/monitor/collect' }}></Link>
<Link to={{ pathname: '/monitor/collect' }}><FormattedMessage id="form.goback" /></Link>
</Button>
</FormItem>
</Form>
<Modal
title={
<span>
Result
{
this.state.logCheckedResultsSuccess ?
<span style={{ color: '#87d068' }}></span> :
<span style={{ color: '#f50' }}></span>
<span style={{ color: '#87d068' }}>success</span> :
<span style={{ color: '#f50' }}>error</span>
}
</span>
}
@ -577,7 +570,7 @@ class CollectForm extends Component<Props, State> {
size="large"
onClick={this.closeLogCheckedResults}
>
close
</Button>,
]}
>
@ -608,4 +601,4 @@ class CollectForm extends Component<Props, State> {
}
}
export default Form.create()(CollectForm);
export default Form.create()(injectIntl(CollectForm));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import _ from 'lodash';
import { Button, Form, Select, Input, InputNumber, TreeSelect } from 'antd';
@ -25,7 +26,7 @@ const defaultFormData = {
step: 10,
};
class CollectForm extends Component<Props> {
class CollectForm extends Component<Props & WrappedComponentProps> {
state = {
submitLoading: false,
};
@ -69,19 +70,19 @@ class CollectForm extends Component<Props> {
<Form layout="horizontal" onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="端口监控指标"
label={<FormattedMessage id="collect.port.title" />}
>
<span className="ant-form-text">proc.port.listen</span>
</FormItem>
<FormItem
{...formItemLayout}
label="归属节点"
label={<FormattedMessage id="collect.common.node" />}
>
{
getFieldDecorator('nid', {
initialValue: initialValues.nid,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<TreeSelect
@ -98,21 +99,20 @@ class CollectForm extends Component<Props> {
)
}
</FormItem>
<FormItem {...formItemLayout} label="采集名称">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.name" />}>
<Input
{...getFieldProps('name', {
initialValue: initialValues.name,
rules: [
{
required: true,
message: '不能为空',
},
nameRule,
],
})}
size="default"
style={{ width: 500 }}
placeholder="对采集配置的说明,例如 web端口采集"
placeholder={this.props.intl.formatMessage({ id: 'collect.port.name.placeholder' })}
/>
</FormItem>
<FormItem {...formItemLayout} label="service">
@ -120,29 +120,29 @@ class CollectForm extends Component<Props> {
{...getFieldProps('service', {
initialValue: service,
rules: [
{ required: true, message: '不能为空!' },
{ pattern: /^[a-zA-Z0-9-]+$/, message: '只能允许填写英文、数字、中划线!' },
{ required: true },
{ pattern: /^[a-zA-Z0-9-]+$/, message: this.props.intl.formatMessage({ id: 'collect.port.pattern.msg' }) },
],
})}
size="default"
style={{ width: 500 }}
placeholder="全局唯一的进程英文名"
// placeholder="全局唯一的进程英文名"
/>
</FormItem>
<FormItem {...formItemLayout} label="端口号" required>
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.port.port" />} required>
<InputNumber
{...getFieldProps('port', {
initialValue: initialValues.port,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
size="default"
style={{ width: 500 }}
placeholder="请输入端口号"
// placeholder="请输入端口号"
/>
</FormItem>
<FormItem {...formItemLayout} label="连接超时">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.port.timeout" />}>
<InputNumber
min={1}
style={{ width: 100 }}
@ -150,28 +150,28 @@ class CollectForm extends Component<Props> {
{...getFieldProps('timeout', {
initialValue: initialValues.timeout,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
/>
/> <FormattedMessage id="collect.port.timeout.unit" />
</FormItem>
<FormItem {...formItemLayout} label="采集周期">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.step" />}>
<Select
size="default"
style={{ width: 100 }}
{...getFieldProps('step', {
initialValue: initialValues.step,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
>
{
_.map(interval, item => <Option key={item} value={item}>{item}</Option>)
}
</Select>
</Select> <FormattedMessage id="collect.common.step.unit" />
</FormItem>
<FormItem {...formItemLayout} label="备注">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.note" />}>
<Input
type="textarea"
placeholder=""
@ -182,11 +182,11 @@ class CollectForm extends Component<Props> {
/>
</FormItem>
<FormItem wrapperCol={{ offset: 6 }} style={{ marginTop: 24 }}>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}></Button>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}><FormattedMessage id="form.submit" /></Button>
<Button
style={{ marginLeft: 8 }}
>
<Link to={{ pathname: '/monitor/collect' }}></Link>
<Link to={{ pathname: '/monitor/collect' }}><FormattedMessage id="form.goback" /></Link>
</Button>
</FormItem>
</Form>
@ -194,4 +194,4 @@ class CollectForm extends Component<Props> {
}
}
export default Form.create()(CollectForm);
export default Form.create()(injectIntl(CollectForm));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import _ from 'lodash';
import { Button, Form, Select, Input, TreeSelect } from 'antd';
@ -25,7 +26,7 @@ const defaultFormData = {
step: 10,
};
class CollectForm extends Component<Props> {
class CollectForm extends Component<Props & WrappedComponentProps> {
state = {
submitLoading: false,
};
@ -70,20 +71,20 @@ class CollectForm extends Component<Props> {
<Form layout="horizontal" onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="进程采集指标"
label={<FormattedMessage id="collect.proc.title" />}
>
<span className="ant-form-text">proc.num</span>
</FormItem>
<FormItem
{...formItemLayout}
label="归属节点"
label={<FormattedMessage id="collect.common.node" />}
required
>
{
getFieldDecorator('nid', {
initialValue: initialValues.nid,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<TreeSelect
@ -100,21 +101,20 @@ class CollectForm extends Component<Props> {
)
}
</FormItem>
<FormItem {...formItemLayout} label="采集名称">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.name" />}>
<Input
{...getFieldProps('name', {
initialValue: initialValues.name,
rules: [
{
required: true,
message: '不能为空',
},
nameRule,
],
})}
size="default"
style={{ width: 500 }}
placeholder="对采集配置的说明,例如 nginx进程采集"
placeholder={this.props.intl.formatMessage({ id: 'collect.proc.name.placeholder' })}
/>
</FormItem>
<FormItem {...formItemLayout} label="service">
@ -122,34 +122,34 @@ class CollectForm extends Component<Props> {
{...getFieldProps('service', {
initialValue: service,
rules: [
{ required: true, message: '不能为空!' },
{ pattern: /^[a-zA-Z0-9-]+$/, message: '只能允许填写英文、数字、中划线!' },
{ required: true },
{ pattern: /^[a-zA-Z0-9-]+$/, message: this.props.intl.formatMessage({ id: 'collect.proc.service.pattern.msg' }) },
],
})}
size="default"
style={{ width: 500 }}
placeholder="全局唯一的进程英文名"
// placeholder="全局唯一的进程英文名"
/>
</FormItem>
<FormItem {...formItemLayout} label="采集方式" required>
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.proc.type" />} required>
<Select
{...getFieldProps('collect_method', {
initialValue: initialValues.collect_method,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
size="default"
style={{ width: 500 }}
>
<Select.Option value="cmd"></Select.Option>
<Select.Option value="name"></Select.Option>
<Select.Option value="cmd"><FormattedMessage id="collect.proc.type.cmd" /></Select.Option>
<Select.Option value="name"><FormattedMessage id="collect.proc.type.name" /></Select.Option>
</Select>
</FormItem>
<FormItem
{...formItemLayout}
label={
getFieldValue('collect_method') === 'cmd' ? '命令行' : '进程名'
getFieldValue('collect_method') === 'cmd' ? <FormattedMessage id="collect.proc.type.cmd" /> : <FormattedMessage id="collect.proc.type.name" />
}
required
>
@ -157,31 +157,31 @@ class CollectForm extends Component<Props> {
{...getFieldProps('target', {
initialValue: initialValues.target,
rules: [
{ required: true, message: '不能为空' },
{ pattern: /^[^\u4e00-\u9fa5]+$/, message: '不能包含中文!' },
{ required: true },
{ pattern: /^[^\u4e00-\u9fa5]+$/, message: this.props.intl.formatMessage({ id: 'collect.proc.type.input.pattern.msg' }) },
],
})}
size="default"
style={{ width: 500 }}
/>
</FormItem>
<FormItem {...formItemLayout} label="采集周期">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.step" />}>
<Select
size="default"
style={{ width: 100 }}
{...getFieldProps('step', {
initialValue: initialValues.step,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})}
>
{
_.map(interval, item => <Option key={item} value={item}>{item}</Option>)
}
</Select>
</Select> <FormattedMessage id="collect.common.step.unit" />
</FormItem>
<FormItem {...formItemLayout} label="备注">
<FormItem {...formItemLayout} label={<FormattedMessage id="collect.common.note" />}>
<Input
type="textarea"
placeholder=""
@ -192,11 +192,11 @@ class CollectForm extends Component<Props> {
/>
</FormItem>
<FormItem wrapperCol={{ offset: 6 }} style={{ marginTop: 24 }}>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}></Button>
<Button type="primary" htmlType="submit" loading={this.state.submitLoading}><FormattedMessage id="form.submit" /></Button>
<Button
style={{ marginLeft: 8 }}
>
<Link to={{ pathname: '/monitor/collect' }}></Link>
<Link to={{ pathname: '/monitor/collect' }}><FormattedMessage id="form.goback" /></Link>
</Button>
</FormItem>
</Form>
@ -205,4 +205,4 @@ class CollectForm extends Component<Props> {
}
}
export default Form.create()(CollectForm);
export default Form.create()(injectIntl(CollectForm));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { Spin, message } from 'antd';
import PropTypes from 'prop-types';
@ -9,14 +10,14 @@ import request from '@common/request';
import api from '@common/api';
import CollectForm from './CollectForm';
class CollectFormMain extends Component<RouteComponentProps> {
class CollectFormMain extends Component<RouteComponentProps & WrappedComponentProps> {
static contextTypes = {
getSelectedNode: PropTypes.func,
};
selectedNodeId: number | undefined = undefined;
state = {
loading: false,
data: {},
data: {} as any,
selectedTreeNode: {},
treeData: [],
};
@ -53,7 +54,7 @@ class CollectFormMain extends Component<RouteComponentProps> {
}
handleSubmit = (values: any) => {
const { action, type } = this.props.match.params;
const { action, type } = this.props.match.params as any;
let reqBody;
if (action === 'add' || action === 'clone') {
@ -75,7 +76,7 @@ class CollectFormMain extends Component<RouteComponentProps> {
method: action === 'modify' ? 'PUT' : 'POST',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('提交成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.submit.success' }));
this.props.history.push({
pathname: '/monitor/collect',
});
@ -83,7 +84,7 @@ class CollectFormMain extends Component<RouteComponentProps> {
}
render() {
const { action, type } = this.props.match.params;
const { action, type } = this.props.match.params as any;
const { treeData, data, loading } = this.state;
const ActiveForm = CollectForm[type];
if (action === 'add') {
@ -103,4 +104,4 @@ class CollectFormMain extends Component<RouteComponentProps> {
}
}
export default CreateIncludeNsTree(withRouter(CollectFormMain));
export default CreateIncludeNsTree(withRouter(injectIntl(CollectFormMain)));

View File

@ -1,4 +1,4 @@
export const typeMap = {
export const typeMap: any = {
log: '日志',
port: '端口',
proc: '进程',

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Row, Col, Input, Divider, Dropdown, Button, Icon, Menu, Select, Popconfirm, Modal, Table, message } from 'antd';
@ -10,7 +11,7 @@ import api from '@common/api';
import { typeMap } from './config';
import BatchCloneToNidModal from './BatchCloneToNidModal';
class Collect extends Component {
class Collect extends Component<WrappedComponentProps> {
static contextTypes = {
getNodes: PropTypes.func,
getSelectedNode: PropTypes.func,
@ -18,7 +19,7 @@ class Collect extends Component {
selectedNodeId: number | undefined = undefined;
state = {
loading: false,
data: [],
data: [] as any[],
collectType: undefined,
searchValue: '',
selectedRowKeys: [],
@ -65,7 +66,7 @@ class Collect extends Component {
ids: [record.id],
}]),
}).then(() => {
message.success('删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
});
}
@ -73,8 +74,8 @@ class Collect extends Component {
handleBatchDelete = () => {
const { selectedRows } = this.state;
Modal.confirm({
title: '批量删除',
content: '确定要删除所选的策略吗?',
title: this.props.intl.formatMessage({ id: 'table.delete.batch' }),
content: this.props.intl.formatMessage({ id: 'table.delete.there.sure' }),
onOk: () => {
const typeGroup = _.groupBy(selectedRows, 'collect_type');
const reqBody = _.map(typeGroup, (value, key) => {
@ -87,7 +88,7 @@ class Collect extends Component {
method: 'DELETE',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('批量删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
});
},
@ -101,7 +102,7 @@ class Collect extends Component {
BatchCloneToNidModal({
treeNodes,
onOk: (nid: number) => {
const reqBody = _.map(selectedRows, (item) => {
const reqBody = _.map(selectedRows, (item: any) => {
const pureItem = _.pickBy(item, (v, k) => {
return !_.includes(['id', 'creator', 'created', 'last_updator', 'last_updated', 'tags'], k);
});
@ -117,7 +118,7 @@ class Collect extends Component {
method: 'POST',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('批量克隆到节点成功!');
message.success(this.props.intl.formatMessage({ id: 'clone.to.other.node.success' }));
this.fetchData();
});
},
@ -128,12 +129,12 @@ class Collect extends Component {
const { searchValue, collectType } = this.state;
let { data } = this.state;
if (searchValue) {
data = _.filter(data, (item) => {
data = _.filter(data, (item: any) => {
return item.name.indexOf(searchValue) > -1;
});
}
if (collectType) {
data = _.filter(data, (item) => {
data = _.filter(data, (item: any) => {
return item.collect_type === collectType;
});
}
@ -152,7 +153,7 @@ class Collect extends Component {
allowClear
style={{ width: 100, marginRight: 8 }}
className="mr10"
placeholder="类型"
placeholder={this.props.intl.formatMessage({ id: 'collect.common.type' })}
value={this.state.collectType}
onChange={(value: string) => {
this.setState({ collectType: value });
@ -160,14 +161,13 @@ class Collect extends Component {
>
{
_.map(typeMap, (value, key) => {
return <Select.Option key={key} value={key}>{value}</Select.Option>;
return <Select.Option key={key} value={key}><FormattedMessage id={`collect.${key}`} /></Select.Option>;
})
}
</Select>
<Input.Search
style={{ width: 200 }}
onSearch={this.handleSearchChange}
placeholder="搜索名称"
/>
</Col>
<Col span={12} style={{ textAlign: 'right' }}>
@ -178,7 +178,7 @@ class Collect extends Component {
_.map(typeMap, (value, key) => {
return (
<Menu.Item key={key}>
<Link to={{ pathname: `/monitor/collect/add/${key}` }}>{value}</Link>
<Link to={{ pathname: `/monitor/collect/add/${key}` }}><FormattedMessage id={`collect.${key}`} /></Link>
</Menu.Item>
);
})
@ -187,29 +187,33 @@ class Collect extends Component {
}
>
<Button style={{ marginRight: 8 }}>
<Icon type="down" />
<FormattedMessage id="table.create" /> <Icon type="down" />
</Button>
</Dropdown>
<Dropdown
overlay={
<Menu>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={this.handleBatchDelete}></Button>
<Button type="link" disabled={!canBatchOper} onClick={this.handleBatchDelete}>
<FormattedMessage id="table.delete" />
</Button>
</Menu.Item>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={this.handleBatchCloneToOtherNid}></Button>
<Button type="link" disabled={!canBatchOper} onClick={this.handleBatchCloneToOtherNid}>
<FormattedMessage id="clone.to.other.node" />
</Button>
</Menu.Item>
</Menu>
}
>
<Button>
<Icon type="down" />
<FormattedMessage id="table.batch.operations" /> <Icon type="down" />
</Button>
</Dropdown>
</Col>
</Row>
<Table
rowKey={record => record.id + record.collect_type}
rowKey={(record: any) => record.id + record.collect_type}
rowSelection={{
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
@ -222,37 +226,41 @@ class Collect extends Component {
dataSource={data}
columns= {[
{
title: '名称',
title: <FormattedMessage id="collect.common.name" />,
dataIndex: 'name',
}, {
title: '类型',
title: <FormattedMessage id="collect.common.type" />,
dataIndex: 'collect_type',
render: (text) => {
return typeMap[text];
return <FormattedMessage id={`collect.${text}`} />;
},
}, {
title: '创建者',
title: <FormattedMessage id="collect.common.creator" />,
dataIndex: 'creator',
}, {
title: '修改时间',
title: <FormattedMessage id="collect.common.last_updated" />,
dataIndex: 'last_updated',
render: (text) => {
return moment(text).format('YYYY-MM-DD HH:mm:ss');
},
}, {
title: '操作',
render: (text, record) => {
title: <FormattedMessage id="table.operations" />,
render: (_text, record: any) => {
return (
<span>
<Link to={{ pathname: `/monitor/collect/modify/${_.lowerCase(record.collect_type)}/${record.id}` }}></Link>
<Link to={{ pathname: `/monitor/collect/modify/${_.lowerCase(record.collect_type)}/${record.id}` }}>
<FormattedMessage id="table.modify" />
</Link>
<Divider type="vertical" />
<Link to={{ pathname: `/monitor/collect/clone/${_.lowerCase(record.collect_type)}/${record.id}` }}></Link>
<Link to={{ pathname: `/monitor/collect/clone/${_.lowerCase(record.collect_type)}/${record.id}` }}>
<FormattedMessage id="table.clone" />
</Link>
<Divider type="vertical" />
<Popconfirm
title="确认删除这条配置吗?"
title={<FormattedMessage id="table.delete.sure" />}
onConfirm={() => { this.handleDelete(record); }}
>
<a></a>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -265,4 +273,4 @@ class Collect extends Component {
}
}
export default CreateIncludeNsTree(Collect, { visible: true });
export default CreateIncludeNsTree(injectIntl(Collect), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Row, Col, Icon, Dropdown, Menu } from 'antd';
import _ from 'lodash';
import Graph, { GraphConfig, Info } from '@cpts/Graph';
@ -63,7 +64,7 @@ export default class Graphs extends Component<Props> {
onChange={onChange}
extraRender={(graph: GraphData) => {
return [
<span className="graph-operationbar-item" key="info" title="详情">
<span className="graph-operationbar-item" key="info">
<Info
graphConfig={graph.getGraphConfig(graph.props.data)}
counterList={graph.counterList}
@ -71,24 +72,24 @@ export default class Graphs extends Component<Props> {
<Icon type="info-circle-o" />
</Info>
</span>,
<span className="graph-operationbar-item" key="setting" title="编辑">
<span className="graph-operationbar-item" key="setting">
<Icon type="setting" onClick={() => {
this.graphConfigForm.showModal('update', '保存', o);
this.graphConfigForm.showModal('update', <FormattedMessage id="graph.save" />, o);
}} />
</span>,
<span className="graph-operationbar-item" key="close" title="关闭">
<span className="graph-operationbar-item" key="close">
<Icon type="close-circle-o" onClick={() => {
this.props.onChange('delete', o.id);
}} />
</span>,
<span className="graph-extra-item" key="more" title="更多">
<span className="graph-extra-item" key="more">
<Dropdown trigger={['click']} overlay={
<Menu>
<Menu.Item>
<a onClick={() => { this.handleSubscribeGraph(o); }}></a>
<a onClick={() => { this.handleSubscribeGraph(o); }}><FormattedMessage id="graph.subscribe" /></a>
</Menu.Item>
<Menu.Item>
<a onClick={() => { this.handleShareGraph(o); }}></a>
<a onClick={() => { this.handleShareGraph(o); }}><FormattedMessage id="graph.share" /></a>
</Menu.Item>
</Menu>
}>
@ -109,12 +110,12 @@ export default class Graphs extends Component<Props> {
<div
className={`${prefixCls}-graph ${prefixCls}-graph-add`}
onClick={() => {
this.graphConfigForm.showModal('push', '看图');
this.graphConfigForm.showModal('push', <FormattedMessage id="graph.view" />);
}}
style={{ height: 350, cursor: 'pointer' }}
>
<div style={{ textAlign: 'center', width: '100%' }}>
<Icon type="plus" />
<Icon type="plus" /> <FormattedMessage id="graph.view" />
</div>
</div>
</Col>

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Switch, Popover, Input, Button, Card, Spin } from 'antd';
import _ from 'lodash';
import Multipicker from '@cpts/Multipicker';
@ -92,7 +93,7 @@ export default class HostSelect extends Component<Props, State> {
const { dynamicSwitch, reloadBtnVisible } = this.state;
return (
<Spin spinning={loading}>
<Card title="机器列表" className={`${prefixCls}-card`}>
<Card title={<FormattedMessage id="graph.machine.list.title" />} className={`${prefixCls}-card`}>
<Multipicker
width="100%"
manualEntry
@ -104,14 +105,14 @@ export default class HostSelect extends Component<Props, State> {
{
dynamicSwitch ?
<span>
<a onClick={() => { this.handleDynamicSelect('=all'); }}></a>
<a onClick={() => { this.handleDynamicSelect('=all'); }}><FormattedMessage id="select.all" /></a>
<span className="ant-divider" />
<Popover
trigger="click"
content={
<div style={{ width: 200 }}>
<Input
placeholder="请输入关键词Enter键提交"
placeholder="Press enter to submit"
onKeyDown={(e: any) => {
if (e.keyCode === 13) {
this.handleDynamicSelect('=+', e.target.value);
@ -120,9 +121,8 @@ export default class HostSelect extends Component<Props, State> {
/>
</div>
}
title="包含"
>
<a></a>
<a><FormattedMessage id="select.include" /></a>
</Popover>
<span className="ant-divider" />
<Popover
@ -130,7 +130,7 @@ export default class HostSelect extends Component<Props, State> {
content={
<div style={{ width: 200 }}>
<Input
placeholder="请输入关键词Enter键提交"
placeholder="Press enter to submit"
onKeyDown={(e: any) => {
if (e.keyCode === 13) {
this.handleDynamicSelect('=-', e.target.value);
@ -139,20 +139,19 @@ export default class HostSelect extends Component<Props, State> {
/>
</div>
}
title="排除"
>
<a></a>
<a><FormattedMessage id="select.exclude" /></a>
</Popover>
</span> :
<div>
<Switch onChange={this.handleDynamicSwitchChange} size="small" />
<FormattedMessage id="select.dynamic" /> <Switch onChange={this.handleDynamicSwitchChange} size="small" />
</div>
}
</div>
{
reloadBtnVisible ?
<div style={{ position: 'absolute', bottom: 3, right: 5 }}>
<Button type="primary" onClick={this.handleReloadBtnClick}></Button>
<Button type="primary" onClick={this.handleReloadBtnClick}><FormattedMessage id="graph.machine.list.update" /></Button>
</div> : null
}
</Card>

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Card, Input, Tabs, Tooltip, Spin } from 'antd';
import _ from 'lodash';
import moment from 'moment';
@ -51,7 +52,7 @@ function getSelectedMetricsLen(metric: string, graphs: GraphData[]) {
return null;
}
export default class MetricSelect extends Component<Props, State> {
class MetricSelect extends Component<Props & WrappedComponentProps, State> {
static defaultProps = {
nid: undefined,
hosts: [],
@ -152,18 +153,18 @@ export default class MetricSelect extends Component<Props, State> {
key={`${metricTabKey}_${metric}`}
placement="right"
visible={this.state.metricTipVisible[`${metricTabKey}_${metric}`]}
title={() => {
const currentMetricMeta = getCurrentMetricMeta(metric);
if (currentMetricMeta) {
return (
<div>
<p>{currentMetricMeta.meaning}</p>
<p>{currentMetricMeta.unit}</p>
</div>
);
}
return '';
}}
// title={() => {
// const currentMetricMeta = getCurrentMetricMeta(metric);
// if (currentMetricMeta) {
// return (
// <div>
// <p>含义:{currentMetricMeta.meaning}</p>
// <p>单位:{currentMetricMeta.unit}</p>
// </div>
// );
// }
// return '';
// }}
onVisibleChange={(visible) => {
const key = `${metricTabKey}_${metric}`;
const currentMetricMeta = getCurrentMetricMeta(metric);
@ -186,7 +187,7 @@ export default class MetricSelect extends Component<Props, State> {
})
}
</ul> :
<div style={{ textAlign: 'center' }}></div>
<div style={{ textAlign: 'center' }}>No data</div>
}
</div>
);
@ -209,14 +210,15 @@ export default class MetricSelect extends Component<Props, State> {
const newMetricMap = this.dynamicMetricMaps();
const tabPanes = _.map(newMetricMap, (val) => {
const tabName = this.props.intl.locale == 'zh' ? val.alias : val.key;
return (
<TabPane tab={val.alias} key={val.key}>
<TabPane tab={tabName} key={val.key}>
{ this.renderMetricList(newMetrics, val.key) }
</TabPane>
);
});
tabPanes.unshift(
<TabPane tab="全部" key="ALL">
<TabPane tab={<FormattedMessage id="graph.metric.list.all" />} key="ALL">
{ this.renderMetricList(newMetrics, 'ALL') }
</TabPane>,
);
@ -239,10 +241,10 @@ export default class MetricSelect extends Component<Props, State> {
className={`${prefixCls}-card`}
title={
<span className={`${prefixCls}-metrics-title`}>
<span></span>
<span><FormattedMessage id="graph.metric.list.title" /></span>
<Input
size="small"
placeholder="搜索指标"
placeholder="Search"
onChange={this.handleMetricsSearch}
/>
</span>
@ -254,3 +256,5 @@ export default class MetricSelect extends Component<Props, State> {
);
}
}
export default injectIntl(MetricSelect);

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Modal, Form, TreeSelect, Select, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -27,9 +28,9 @@ interface State {
const FormItem = Form.Item;
const { Option } = Select;
class SubscribeModal extends Component<Props & FormProps, State> {
static defaultProps = {
title: '订阅到大盘',
class SubscribeModal extends Component<Props & FormProps & WrappedComponentProps, State> {
static defaultProps: any = {
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -95,7 +96,7 @@ class SubscribeModal extends Component<Props & FormProps, State> {
});
}),
);
message.success('图表订阅成功!');
message.success(this.props.intl.formatMessage({ id: 'graph.subscribe.success' }));
this.props.onOk();
this.props.destroy();
} catch (e) {
@ -121,15 +122,15 @@ class SubscribeModal extends Component<Props & FormProps, State> {
onOk={this.handleOk}
onCancel={this.handleCancel}
bodyStyle={{ padding: 14 }}
okText="订阅"
okText={<FormattedMessage id="graph.subscribe" />}
>
<Form layout="vertical" onSubmit={(e) => {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="所属节点">
<FormItem label={<FormattedMessage id="graph.subscribe.node" />}>
{getFieldDecorator('nid', {
rules: [{ required: true, message: '请选择所属节点!' }],
rules: [{ required: true }],
})(
<TreeSelect
showSearch
@ -143,9 +144,9 @@ class SubscribeModal extends Component<Props & FormProps, State> {
</TreeSelect>,
)}
</FormItem>
<FormItem label="选择大盘">
<FormItem label={<FormattedMessage id="graph.subscribe.screen" />}>
{getFieldDecorator('scrrenId', {
rules: [{ required: true, message: '请选择所属大盘!' }],
rules: [{ required: true }],
})(
<Select
onDropdownVisibleChange={(dropdownVisible) => {
@ -162,9 +163,9 @@ class SubscribeModal extends Component<Props & FormProps, State> {
</Select>,
)}
</FormItem>
<FormItem label="选择分类">
<FormItem label={<FormattedMessage id="graph.subscribe.tag" />}>
{getFieldDecorator('subclassId', {
rules: [{ required: true, message: '请选择所属分类!' }],
rules: [{ required: true }],
})(
<Select
onDropdownVisibleChange={(dropdownVisible) => {
@ -187,4 +188,4 @@ class SubscribeModal extends Component<Props & FormProps, State> {
}
}
export default ModalControl(Form.create()(SubscribeModal));
export default ModalControl(Form.create()(injectIntl(SubscribeModal)));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { RouteComponentProps } from 'react-router-dom';
import PropTypes from 'prop-types';
import update from 'react-addons-update';
@ -39,7 +40,7 @@ interface State {
const { Content } = Layout;
class MonitorDashboard extends Component<Props, State> {
class MonitorDashboard extends Component<Props & WrappedComponentProps, State> {
metricSelect: any;
static contextTypes = {
nsTreeVisibleChange: PropTypes.func,
@ -52,7 +53,7 @@ class MonitorDashboard extends Component<Props, State> {
onceLoad = false;
sidebarWidth = 200;
constructor(props: Props) {
constructor(props: Props & WrappedComponentProps) {
super(props);
const now = moment();
this.state = {
@ -264,6 +265,8 @@ class MonitorDashboard extends Component<Props, State> {
return JSON.stringify(data);
});
SubscribeModal({
title: <FormattedMessage id="graph.subscribe" />,
language: this.props.intl.locale,
configsList,
});
}
@ -301,7 +304,7 @@ class MonitorDashboard extends Component<Props, State> {
if (!this.allHostsMode && !selectedTreeNode) {
return (
<div>
<FormattedMessage id="please.select.node" />
</div>
);
}
@ -364,21 +367,21 @@ class MonitorDashboard extends Component<Props, State> {
disabled={!graphs.length}
style={{ background: '#fff', marginRight: 8 }}
>
<FormattedMessage id="graph.subscribe" />
</Button>
<Button
onClick={this.handleShareGraphs}
disabled={!graphs.length}
style={{ background: '#fff', marginRight: 8 }}
>
<FormattedMessage id="graph.share" />
</Button>
<Button
onClick={this.handleRemoveGraphs}
disabled={!graphs.length}
style={{ background: '#fff' }}
>
<FormattedMessage id="graph.clear" />
</Button>
</Col>
</Row>
@ -394,4 +397,4 @@ class MonitorDashboard extends Component<Props, State> {
}
}
export default CreateIncludeNsTree(MonitorDashboard as any, { visible: true });
export default CreateIncludeNsTree(injectIntl(MonitorDashboard), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { RouteComponentProps } from 'react-router-dom';
import { Card, Table, Divider, Popconfirm, Icon, message } from 'antd';
import { Link } from 'react-router-dom';
@ -22,7 +23,7 @@ export function normalizeGraphData(data: any) {
});
return cloneData;
}
class Detail extends Component<RouteComponentProps> {
class Detail extends Component<RouteComponentProps & WrappedComponentProps> {
state: {
loading: boolean,
data: any,
@ -176,7 +177,7 @@ class Detail extends Component<RouteComponentProps> {
</div>
<div className={`${nPrefixCls}-detail mt10`}>
<Card
title="报警事件详情"
title={<FormattedMessage id="event.table.detail.title" />}
bodyStyle={{
padding: '10px 16px',
}}
@ -186,14 +187,14 @@ class Detail extends Component<RouteComponentProps> {
pathname: '/monitor/silence/add',
search: `${historyType}=${historyId}&nid=${nid}`,
}}>
<FormattedMessage id="event.table.shield" />
</Link>
{
historyType === 'cur' ?
<span>
<Divider type="vertical" />
<Popconfirm title="确定要认领这条报警吗?" onConfirm={() => this.handleClaim(historyId)}>
<a></a>
<Popconfirm title={<FormattedMessage id="event.table.claim.sure" />} onConfirm={() => this.handleClaim(historyId)}>
<a><FormattedMessage id="event.table.claim" /></a>
</Popconfirm>
</span> : null
}
@ -202,47 +203,47 @@ class Detail extends Component<RouteComponentProps> {
>
<div className={`${nPrefixCls}-detail-list`}>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.stra" /></span>
<Link target="_blank" to={{ pathname: `/monitor/strategy/${data.sid}` }}>{data.sname}</Link>
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.status" /></span>
{_.get(_.find(priorityOptions, { value: data.priority }), 'label')}
<span style={{ paddingLeft: 8 }}>{_.get(_.find(eventTypeOptions, { value: data.event_type }), 'label')}</span>
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.notify" /></span>
{_.join(data.status, ', ')}
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.time" /></span>
{moment.unix(data.etime).format('YYYY-MM-DD HH:mm:ss')}
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.node" /></span>
{data.node_path}
</div>
<div>
<span className="label">endpoint</span>
<span className="label">Endpoint</span>
{data.endpoint}
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.metric" /></span>
{_.get(data.detail, '[0].metric')}
</div>
<div>
<span className="label">tags</span>
<span className="label">Tags</span>
{data.tags}
</div>
<div>
<span className="label"></span>
<span className="label"><FormattedMessage id="event.table.expression" /></span>
{data.info}
</div>
{
_.map(points, (item) => {
return (
<div>
<div className="label"></div>
<div className="label"><FormattedMessage id="event.table.scene" /></div>
{item.metric}
<Table
style={{
@ -254,14 +255,14 @@ class Detail extends Component<RouteComponentProps> {
dataSource={item.points}
columns={[
{
title: '时间',
title: <FormattedMessage id="event.table.scene.time" />,
dataIndex: 'timestamp',
width: 200,
render(text) {
return <span>{moment.unix(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
},
}, {
title: '数值',
title: <FormattedMessage id="event.table.scene.value" />,
dataIndex: 'value',
},
]}
@ -279,4 +280,4 @@ class Detail extends Component<RouteComponentProps> {
}
}
export default CreateIncludeNsTree(Detail as any);
export default CreateIncludeNsTree(injectIntl(Detail));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { Row, Col, Select, Input, DatePicker, Tag, Divider, message, Popconfirm, Badge, Button } from 'antd';
import { ColumnProps } from 'antd/lib/table';
@ -33,14 +34,14 @@ const nPrefixCls = `${prefixCls}-history`;
const { Option } = Select;
const { Search } = Input;
export default class index extends Component<Props, State> {
class index extends Component<Props & WrappedComponentProps, State> {
static defaultProps = {
nodepath: undefined,
nid: undefined,
};
fetchTable: any;
otherParamsKey: string[];
constructor(props: Props) {
constructor(props: Props & WrappedComponentProps) {
super(props);
const now = moment();
if (props.type === 'alert') {
@ -138,7 +139,7 @@ export default class index extends Component<Props, State> {
getColumns() {
const columns: ColumnProps<DataItem>[] = [
{
title: '发生时间',
title: <FormattedMessage id="event.table.time" />,
dataIndex: 'etime',
fixed: 'left',
width: 100,
@ -146,12 +147,12 @@ export default class index extends Component<Props, State> {
return moment.unix(text).format('YYYY-MM-DD HH:mm:ss');
},
}, {
title: '策略名称',
title: <FormattedMessage id="event.table.stra" />,
dataIndex: 'sname',
width: 100,
fixed: 'left',
}, {
title: '级别',
title: <FormattedMessage id="event.table.priority" />,
dataIndex: 'priority',
width: 50,
render: (text) => {
@ -169,7 +170,7 @@ export default class index extends Component<Props, State> {
title: 'tags',
dataIndex: 'tags',
}, {
title: '通知结果',
title: <FormattedMessage id="event.table.notify" />,
dataIndex: 'status',
fixed: 'right',
width: 70,
@ -177,9 +178,10 @@ export default class index extends Component<Props, State> {
return _.join(text, ', ');
},
}, {
title: '操作',
title: <FormattedMessage id="table.operations" />,
fixed: 'right',
width: this.props.type === 'alert' ? 165 : 90,
// width: this.props.type === 'alert' ? 100 : 90,
width: this.props.intl.locale === 'zh' ? 100 : 130,
render: (text, record) => {
return (
<span>
@ -189,18 +191,18 @@ export default class index extends Component<Props, State> {
}}
target="_blank"
>
<FormattedMessage id="table.detail" />
</Link>
{
this.props.type === 'alert' ?
<span>
<Divider type="vertical" />
<Popconfirm title="确定要忽略这条报警吗?" onConfirm={() => this.handleDelete(record.id)}>
<a></a>
<Popconfirm title={<FormattedMessage id="event.table.ignore.sure" />} onConfirm={() => this.handleDelete(record.id)}>
<a><FormattedMessage id="event.table.ignore" /></a>
</Popconfirm>
<Divider type="vertical" />
<Popconfirm title="确定要认领这条报警吗?" onConfirm={() => this.handleClaim(record.id)}>
<a></a>
<Popconfirm title={<FormattedMessage id="event.table.claim.sure" />} onConfirm={() => this.handleClaim(record.id)}>
<a><FormattedMessage id="event.table.claim" /></a>
</Popconfirm>
</span> : null
}
@ -212,7 +214,7 @@ export default class index extends Component<Props, State> {
}}
target="_blank"
>
<FormattedMessage id="event.table.shield" />
</Link>
</span>
);
@ -221,7 +223,7 @@ export default class index extends Component<Props, State> {
];
if (this.props.type === 'alert') {
columns.splice(5, 0, {
title: '认领人',
title: <FormattedMessage id="event.table.assignees" />,
dataIndex: 'claimants',
width: 50,
fixed: 'right',
@ -232,7 +234,7 @@ export default class index extends Component<Props, State> {
}
if (this.props.type === 'all') {
columns.splice(3, 0, {
title: '状态',
title: <FormattedMessage id="event.table.status" />,
dataIndex: 'event_type',
width: 70,
render: (text) => {
@ -240,7 +242,7 @@ export default class index extends Component<Props, State> {
return (
<span style={{ color: eventTypeObj.color }}>
<Badge status={eventTypeObj.status} />
{eventTypeObj.label}
<FormattedMessage id={`event.table.status.${eventTypeObj.value}`} />
</span>
);
},
@ -276,7 +278,7 @@ export default class index extends Component<Props, State> {
>
{
_.map(timeOptions, (option) => {
return <Option key={option.value} value={option.value}>{option.label}</Option>;
return <Option key={option.value} value={option.value}><FormattedMessage id={option.label} /></Option>;
})
}
</Select>
@ -317,7 +319,7 @@ export default class index extends Component<Props, State> {
this.props.type === 'all' ?
<Select
style={{ minWidth: 90, marginRight: 8 }}
placeholder="报警状态"
placeholder={this.props.intl.formatMessage({ id: 'event.table.status' })}
allowClear
value={type}
onChange={(value: string) => {
@ -330,14 +332,14 @@ export default class index extends Component<Props, State> {
>
{
_.map(eventTypeOptions, (option) => {
return <Option key={option.value} value={option.value}>{option.label}</Option>;
return <Option key={option.value} value={option.value}><FormattedMessage id={`event.table.status.${option.value}`} /></Option>;
})
}
</Select> : null
}
<Select
style={{ minWidth: 90, marginRight: 8 }}
placeholder="报警级别"
placeholder={this.props.intl.formatMessage({ id: 'event.table.priority' })}
allowClear
mode="multiple"
value={priorities ? _.map(_.split(priorities, ','), _.toNumber) : []}
@ -366,8 +368,8 @@ export default class index extends Component<Props, State> {
<Col span={6} style={{ textAlign: 'right' }}>
{
this.props.type === 'alert' ?
<Popconfirm title="确定认领该节点下所有未恢复的报警吗?" onConfirm={() => this.handleClaimAll()}>
<Button></Button>
<Popconfirm title={<FormattedMessage id="event.table.claim.all.sure" />} onConfirm={() => this.handleClaimAll()}>
<Button><FormattedMessage id="event.table.claim.all" /></Button>
</Popconfirm> : null
}
</Col>
@ -388,3 +390,5 @@ export default class index extends Component<Props, State> {
);
}
}
export default injectIntl(index);

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Tabs } from 'antd';
import _ from 'lodash';
@ -36,10 +37,10 @@ class index extends Component {
this.setState({ activeKey });
}}
>
<TabPane tab="未恢复报警" key="alert">
<TabPane tab={<FormattedMessage id="event.tab.alert" />} key="alert">
<List nodepath={this.state.nodepath} nid={this.state.nid} type="alert" activeKey={this.state.activeKey} />
</TabPane>
<TabPane tab="所有历史报警" key="all">
<TabPane tab={<FormattedMessage id="event.tab.all" />} key="all">
<List nodepath={this.state.nodepath} nid={this.state.nid} type="all" activeKey={this.state.activeKey} />
</TabPane>
</Tabs>

View File

@ -20,7 +20,7 @@
}
.label {
display: inline-block;
width: 80px;
width: 100px;
text-align: right;
}
.ant-table-wrapper {

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, Input } from 'antd';
import _ from 'lodash';
import { FormProps } from 'antd/lib/form';
@ -51,9 +52,9 @@ class AddModal extends Component<Props & FormProps> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="名称">
<FormItem label={<FormattedMessage id="table.name" />}>
{getFieldDecorator('name', {
rules: [{ required: true, message: '请填写大盘名称!' }],
rules: [{ required: true }],
})(
<Input />,
)}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, Input } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -52,10 +53,10 @@ class ModifyModal extends Component<Props & FormProps> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="名称">
<FormItem label={<FormattedMessage id="table.name" />}>
{getFieldDecorator('name', {
initialValue: this.props.name,
rules: [{ required: true, message: '请填写大盘名称!' }],
rules: [{ required: true }],
})(
<Input />,
)}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, Input } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -51,9 +52,9 @@ class AddModal extends Component<Props> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="名称">
<FormItem label={<FormattedMessage id="table.name" />}>
{getFieldDecorator('name', {
rules: [{ required: true, message: '请填写分类名称!' }],
rules: [{ required: true }],
})(
<Input />,
)}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, TreeSelect, Select } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -22,7 +23,7 @@ const { Option } = Select;
class BatchMoveSubclass extends Component<Props> {
static defaultProps = {
title: '批量移动分类',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -58,7 +59,7 @@ class BatchMoveSubclass extends Component<Props> {
return (
<Modal
title={title}
title={<FormattedMessage id="screen.tag.batch.modify" />}
visible={visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
@ -67,9 +68,9 @@ class BatchMoveSubclass extends Component<Props> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="需要移动的分类">
<FormItem label={<FormattedMessage id="screen.tag.batch.modify.tag" />}>
{getFieldDecorator('subclasses', {
rules: [{ required: true, message: '请选择分类!' }],
rules: [{ required: true }],
})(
<Select mode="multiple">
{
@ -80,9 +81,9 @@ class BatchMoveSubclass extends Component<Props> {
</Select>,
)}
</FormItem>
<FormItem label="将要移动到的节点">
<FormItem label={<FormattedMessage id="screen.tag.batch.modify.target.node" />}>
{getFieldDecorator('nid', {
rules: [{ required: true, message: '请选择节点!' }],
rules: [{ required: true }],
onChange: this.handleSelectedTreeNodeIdChange,
})(
<TreeSelect
@ -96,9 +97,9 @@ class BatchMoveSubclass extends Component<Props> {
</TreeSelect>,
)}
</FormItem>
<FormItem label="将要移动到的大盘">
<FormItem label={<FormattedMessage id="screen.tag.batch.modify.target.screen" />}>
{getFieldDecorator('screenId', {
rules: [{ required: true, message: '请选择大盘!' }],
rules: [{ required: true }],
})(
<Select>
{

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Form, Input } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -52,10 +53,10 @@ class ModifyModal extends Component<Props> {
e.preventDefault();
this.handleOk();
}}>
<FormItem label="名称">
<FormItem label={<FormattedMessage id="table.name" />}>
{getFieldDecorator('name', {
initialValue: this.props.name,
rules: [{ required: true, message: '请填写分类名称!' }],
rules: [{ required: true }],
})(
<Input />,
)}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Popconfirm, Menu, Col } from 'antd';
import { SortableElement } from 'react-sortable-hoc';
import _ from 'lodash';
@ -57,14 +58,14 @@ class RenderGraph extends Component<any> {
}}
extraMoreList={[
<Menu.Item key="share">
<a onClick={() => { this.handleShareGraph(data.configs); }}></a>
<a onClick={() => { this.handleShareGraph(data.configs); }}><FormattedMessage id="screen.graph.extraMoreList.share" /></a>
</Menu.Item>,
<Menu.Item key="clone">
<a onClick={() => { this.handleCloneGraph(data.configs); }}></a>
<a onClick={() => { this.handleCloneGraph(data.configs); }}><FormattedMessage id="screen.graph.extraMoreList.clone" /></a>
</Menu.Item>,
<Menu.Item key="del">
<Popconfirm title="确定要删除这个图表吗?" onConfirm={() => { this.props.onDelChart(data.id); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="screen.graph.extraMoreList.delete.sure" />} onConfirm={() => { this.props.onDelChart(data.id); }}>
<a><FormattedMessage id="screen.graph.extraMoreList.delete" /></a>
</Popconfirm>
</Menu.Item>,
]}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Button, Card, Divider, Popconfirm, message, Row, Col, Select, Checkbox } from 'antd';
import { FormProps } from 'antd/lib/form';
import moment from 'moment';
@ -37,7 +38,7 @@ function updateTime(nowMoment: moment.Moment, graphConfig: any) {
const COUNTDOWN = 9; // 0 ~ 9
class ScreenDetail extends Component<FormProps> {
class ScreenDetail extends Component<FormProps & WrappedComponentProps> {
timer: NodeJS.Timeout | undefined = undefined;
state = {
subclassLoading: false,
@ -398,30 +399,30 @@ class ScreenDetail extends Component<FormProps> {
<a onClick={() => {
if (this.graphConfigForm) {
this.currentSubclassId = subclassObj.id;
this.graphConfigForm.showModal('push', '新增');
this.graphConfigForm.showModal('push', this.props.intl.formatMessage({ id: 'table.create' }));
}
}}>
<FormattedMessage id="screen.tag.graph.add" />
</a>
<Divider type="vertical" />
<a onClick={() => this.handleModSubclass(subclassObj)}></a>
<a onClick={() => this.handleModSubclass(subclassObj)}><FormattedMessage id="table.modify" /></a>
<Divider type="vertical" />
<Popconfirm title="确认要删除这个分类吗?" onConfirm={() => this.handleDelSubclass(subclassObj.id)}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => this.handleDelSubclass(subclassObj.id)}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
<Divider type="vertical" />
<a
disabled={idx === 0}
onClick={() => this.handleMoveSubclass('up', idx)}
>
<FormattedMessage id="screen.tag.up" />
</a>
<Divider type="vertical" />
<a
disabled={idx === subclassData.length - 1}
onClick={() => this.handleMoveSubclass('down', idx)}
>
<FormattedMessage id="screen.tag.down" />
</a>
</span>
}
@ -481,7 +482,7 @@ class ScreenDetail extends Component<FormProps> {
}}
onCloneGraph={(configs: any) => {
this.currentSubclassId = subclassObj.id;
this.graphConfigForm.showModal('push', '克隆图表', {
this.graphConfigForm.showModal('push', this.props.intl.formatMessage({ id: 'table.create' }), {
...configs,
});
}}
@ -502,12 +503,12 @@ class ScreenDetail extends Component<FormProps> {
<div>
<Row className="mb10">
<Col span={6}>
<Button onClick={this.handleAddSubclass} style={{ marginRight: 8 }}></Button>
<Button onClick={this.handleBatchMoveSubclass}></Button>
<Button onClick={this.handleAddSubclass} style={{ marginRight: 8 }}><FormattedMessage id="screen.tag.add" /></Button>
<Button onClick={this.handleBatchMoveSubclass}><FormattedMessage id="screen.tag.batch.modify" /></Button>
</Col>
<Col span={18} className="textAlignRight">
<span style={{ paddingRight: 10 }}>
<FormattedMessage id="graph.config.time" />
<Select size="default" style={
timeVal === 'custom' ?
{
@ -517,12 +518,12 @@ class ScreenDetail extends Component<FormProps> {
width: 80,
}
}
placeholder="无"
// placeholder="无"
value={timeVal}
onChange={this.handleTimeOptionChange}
>
{
_.map(graphcConfig.time, o => <Option key={o.value} value={o.value}>{o.label}</Option>)
_.map(graphcConfig.time, o => <Option key={o.value} value={o.value}><FormattedMessage id={o.label} /></Option>)
}
</Select>
{
@ -566,7 +567,7 @@ class ScreenDetail extends Component<FormProps> {
});
}}
>
{ this.state.autoRefresh ? `(${this.state.countdown})` : '' }
<FormattedMessage id="screen.auto.refresh" /> { this.state.autoRefresh ? `(${this.state.countdown})` : '' }
</Checkbox>
<Select
style={{ width: 70 }}
@ -577,10 +578,10 @@ class ScreenDetail extends Component<FormProps> {
});
}}
>
<Option key="1" value={1}>1</Option>
<Option key="2" value={2}>2</Option>
<Option key="3" value={3}>3</Option>
<Option key="4" value={4}>4</Option>
<Option key="1" value={1}>1 <FormattedMessage id="screen.col" /></Option>
<Option key="2" value={2}>2 <FormattedMessage id="screen.col" /></Option>
<Option key="3" value={3}>3 <FormattedMessage id="screen.col" /></Option>
<Option key="4" value={4}>4 <FormattedMessage id="screen.col" /></Option>
</Select>
</Col>
</Row>
@ -600,4 +601,4 @@ class ScreenDetail extends Component<FormProps> {
}
}
export default CreateIncludeNsTree(ScreenDetail);
export default CreateIncludeNsTree(injectIntl(ScreenDetail));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage, WrappedComponentProps, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Button, Input, Divider, Popconfirm, Table, message } from 'antd';
@ -89,7 +90,7 @@ const DragableBodyRow = DropTarget(
)(BodyRow),
);
class Screen extends Component {
class Screen extends Component<WrappedComponentProps> {
static contextTypes = {
getSelectedNode: PropTypes.func,
};
@ -137,7 +138,8 @@ class Screen extends Component {
handleAdd = () => {
AddModal({
title: '新增大盘',
language: this.props.intl.locale,
title: this.props.intl.formatMessage({ id: 'table.create' }),
onOk: (values: any) => {
request(`${api.node}/${this.selectedNodeId}/screen`, {
method: 'POST',
@ -155,8 +157,9 @@ class Screen extends Component {
handleModify = (record: any) => {
ModifyModal({
language: this.props.intl.locale,
name: record.name,
title: '修改大盘',
title: this.props.intl.formatMessage({ id: 'table.modify' }),
onOk: (values: any) => {
request(`${api.screen}/${record.id}`, {
method: 'PUT',
@ -165,7 +168,7 @@ class Screen extends Component {
node_id: record.node_id,
}),
}).then(() => {
message.success('修改大盘成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
this.fetchData();
});
},
@ -176,7 +179,7 @@ class Screen extends Component {
request(`${api.screen}/${id}`, {
method: 'DELETE',
}).then(() => {
message.success('删除大盘成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
});
}
@ -193,7 +196,7 @@ class Screen extends Component {
},
}),
() => {
const reqBody = _.map(this.state.data, (item, i) => {
const reqBody = _.map(this.state.data, (item: any, i) => {
return {
id: item.id,
weight: i,
@ -203,7 +206,7 @@ class Screen extends Component {
method: 'PUT',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('大盘排序成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.sort.success' }));
});
},
);
@ -212,7 +215,7 @@ class Screen extends Component {
filterData() {
const { data, search } = this.state;
if (search) {
return _.filter(data, (item) => {
return _.filter(data, (item: any) => {
return item.name.indexOf(search) > -1;
});
}
@ -226,10 +229,10 @@ class Screen extends Component {
return (
<div className={prefixCls}>
<div className="mb10">
<Button className="mr10" onClick={this.handleAdd}></Button>
<Button className="mr10" onClick={this.handleAdd}><FormattedMessage id="screen.create" /></Button>
<Input
style={{ width: 200 }}
placeholder="搜索"
placeholder="Search"
value={search}
onChange={(e) => {
this.setState({ search: e.target.value });
@ -251,25 +254,25 @@ class Screen extends Component {
})}
columns={[
{
title: '名称',
title: <FormattedMessage id="table.name" />,
dataIndex: 'name',
render: (text, record) => {
return <Link to={{ pathname: `/monitor/screen/${record.id}` }}>{text}</Link>;
},
}, {
title: '创建人',
title: <FormattedMessage id="table.creator" />,
width: 200,
dataIndex: 'last_updator',
}, {
title: '操作',
title: <FormattedMessage id="table.operations" />,
width: 200,
render: (text, record) => {
render: (text, record: any) => {
return (
<span>
<a onClick={() => this.handleModify(record)}></a>
<a onClick={() => this.handleModify(record)}><FormattedMessage id="table.modify" /></a>
<Divider type="vertical" />
<Popconfirm title="确定要删除这个大盘吗?" onConfirm={() => this.handleDel(record.id)}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => this.handleDel(record.id)}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -282,4 +285,4 @@ class Screen extends Component {
}
}
export default CreateIncludeNsTree(DragDropContext(HTML5Backend)(Screen), { visible: true });
export default CreateIncludeNsTree(DragDropContext(HTML5Backend)(injectIntl(Screen)), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Button, Row, Col, message } from 'antd';
import _ from 'lodash';
import moment from 'moment';
@ -9,7 +10,7 @@ import api from '@common/api';
import CustomForm from './CustomForm';
import { normalizReqData } from './utils';
class Add extends Component<any> {
class Add extends Component<WrappedComponentProps> {
customForm: any;
state = {
nid: undefined,
@ -55,12 +56,12 @@ class Add extends Component<any> {
method: 'POST',
body: JSON.stringify(reqData),
}).then(() => {
message.success('新增屏蔽成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.create.success' }));
history.push({
pathname: '/monitor/silence',
});
}).catch(() => {
message.error('新增屏蔽失败!');
// message.error('新增屏蔽失败!');
}).finally(() => {
this.setState({ submitLoading: false });
});
@ -79,13 +80,15 @@ class Add extends Component<any> {
initialValues={{
btime: now.clone().unix(),
etime: now.clone().add(1, 'hours').unix(),
cause: '快速屏蔽',
cause: this.props.intl.formatMessage({ id: 'silence.cause.default' }),
...initialValues,
}}
/>
<Row>
<Col offset={6}>
<Button onClick={this.handleSubmit} loading={submitLoading} type="primary"></Button>
<Button onClick={this.handleSubmit} loading={submitLoading} type="primary">
<FormattedMessage id="form.submit" />
</Button>
</Col>
</Row>
</div>
@ -93,4 +96,4 @@ class Add extends Component<any> {
}
}
export default CreateIncludeNsTree(Add);
export default CreateIncludeNsTree(injectIntl(Add));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, Form, Input, DatePicker } from 'antd';
import { FormProps } from 'antd/lib/form';
import moment from 'moment';
@ -88,7 +89,7 @@ class CustomForm extends Component<Props> {
key={o.value}
type={o.value === timeSpan ? 'primary' : undefined}
>
{o.label}
<FormattedMessage id={o.label} />
</Button>
))
}
@ -105,12 +106,12 @@ class CustomForm extends Component<Props> {
<Form className={readOnly ? 'readOnly' : ''}>
<FormItem
{...formItemLayout}
label="屏蔽指标"
label={<FormattedMessage id="silence.form.metric" />}
>
{getFieldDecorator('metric', {
initialValue: initialValues.metric,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<Input />,
@ -118,12 +119,12 @@ class CustomForm extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="屏蔽 endpoints"
label={<FormattedMessage id="silence.form.endpoints" />}
>
{getFieldDecorator('endpoints', {
initialValue: _.isArray(initialValues.endpoints) ? _.join(initialValues.endpoints, '\n') : initialValues.endpoints,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<TextArea
@ -134,14 +135,11 @@ class CustomForm extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="屏蔽 tags"
help="示例:key1=value1,key2=value2"
label={<FormattedMessage id="silence.form.tags" />}
help="eg. key1=value1,key2=value2"
>
{getFieldDecorator('tags', {
initialValue: initialValues.tags,
rules: [
// { required: true, message: '不能为空' },
],
})(
<TextArea
autosize={{ minRows: 2, maxRows: 6 }}
@ -156,12 +154,12 @@ class CustomForm extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="开始时间"
label={<FormattedMessage id="silence.form.stime" />}
>
{getFieldDecorator('btime', {
initialValue: moment.unix(initialValues.btime),
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<DatePicker
@ -173,12 +171,12 @@ class CustomForm extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="结束时间"
label={<FormattedMessage id="silence.form.etime" />}
>
{getFieldDecorator('etime', {
initialValue: moment.unix(initialValues.etime),
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<DatePicker
@ -190,12 +188,12 @@ class CustomForm extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="屏蔽原因"
label={<FormattedMessage id="silence.cause" />}
>
{getFieldDecorator('cause', {
initialValue: initialValues.cause,
rules: [
{ required: true, message: '不能为空' },
{ required: true },
],
})(
<TextArea

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import ReactDOM from 'react-dom';
import { Modal } from 'antd';
import { FormProps } from 'antd/lib/form';
@ -17,7 +18,7 @@ interface Props extends FormProps {
class DetailModal extends Component<Props> {
static defaultProps = {
title: '屏蔽详情',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -48,7 +49,7 @@ class DetailModal extends Component<Props> {
<div>
<Modal
width={900}
title={title}
title={<FormattedMessage id="silence.detail.title" />}
visible={visible}
onOk={this.handleOk}
onCancel={this.handleCancel}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Button, Popconfirm, Input, Table, message } from 'antd';
@ -16,7 +17,7 @@ const timeFormatMap = {
moment: 'YYYY-MM-DD HH:mm:ss',
};
class index extends Component {
class index extends Component<WrappedComponentProps> {
static contextTypes = {
getSelectedNode: PropTypes.func,
};
@ -66,27 +67,12 @@ class index extends Component {
}
}
handleBatchDelConfirm = () => {
const { selectedRowKeys } = this.state;
request(`${api.maskconf}/${selectedRowKeys}`, {
method: 'DELETE',
}).then(() => {
this.setState({ selectedRowKeys: [] });
message.success('批量解除成功!');
this.fetchData();
}).catch(() => {
message.error('批量解除失败!');
});
}
handleDelConfirm = (id: number) => {
request(`${api.maskconf}/${id}`, {
method: 'DELETE',
}).then(() => {
message.success('解除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
}).catch(() => {
message.error('解除失败!');
});
}
@ -118,11 +104,13 @@ class index extends Component {
<Button
style={{ marginRight: 8 }}
>
<Link to={{ pathname: '/monitor/silence/add', search: `nid=${this.selectedNodeId}` }}></Link>
<Link to={{ pathname: '/monitor/silence/add', search: `nid=${this.selectedNodeId}` }}>
<FormattedMessage id="silence.add" />
</Link>
</Button>
<Input.Search
style={{ width: 200, marginLeft: 8 }}
placeholder="搜索"
placeholder="Search"
value={filterValue.search}
onChange={(e) => {
this.setState({
@ -140,7 +128,7 @@ class index extends Component {
dataSource={data}
columns={[
{
title: '指标',
title: <FormattedMessage id="silence.metric" />,
dataIndex: 'metric',
width: 150,
render: (text, record) => {
@ -160,10 +148,10 @@ class index extends Component {
});
},
}, {
title: '关联节点',
title: <FormattedMessage id="silence.bindNode" />,
dataIndex: 'node_path',
}, {
title: '屏蔽时间',
title: <FormattedMessage id="silence.time" />,
width: 180,
render(text, record) {
const beginTs = record.btime;
@ -178,19 +166,19 @@ class index extends Component {
return <span>unknown</span>;
},
}, {
title: '屏蔽原因',
title: <FormattedMessage id="silence.cause" />,
dataIndex: 'cause',
width: 120,
}, {
title: '操作者',
title: <FormattedMessage id="silence.user" />,
dataIndex: 'user',
}, {
title: '操作',
title: <FormattedMessage id="table.operations" />,
width: 60,
render: (text, record) => (
<span>
<Popconfirm title="确定要解除这个策略吗?" onConfirm={() => { this.handleDelConfirm(record.id); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => { this.handleDelConfirm(record.id); }}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
),
@ -203,4 +191,4 @@ class index extends Component {
}
}
export default CreateIncludeNsTree(index, { visible: true });
export default CreateIncludeNsTree(injectIntl(index), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { RouteComponentProps } from 'react-router-dom';
import { message } from 'antd';
import queryString from 'query-string';
@ -9,14 +10,14 @@ import api from '@common/api';
import SettingFields from './SettingFields';
import './style.less';
class Add extends Component<RouteComponentProps> {
class Add extends Component<RouteComponentProps & WrappedComponentProps> {
handleSubmit = (values: any) => {
const { history } = this.props;
request(api.stra, {
method: 'POST',
body: JSON.stringify(values),
}).then(() => {
message.success('添加报警策略成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.add.success' }));
history.push({
pathname: '/monitor/strategy',
});
@ -40,4 +41,4 @@ class Add extends Component<RouteComponentProps> {
}
}
export default CreateIncludeNsTree(Add as any);
export default CreateIncludeNsTree(injectIntl(Add));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, Form, Input, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -20,7 +21,7 @@ interface Props extends FormProps{
const FormItem = Form.Item;
const { TextArea } = Input;
class BatchImportExportModal extends Component<Props> {
class BatchImportExportModal extends Component<Props & WrappedComponentProps> {
static defaultProps = {
data: undefined,
selectedNid: undefined,
@ -55,7 +56,7 @@ class BatchImportExportModal extends Component<Props> {
});
});
Promise.all(promises).then(() => {
message.success('批量导入成功!');
message.success(this.props.intl.formatMessage({ id: 'stra.batch.import.success' }));
this.props.onOk();
this.props.destroy();
});
@ -102,4 +103,4 @@ class BatchImportExportModal extends Component<Props> {
}
}
export default ModalControl(Form.create()(BatchImportExportModal));
export default ModalControl(Form.create()(injectIntl(BatchImportExportModal)));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Modal, Form, Select, TreeSelect, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -22,7 +23,7 @@ interface Props extends FormProps{
const FormItem = Form.Item;
const { Option } = Select;
class BatchModModal extends Component<Props> {
class BatchModModal extends Component<Props & WrappedComponentProps> {
static defaultProps = {
selectedNid: undefined,
treeNodes: [],
@ -84,9 +85,9 @@ class BatchModModal extends Component<Props> {
});
});
await Promise.all(requests).then(() => {
message.success('批量操作成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
}).catch(() => {
message.error('批量操作失败!');
// message.error('批量操作失败!');
});
} catch (e) {
console.log(e);
@ -118,7 +119,7 @@ class BatchModModal extends Component<Props> {
{
this.props.type === 'exclNid' ?
<FormItem
label="排除节点"
label={<FormattedMessage id="stra.node.exclude" />}
>
{
getFieldDecorator('excl_nid', {
@ -144,7 +145,7 @@ class BatchModModal extends Component<Props> {
[
<FormItem
key="group"
label="报警接收团队"
label={<FormattedMessage id="stra.notify.team" />}
>
{
getFieldDecorator('notify_group', {
@ -155,7 +156,6 @@ class BatchModModal extends Component<Props> {
size="default"
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收团队"
>
{
_.map(this.state.notifyGroupData, (item: any, i) => {
@ -170,7 +170,7 @@ class BatchModModal extends Component<Props> {
</FormItem>,
<FormItem
key="user"
label="报警接收人"
label={<FormattedMessage id="stra.notify.user" />}
>
{
getFieldDecorator('notify_user', {
@ -181,7 +181,6 @@ class BatchModModal extends Component<Props> {
size="default"
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收人"
>
{
_.map(this.state.notifyUserData, (item: any, i) => {
@ -199,7 +198,7 @@ class BatchModModal extends Component<Props> {
{
this.props.type === 'clone' ?
<FormItem
label="生效节点"
label={<FormattedMessage id="stra.node" />}
>
{
getFieldDecorator('nid', {
@ -224,4 +223,4 @@ class BatchModModal extends Component<Props> {
}
}
export default ModalControl(Form.create()(BatchModModal));
export default ModalControl(Form.create()(injectIntl(BatchModModal)));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { RouteComponentProps } from 'react-router-dom';
import { message } from 'antd';
import _ from 'lodash';
@ -9,7 +10,7 @@ import SettingFields from './SettingFields';
import { normalizeFormData } from './utils';
import './style.less';
class Clone extends Component<RouteComponentProps> {
class Clone extends Component<RouteComponentProps & WrappedComponentProps> {
state = {
values: undefined,
};
@ -35,7 +36,7 @@ class Clone extends Component<RouteComponentProps> {
method: 'POST',
body: JSON.stringify(values),
}).then(() => {
message.success('添加报警策略成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.add.success' }));
history.push({
pathname: '/monitor/strategy',
});
@ -59,4 +60,4 @@ class Clone extends Component<RouteComponentProps> {
}
}
export default CreateIncludeNsTree(Clone as any);
export default CreateIncludeNsTree(injectIntl(Clone));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { RouteComponentProps } from 'react-router-dom';
import { message } from 'antd';
import _ from 'lodash';
@ -9,7 +10,7 @@ import SettingFields from './SettingFields';
import { normalizeFormData } from './utils';
import './style.less';
class Modify extends Component<RouteComponentProps> {
class Modify extends Component<RouteComponentProps & WrappedComponentProps> {
state = {
values: undefined,
} as { values: any };
@ -39,7 +40,7 @@ class Modify extends Component<RouteComponentProps> {
id: values.id,
}),
}).then(() => {
message.success('修改报警策略成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
history.push({
pathname: '/monitor/strategy',
});
@ -62,4 +63,4 @@ class Modify extends Component<RouteComponentProps> {
}
}
export default CreateIncludeNsTree(Modify as any);
export default CreateIncludeNsTree(injectIntl(Modify));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Form, Button, Input, Radio, Tooltip, Icon, InputNumber, TreeSelect, Checkbox, Row, Col } from 'antd';
import { FormProps } from 'antd/lib/form';
@ -20,7 +21,7 @@ interface Props extends FormProps{
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
class SettingFields extends Component<Props> {
class SettingFields extends Component<Props & WrappedComponentProps> {
static contextTypes = {
habitsId: PropTypes.string,
};
@ -42,7 +43,7 @@ class SettingFields extends Component<Props> {
advanced: false,
};
constructor(props: Props) {
constructor(props: Props & WrappedComponentProps) {
super(props);
this.fetchNotifyData = _.debounce(this.fetchNotifyData, 500);
}
@ -180,13 +181,13 @@ class SettingFields extends Component<Props> {
<Form className={`${prefixCls}-strategy-form`} layout="horizontal" onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="策略名称:"
label={<FormattedMessage id="stra.name" />}
>
{
getFieldDecorator('name', {
initialValue: this.props.initialValues.name,
rules: [{
required: true, message: '请输入策略名称!',
required: true
}],
})(
<Input />,
@ -195,7 +196,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="生效节点:"
label={<FormattedMessage id="stra.node" />}
>
{
getFieldDecorator('nid', {
@ -222,7 +223,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="排除节点:"
label={<FormattedMessage id="stra.node.exclude" />}
>
{
getFieldDecorator('excl_nid', {
@ -247,12 +248,12 @@ class SettingFields extends Component<Props> {
label={
<Tooltip title={
<div>
, , IM, <br />
, IM, <br />
IM
<FormattedMessage id="stra.priority.1.tip" /><br />
<FormattedMessage id="stra.priority.2.tip" /><br />
<FormattedMessage id="stra.priority.3.tip" />
</div>
}>
<span> <Icon type="info-circle-o" /></span>
<span><FormattedMessage id="stra.priority" /> <Icon type="info-circle-o" /></span>
</Tooltip>
}
required
@ -265,15 +266,15 @@ class SettingFields extends Component<Props> {
{
_.map({
1: {
alias: '一级报警',
alias: <FormattedMessage id="stra.priority.1" />,
color: 'red',
},
2: {
alias: '二级报警',
alias: <FormattedMessage id="stra.priority.2" />,
color: 'yellow',
},
3: {
alias: '三级报警',
alias: <FormattedMessage id="stra.priority.3" />,
color: 'blue',
},
}, (val, key) => {
@ -286,7 +287,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="统计周期:"
label={<FormattedMessage id="stra.alertDur" />}
>
{
getFieldDecorator('alert_dur', {
@ -295,11 +296,11 @@ class SettingFields extends Component<Props> {
<InputNumber min={0} />,
)
}
<FormattedMessage id="stra.seconds" />
</FormItem>
<FormItem
{...formItemLayout}
label="触发条件:"
label={<FormattedMessage id="stra.trigger" />}
validateStatus="success" // 兼容
help="" // 兼容
>
@ -321,7 +322,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="Tag 过滤:"
label={<FormattedMessage id="stra.tag" />}
>
{
getFieldDecorator('tags', {
@ -335,7 +336,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="执行动作:"
label={<FormattedMessage id="stra.action" />}
validateStatus="success" // 兼容
help="" // 兼容
>
@ -362,14 +363,14 @@ class SettingFields extends Component<Props> {
onClick={() => {
this.setState({ advanced: !this.state.advanced });
}}
> <Icon type={this.state.advanced ? 'up' : 'down'} />
><FormattedMessage id="stra.advanced" /> <Icon type={this.state.advanced ? 'up' : 'down'} />
</a>
</Col>
</Row>
<div style={{ display: this.state.advanced ? 'block' : 'none' }}>
<FormItem
{...formItemLayout}
label="留观时长:"
label={<FormattedMessage id="stra.recovery.dur" />}
>
{
getFieldDecorator('recovery_dur', {
@ -378,11 +379,12 @@ class SettingFields extends Component<Props> {
<InputNumber min={0} />,
)
}
{getFieldValue('recovery_dur')}
<FormattedMessage id="stra.seconds" /> (
<FormattedMessage id="stra.recovery.dur.help.1" /> {getFieldValue('recovery_dur')} <FormattedMessage id="stra.recovery.dur.help.2" /> )
</FormItem>
<FormItem
{...formItemLayout}
label="静默恢复:"
label={<FormattedMessage id="stra.recovery.notify" />}
>
{
getFieldDecorator('recovery_notify', {
@ -390,14 +392,14 @@ class SettingFields extends Component<Props> {
valuePropName: 'checked',
})(
<Checkbox>
<FormattedMessage id="stra.recovery.notify.checkbox" />
</Checkbox>,
)
}
</FormItem>
<FormItem
{...formItemLayout}
label="生效时间:"
label={<FormattedMessage id="stra.period.time" />}
>
{
getFieldDecorator('period_time', {
@ -409,7 +411,7 @@ class SettingFields extends Component<Props> {
</FormItem>
<FormItem
{...formItemLayout}
label="报警升级:"
label={<FormattedMessage id="stra.alert.upgrade" />}
validateStatus="success" // 兼容
help="" // 兼容
>
@ -432,11 +434,11 @@ class SettingFields extends Component<Props> {
</FormItem>
</div>
<FormItem wrapperCol={{ span: 16, offset: 4 }} style={{ marginTop: 24 }}>
<Button type="primary" htmlType="submit"></Button>
<Button type="primary" htmlType="submit"><FormattedMessage id="form.submit" /></Button>
</FormItem>
</Form>
);
}
}
export default Form.create()(SettingFields);
export default Form.create()(injectIntl(SettingFields));

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { InputNumber, Select, Input, Tag, Spin } from 'antd';
import { FormattedMessage } from 'react-intl';
import { InputNumber, Select, Input, Spin } from 'antd';
import _ from 'lodash';
interface Props {
@ -75,23 +76,10 @@ export default class Actions extends Component<Props> {
const { converge } = value;
const errors = checkActions(null, this.props.value, _.noop) || {} as any;
if (readOnly) {
return (
<div className="strategy-actions">
<div> {converge[0]} , {converge[1]} </div>
<div>
: {_.map(value.notify_group, o => <Tag key={o}>{o}</Tag>)}
</div>
{
value.callback ? <div>: {value.callback}</div> : null
}
</div>
);
}
return (
<div className="strategy-actions">
<div className={!_.isEmpty(errors.converge) ? 'has-error' : undefined}>
<FormattedMessage id="stra.action.d1" />
<InputNumber
style={{ marginLeft: 8 }}
size="default"
@ -99,8 +87,7 @@ export default class Actions extends Component<Props> {
value={converge[0] / 60}
onChange={(val) => { this.handleConvergeChange(0, val); }}
/>
,
<FormattedMessage id="stra.action.d2" />, <FormattedMessage id="stra.action.d3" />
<InputNumber
style={{ marginLeft: 8 }}
size="default"
@ -108,11 +95,11 @@ export default class Actions extends Component<Props> {
value={converge[1]}
onChange={(val) => { this.handleConvergeChange(1, val); }}
/>
<FormattedMessage id="stra.action.d4" />
<div className="ant-form-explain">{errors.converge}</div>
</div>
<div>
<FormattedMessage id="stra.notify.team" />
</div>
<div className={errors.notifyGroup ? 'has-error' : undefined}>
<Select
@ -122,7 +109,7 @@ export default class Actions extends Component<Props> {
notFoundContent={this.props.notifyGroupLoading ? <Spin size="small" /> : null}
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收团队"
// placeholder="报警接收团队"
value={value.notify_group}
onChange={this.handleNotifyGroupChange}
onSearch={(val) => {
@ -140,7 +127,7 @@ export default class Actions extends Component<Props> {
<div className="ant-form-explain">{errors.notifyGroup}</div>
</div>
<div>
<FormattedMessage id="stra.notify.user" />
</div>
<div className={errors.notifyGroup ? 'has-error' : undefined}>
<Select
@ -150,7 +137,7 @@ export default class Actions extends Component<Props> {
notFoundContent={this.props.notifyUserLoading ? <Spin size="small" /> : null}
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收人"
// placeholder="报警接收人"
value={value.notify_user}
onChange={this.handleNotifyUserChange}
onSearch={(val) => {
@ -168,7 +155,7 @@ export default class Actions extends Component<Props> {
<div className="ant-form-explain">{errors.notifyUser}</div>
</div>
<div>
, IDC 访
<FormattedMessage id="stra.notify.callback" />
</div>
<div className={errors.callback ? 'has-error' : undefined}>
<Input
@ -185,7 +172,7 @@ export default class Actions extends Component<Props> {
}
function checkActions(rule: any, value: any, callbackFunc: any) {
const emptyErrorText = '不能为空';
const emptyErrorText = 'is required';
const { converge } = value;
const errors: any = {
converge: '',

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { InputNumber, Select, Spin, Checkbox } from 'antd';
import _ from 'lodash';
@ -51,11 +52,11 @@ export default class AlarmUpgrade extends Component<Props> {
});
}}
>
<FormattedMessage id="stra.alert.upgrade.checkbox" />
</Checkbox>
</div>
<div>
<FormattedMessage id="stra.alert.upgrade.d1" />
<InputNumber
min={0}
style={{ margin: '0 8px' }}
@ -67,7 +68,7 @@ export default class AlarmUpgrade extends Component<Props> {
});
}}
/>
<FormattedMessage id="stra.minutes" />, <FormattedMessage id="stra.alert.upgrade.d2" />, <FormattedMessage id="stra.alert.upgrade.d3" />
<Select
style={{ width: 100, margin: '0 8px' }}
value={value.level}
@ -78,14 +79,14 @@ export default class AlarmUpgrade extends Component<Props> {
});
}}
>
<Option key="1" value={1}></Option>
<Option key="2" value={2}></Option>
<Option key="3" value={3}></Option>
<Option key="1" value={1}><FormattedMessage id="stra.priority.1" /></Option>
<Option key="2" value={2}><FormattedMessage id="stra.priority.2" /></Option>
<Option key="3" value={3}><FormattedMessage id="stra.priority.3" /></Option>
</Select>
<FormattedMessage id="stra.alert.upgrade.d4" />
</div>
<div>
<FormattedMessage id="stra.notify.team" />
</div>
<div className={errors.notify ? 'has-error' : undefined}>
<Select
@ -95,7 +96,7 @@ export default class AlarmUpgrade extends Component<Props> {
notFoundContent={this.props.notifyGroupLoading ? <Spin size="small" /> : null}
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收团队"
// placeholder="报警接收团队"
value={value.groups}
onChange={(val: any) => {
this.props.onChange({
@ -118,7 +119,7 @@ export default class AlarmUpgrade extends Component<Props> {
<div className="ant-form-explain">{errors.notify}</div>
</div>
<div>
<FormattedMessage id="stra.notify.user" />
</div>
<div className={errors.notify ? 'has-error' : undefined}>
<Select
@ -128,7 +129,7 @@ export default class AlarmUpgrade extends Component<Props> {
notFoundContent={this.props.notifyUserLoading ? <Spin size="small" /> : null}
defaultActiveFirstOption={false}
filterOption={false}
placeholder="报警接收人"
// placeholder="报警接收人"
value={value.users}
onChange={(val: any) => {
this.props.onChange({
@ -163,7 +164,7 @@ function checkAlarmUpgrade(rule: any, value: any, callbackFunc: any) {
if (value.enabled && _.isEmpty(value.users) && _.isEmpty(value.groups)) {
hasError = true;
errors.notify = '必须存在一个报警接收人或接收组';
errors.notify = 'Must be an alarm receiver or receiving group';
}
if (hasError) {

View File

@ -1,29 +1,27 @@
/* eslint-disable no-template-curly-in-string */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Card, Select, InputNumber } from 'antd';
import _ from 'lodash';
import { funcMap, defaultExpressionValue, commonPropDefaultValue } from './config';
interface Props {
metricError: string,
value: any,
alertDuration: number,
readOnly: boolean,
metrics: any[],
onChange: (values: any) => void,
renderHeader: (value: any) => React.ReactNode,
renderFooter: (value: any) => React.ReactNode,
}
import { FormattedMessage, injectIntl } from 'react-intl';
import { funcMap, defaultExpressionValue, commonPropTypes, commonPropDefaultValue } from './config';
const { Option } = Select;
export default class Expression extends Component<Props> {
class Expression extends Component {
static propTypes = {
...commonPropTypes,
value: PropTypes.object,
metricError: PropTypes.string,
};
static defaultProps = {
...commonPropDefaultValue,
value: defaultExpressionValue,
metricError: '',
};
handleMetricChange = (val: string) => {
handleMetricChange = (val) => {
const { value, onChange } = this.props;
onChange({
@ -32,7 +30,7 @@ export default class Expression extends Component<Props> {
});
}
handleFuncChange = (val: string) => {
handleFuncChange = (val) => {
const { value, onChange } = this.props;
const currentFuncDefaultValue = _.get(funcMap[val], 'defaultValue', []);
@ -43,7 +41,7 @@ export default class Expression extends Component<Props> {
});
}
handleParamsChange = (index: number, val: any) => {
handleParamsChange = (index, val) => {
const { value, onChange } = this.props;
const currentFuncDefaultValue = _.get(funcMap[value.func], 'defaultValue', []);
const { params = [] } = value;
@ -60,7 +58,7 @@ export default class Expression extends Component<Props> {
});
}
handleEoptChange = (val: string) => {
handleEoptChange = (val) => {
const { value, onChange } = this.props;
onChange({
@ -69,7 +67,7 @@ export default class Expression extends Component<Props> {
});
}
handleThresholdChange = (val: any) => {
handleThresholdChange = (val) => {
const { value, onChange } = this.props;
let newVal = val;
@ -83,21 +81,24 @@ export default class Expression extends Component<Props> {
});
}
renderPreview(readOnly?: boolean) {
renderPreview(readOnly: boolean) {
const { value, alertDuration } = this.props;
const { metric, func, eopt, threshold } = value;
if (func === 'canary') return '智能报警 - canary';
const { params = [] } = value;
const str = _.get(funcMap[func], 'meaning', '');
const index1 = str.indexOf('n');
const index2 = str.indexOf('m');
const index3 = _.lastIndexOf(str, 'v');
const meaningKey = this.props.intl.locale === 'en' ? 'meaningEn' : 'meaning';
const str = _.get(funcMap[func], meaningKey, '');
const index1 = str.indexOf('$n');
const index2 = str.indexOf('$m');
const index3 = str.lastIndexOf('$v');
const nPrefix = str.substring(0, index1);
const vPostfix = str.substring(index3 + 1);
const vPostfix = str.substring(index3 + 2);
let mVal;
if (func === 'c_avg_rate_abs' || func === 'c_avg_rate') {
if (
func === 'c_avg_rate_abs'
|| func === 'c_avg_rate'
|| func === 'c_avg_abs'
|| func === 'c_avg'
) {
mVal = params[0] !== 1 ? params[0] / 86400 : 1;
} else {
mVal = params[0] || 1;
@ -119,7 +120,7 @@ export default class Expression extends Component<Props> {
);
if (index2 > -1) {
const mPrefix = str.substring(index1 + 1, index2);
const mPrefix = str.substring(index1 + 2, index2);
previewNode = (
<span>
{previewNode}
@ -132,7 +133,7 @@ export default class Expression extends Component<Props> {
if (func !== 'nodata') {
// eslint-disable-next-line no-underscore-dangle
const _index = index2 > -1 ? index2 : index1;
const vPrefix = str.substring(_index + 1, index3);
const vPrefix = str.substring(_index + 2, index3);
previewNode = (
<span>
{previewNode}
@ -142,7 +143,7 @@ export default class Expression extends Component<Props> {
</span>
);
} else {
const nPostfix = str.substring(index1 + 1);
const nPostfix = str.substring(index1 + 2);
previewNode = (
<span>
{previewNode}
@ -152,45 +153,54 @@ export default class Expression extends Component<Props> {
}
return (
<div>
{ !readOnly && <span style={{ color: '#999' }}></span> }
{ !readOnly && <span style={{ color: '#999' }}><FormattedMessage id="stra.preview" /></span> }
<span style={{ paddingRight: 5 }}>{metric || '${metric}' }</span>
{ previewNode }
</div>
);
}
renderFuncParams(i: number) {
renderFuncParams(i) {
const { value } = this.props;
const { func, params = [] } = value;
const minnum = ['diff', 'pdiff'].indexOf(func) > -1 ? 2 : 1;
let val = _.toNumber(params[i]) as any;
// const maxnum = func === 'nodata' ? 7200 : 60;
let val = _.toNumber(params[i]);
if (func === 'c_avg_rate_abs' || func === 'c_avg_rate') {
// 相对天数
val = _.toString(params[i] !== 1 ? params[i] : 86400);
return (
<Select
style={{ display: 'inline-block', width: 80, marginRight: 8 }}
value={val}
onChange={(newVal: string) => { this.handleParamsChange(i, _.toNumber(newVal)); }}
>
<Option value="86400">1</Option>
<Option value="604800">7</Option>
</Select>
);
}
if (func === 'happen' || func === 'ndiff') {
// 发生次数
return (
<InputNumber
key={i}
value={val}
min={minnum}
max={_.toNumber(params[0])}
style={{ display: 'inline-block' }}
onChange={(newVal) => { this.handleParamsChange(i, newVal); }}
/>
);
if (i === 0) {
if (
func === 'c_avg_rate_abs'
|| func === 'c_avg_rate'
|| func === 'c_avg_abs'
|| func === 'c_avg'
) {
// 相对天数
val = _.toString(params[i] !== 1 ? params[i] : 86400);
return (
<Select
style={{ display: 'inline-block', width: 80, marginRight: 8 }}
value={val}
onChange={(newVal) => { this.handleParamsChange(i, _.toNumber(newVal)); }}
>
<Option value="86400">1</Option>
<Option value="604800">7</Option>
</Select>
);
}
if (func === 'happen' || func === 'ndiff') {
// 发生次数
return (
<InputNumber
key={i}
value={val}
min={minnum}
max={_.toNumber(params[0])}
style={{ display: 'inline-block' }}
onChange={(newVal) => { this.handleParamsChange(i, newVal); }}
/>
);
}
return <span> param</span>;
}
return <span> param</span>;
}
@ -205,7 +215,7 @@ export default class Expression extends Component<Props> {
<div style={{ marginTop: 5 }}>
{
// render params
_.map(_.get(funcMap[value.func], 'params', []), (o, i: number) => {
_.map(_.get(funcMap[value.func], 'params', []), (o, i) => {
return (
<div key={o} style={{ display: 'inline-block', verticalAlign: 'top' }}>
<span style={{ color: i === 0 ? '#2DB7F5' : '#FFB727' }}>{o}</span>
@ -250,18 +260,8 @@ export default class Expression extends Component<Props> {
}
render() {
const { value, readOnly, metrics, renderHeader, renderFooter, metricError } = this.props;
const { value, metrics, renderHeader, renderFooter, metricError } = this.props;
if (readOnly) {
return (
<Card
bodyStyle={{ padding: 10 }}
style={{ marginTop: 10 }}
>
{ this.renderPreview(readOnly) }
</Card>
);
}
return (
<Card
bodyStyle={{ padding: 10 }}
@ -278,7 +278,7 @@ export default class Expression extends Component<Props> {
notFoundContent=""
size="default"
style={{ width: 250 }}
placeholder="指标名称"
placeholder="Metric name"
defaultActiveFirstOption={false}
dropdownMatchSelectWidth={false}
showSearch
@ -304,12 +304,10 @@ export default class Expression extends Component<Props> {
</div>
{this.renderParams()}
</div>
{
value.func !== 'canary' ? this.renderPreview() : null
}
{ this.renderPreview() }
{
value.func === 'all' ?
<div style={{ color: '#f50', lineHeight: 1 }}>线happen</div> : null
<div style={{ color: '#f50', lineHeight: 1 }}><FormattedMessage id="stra.preview.all.help" /></div> : null
}
<div className="expression-footerExtra">
{renderFooter(value)}
@ -318,3 +316,5 @@ export default class Expression extends Component<Props> {
);
}
}
export default injectIntl(Expression);

View File

@ -1,57 +1,66 @@
import PropTypes from 'prop-types';
export const funcMap: { [index: string]: any } = {
export const funcMap = {
all: {
label: '连续发生',
meaning: '持续 n 秒每个值都 v',
meaning: '持续 $n 秒每个值都 $v',
meaningEn: 'duration $n s, every value $v',
params: [],
defaultValue: [],
},
happen: {
label: '发生次数',
meaning: '持续 n 秒内 m 次值 v',
meaning: '持续 $n 秒内 $m 次值 $v',
meaningEn: 'duration $n s, $m times value $v',
params: ['m'],
defaultValue: [1],
},
nodata: {
label: '数据上报中断',
meaning: '持续 n 秒无数据上报',
meaning: '持续 $n 秒无数据上报',
meaningEn: 'duration $n s, no data',
params: [],
defaultValue: [],
},
max: {
label: '最大值',
meaning: '持续 n 秒最大值 v',
meaning: '持续 $n 秒最大值 $v',
meaningEn: 'duration $n s, max $v',
params: [],
defaultValue: [],
},
min: {
label: '最小值',
meaning: '持续 n 秒最小值 v',
meaning: '持续 $n 秒最小值 $v',
meaningEn: 'duration $n s, min $v',
params: [],
defaultValue: [],
},
avg: {
label: '均值',
meaning: '持续 n 秒均值 v',
meaning: '持续 $n 秒均值 $v',
meaningEn: 'duration $n s, avg $v',
params: [],
defaultValue: [],
},
sum: {
label: '求和',
meaning: '持续 n 秒求和值 v',
meaning: '持续 $n 秒求和值 $v',
meaningEn: 'duration $n s, sum $v',
params: [],
defaultValue: [],
},
diff: {
label: '突增突降值',
meaning: '最新值与其之前 n 秒的任意值之差 (区分正负) v',
meaning: '最新值与其之前 $n 秒的任意值之差 (区分正负) $v',
meaningEn: 'The difference between the latest value and any previous value of $n seconds $v',
params: [],
defaultValue: [],
},
pdiff: {
label: '突增突降率',
meaning: '(最新值与其之前 n 秒的任意值之差)除以对应历史值 (区分正负) v ',
meaning: '(最新值与其之前 $n 秒的任意值之差)除以对应历史值 (区分正负) $v ',
meaningEn: '(the difference between the latest value and any previous value of $n seconds) divided by the corresponding historical value $v',
params: [],
defaultValue: [],
},

View File

@ -1,33 +1,28 @@
/* eslint-disable no-use-before-define */
import React, { Component } from 'react';
import { Select, Tag } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import Expression from './Expression';
import { defaultExpressionValue, commonPropDefaultValue } from './config';
import { defaultExpressionValue, commonPropTypes, commonPropDefaultValue } from './config';
import './style.less';
interface Props {
metricError: string,
value: any,
alertDuration: number,
readOnly: boolean,
metrics: any[],
onChange: (values: any) => void,
renderHeader: (value: any) => React.ReactNode,
renderFooter: (value: any) => React.ReactNode,
}
const { Option } = Select;
export default class Expressions extends Component<Props> {
export default class Expressions extends Component {
static defaultExpressionValue = defaultExpressionValue;
static checkExpressions = checkExpressions;
static propTypes = {
...commonPropTypes,
};
static defaultProps = {
...commonPropDefaultValue,
};
handleTypeChange = (type: string) => {
handleTypeChange = (type) => {
const { value } = this.props;
const valueClone = _.cloneDeep(value);
@ -39,7 +34,7 @@ export default class Expressions extends Component<Props> {
}
}
handleExpressionChange = (index: number, val: any) => {
handleExpressionChange = (index, val) => {
const { value, onChange } = this.props;
const valueClone = _.cloneDeep(value);
@ -50,6 +45,7 @@ export default class Expressions extends Component<Props> {
render() {
const { alertDuration, value, readOnly, metrics, renderHeader, renderFooter } = this.props;
const type = value[1] ? 'and' : 'normal';
// eslint-disable-next-line no-use-before-define
const errors = checkExpressions(null, value, _.noop) || [];
return (
@ -57,13 +53,13 @@ export default class Expressions extends Component<Props> {
{
!readOnly &&
<Select
style={{ width: 80 }}
style={{ width: 90 }}
size="default"
value={type}
onChange={this.handleTypeChange}
>
<Option value="normal"></Option>
<Option value="and"></Option>
<Option value="normal"><FormattedMessage id="stra.trigger.normal" /></Option>
<Option value="and"><FormattedMessage id="stra.trigger.and" /></Option>
</Select>
}
<div>
@ -82,7 +78,7 @@ export default class Expressions extends Component<Props> {
<div className="expressions-and">
<div className="expressions-and-tagBorder" />
<span className="expressions-and-tag">
<Tag></Tag>
<Tag><FormattedMessage id="stra.trigger.and" /></Tag>
</span>
<Expression
alertDuration={alertDuration}
@ -102,14 +98,14 @@ export default class Expressions extends Component<Props> {
}
}
function checkExpressions(rule: any, value: any, callback: any) {
function checkExpressions(rule, value, callback) {
let error0;
let error1;
const emptyErrorText = '不能为空';
const samenameErrorText = '与条件, 不能选择相同的 metric';
const emptyErrorText = 'is required';
const samenameErrorText = 'Cannot select the same metric';
let hasError = false;
_.each(value, (item, i: number) => {
_.each(value, (item, i) => {
if (item.metric === '') {
if (i === 0) {
error0 = emptyErrorText;

View File

@ -1,13 +1,14 @@
import React from 'react';
import { Card, Tag } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
const toptMap: any = {
'=': '包含',
'!=': '排除',
const toptMap = {
'=': 'stra.tag.include',
'!=': 'stra.tag.exclude',
};
export default function Filter(props: any) {
export default function Filter(props) {
const { data, extra } = props;
const { tkey, topt, tval } = data;
@ -17,7 +18,7 @@ export default function Filter(props: any) {
title={
<span>
{tkey}
<span style={{ paddingLeft: 10 }}>{toptMap[topt]}</span>
<span style={{ paddingLeft: 10 }}>{<FormattedMessage id={toptMap[topt]} />}</span>
</span>
}
extra={extra}

View File

@ -1,20 +1,9 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { Modal, Form, Select, Radio } from 'antd';
import _ from 'lodash';
interface Props {
title: string,
visible: boolean,
data: any,
tags: any,
onOk: (values: any) => void | boolean,
destroy: () => void,
}
interface State {
data: any;
}
import { FormattedMessage } from 'react-intl';
import ModalControl from '@cpts/ModalControl';
const FormItem = Form.Item;
const { Option } = Select;
@ -24,11 +13,20 @@ const formItemLayout = {
wrapperCol: { span: 16 },
};
const toptMap = {
'=': '包含',
'!=': '排除',
'=': 'stra.tag.include',
'!=': 'stra.tag.exclude',
};
class FilterFormModal extends Component<Props, State> {
class FilterFormModal extends Component {
static propTypes = {
title: PropTypes.string,
visible: PropTypes.bool,
data: PropTypes.object,
tags: PropTypes.object,
onOk: PropTypes.func,
destroy: PropTypes.func,
};
static defaultProps = {
title: '',
visible: false,
@ -38,18 +36,18 @@ class FilterFormModal extends Component<Props, State> {
destroy: _.noop,
};
formId = _.uniqueId('tagFilterConditionForm');
constructor(props: Props) {
constructor(props) {
super(props);
this.formId = _.uniqueId('tagFilterConditionForm');
this.state = {
data: {
topt: '=',
...props.data,
},
} as State;
};
}
componentWillReceiveProps(nextProps: Props) {
componentWillReceiveProps(nextProps) {
if (!_.isEqual(nextProps.data, this.props.data)) {
this.setState({
data: {
@ -63,10 +61,10 @@ class FilterFormModal extends Component<Props, State> {
getTvalOptions() {
const { tags } = this.props;
const { data } = this.state;
let tvalOptions: any[] = [];
let tvalOptions = [];
if (!_.isEmpty(tags) && data.tkey) {
const tval = _.filter(tags[data.tkey], (item, i: number) => i < 500);
const tval = _.filter(tags[data.tkey], (item, i) => i < 500);
tvalOptions = _.map(tval, item => <Option key={item} value={item}>{item}</Option>);
}
return tvalOptions;
@ -88,7 +86,7 @@ class FilterFormModal extends Component<Props, State> {
this.props.destroy();
}
handleFieldChange = (field: string, val: any) => {
handleFieldChange = (field, val) => {
const { data } = this.state;
this.setState({
data: {
@ -117,18 +115,18 @@ class FilterFormModal extends Component<Props, State> {
>
<FormItem
{...formItemLayout}
label="Tag 名称"
label="Tag name"
validateStatus={_.isEmpty(data.tkey) ? 'error' : ''}
help={_.isEmpty(data.tkey) && '不能为空'}
help={_.isEmpty(data.tkey) && 'is required'}
>
<Select
mode="combobox"
notFoundContent=""
placeholder="支持自定义,非叶子节点或者'与'条件时没有自动补全功能"
// placeholder="支持自定义,非叶子节点或者'与'条件时没有自动补全功能"
defaultActiveFirstOption={false}
// getPopupContainer={() => document.getElementById(this.formId)}
value={data.tkey}
onChange={(val: any) => this.handleFieldChange('tkey', val)}
onChange={val => this.handleFieldChange('tkey', val)}
>
{
_.map(tags, (tval, tkey) => <Option key={tkey} value={tkey}>{tkey}</Option>)
@ -141,24 +139,24 @@ class FilterFormModal extends Component<Props, State> {
onChange={e => this.handleFieldChange('topt', e.target.value)}
>
{
_.map(toptMap, (val, key) => <Radio key={key} value={key}>{val}</Radio>)
_.map(toptMap, (val, key) => <Radio key={key} value={key}><FormattedMessage id={val} /></Radio>)
}
</RadioGroup>
</FormItem>
<FormItem
{...formItemLayout}
label="Tag 取值"
label="Tag value"
validateStatus={_.isEmpty(data.tval) ? 'error' : ''}
help={_.isEmpty(data.tval) && '不能为空'}
help={_.isEmpty(data.tval) && 'is required'}
>
<Select
mode="tags"
showSearch
notFoundContent=""
placeholder="支持自定义,必须完全匹配不支持正则"
// placeholder="支持自定义,必须完全匹配不支持正则"
// getPopupContainer={() => document.getElementById(this.formId)}
value={data.tval}
onChange={(val: any) => this.handleFieldChange('tval', val)}
onChange={val => this.handleFieldChange('tval', val)}
>
{tvalOptions}
</Select>
@ -169,24 +167,4 @@ class FilterFormModal extends Component<Props, State> {
}
}
export default function filterFormModal(config: any) {
const div = document.createElement('div');
document.body.appendChild(div);
function destroy() {
const unmountResult = ReactDOM.unmountComponentAtNode(div);
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
}
function render(props: any) {
ReactDOM.render(<FilterFormModal {...props} />, div);
}
render({ ...config, visible: true, destroy });
return {
destroy,
};
}
export default ModalControl(FilterFormModal);

View File

@ -1,23 +1,28 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Row, Col, Button, Icon, message, Popconfirm } from 'antd';
import _ from 'lodash';
import { FormattedMessage, injectIntl } from 'react-intl';
import Filter from './Filter';
import filterFormModal from './FilterFormModal';
import './style.less';
interface Props {
value: any[],
onChange: (values: any) => void,
readOnly: boolean,
tags: any,
}
const commonPropTypes = {
value: PropTypes.array,
onChange: PropTypes.func,
readOnly: PropTypes.bool,
tags: PropTypes.object,
};
const commonPropDefaultValue = {
readOnly: false,
tags: {},
};
export default class Filters extends Component<Props> {
class Filters extends Component {
static propTypes = {
...commonPropTypes,
};
static defaultProps = {
...commonPropDefaultValue,
@ -28,9 +33,10 @@ export default class Filters extends Component<Props> {
const valueClone = _.cloneDeep(value);
filterFormModal({
title: '添加 Tag 条件',
language: this.props.intl.locale,
title: <FormattedMessage id="stra.tag.add" />,
tags,
onOk: (data: any) => {
onOk: (data) => {
if (!_.find(value, { tkey: data.tkey })) {
valueClone.push(data);
onChange(valueClone);
@ -41,15 +47,16 @@ export default class Filters extends Component<Props> {
});
}
updateFilter = (val: any) => {
updateFilter = (val) => {
const { tags, value, onChange } = this.props;
const valueClone = _.cloneDeep(value);
filterFormModal({
title: '修改 Tag 条件',
language: this.props.intl.locale,
title: <FormattedMessage id="stra.tag.modify" />,
tags,
data: val,
onOk: (data: any) => {
onOk: (data) => {
if (!_.find(value, { tkey: data.tkey }) || val.tkey === data.tkey) {
_.remove(valueClone, o => o.tkey === val.tkey);
valueClone.push(data);
@ -61,7 +68,7 @@ export default class Filters extends Component<Props> {
});
}
deleteFilter = (val: any) => {
deleteFilter = (val) => {
const { value, onChange } = this.props;
const valueClone = _.cloneDeep(value);
@ -80,7 +87,7 @@ export default class Filters extends Component<Props> {
!readOnly &&
<span className="strategy-filter-operation">
<Icon type="edit" onClick={() => this.updateFilter(item)} />
<Popconfirm title="确定要删除该 Tag 条件吗?" onConfirm={() => this.deleteFilter(item)}>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => this.deleteFilter(item)}>
<Icon type="cross" />
</Popconfirm>
</span>
@ -110,7 +117,7 @@ export default class Filters extends Component<Props> {
size="default"
onClick={this.addFilter}
>
<FormattedMessage id="stra.tag.add" />
</Button>
{
value.length ? this.renderFilters() : null
@ -119,3 +126,5 @@ export default class Filters extends Component<Props> {
);
}
}
export default injectIntl(Filters);

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Row, Col, Table, Button, Input, Select, Tag, Divider, message, Popconfirm, Dropdown, Menu, Modal } from 'antd';
@ -13,7 +14,7 @@ import BatchImportExportModal from './BatchImportExportModal';
const { Option } = Select;
class index extends Component {
class index extends Component<WrappedComponentProps> {
static contextTypes = {
getNodes: PropTypes.func,
getSelectedNode: PropTypes.func,
@ -86,7 +87,7 @@ class index extends Component {
ids: [id],
}),
}).then(() => {
message.success('删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
});
}
@ -96,6 +97,7 @@ class index extends Component {
const { getNodes } = this.context;
const treeNodes = getNodes();
BatchModModal({
language: this.props.intl.locale,
type: 'exclNid',
selectedNid: this.selectedNodeId,
treeNodes,
@ -109,6 +111,7 @@ class index extends Component {
handleBatchModNotifyBtnClick = () => {
const { selectedRows } = this.state;
BatchModModal({
language: this.props.intl.locale,
type: 'notify',
data: selectedRows,
onOk: () => {
@ -122,6 +125,7 @@ class index extends Component {
const { getNodes } = this.context;
const treeNodes = getNodes();
BatchModModal({
language: this.props.intl.locale,
type: 'clone',
data: selectedRows,
treeNodes,
@ -137,8 +141,8 @@ class index extends Component {
if (ids.length) {
Modal.confirm({
title: '批量删除',
content: '确定要删除所选的策略吗?',
title: this.props.intl.formatMessage({ id: 'stra.batch.delete' }),
content: this.props.intl.formatMessage({ id: 'table.delete.sure' }),
onOk: () => {
request(api.stra, {
method: 'DELETE',
@ -146,7 +150,7 @@ class index extends Component {
ids,
}),
}).then(() => {
message.success('批量删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.fetchData();
});
},
@ -157,7 +161,8 @@ class index extends Component {
handleBatchImportBtnClick = () => {
BatchImportExportModal({
type: 'import',
title: '批量导入策略',
title: this.props.intl.formatMessage({ id: 'stra.batch.import' }),
language: this.props.intl.locale,
selectedNid: this.selectedNodeId,
onOk: () => {
this.fetchData();
@ -187,7 +192,8 @@ class index extends Component {
BatchImportExportModal({
data: newSelectedRows,
type: 'export',
title: '批量导出策略',
title: this.props.intl.formatMessage({ id: 'stra.batch.export' }),
language: this.props.intl.locale,
});
}
@ -243,13 +249,15 @@ class index extends Component {
<Row className="mb10">
<Col span={18}>
<Button className="mr10">
<Link to={{ pathname: '/monitor/strategy/add', search: `nid=${this.selectedNodeId}` }}></Link>
<Link to={{ pathname: '/monitor/strategy/add', search: `nid=${this.selectedNodeId}` }}>
<FormattedMessage id="stra.add" />
</Link>
</Button>
<Select
allowClear
style={{ width: 100 }}
className="mr10"
placeholder="策略级别"
placeholder={this.props.intl.formatMessage({ id: 'stra.priority' })}
value={this.state.priority}
onChange={(value: number) => {
this.setState({ priority: value });
@ -264,7 +272,7 @@ class index extends Component {
<Input
style={{ width: 300 }}
className="mr10"
placeholder="策略名称、指标、报警接受组、人员关键词搜索"
placeholder="Search"
value={this.state.search}
onChange={(e) => {
this.setState({ search: e.target.value });
@ -276,27 +284,27 @@ class index extends Component {
overlay={
<Menu>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchModExclNidBtnClick(); }}></Button>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchModExclNidBtnClick(); }}><FormattedMessage id="stra.batch.modify.excludeNs" /></Button>
</Menu.Item>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchModNotifyBtnClick(); }}></Button>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchModNotifyBtnClick(); }}><FormattedMessage id="stra.batch.modify.notify" /></Button>
</Menu.Item>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchCloneToOtherNidBtnClick(); }}></Button>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchCloneToOtherNidBtnClick(); }}><FormattedMessage id="stra.batch.cloneTo.otherNode" /></Button>
</Menu.Item>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchDelBtnClick(); }}></Button>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchDelBtnClick(); }}><FormattedMessage id="stra.batch.delete" /></Button>
</Menu.Item>
<Menu.Item>
<Button type="link" onClick={() => { this.handleBatchImportBtnClick(); }}></Button>
<Button type="link" onClick={() => { this.handleBatchImportBtnClick(); }}><FormattedMessage id="stra.batch.import" /></Button>
</Menu.Item>
<Menu.Item>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchExportBtnClick(); }}></Button>
<Button type="link" disabled={!canBatchOper} onClick={() => { this.handleBatchExportBtnClick(); }}><FormattedMessage id="stra.batch.export" /></Button>
</Menu.Item>
</Menu>
}
>
<Button icon="down"></Button>
<Button icon="down"><FormattedMessage id="table.batch.operations" /></Button>
</Dropdown>
</Col>
</Row>
@ -316,14 +324,14 @@ class index extends Component {
}}
columns={[
{
title: '策略名称',
title: <FormattedMessage id="stra.name" />,
dataIndex: 'name',
width: 150,
render: (text, record) => {
return <Link to={{ pathname: `/monitor/strategy/${record.id}` }}>{text}</Link>;
},
}, {
title: '级别',
title: <FormattedMessage id="stra.priority" />,
width: 40,
dataIndex: 'priority',
render: (text) => {
@ -331,7 +339,7 @@ class index extends Component {
return <Tag color={currentPriority.color}>{currentPriority.label}</Tag>;
},
}, {
title: '指标',
title: <FormattedMessage id="stra.metric" />,
width: 100,
render: (text, record) => {
const { exprs } = record;
@ -342,7 +350,7 @@ class index extends Component {
});
},
}, {
title: '报警接收',
title: <FormattedMessage id="stra.notify" />,
render: (text, record) => {
const { userData, teamData } = this.state;
const team = _.map(record.notify_group, (item) => {
@ -357,7 +365,7 @@ class index extends Component {
},
}, {
width: 90,
title: '更新时间',
title: <FormattedMessage id="table.lastupdated" />,
render: (text, record) => {
return (
<div>
@ -367,16 +375,16 @@ class index extends Component {
},
}, {
width: 140,
title: '操作',
title: <FormattedMessage id="table.operations" />,
render: (text, record) => {
return (
<span className="operation-btns">
<Link to={{ pathname: `/monitor/strategy/${record.id}` }}></Link>
<Link to={{ pathname: `/monitor/strategy/${record.id}` }}><FormattedMessage id="table.modify" /></Link>
<Divider type="vertical" />
<Link to={{ pathname: `/monitor/strategy/${record.id}/clone` }}></Link>
<Link to={{ pathname: `/monitor/strategy/${record.id}/clone` }}><FormattedMessage id="table.clone" /></Link>
<Divider type="vertical" />
<Popconfirm title="是否删除这条策略?" onConfirm={() => { this.handleDel(record.id); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => { this.handleDel(record.id); }}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -389,4 +397,4 @@ class index extends Component {
}
}
export default CreateIncludeNsTree(index, { visible: true });
export default CreateIncludeNsTree(injectIntl(index), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component }from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, Form, Input, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -17,10 +18,10 @@ interface Props {
const FormItem = Form.Item;
class BatchDel extends Component<Props & FormProps> {
class BatchDel extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps = {
selectedIps: [],
title: '批量删除',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -28,7 +29,6 @@ class BatchDel extends Component<Props & FormProps> {
};
handleOk = () => {
const { title } = this.props;
this.props.form!.validateFields((err, values) => {
if (!err) {
const idents = _.split(values.idents, '\n');
@ -39,7 +39,7 @@ class BatchDel extends Component<Props & FormProps> {
method: 'DELETE',
body: JSON.stringify(reqBody),
}).then(() => {
message.success(`${title}成功`);
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
this.props.onOk();
this.props.destroy();
});
@ -63,10 +63,10 @@ class BatchDel extends Component<Props & FormProps> {
onCancel={this.handleCancel}
>
<Form layout="vertical">
<FormItem label="已选 endpoints">
<FormItem label="Endpoints">
{getFieldDecorator('idents', {
initialValue: _.join(selectedIdents, '\n'),
rules: [{ required: true, message: '请填写批量操作的 endpoints!' }],
rules: [{ required: true }],
})(
<Input.TextArea
autosize={{ minRows: 2, maxRows: 10 }}
@ -79,4 +79,4 @@ class BatchDel extends Component<Props & FormProps> {
}
}
export default ModalControl(Form.create()(BatchDel));
export default ModalControl(Form.create()(injectIntl(BatchDel)));

View File

@ -1,7 +1,9 @@
import React, { Component }from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, Form, Input, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import ModalControl from '@cpts/ModalControl';
import request from '@common/request';
import api from '@common/api';
@ -16,9 +18,9 @@ interface Props {
const FormItem = Form.Item;
class BatchImport extends Component<Props & FormProps> {
class BatchImport extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps = {
title: '批量导入',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -26,7 +28,6 @@ class BatchImport extends Component<Props & FormProps> {
};
handleOk = () => {
const { title } = this.props;
this.props.form!.validateFields((err, values) => {
if (!err) {
request(api.endpoint, {
@ -35,7 +36,7 @@ class BatchImport extends Component<Props & FormProps> {
endpoints: _.split(values.endpoints, '\n'),
}),
}).then(() => {
message.success(`${title}成功`);
message.success(this.props.intl.formatMessage({ id: 'msg.submit.success' }));
this.props.onOk();
this.props.destroy();
});
@ -60,11 +61,11 @@ class BatchImport extends Component<Props & FormProps> {
>
<Form layout="vertical">
<FormItem
label="导入的 endpoints"
help="每一条是 ident::alias 拼接在一起"
label="Endpoints"
help={<FormattedMessage id="endpoints.import.batch.help" />}
>
{getFieldDecorator('endpoints', {
rules: [{ required: true, message: '请填写导入的机器 endpoints!' }],
rules: [{ required: true }],
})(
<Input.TextArea
autosize={{ minRows: 2, maxRows: 10 }}
@ -77,4 +78,4 @@ class BatchImport extends Component<Props & FormProps> {
}
}
export default ModalControl(Form.create()(BatchImport));
export default ModalControl(Form.create()(injectIntl(BatchImport)));

View File

@ -1,6 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Menu, Divider, Popconfirm, message } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import CreateIncludeNsTree from '@cpts/Layout/CreateIncludeNsTree';
import exportXlsx from '@common/exportXlsx';
import request from '@common/request';
@ -15,6 +17,10 @@ class index extends Component {
endpointList: any;
state = {};
static contextTypes = {
intl: PropTypes.any,
};
async exportEndpoints(endpoints: Endpoint[]) {
const data = _.map(endpoints, (item) => {
return {
@ -27,7 +33,8 @@ class index extends Component {
handleModifyBtnClick(record: Endpoint) {
EditEndpoint({
title: '修改信息',
title: this.context.intl.formatMessage({ id: 'table.modify' }),
language: this.context.intl.locale,
type: 'admin',
data: record,
onOk: () => {
@ -50,6 +57,8 @@ class index extends Component {
handleBatchImport() {
BatchImport({
title: this.context.intl.formatMessage({ id: 'endpoints.import' }),
language: this.context.intl.locale,
onOk: () => {
this.endpointList.reload();
},
@ -58,6 +67,7 @@ class index extends Component {
handleBatchDel(selectedIdents: string[]) {
BatchDel({
language: this.context.intl.locale,
selectedIdents,
onOk: () => {
this.endpointList.reload();
@ -81,10 +91,12 @@ class index extends Component {
renderOper={(record) => {
return (
<span>
<a onClick={() => { this.handleModifyBtnClick(record); }}></a>
<a onClick={() => { this.handleModifyBtnClick(record); }}>
<FormattedMessage id="table.modify" />
</a>
<Divider type="vertical" />
<Popconfirm title="确认要删除吗?" onConfirm={() => { this.handleDeleteBtnClick(record.ident); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => { this.handleDeleteBtnClick(record.ident); }}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -92,10 +104,10 @@ class index extends Component {
renderBatchOper={(selectedIdents) => {
return [
<Menu.Item key="batch-import">
<a onClick={() => { this.handleBatchImport(); }}> endpoints</a>
<a onClick={() => { this.handleBatchImport(); }}><FormattedMessage id="endpoints.import" /></a>
</Menu.Item>,
<Menu.Item key="batch-delete">
<a onClick={() => { this.handleBatchDel(selectedIdents); }}> endpoints</a>
<a onClick={() => { this.handleBatchDel(selectedIdents); }}><FormattedMessage id="endpoints.delete" /></a>
</Menu.Item>,
];
}}

View File

@ -1,7 +1,9 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, Form, Input, Checkbox, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import ModalControl from '@cpts/ModalControl';
import { TreeNode } from '@interface';
import request from '@common/request';
@ -18,9 +20,9 @@ interface Props {
const FormItem = Form.Item;
class BatchBind extends Component<Props & FormProps> {
class BatchBind extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps = {
title: '挂载 endpoints',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -39,7 +41,7 @@ class BatchBind extends Component<Props & FormProps> {
method: 'POST',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('挂载成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.submit.success' }));
this.props.onOk();
this.props.destroy();
});
@ -63,12 +65,12 @@ class BatchBind extends Component<Props & FormProps> {
onCancel={this.handleCancel}
>
<Form layout="vertical">
<FormItem label="挂载的节点">
<FormItem label={<FormattedMessage id="endpoints.bind.node" />}>
<span className="ant-form-text" style={{ wordBreak: 'break-word' }}>{_.get(selectedNode, 'path')}</span>
</FormItem>
<FormItem label="待挂载的 endpoint">
<FormItem label={<span>Endpoints <FormattedMessage id="endpoints.ident" /></span>}>
{getFieldDecorator('idents', {
rules: [{ required: true, message: '请填写需要挂载的 endpoints!' }],
rules: [{ required: true }],
})(
<Input.TextArea
autosize={{ minRows: 2, maxRows: 10 }}
@ -77,7 +79,7 @@ class BatchBind extends Component<Props & FormProps> {
</FormItem>
{getFieldDecorator('del_old', {
})(
<Checkbox className="mt10"></Checkbox>,
<Checkbox className="mt10"><FormattedMessage id="endpoints.delete.old.bind" /></Checkbox>,
)}
</Form>
</Modal>
@ -85,4 +87,4 @@ class BatchBind extends Component<Props & FormProps> {
}
}
export default ModalControl(Form.create()(BatchBind));
export default ModalControl(Form.create()(injectIntl(BatchBind)));

View File

@ -1,7 +1,9 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, Form, Input, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import ModalControl from '@cpts/ModalControl';
import { TreeNode } from '@interface';
import request from '@common/request';
@ -19,9 +21,9 @@ interface Props {
const FormItem = Form.Item;
class BatchHostUnbind extends Component<Props & FormProps> {
class BatchHostUnbind extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps = {
title: '解挂 endpoints',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -39,7 +41,7 @@ class BatchHostUnbind extends Component<Props & FormProps> {
method: 'POST',
body: JSON.stringify(reqBody),
}).then(() => {
message.success('解除挂载成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.submit.success' }));
this.props.onOk();
this.props.destroy();
});
@ -63,13 +65,13 @@ class BatchHostUnbind extends Component<Props & FormProps> {
onCancel={this.handleCancel}
>
<Form layout="vertical">
<FormItem label="解除挂载的节点">
<FormItem label={<FormattedMessage id="endpoints.unbind.node" />}>
<span className="ant-form-text" style={{ wordBreak: 'break-word' }}>{_.get(selectedNode, 'path')}</span>
</FormItem>
<FormItem label="待解除挂载的 endpoints">
<FormItem label={<span>Endpoints <FormattedMessage id="endpoints.ident" /></span>}>
{getFieldDecorator('idents', {
initialValue: _.join(selectedIdents, '\n'),
rules: [{ required: true, message: '请填写需要解除挂载的机器列表!' }],
rules: [{ required: true }],
})(
<Input.TextArea
autosize={{ minRows: 2, maxRows: 10 }}
@ -82,4 +84,4 @@ class BatchHostUnbind extends Component<Props & FormProps> {
}
}
export default ModalControl(Form.create()(BatchHostUnbind));
export default ModalControl(Form.create()(injectIntl(BatchHostUnbind)));

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Menu } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import CreateIncludeNsTree from '@cpts/Layout/CreateIncludeNsTree';
import exportXlsx from '@common/exportXlsx';
import api from '@common/api';
@ -16,6 +17,7 @@ class index extends Component {
selectedNodeId: number | undefined = undefined;
static contextTypes = {
getSelectedNode: PropTypes.func,
intl: PropTypes.any,
};
componentWillMount = () => {
@ -53,6 +55,8 @@ class index extends Component {
const { getSelectedNode } = this.context;
const selectedNode = getSelectedNode();
BatchBind({
title: this.context.intl.formatMessage({ id: 'endpoints.bind' }),
language: this.context.intl.locale,
selectedNode,
onOk: () => {
this.endpointList.reload();
@ -64,6 +68,8 @@ class index extends Component {
const { getSelectedNode } = this.context;
const selectedNode = getSelectedNode();
BatchUnbind({
title: this.context.intl.formatMessage({ id: 'endpoints.unbind' }),
language: this.context.intl.locale,
selectedNode,
selectedIdents,
onOk: () => {
@ -74,7 +80,8 @@ class index extends Component {
handleModifyAliasBtnClick = (record: Endpoint) => {
EditEndpoint({
title: '修改别名',
title: this.context.intl.formatMessage({ id: 'table.modify' }),
language: this.context.intl.locale,
data: record,
onOk: () => {
this.endpointList.reload();
@ -86,7 +93,7 @@ class index extends Component {
if (!this.selectedNodeId) {
return (
<div>
<FormattedMessage id="please.select.node" />
</div>
);
}
@ -100,17 +107,23 @@ class index extends Component {
renderOper={(record) => {
return (
<span>
<a onClick={() => { this.handleModifyAliasBtnClick(record); }}></a>
<a onClick={() => { this.handleModifyAliasBtnClick(record); }}>
<FormattedMessage id="endpoints.modify.alias" />
</a>
</span>
);
}}
renderBatchOper={(selectedIdents) => {
return [
<Menu.Item key="batch-bind">
<a onClick={() => { this.handleHostBindBtnClick(); }}> endpoint</a>
<a onClick={() => { this.handleHostBindBtnClick(); }}>
<FormattedMessage id="endpoints.bind" />
</a>
</Menu.Item>,
<Menu.Item key="batch-unbind">
<a onClick={() => { this.handleHostUnbindBtnClick(selectedIdents); }}> endpoint</a>
<a onClick={() => { this.handleHostUnbindBtnClick(selectedIdents); }}>
<FormattedMessage id="endpoints.unbind" />
</a>
</Menu.Item>,
];
}}

View File

@ -1,7 +1,9 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import PropTypes from 'prop-types';
import { Row, Col, Input, Button, Checkbox, Popconfirm, message } from 'antd';
import _ from 'lodash';
import { FormattedMessage } from 'react-intl';
import CreateIncludeNsTree from '@cpts/Layout/CreateIncludeNsTree';
import request from '@common/request';
import api from '@common/api';
@ -24,7 +26,7 @@ function updatePathByName(path: string, name: string) {
return path;
}
class index extends Component<null, State> {
class index extends Component<WrappedComponentProps, State> {
static contextTypes = {
getSelectedNode: PropTypes.func,
updateSelectedNode: PropTypes.func,
@ -86,7 +88,7 @@ class index extends Component<null, State> {
name: selectedNodeName,
path: updatePathByName(selectedNode.path, selectedNodeName),
});
message.success('节点重命名成功!');
message.success(this.props.intl.formatMessage({ id: 'node.rename.success' }));
});
}
}
@ -109,7 +111,7 @@ class index extends Component<null, State> {
}),
}).then(() => {
reloadNsTree();
message.success('创建子节点成功!');
message.success(this.props.intl.formatMessage({ id: 'node.child.create.success' }));
});
}
}
@ -123,7 +125,7 @@ class index extends Component<null, State> {
}).then(() => {
reloadNsTree();
deleteSelectedNode();
message.success('节点删除成功!');
message.success(this.props.intl.formatMessage({ id: 'node.delete.success' }));
});
}
}
@ -135,7 +137,7 @@ class index extends Component<null, State> {
if (!selectedNode) {
return (
<div>
<FormattedMessage id="please.select.node" />
</div>
);
}
@ -143,25 +145,25 @@ class index extends Component<null, State> {
<div>
<Row gutter={20}>
<Col span={8} className="mb10">
<FormattedMessage id="node.rename" />
<div className="mt10 mb10">
<Input
style={{ width: 200 }}
value={selectedNodeName}
onChange={this.handlePutNodeChange}
placeholder="新节点名称"
placeholder={this.props.intl.formatMessage({ id: 'node.rename.newname' })}
/>
</div>
<Button onClick={this.handlePutNode}></Button>
<Button onClick={this.handlePutNode}><FormattedMessage id="form.save" /></Button>
</Col>
<Col span={8} className="mb10">
<FormattedMessage id="node.child.create" />
<div className="mt10 mb10">
<Input
style={{ width: 200 }}
value={newNodeName}
onChange={this.handleNewNodeNameChange}
placeholder="子节点名称"
placeholder={this.props.intl.formatMessage({ id: 'node.child.newname' })}
disabled={isLeafNode}
/>
</div>
@ -171,24 +173,26 @@ class index extends Component<null, State> {
onChange={this.handleNewNodeLeafChange}
disabled={isLeafNode}
>
<FormattedMessage id="node.isLeaf" />
</Checkbox>
</div>
<Button disabled={isLeafNode} onClick={this.handlePostNode}></Button>
<Button disabled={isLeafNode} onClick={this.handlePostNode}>
<FormattedMessage id="form.create" />
</Button>
{
isLeafNode ? <p className="fc50 mt10"></p> : null
isLeafNode ? <p className="fc50 mt10"><FormattedMessage id="node.leaf.cannot.create" /></p> : null
}
</Col>
<Col span={8} className="mb10">
<FormattedMessage id="node.delete" />
<div className="mt10 mb10" style={{ wordBreak: 'break-word' }}>
{_.get(selectedNode, 'path')}
</div>
<Popconfirm disabled={isPdlNode} title="确定要删除这个节点吗?" onConfirm={this.handleDelNode}>
<Button disabled={isPdlNode}></Button>
<Popconfirm disabled={isPdlNode} title={<FormattedMessage id="table.delete.sure" />} onConfirm={this.handleDelNode}>
<Button disabled={isPdlNode}><FormattedMessage id="form.delete" /></Button>
</Popconfirm>
{
isPdlNode ? <p className="fc50 mt10">{config.aliasMap.dept}</p> : null
isPdlNode ? <p className="fc50 mt10"><FormattedMessage id={`${config.aliasMap.dept}节点不能删除`} /></p> : null
}
</Col>
</Row>
@ -197,4 +201,4 @@ class index extends Component<null, State> {
}
}
export default CreateIncludeNsTree(index, { visible: true });
export default CreateIncludeNsTree(injectIntl(index), { visible: true });

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, message } from 'antd';
import _ from 'lodash';
import ModalControl from '@cpts/ModalControl';
@ -15,10 +16,10 @@ interface Props {
destroy: () => void,
}
class CreateUser extends Component<Props> {
class CreateUser extends Component<Props & WrappedComponentProps> {
profileFormRef: any;
static defaultProps = {
title: '新建用户',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -35,7 +36,7 @@ class CreateUser extends Component<Props> {
is_root: values.is_root ? 1 : 0,
}),
}).then(() => {
message.success('新建用户成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.create.success' }));
this.props.onOk();
this.props.destroy();
});
@ -66,4 +67,4 @@ class CreateUser extends Component<Props> {
}
}
export default ModalControl(CreateUser);
export default ModalControl(injectIntl(CreateUser));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Modal, Form, Input, Icon, message } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -17,9 +18,9 @@ interface Props {
const FormItem = Form.Item;
class PutPassword extends Component<Props & FormProps> {
class PutPassword extends Component<Props & FormProps & WrappedComponentProps> {
static defaultProps = {
title: '修改密码',
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -33,7 +34,7 @@ class PutPassword extends Component<Props & FormProps> {
method: 'PUT',
body: JSON.stringify(values),
}).then(() => {
message.success('密码修改成功!');
message.success(this.props.intl.formatMessage({ id: 'user.reset.password.success' }));
this.props.onOk();
this.props.destroy();
});
@ -57,9 +58,9 @@ class PutPassword extends Component<Props & FormProps> {
onCancel={this.handleCancel}
>
<Form layout="vertical">
<FormItem label="新密码" required>
<FormItem label={<FormattedMessage id="password.new" />} required>
{getFieldDecorator('password', {
rules: [{ required: true, message: '请输入新密码!' }],
rules: [{ required: true }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
@ -73,4 +74,4 @@ class PutPassword extends Component<Props & FormProps> {
}
}
export default ModalControl(Form.create()(PutPassword as any));
export default ModalControl(Form.create()(injectIntl(PutPassword)));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, message } from 'antd';
import _ from 'lodash';
import ModalControl from '@cpts/ModalControl';
@ -17,7 +18,7 @@ interface Props {
destroy: () => void,
}
class PutProfile extends Component<Props> {
class PutProfile extends Component<Props & WrappedComponentProps> {
profileForm: any;
static defaultProps = {
@ -38,7 +39,7 @@ class PutProfile extends Component<Props> {
is_root: values.is_root ? 1 : 0,
}),
}).then(() => {
message.success('用户信息修改成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
this.props.onOk();
this.props.destroy();
});
@ -72,4 +73,4 @@ class PutProfile extends Component<Props> {
}
}
export default ModalControl(PutProfile);
export default ModalControl(injectIntl(PutProfile));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Row, Col, Input, Button, Divider, Popover, Popconfirm, message, Tooltip, Alert } from 'antd';
import { ColumnProps } from 'antd/lib/table';
import { UserProfile } from '@interface';
@ -22,7 +23,7 @@ interface State {
const ButtonGroup = Button.Group;
class User extends Component<null, State> {
class User extends Component<WrappedComponentProps, State> {
fetchtable: any;
state = {
inviteTooltipVisible: false,
@ -48,6 +49,8 @@ class User extends Component<null, State> {
handleAddBtnClick = () => {
CreateUser({
title: this.props.intl.formatMessage({ id: 'user.create' }),
language: this.props.intl.locale,
onOk: () => {
this.fetchtable.reload();
},
@ -56,6 +59,8 @@ class User extends Component<null, State> {
handlePutBtnClick = (record: UserProfile) => {
PutProfile({
title: this.props.intl.formatMessage({ id: 'user.modify' }),
language: this.props.intl.locale,
data: record,
onOk: () => {
this.fetchtable.reload();
@ -65,6 +70,8 @@ class User extends Component<null, State> {
handlePutPassBtnClick = (id: number) => {
PutPassword({
title: this.props.intl.formatMessage({ id: 'user.reset.password' }),
language: this.props.intl.locale,
id,
onOk: () => {
this.fetchtable.reload();
@ -77,7 +84,7 @@ class User extends Component<null, State> {
method: 'DELETE',
}).then(() => {
this.fetchtable.reload();
message.success('用户删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
});
}
@ -91,44 +98,47 @@ class User extends Component<null, State> {
const { isroot } = auth.getSelftProfile();
const columns: ColumnProps<UserProfile>[] = [
{
title: '登录名',
title: <FormattedMessage id="user.username" />,
dataIndex: 'username',
}, {
title: '显示名',
title: <FormattedMessage id="user.dispname" />,
dataIndex: 'dispname',
}, {
title: '邮箱',
title: <FormattedMessage id="user.email" />,
dataIndex: 'email',
}, {
title: '手机',
title: <FormattedMessage id="user.phone" />,
dataIndex: 'phone',
}, {
title: 'im',
dataIndex: 'im',
}, {
title: '是否超管',
title: <FormattedMessage id="user.isroot" />,
dataIndex: 'is_root',
width: 70,
className: 'textAlignCenter',
render: (text) => {
return text === 1 ? '是' : '否';
if (this.props.intl.locale === 'zh') {
return text === 1 ? '是' : '否';
}
return text === 1 ? 'Y' : 'N';
},
},
];
if (isroot) {
columns.push({
title: '操作',
title: <FormattedMessage id="table.operations" />,
className: 'textAlignCenter',
width: 200,
render: (text, record) => {
width: this.props.intl.locale === 'zh' ? 200 : 250,
render: (_text, record) => {
return (
<span>
<a onClick={() => { this.handlePutPassBtnClick(record.id); }}></a>
<a onClick={() => { this.handlePutPassBtnClick(record.id); }}><FormattedMessage id="user.reset.password" /></a>
<Divider type="vertical" />
<a onClick={() => { this.handlePutBtnClick(record); }}></a>
<a onClick={() => { this.handlePutBtnClick(record); }}><FormattedMessage id="table.modify" /></a>
<Divider type="vertical" />
<Popconfirm title="确认要删除这个用户吗?" onConfirm={() => { this.handleDelBtnClick(record.id); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => { this.handleDelBtnClick(record.id); }}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -149,7 +159,7 @@ class User extends Component<null, State> {
<Col span={16} className="textAlignRight">
<ButtonGroup>
{
isroot ? <Button onClick={this.handleAddBtnClick}></Button> : null
isroot ? <Button onClick={this.handleAddBtnClick}><FormattedMessage id="user.create" /></Button> : null
}
<Popover
trigger="click"
@ -162,10 +172,10 @@ class User extends Component<null, State> {
}}
content={
copySucceeded ?
<Alert message="邀请用户的链接复制成功" type="success" /> :
<Alert message={<FormattedMessage id="invite.user.copy.success" />} type="success" /> :
<Alert message={
<div>
<p></p>
<p><FormattedMessage id="invite.user.copy.faile" /></p>
<span>{inviteLink}</span>
</div>
} type="warning" />
@ -175,9 +185,9 @@ class User extends Component<null, State> {
placement="topRight"
visible={inviteTooltipVisible}
onVisibleChange={(visible) => { this.setState({ inviteTooltipVisible: visible }); }}
title="点击生成一个邀请用户的链接"
title={<FormattedMessage id="user.invite.tips" />}
>
<Button className="ml10" onClick={this.handleInviteBtnClick}></Button>
<Button className="ml10" onClick={this.handleInviteBtnClick}><FormattedMessage id="user.invite" /></Button>
</Tooltip>
</Popover>
</ButtonGroup>
@ -196,4 +206,4 @@ class User extends Component<null, State> {
);
}
}
export default CreateIncludeNsTree(User);
export default CreateIncludeNsTree(injectIntl(User));

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, message } from 'antd';
import _ from 'lodash';
import ModalControl from '@cpts/ModalControl';
@ -14,10 +15,10 @@ interface Props {
destroy: () => void,
}
class AddTeam extends Component<Props> {
class AddTeam extends Component<Props & WrappedComponentProps> {
teamFormRef: any;
static defaultProps = {
title: '编辑团队',
static defaultProps: any = {
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -31,7 +32,7 @@ class AddTeam extends Component<Props> {
method: 'POST',
body: JSON.stringify(values),
}).then(() => {
message.success('团队创建成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.create.success' }));
this.props.onOk();
this.props.destroy();
});
@ -61,4 +62,4 @@ class AddTeam extends Component<Props> {
}
}
export default ModalControl(AddTeam);
export default ModalControl(injectIntl(AddTeam));

View File

@ -1,5 +1,5 @@
import React, { Component }from 'react';
import PropTypes from 'prop-types';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Modal, message } from 'antd';
import _ from 'lodash';
import ModalControl from '@cpts/ModalControl';
@ -17,19 +17,10 @@ interface Props {
destroy: () => void,
}
class PutTeam extends Component<Props> {
class PutTeam extends Component<Props & WrappedComponentProps> {
teamFormRef: any;
static propTypes = {
data: PropTypes.object.isRequired,
title: PropTypes.string,
visible: PropTypes.bool,
onOk: PropTypes.func,
onCancel: PropTypes.func,
destroy: PropTypes.func,
};
static defaultProps = {
title: '编辑团队',
static defaultProps: any = {
title: '',
visible: true,
onOk: _.noop,
onCancel: _.noop,
@ -46,7 +37,7 @@ class PutTeam extends Component<Props> {
...values,
}),
}).then(() => {
message.success('团队信息修改成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.modify.success' }));
this.props.onOk();
this.props.destroy();
});
@ -77,4 +68,4 @@ class PutTeam extends Component<Props> {
}
}
export default ModalControl(PutTeam);
export default ModalControl(injectIntl(PutTeam));

View File

@ -1,4 +1,5 @@
import React, { Component }from 'react';
import { FormattedMessage } from 'react-intl';
import { Form, Input, Radio, Select, Spin } from 'antd';
import { FormProps } from 'antd/lib/form';
import _ from 'lodash';
@ -21,13 +22,13 @@ const RadioGroup = Radio.Group;
const { Option } = Select;
class TeamForm extends Component<Props & FormProps, State> {
static defaultProps = {
initialValue: {},
static defaultProps: any = {
initialValue: {} as any,
};
lastFetchId = 0;
constructor(props: Props) {
constructor(props: Props & FormProps) {
super(props);
this.fetchUser = _.debounce(this.fetchUser, 500);
}
@ -89,48 +90,48 @@ class TeamForm extends Component<Props & FormProps, State> {
const { getFieldDecorator, getFieldValue } = this.props.form!;
return (
<Form layout="vertical">
<FormItem label="英文标识" required>
<FormItem label={<FormattedMessage id="team.ident" />} required>
{getFieldDecorator('ident', {
initialValue: initialValue.ident,
rules: [{ required: true, message: '请填写英文标识!' }],
rules: [{ required: true }],
})(
<Input />,
)}
</FormItem>
<FormItem label="中文名称" required>
<FormItem label={<FormattedMessage id="team.name" />} required>
{getFieldDecorator('name', {
initialValue: initialValue.name,
rules: [{ required: true, message: '请填写中文名称!' }],
rules: [{ required: true }],
})(
<Input />,
)}
</FormItem>
<FormItem label="管理方式" required>
<FormItem label={<FormattedMessage id="team.mgmt" />} required>
{getFieldDecorator('mgmt', {
initialValue: initialValue.mgmt || 0,
rules: [{ required: true, message: '请选择管理方式!' }],
rules: [{ required: true }],
})(
<RadioGroup>
<Radio value={0}></Radio>
<Radio value={1}></Radio>
<Radio value={0}><FormattedMessage id="team.mgmt.member" /></Radio>
<Radio value={1}><FormattedMessage id="team.mgmt.admin" /></Radio>
</RadioGroup>,
)}
</FormItem>
{
getFieldValue('mgmt') === 1 ?
<FormItem label="管理员">
<FormItem label={<FormattedMessage id="team.admins" />}>
{getFieldDecorator('admins', {
initialValue: initialValue.admins,
rules: [{
required: getFieldValue('mgmt') === 1,
message: '管理员管理制必须选择管理员!',
// message: '管理员管理制必须选择管理员!',
}],
})(
this.renderUserSelect(),
)}
</FormItem> : null
}
<FormItem label="普通组员">
<FormItem label={<FormattedMessage id="team.members" />}>
{getFieldDecorator('members', {
initialValue: initialValue.members,
})(
@ -142,4 +143,4 @@ class TeamForm extends Component<Props & FormProps, State> {
}
}
export default Form.create()(TeamForm as any);
export default Form.create()(TeamForm);

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { injectIntl, WrappedComponentProps, FormattedMessage } from 'react-intl';
import { Row, Col, Input, Divider, Popconfirm, Button, message } from 'antd';
import _ from 'lodash';
import CreateIncludeNsTree from '@cpts/Layout/CreateIncludeNsTree';
@ -13,13 +14,15 @@ interface State {
searchValue: string,
}
class UserTeam extends Component<null, State> {
class UserTeam extends Component<WrappedComponentProps, State> {
fetchtable: any;
state = {} as State;
handleAddBtnClick = () => {
AddTeam({
title: <FormattedMessage id="table.create" />,
language: this.props.intl.locale,
onOk: () => {
this.fetchtable.reload();
},
@ -28,6 +31,8 @@ class UserTeam extends Component<null, State> {
handlePutBtnClick = (record: Team) => {
PutTeam({
title: <FormattedMessage id="table.modify" />,
language: this.props.intl.locale,
data: {
...record,
admins: _.map(record.admin_objs, n => n.id),
@ -44,7 +49,7 @@ class UserTeam extends Component<null, State> {
method: 'DELETE',
}).then(() => {
this.fetchtable.reload();
message.success('团队删除成功!');
message.success(this.props.intl.formatMessage({ id: 'msg.delete.success' }));
});
}
@ -61,7 +66,9 @@ class UserTeam extends Component<null, State> {
/>
</Col>
<Col span={16} className="textAlignRight">
<Button onClick={this.handleAddBtnClick} icon="plus"></Button>
<Button onClick={this.handleAddBtnClick} icon="plus">
<FormattedMessage id="table.create" />
</Button>
</Col>
</Row>
<FetchTable
@ -72,37 +79,37 @@ class UserTeam extends Component<null, State> {
tableProps={{
columns: [
{
title: '英文标识',
title: <FormattedMessage id="team.ident" />,
dataIndex: 'ident',
width: 130,
}, {
title: '中文名称',
title: <FormattedMessage id="team.name" />,
dataIndex: 'name',
width: 130,
}, {
title: '管理员',
title: <FormattedMessage id="team.admins" />,
dataIndex: 'admin_objs',
render(text) {
const users = _.map(text, item => item.username);
return _.join(users, ', ');
},
}, {
title: '普通成员',
title: <FormattedMessage id="team.members" />,
dataIndex: 'member_objs',
render(text) {
const users = _.map(text, item => item.username);
return _.join(users, ', ');
},
}, {
title: '操作',
width: 100,
render: (text, record) => {
title: <FormattedMessage id="table.operations" />,
width: this.props.intl.locale === 'zh' ? 100 : 150,
render: (_text, record) => {
return (
<span>
<a onClick={() => { this.handlePutBtnClick(record); }}></a>
<a onClick={() => { this.handlePutBtnClick(record); }}><FormattedMessage id="table.modify" /></a>
<Divider type="vertical" />
<Popconfirm title="确认要删除这个团队吗?" onConfirm={() => { this.handleDelBtnClick(record.id); }}>
<a></a>
<Popconfirm title={<FormattedMessage id="table.delete.sure" />} onConfirm={() => { this.handleDelBtnClick(record.id); }}>
<a><FormattedMessage id="table.delete" /></a>
</Popconfirm>
</span>
);
@ -115,4 +122,4 @@ class UserTeam extends Component<null, State> {
);
}
}
export default CreateIncludeNsTree(UserTeam);
export default CreateIncludeNsTree(injectIntl(UserTeam));