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
pub
src/modules/monapi/cron
web

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