Merge remote-tracking branch 'amis/master'

This commit is contained in:
rickcole 2020-06-03 11:41:53 +08:00
commit faf79f1073
80 changed files with 1269 additions and 683 deletions

View File

@ -1,3 +1,4 @@
{ {
"editor.formatOnSave": true "editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib"
} }

View File

@ -4,6 +4,7 @@ import Layout from '../../src/components/Layout';
import AsideNav from '../../src/components/AsideNav'; import AsideNav from '../../src/components/AsideNav';
import {AlertComponent, ToastComponent} from '../../src/components/index'; import {AlertComponent, ToastComponent} from '../../src/components/index';
import {mapTree} from '../../src/utils/helper'; import {mapTree} from '../../src/utils/helper';
import '../../src/locale/en';
import { import {
Router, Router,
Route, Route,
@ -560,6 +561,18 @@ const themes = [
} }
]; ];
const locales = [
{
label: '默认',
value: 'zh-cn'
},
{
label: 'English',
value: 'en'
}
];
@withRouter @withRouter
export class App extends React.PureComponent { export class App extends React.PureComponent {
state = { state = {
@ -568,7 +581,8 @@ export class App extends React.PureComponent {
headerVisible: true, headerVisible: true,
themeIndex: 0, themeIndex: 0,
themes: themes, themes: themes,
theme: themes[localStorage.getItem('themeIndex') || 0] theme: themes[localStorage.getItem('themeIndex') || 0],
locale: localStorage.getItem('locale') || ''
}; };
constructor(props) { constructor(props) {
@ -767,10 +781,27 @@ export class App extends React.PureComponent {
</Button> </Button>
</div> </div>
<div className="hidden-xs p-t-sm pull-right m-l-sm">
语言
{
<Select
clearable={false}
theme={this.state.theme.value}
value={this.state.locale || 'zh-cn'}
options={locales}
onChange={locale => {
this.setState({locale: locale.value});
localStorage.setItem('locale', locale.value);
}}
/>
}
</div>
<div className="hidden-xs p-t-sm pull-right"> <div className="hidden-xs p-t-sm pull-right">
主题 主题
{ {
<Select <Select
clearable={false}
theme={this.state.theme.value} theme={this.state.theme.value}
value={this.state.theme} value={this.state.theme}
options={this.state.themes} options={this.state.themes}
@ -802,14 +833,15 @@ export class App extends React.PureComponent {
folded={this.state.asideFolded} folded={this.state.asideFolded}
aside={this.renderAside()} aside={this.renderAside()}
> >
<ToastComponent theme={theme.value} /> <ToastComponent theme={theme.value} locale={this.state.locale} />
<AlertComponent theme={theme.value} /> <AlertComponent theme={theme.value} locale={this.state.locale} />
{React.cloneElement(this.props.children, { {React.cloneElement(this.props.children, {
...this.props.children.props, ...this.props.children.props,
setAsideFolded: this.setAsideFolded, setAsideFolded: this.setAsideFolded,
setHeaderVisible: this.setHeaderVisible, setHeaderVisible: this.setHeaderVisible,
theme: theme.value, theme: theme.value,
classPrefix: theme.ns classPrefix: theme.ns,
locale: this.state.locale
})} })}
</Layout> </Layout>
); );

View File

@ -161,13 +161,14 @@ export default function (schema) {
} }
renderSchema() { renderSchema() {
const {router, location, theme} = this.props; const {router, location, theme, locale} = this.props;
return render( return render(
schema, schema,
{ {
location, location,
theme theme,
locale
}, },
this.env this.env
); );

View File

@ -81,7 +81,7 @@
"@types/classnames": "^2.2.3", "@types/classnames": "^2.2.3",
"@types/dom-helpers": "^3.4.1", "@types/dom-helpers": "^3.4.1",
"@types/history": "^4.6.0", "@types/history": "^4.6.0",
"@types/hoist-non-react-statics": "^3.0.1", "@types/hoist-non-react-statics": "^3.3.1",
"@types/jest": "^24.0.11", "@types/jest": "^24.0.11",
"@types/jquery": "^3.3.1", "@types/jquery": "^3.3.1",
"@types/lodash": "^4.14.76", "@types/lodash": "^4.14.76",
@ -89,7 +89,7 @@
"@types/pretty-bytes": "^4.0.0", "@types/pretty-bytes": "^4.0.0",
"@types/prop-types": "^15.5.2", "@types/prop-types": "^15.5.2",
"@types/qs": "^6.5.1", "@types/qs": "^6.5.1",
"@types/react": "^16.8.1", "@types/react": "^16.9.35",
"@types/react-addons-update": "^0.14.19", "@types/react-addons-update": "^0.14.19",
"@types/react-color": "^2.13.3", "@types/react-color": "^2.13.3",
"@types/react-cropper": "^0.10.1", "@types/react-cropper": "^0.10.1",
@ -139,7 +139,7 @@
"react-testing-library": "6.0.4", "react-testing-library": "6.0.4",
"strip-json-comments": "^2.0.1", "strip-json-comments": "^2.0.1",
"ts-jest": "^24.0.0", "ts-jest": "^24.0.0",
"typescript": "^3.9.2" "typescript": "^3.9.3"
}, },
"jest": { "jest": {
"testEnvironment": "jsdom", "testEnvironment": "jsdom",

View File

@ -11,7 +11,7 @@ rm -rf lib/node_modules
rm -rf sdk && fis3 release publish-sdk -c rm -rf sdk && fis3 release publish-sdk -c
# 生成 .d.ts 文件 # 生成 .d.ts 文件
tsc --allowJs --declaration ./node_modules/.bin/tsc --allowJs --declaration
cd output cd output

View File

@ -7,18 +7,16 @@ import React from 'react';
import {render} from 'react-dom'; import {render} from 'react-dom';
import Modal from './Modal'; import Modal from './Modal';
import Button from './Button'; import Button from './Button';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {LocaleProps, localeable} from '../locale';
export interface AlertProps { export interface AlertProps extends ThemeProps, LocaleProps {
container?: any; container?: any;
confirmText?: string; confirmText?: string;
cancelText?: string; cancelText?: string;
title?: string; title?: string;
confirmBtnLevel?: string; confirmBtnLevel?: string;
alertBtnLevel?: string; alertBtnLevel?: string;
classPrefix: string;
classnames: ClassNamesFn;
theme?: string;
} }
export interface AlertState { export interface AlertState {
@ -37,7 +35,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
const container = document.body; const container = document.body;
const div = document.createElement('div'); const div = document.createElement('div');
container.appendChild(div); container.appendChild(div);
render(<ThemedAlert />, div); render(<FinnalAlert />, div);
} }
return Alert.instance; return Alert.instance;
@ -150,6 +148,8 @@ export class Alert extends React.Component<AlertProps, AlertState> {
classnames: cx, classnames: cx,
classPrefix classPrefix
} = this.props; } = this.props;
const __ = this.props.translate;
return ( return (
<Modal <Modal
show={this.state.show} show={this.state.show}
@ -158,20 +158,22 @@ export class Alert extends React.Component<AlertProps, AlertState> {
ref={this.modalRef} ref={this.modalRef}
> >
<div className={cx('Modal-header')}> <div className={cx('Modal-header')}>
<div className={cx('Modal-title')}>{this.state.title || title}</div> <div className={cx('Modal-title')}>
{__(this.state.title || title)}
</div>
</div> </div>
<div className={cx('Modal-body')}> <div className={cx('Modal-body')}>
<div ref={this.bodyRef} /> <div ref={this.bodyRef} />
</div> </div>
<div className={cx('Modal-footer')}> <div className={cx('Modal-footer')}>
{this.state.confirm ? ( {this.state.confirm ? (
<Button onClick={this.handleCancel}>{cancelText}</Button> <Button onClick={this.handleCancel}>{__(cancelText)}</Button>
) : null} ) : null}
<Button <Button
level={this.state.confirm ? confirmBtnLevel : alertBtnLevel} level={this.state.confirm ? confirmBtnLevel : alertBtnLevel}
onClick={this.handleConfirm} onClick={this.handleConfirm}
> >
{this.state.confirmText || confirmText} {__(this.state.confirmText || confirmText)}
</Button> </Button>
</div> </div>
</Modal> </Modal>
@ -189,5 +191,5 @@ export const confirm: (
confirmText?: string confirmText?: string
) => Promise<any> = (content, title, confirmText) => ) => Promise<any> = (content, title, confirmText) =>
Alert.getInstance().confirm(content, title, confirmText); Alert.getInstance().confirm(content, title, confirmText);
export const ThemedAlert = themeable(Alert); export const FinnalAlert = themeable(localeable(Alert));
export default ThemedAlert; export default FinnalAlert;

View File

@ -5,7 +5,7 @@
*/ */
import React from 'react'; import React from 'react';
import {CheckboxesProps, Checkboxes} from './Checkboxes'; import {BaseCheckboxesProps, BaseCheckboxes} from './Checkboxes';
import {Options, Option} from './Select'; import {Options, Option} from './Select';
import ListMenu from './ListMenu'; import ListMenu from './ListMenu';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
@ -19,8 +19,9 @@ import ChainedCheckboxes from './ChainedCheckboxes';
import Spinner from './Spinner'; import Spinner from './Spinner';
import TreeRadios from './TreeRadios'; import TreeRadios from './TreeRadios';
import {Icon} from './icons'; import {Icon} from './icons';
import {localeable} from '../locale';
export interface AssociatedCheckboxesProps extends CheckboxesProps { export interface AssociatedCheckboxesProps extends BaseCheckboxesProps {
leftOptions: Options; leftOptions: Options;
leftDefaultValue?: any; leftDefaultValue?: any;
leftMode?: 'tree' | 'list'; leftMode?: 'tree' | 'list';
@ -42,7 +43,7 @@ export interface AssociatedCheckboxesState {
leftValue?: Option; leftValue?: Option;
} }
export class AssociatedCheckboxes extends Checkboxes< export class AssociatedCheckboxes extends BaseCheckboxes<
AssociatedCheckboxesProps, AssociatedCheckboxesProps,
AssociatedCheckboxesState AssociatedCheckboxesState
> { > {
@ -58,7 +59,7 @@ export class AssociatedCheckboxes extends Checkboxes<
const selectdOption = ListRadios.resolveSelected( const selectdOption = ListRadios.resolveSelected(
leftValue, leftValue,
options, options,
option => option.ref (option: Option) => option.ref
); );
if (selectdOption && onDeferLoad && selectdOption.defer) { if (selectdOption && onDeferLoad && selectdOption.defer) {
@ -80,7 +81,7 @@ export class AssociatedCheckboxes extends Checkboxes<
const selectdOption = ListRadios.resolveSelected( const selectdOption = ListRadios.resolveSelected(
value, value,
options, options,
option => option.ref (option: Option) => option.ref
); );
if (selectdOption && onDeferLoad && selectdOption.defer) { if (selectdOption && onDeferLoad && selectdOption.defer) {
@ -111,8 +112,9 @@ export class AssociatedCheckboxes extends Checkboxes<
const selectdOption = ListRadios.resolveSelected( const selectdOption = ListRadios.resolveSelected(
this.state.leftValue, this.state.leftValue,
options, options,
option => option.ref (option: Option) => option.ref
); );
const __ = this.props.translate;
return ( return (
<div className={cx('AssociatedCheckboxes', className)}> <div className={cx('AssociatedCheckboxes', className)}>
@ -155,9 +157,9 @@ export class AssociatedCheckboxes extends Checkboxes<
</div> </div>
{selectdOption.loading ? ( {selectdOption.loading ? (
<p></p> <p>{__('加载中')}</p>
) : ( ) : (
<p></p> <p>{__('点击刷新重新加载')}</p>
)} )}
</div> </div>
) : rightMode === 'table' ? ( ) : rightMode === 'table' ? (
@ -193,12 +195,12 @@ export class AssociatedCheckboxes extends Checkboxes<
) )
) : ( ) : (
<div className={cx('AssociatedCheckboxes-box')}> <div className={cx('AssociatedCheckboxes-box')}>
{__('配置错误,选项无法与左侧选项对应')}
</div> </div>
) )
) : ( ) : (
<div className={cx('AssociatedCheckboxes-box')}> <div className={cx('AssociatedCheckboxes-box')}>
{__('请先选择左侧数据')}
</div> </div>
)} )}
</div> </div>
@ -208,7 +210,9 @@ export class AssociatedCheckboxes extends Checkboxes<
} }
export default themeable( export default themeable(
uncontrollable(AssociatedCheckboxes, { localeable(
value: 'onChange' uncontrollable(AssociatedCheckboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -1,7 +1,7 @@
/** /**
* *
*/ */
import {Checkboxes, CheckboxesProps} from './Checkboxes'; import {BaseCheckboxes, BaseCheckboxesProps} from './Checkboxes';
import {themeable} from '../theme'; import {themeable} from '../theme';
import React from 'react'; import React from 'react';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
@ -10,8 +10,9 @@ import {Option} from './Select';
import {getTreeDepth} from '../utils/helper'; import {getTreeDepth} from '../utils/helper';
import times from 'lodash/times'; import times from 'lodash/times';
import Spinner from './Spinner'; import Spinner from './Spinner';
import {localeable} from '../locale';
export interface ChainedCheckboxesProps extends CheckboxesProps { export interface ChainedCheckboxesProps extends BaseCheckboxesProps {
defaultSelectedIndex?: string; defaultSelectedIndex?: string;
} }
@ -19,7 +20,7 @@ export interface ChainedCheckboxesState {
selected: Array<string>; selected: Array<string>;
} }
export class ChainedCheckboxes extends Checkboxes< export class ChainedCheckboxes extends BaseCheckboxes<
ChainedCheckboxesProps, ChainedCheckboxesProps,
ChainedCheckboxesState ChainedCheckboxesState
> { > {
@ -122,7 +123,7 @@ export class ChainedCheckboxes extends Checkboxes<
itemRender itemRender
} = this.props; } = this.props;
this.valueArray = Checkboxes.value2array(value, options, option2value); this.valueArray = BaseCheckboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -188,13 +189,15 @@ export class ChainedCheckboxes extends Checkboxes<
); );
} }
const __ = this.props.translate;
return ( return (
<div className={cx('ChainedCheckboxes', className)}> <div className={cx('ChainedCheckboxes', className)}>
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<div className={cx('ChainedCheckboxes-placeholder')}> <div className={cx('ChainedCheckboxes-placeholder')}>
{placeholder} {__(placeholder)}
</div> </div>
)} )}
</div> </div>
@ -203,7 +206,9 @@ export class ChainedCheckboxes extends Checkboxes<
} }
export default themeable( export default themeable(
uncontrollable(ChainedCheckboxes, { localeable(
value: 'onChange' uncontrollable(ChainedCheckboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -13,9 +13,10 @@ import {Option, value2array, Options} from './Select';
import find from 'lodash/find'; import find from 'lodash/find';
import {autobind, findTree} from '../utils/helper'; import {autobind, findTree} from '../utils/helper';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import {LocaleProps, localeable} from '../locale';
// import isPlainObject from 'lodash/isPlainObject'; // import isPlainObject from 'lodash/isPlainObject';
export interface CheckboxesProps extends ThemeProps { export interface BaseCheckboxesProps extends ThemeProps, LocaleProps {
options: Options; options: Options;
className?: string; className?: string;
placeholder?: string; placeholder?: string;
@ -31,8 +32,8 @@ export interface CheckboxesProps extends ThemeProps {
disabled?: boolean; disabled?: boolean;
} }
export class Checkboxes< export class BaseCheckboxes<
T extends CheckboxesProps = CheckboxesProps, T extends BaseCheckboxesProps = BaseCheckboxesProps,
S = any S = any
> extends React.Component<T, S> { > extends React.Component<T, S> {
static defaultProps = { static defaultProps = {
@ -68,7 +69,7 @@ export class Checkboxes<
return; return;
} }
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
let idx = valueArray.indexOf(option); let idx = valueArray.indexOf(option);
if (~idx) { if (~idx) {
@ -115,7 +116,9 @@ export class Checkboxes<
itemRender itemRender
} = this.props; } = this.props;
let valueArray = Checkboxes.value2array(value, options, option2value); const __ = this.props.translate;
let valueArray = BaseCheckboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -142,14 +145,18 @@ export class Checkboxes<
inline ? 'Checkboxes--inline' : '' inline ? 'Checkboxes--inline' : ''
)} )}
> >
{body && body.length ? body : <div>{placeholder}</div>} {body && body.length ? body : <div>{__(placeholder)}</div>}
</div> </div>
); );
} }
} }
export class Checkboxes extends BaseCheckboxes {}
export default themeable( export default themeable(
uncontrollable(Checkboxes, { localeable(
value: 'onChange' uncontrollable(Checkboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -12,10 +12,11 @@ import {Icon} from './icons';
import Overlay from './Overlay'; import Overlay from './Overlay';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
import PopOver from './PopOver'; import PopOver from './PopOver';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import {localeable, LocaleProps} from '../locale';
export interface ColorProps { export interface ColorProps extends LocaleProps, ThemeProps {
placeholder?: string; placeholder?: string;
format: string; format: string;
// closeOnSelect:boolean; // closeOnSelect:boolean;
@ -25,8 +26,6 @@ export interface ColorProps {
popOverContainer?: any; popOverContainer?: any;
placement?: string; placement?: string;
value: any; value: any;
classPrefix: string;
classnames: ClassNamesFn;
onChange: (value: any) => void; onChange: (value: any) => void;
presetColors?: string[]; presetColors?: string[];
resetValue?: string; resetValue?: string;
@ -219,6 +218,7 @@ export class ColorControl extends React.PureComponent<
allowCustomColor allowCustomColor
} = this.props; } = this.props;
const __ = this.props.translate;
const isOpened = this.state.isOpened; const isOpened = this.state.isOpened;
const isFocused = this.state.isFocused; const isFocused = this.state.isFocused;
@ -240,7 +240,7 @@ export class ColorControl extends React.PureComponent<
size={10} size={10}
className={cx('ColorPicker-input')} className={cx('ColorPicker-input')}
value={this.state.inputValue || ''} value={this.state.inputValue || ''}
placeholder={placeholder} placeholder={__(placeholder)}
disabled={disabled} disabled={disabled}
onChange={this.handleInputChange} onChange={this.handleInputChange}
onFocus={this.handleFocus} onFocus={this.handleFocus}
@ -300,7 +300,9 @@ export class ColorControl extends React.PureComponent<
} }
export default themeable( export default themeable(
uncontrollable(ColorControl, { localeable(
value: 'onChange' uncontrollable(ColorControl, {
}) value: 'onChange'
})
)
); );

View File

@ -11,10 +11,11 @@ import 'moment/locale/zh-cn';
import {Icon} from './icons'; import {Icon} from './icons';
import PopOver from './PopOver'; import PopOver from './PopOver';
import Overlay from './Overlay'; import Overlay from './Overlay';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {PlainObject} from '../types'; import {PlainObject} from '../types';
import Calendar from './calendar/Calendar'; import Calendar from './calendar/Calendar';
import 'react-datetime/css/react-datetime.css'; import 'react-datetime/css/react-datetime.css';
import {localeable, LocaleProps, TranslateFn} from '../locale';
const availableShortcuts: {[propName: string]: any} = { const availableShortcuts: {[propName: string]: any} = {
now: { now: {
@ -97,9 +98,9 @@ const availableShortcuts: {[propName: string]: any} = {
const advancedShortcuts = [ const advancedShortcuts = [
{ {
regexp: /^(\d+)hoursago$/, regexp: /^(\d+)hoursago$/,
resolve: (_: string, hours: string) => { resolve: (__: TranslateFn, _: string, hours: string) => {
return { return {
label: `${hours}小时前`, label: __('{{hours}}小时前', {hours}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.subtract(hours, 'hours'); return now.subtract(hours, 'hours');
} }
@ -108,9 +109,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)hourslater$/, regexp: /^(\d+)hourslater$/,
resolve: (_: string, hours: string) => { resolve: (__: TranslateFn, _: string, hours: string) => {
return { return {
label: `${hours}小时后`, label: __('{{hours}}小时后', {hours}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.add(hours, 'hours'); return now.add(hours, 'hours');
} }
@ -119,9 +120,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)daysago$/, regexp: /^(\d+)daysago$/,
resolve: (_: string, days: string) => { resolve: (__: TranslateFn, _: string, days: string) => {
return { return {
label: `${days}天前`, label: __('{{days}}天前', {days}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.subtract(days, 'days'); return now.subtract(days, 'days');
} }
@ -130,9 +131,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)dayslater$/, regexp: /^(\d+)dayslater$/,
resolve: (_: string, days: string) => { resolve: (__: TranslateFn, _: string, days: string) => {
return { return {
label: `${days}天后`, label: __('{{days}}天后', {days}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.add(days, 'days'); return now.add(days, 'days');
} }
@ -141,9 +142,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)weeksago$/, regexp: /^(\d+)weeksago$/,
resolve: (_: string, weeks: string) => { resolve: (__: TranslateFn, _: string, weeks: string) => {
return { return {
label: `${weeks}周前`, label: __('{{weeks}}周前', {weeks}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.subtract(weeks, 'weeks'); return now.subtract(weeks, 'weeks');
} }
@ -152,9 +153,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)weekslater$/, regexp: /^(\d+)weekslater$/,
resolve: (_: string, weeks: string) => { resolve: (__: TranslateFn, _: string, weeks: string) => {
return { return {
label: `${weeks}周后`, label: __('{{weeks}}周后', {weeks}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.add(weeks, 'weeks'); return now.add(weeks, 'weeks');
} }
@ -163,9 +164,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)monthsago$/, regexp: /^(\d+)monthsago$/,
resolve: (_: string, months: string) => { resolve: (__: TranslateFn, _: string, months: string) => {
return { return {
label: `${months}月前`, label: __('{{months}}月前', {months}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.subtract(months, 'months'); return now.subtract(months, 'months');
} }
@ -174,9 +175,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)monthslater$/, regexp: /^(\d+)monthslater$/,
resolve: (_: string, months: string) => { resolve: (__: TranslateFn, _: string, months: string) => {
return { return {
label: `${months}月后`, label: __('{{months}}月后', {months}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.add(months, 'months'); return now.add(months, 'months');
} }
@ -185,9 +186,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)quartersago$/, regexp: /^(\d+)quartersago$/,
resolve: (_: string, quarters: string) => { resolve: (__: TranslateFn, _: string, quarters: string) => {
return { return {
label: `${quarters}季度前`, label: __('{{quarters}}季度前', {quarters}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.subtract(quarters, 'quarters'); return now.subtract(quarters, 'quarters');
} }
@ -196,9 +197,9 @@ const advancedShortcuts = [
}, },
{ {
regexp: /^(\d+)quarterslater$/, regexp: /^(\d+)quarterslater$/,
resolve: (_: string, quarters: string) => { resolve: (__: TranslateFn, _: string, quarters: string) => {
return { return {
label: `${quarters}季度后`, label: __('{{quarters}}季度后', {quarters}),
date: (now: moment.Moment) => { date: (now: moment.Moment) => {
return now.add(quarters, 'quarters'); return now.add(quarters, 'quarters');
} }
@ -226,17 +227,15 @@ export type ShortCuts =
| ShortCutDate | ShortCutDate
| ShortCutDateRange; | ShortCutDateRange;
export interface DateProps { export interface DateProps extends LocaleProps, ThemeProps {
viewMode: 'years' | 'months' | 'days' | 'time'; viewMode: 'years' | 'months' | 'days' | 'time';
className?: string; className?: string;
classPrefix: string;
classnames: ClassNamesFn;
placeholder?: string; placeholder?: string;
inputFormat?: string; inputFormat?: string;
timeFormat?: string; timeFormat?: string;
format?: string; format?: string;
timeConstrainst?: object; timeConstrainst?: object;
closeOnSelect?: boolean; closeOnSelect: boolean;
disabled?: boolean; disabled?: boolean;
minDate?: moment.Moment; minDate?: moment.Moment;
maxDate?: moment.Moment; maxDate?: moment.Moment;
@ -247,7 +246,14 @@ export interface DateProps {
value: any; value: any;
shortcuts: string | Array<ShortCuts>; shortcuts: string | Array<ShortCuts>;
overlayPlacement: string; overlayPlacement: string;
[propName: string]: any; minTime?: moment.Moment;
maxTime?: moment.Moment;
dateFormat?: string;
timeConstraints?: any;
popOverContainer?: any;
// 下面那个千万不要写,写了就会导致 keyof DateProps 得到的结果是 string | number;
// [propName: string]: any;
} }
export interface DatePickerState { export interface DatePickerState {
@ -257,11 +263,8 @@ export interface DatePickerState {
} }
export class DatePicker extends React.Component<DateProps, DatePickerState> { export class DatePicker extends React.Component<DateProps, DatePickerState> {
static defaultProps: Pick< static defaultProps = {
DateProps, viewMode: 'days' as 'years' | 'months' | 'days' | 'time',
'viewMode' | 'shortcuts' | 'closeOnSelect' | 'overlayPlacement'
> = {
viewMode: 'days',
shortcuts: '', shortcuts: '',
closeOnSelect: true, closeOnSelect: true,
overlayPlacement: 'auto' overlayPlacement: 'auto'
@ -429,12 +432,14 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
return availableShortcuts[key]; return availableShortcuts[key];
} }
const __ = this.props.translate;
for (let i = 0, len = advancedShortcuts.length; i < len; i++) { for (let i = 0, len = advancedShortcuts.length; i < len; i++) {
let item = advancedShortcuts[i]; let item = advancedShortcuts[i];
const m = item.regexp.exec(key); const m = item.regexp.exec(key);
if (m) { if (m) {
return item.resolve.apply(item, m); return item.resolve.apply(item, [__, ...m]);
} }
} }
@ -452,6 +457,8 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
} else { } else {
shortcutArr = shortcuts; shortcutArr = shortcuts;
} }
const __ = this.props.translate;
return ( return (
<ul className={`${ns}DatePicker-shortcuts`}> <ul className={`${ns}DatePicker-shortcuts`}>
{shortcutArr.map(item => { {shortcutArr.map(item => {
@ -474,7 +481,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
onClick={() => this.selectRannge(shortcut)} onClick={() => this.selectRannge(shortcut)}
key={shortcut.key || shortcut.label} key={shortcut.key || shortcut.label}
> >
<a>{shortcut.label}</a> <a>{__(shortcut.label)}</a>
</li> </li>
); );
})} })}
@ -498,9 +505,11 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
clearable, clearable,
shortcuts, shortcuts,
utc, utc,
overlayPlacement overlayPlacement,
locale
} = this.props; } = this.props;
const __ = this.props.translate;
const isOpened = this.state.isOpened; const isOpened = this.state.isOpened;
let date: moment.Moment | undefined = this.state.value; let date: moment.Moment | undefined = this.state.value;
@ -526,7 +535,9 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
{date.format(inputFormat)} {date.format(inputFormat)}
</span> </span>
) : ( ) : (
<span className={`${ns}DatePicker-placeholder`}>{placeholder}</span> <span className={`${ns}DatePicker-placeholder`}>
{__(placeholder)}
</span>
)} )}
{clearable && !disabled && value ? ( {clearable && !disabled && value ? (
@ -565,6 +576,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
timeConstraints={timeConstraints} timeConstraints={timeConstraints}
input={false} input={false}
onClose={this.close} onClose={this.close}
locale={locale}
// utc={utc} // utc={utc}
/> />
</PopOver> </PopOver>
@ -575,4 +587,4 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
} }
} }
export default themeable(DatePicker); export default themeable(localeable(DatePicker));

View File

@ -13,14 +13,13 @@ import Overlay from './Overlay';
import {ShortCuts, ShortCutDateRange} from './DatePicker'; import {ShortCuts, ShortCutDateRange} from './DatePicker';
import Calendar from './calendar/Calendar'; import Calendar from './calendar/Calendar';
import PopOver from './PopOver'; import PopOver from './PopOver';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {PlainObject} from '../types'; import {PlainObject} from '../types';
import {noop} from '../utils/helper'; import {noop} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
export interface DateRangePickerProps { export interface DateRangePickerProps extends ThemeProps, LocaleProps {
className?: string; className?: string;
classPrefix: string;
classnames: ClassNamesFn;
placeholder?: string; placeholder?: string;
theme?: any; theme?: any;
format: string; format: string;
@ -39,7 +38,10 @@ export interface DateRangePickerProps {
disabled?: boolean; disabled?: boolean;
closeOnSelect?: boolean; closeOnSelect?: boolean;
overlayPlacement: string; overlayPlacement: string;
[propName: string]: any; timeFormat?: string;
resetValue?: any;
popOverContainer?: any;
dateFormat?: string;
} }
export interface DateRangePickerState { export interface DateRangePickerState {
@ -106,10 +108,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('week').add(-1, 'weeks'); return now.startOf('week').add(-1, 'weeks');
}, },
endDate: (now: moment.Moment) => { endDate: (now: moment.Moment) => {
return now return now.startOf('week').add(-1, 'days').endOf('day');
.startOf('week')
.add(-1, 'days')
.endOf('day');
} }
}, },
@ -129,10 +128,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('month').add(-1, 'month'); return now.startOf('month').add(-1, 'month');
}, },
endDate: (now: moment.Moment) => { endDate: (now: moment.Moment) => {
return now return now.startOf('month').add(-1, 'day').endOf('day');
.startOf('month')
.add(-1, 'day')
.endOf('day');
} }
}, },
@ -142,10 +138,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('quarter').add(-1, 'quarter'); return now.startOf('quarter').add(-1, 'quarter');
}, },
endDate: (now: moment.Moment) => { endDate: (now: moment.Moment) => {
return now return now.startOf('quarter').add(-1, 'day').endOf('day');
.startOf('quarter')
.add(-1, 'day')
.endOf('day');
} }
}, },
@ -416,6 +409,8 @@ export class DateRangePicker extends React.Component<
} else { } else {
rangeArr = ranges; rangeArr = ranges;
} }
const __ = this.props.translate;
return ( return (
<ul className={`${ns}DateRangePicker-rangers`}> <ul className={`${ns}DateRangePicker-rangers`}>
{rangeArr.map(item => { {rangeArr.map(item => {
@ -442,7 +437,7 @@ export class DateRangePicker extends React.Component<
onClick={() => this.selectRannge(range)} onClick={() => this.selectRannge(range)}
key={range.key || range.label} key={range.key || range.label}
> >
<a>{range.label}</a> <a>{__(range.label)}</a>
</li> </li>
); );
})} })}
@ -551,6 +546,7 @@ export class DateRangePicker extends React.Component<
const arr = []; const arr = [];
startViewValue && arr.push(startViewValue); startViewValue && arr.push(startViewValue);
endViewValue && arr.push(endViewValue); endViewValue && arr.push(endViewValue);
const __ = this.props.translate;
return ( return (
<div <div
@ -571,11 +567,11 @@ export class DateRangePicker extends React.Component<
> >
{arr.length ? ( {arr.length ? (
<span className={`${ns}DateRangePicker-value`}> <span className={`${ns}DateRangePicker-value`}>
{arr.join(' 至 ')} {arr.join(__(' 至 '))}
</span> </span>
) : ( ) : (
<span className={`${ns}DateRangePicker-placeholder`}> <span className={`${ns}DateRangePicker-placeholder`}>
{placeholder} {__(placeholder)}
</span> </span>
)} )}
@ -646,10 +642,10 @@ export class DateRangePicker extends React.Component<
})} })}
onClick={this.confirm} onClick={this.confirm}
> >
{__('确认')}
</a> </a>
<a className="rdtBtn rdtBtnCancel" onClick={this.close}> <a className="rdtBtn rdtBtnCancel" onClick={this.close}>
{__('取消')}
</a> </a>
</div> </div>
</div> </div>
@ -661,4 +657,4 @@ export class DateRangePicker extends React.Component<
} }
} }
export default themeable(DateRangePicker); export default themeable(localeable(DateRangePicker));

View File

@ -1,12 +1,11 @@
import React from 'react'; import React from 'react';
import {themeable, ClassNamesFn} from '../theme'; import {themeable, ClassNamesFn, ThemeProps} from '../theme';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import Modal from './Modal'; import Modal from './Modal';
import {Icon} from './icons'; import {Icon} from './icons';
import {LocaleProps, localeable} from '../locale';
export interface ImageGalleryProps { export interface ImageGalleryProps extends ThemeProps, LocaleProps {
classnames: ClassNamesFn;
classPrefix: string;
children: React.ReactNode; children: React.ReactNode;
modalContainer?: () => HTMLElement; modalContainer?: () => HTMLElement;
} }
@ -87,6 +86,7 @@ export class ImageGallery extends React.Component<
render() { render() {
const {children, classnames: cx, modalContainer} = this.props; const {children, classnames: cx, modalContainer} = this.props;
const {index, items} = this.state; const {index, items} = this.state;
const __ = this.props.translate;
return ( return (
<> <>
@ -103,7 +103,7 @@ export class ImageGallery extends React.Component<
container={modalContainer} container={modalContainer}
> >
<a <a
data-tooltip="关闭" data-tooltip={__('关闭')}
data-position="left" data-position="left"
className={cx('ImageGallery-close')} className={cx('ImageGallery-close')}
onClick={this.close} onClick={this.close}
@ -176,4 +176,4 @@ export class ImageGallery extends React.Component<
} }
} }
export default themeable(ImageGallery); export default themeable(localeable(ImageGallery));

View File

@ -1,11 +1,12 @@
import {Checkboxes} from './Checkboxes'; import {BaseCheckboxes} from './Checkboxes';
import {themeable} from '../theme'; import {themeable} from '../theme';
import React from 'react'; import React from 'react';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import {Option} from './Select'; import {Option} from './Select';
import {localeable} from '../locale';
export class ListCheckboxes extends Checkboxes { export class ListCheckboxes extends BaseCheckboxes {
valueArray: Array<Option>; valueArray: Array<Option>;
renderOption(option: Option, index: number) { renderOption(option: Option, index: number) {
@ -72,8 +73,9 @@ export class ListCheckboxes extends Checkboxes {
classnames: cx, classnames: cx,
option2value option2value
} = this.props; } = this.props;
const __ = this.props.translate;
this.valueArray = Checkboxes.value2array(value, options, option2value); this.valueArray = BaseCheckboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -85,7 +87,9 @@ export class ListCheckboxes extends Checkboxes {
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<div className={cx('ListCheckboxes-placeholder')}>{placeholder}</div> <div className={cx('ListCheckboxes-placeholder')}>
{__(placeholder)}
</div>
)} )}
</div> </div>
); );
@ -93,7 +97,9 @@ export class ListCheckboxes extends Checkboxes {
} }
export default themeable( export default themeable(
uncontrollable(ListCheckboxes, { localeable(
value: 'onChange' uncontrollable(ListCheckboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -1,8 +1,9 @@
import {ThemeProps, themeable} from '../theme'; import {ThemeProps, themeable} from '../theme';
import React from 'react'; import React from 'react';
import {Options, Option} from './Select'; import {Options, Option} from './Select';
import {LocaleProps, localeable} from '../locale';
export interface ListMenuProps extends ThemeProps { export interface ListMenuProps extends ThemeProps, LocaleProps {
options: Options; options: Options;
disabled?: boolean; disabled?: boolean;
selectedOptions?: Options; selectedOptions?: Options;
@ -85,6 +86,8 @@ export class ListMenu extends React.Component<ListMenuProps> {
render() { render() {
const {classnames: cx, options, placeholder, prefix, children} = this.props; const {classnames: cx, options, placeholder, prefix, children} = this.props;
const __ = this.props.translate;
return ( return (
<div className={cx('ListMenu')}> <div className={cx('ListMenu')}>
{prefix} {prefix}
@ -98,7 +101,7 @@ export class ListMenu extends React.Component<ListMenuProps> {
} }
).items ).items
) : ( ) : (
<span className={cx('ListMenu-placeholder')}>{placeholder}</span> <span className={cx('ListMenu-placeholder')}>{__(placeholder)}</span>
)} )}
{children} {children}
</div> </div>
@ -106,4 +109,4 @@ export class ListMenu extends React.Component<ListMenuProps> {
} }
} }
export default themeable(ListMenu); export default themeable(localeable(ListMenu));

View File

@ -1,4 +1,4 @@
import {Checkboxes} from './Checkboxes'; import {BaseCheckboxes} from './Checkboxes';
import {themeable, ThemeProps} from '../theme'; import {themeable, ThemeProps} from '../theme';
import React from 'react'; import React from 'react';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
@ -6,8 +6,9 @@ import Checkbox from './Checkbox';
import {Option, Options} from './Select'; import {Option, Options} from './Select';
import {findTree, autobind} from '../utils/helper'; import {findTree, autobind} from '../utils/helper';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import {LocaleProps, localeable} from '../locale';
export interface ListRadiosProps extends ThemeProps { export interface BaseRadiosProps extends ThemeProps, LocaleProps {
options: Options; options: Options;
className?: string; className?: string;
placeholder: string; placeholder: string;
@ -22,9 +23,9 @@ export interface ListRadiosProps extends ThemeProps {
showRadio?: boolean; showRadio?: boolean;
} }
export class ListRadios< export class BaseRadios<
T extends ListRadiosProps = ListRadiosProps, T extends BaseRadiosProps = BaseRadiosProps,
S = any S = {}
> extends React.Component<T, S> { > extends React.Component<T, S> {
selected: Option | undefined | null; selected: Option | undefined | null;
@ -47,7 +48,7 @@ export class ListRadios<
let newValue: Option | null = option; let newValue: Option | null = option;
if (clearable) { if (clearable) {
const prevSelected = ListRadios.resolveSelected( const prevSelected = BaseRadios.resolveSelected(
value, value,
options, options,
option2value option2value
@ -120,8 +121,9 @@ export class ListRadios<
classnames: cx, classnames: cx,
option2value option2value
} = this.props; } = this.props;
const __ = this.props.translate;
this.selected = ListRadios.resolveSelected(value, options, option2value); this.selected = BaseRadios.resolveSelected(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -133,19 +135,23 @@ export class ListRadios<
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<div className={cx('ListRadios-placeholder')}>{placeholder}</div> <div className={cx('ListRadios-placeholder')}>{__(placeholder)}</div>
)} )}
</div> </div>
); );
} }
} }
export class ListRadios extends BaseRadios {}
const themedListRadios = themeable( const themedListRadios = themeable(
uncontrollable(ListRadios, { localeable(
value: 'onChange' uncontrollable(ListRadios, {
}) value: 'onChange'
})
)
); );
themedListRadios.resolveSelected = ListRadios.resolveSelected; themedListRadios.resolveSelected = BaseRadios.resolveSelected;
export default themedListRadios; export default themedListRadios;

View File

@ -1,13 +1,14 @@
import React from 'react'; import React from 'react';
import {themeable, ClassNamesFn} from '../theme'; import {themeable, ClassNamesFn, ThemeProps} from '../theme';
import Overlay from './Overlay'; import Overlay from './Overlay';
import PopOver from './PopOver'; import PopOver from './PopOver';
import {Icon} from './icons'; import {Icon} from './icons';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import Alert2 from './Alert2'; import Alert2 from './Alert2';
import BaiduMapPicker from './BaiduMapPicker'; import BaiduMapPicker from './BaiduMapPicker';
import {LocaleProps, localeable} from '../locale';
export interface LocationProps { export interface LocationProps extends ThemeProps, LocaleProps {
vendor: 'baidu' | 'gaode' | 'tenxun'; vendor: 'baidu' | 'gaode' | 'tenxun';
placeholder: string; placeholder: string;
clearable: boolean; clearable: boolean;
@ -21,8 +22,6 @@ export interface LocationProps {
disabled?: boolean; disabled?: boolean;
className?: string; className?: string;
onChange: (value: any) => void; onChange: (value: any) => void;
classnames: ClassNamesFn;
classPrefix: string;
popOverContainer?: any; popOverContainer?: any;
} }
@ -137,6 +136,7 @@ export class LocationPicker extends React.Component<
vendor, vendor,
ak ak
} = this.props; } = this.props;
const __ = this.props.translate;
const {isFocused, isOpened} = this.state; const {isFocused, isOpened} = this.state;
return ( return (
@ -161,7 +161,7 @@ export class LocationPicker extends React.Component<
<span className={cx('LocationPicker-value')}>{value.address}</span> <span className={cx('LocationPicker-value')}>{value.address}</span>
) : ( ) : (
<span className={cx('LocationPicker-placeholder')}> <span className={cx('LocationPicker-placeholder')}>
{placeholder} {__(placeholder)}
</span> </span>
)} )}
@ -189,8 +189,14 @@ export class LocationPicker extends React.Component<
style={{width: this.getTarget()?.offsetWidth}} style={{width: this.getTarget()?.offsetWidth}}
> >
{vendor === 'baidu' ? ( {vendor === 'baidu' ? (
<BaiduMapPicker ak={ak} value={value} onChange={this.handleChange} /> <BaiduMapPicker
) : (<Alert2>{vendor} </Alert2>)} ak={ak}
value={value}
onChange={this.handleChange}
/>
) : (
<Alert2>{__('${vendor} 地图控件不支持', {vendor})}</Alert2>
)}
</PopOver> </PopOver>
</Overlay> </Overlay>
</div> </div>
@ -198,5 +204,5 @@ export class LocationPicker extends React.Component<
} }
} }
const ThemedCity = themeable(LocationPicker); const ThemedCity = themeable(localeable(LocationPicker));
export default ThemedCity; export default ThemedCity;

View File

@ -14,8 +14,9 @@ import {Portal} from 'react-overlays';
import {current, addModal, removeModal} from './ModalManager'; import {current, addModal, removeModal} from './ModalManager';
import {ClassNamesFn, themeable, ThemeProps} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {Icon} from './icons'; import {Icon} from './icons';
import {LocaleProps, localeable} from '../locale';
export interface ModalProps extends ThemeProps { export interface ModalProps extends ThemeProps, LocaleProps {
className?: string; className?: string;
contentClassName?: string; contentClassName?: string;
size?: any; size?: any;
@ -44,33 +45,37 @@ export class Modal extends React.Component<ModalProps, ModalState> {
}; };
static Header = themeable( static Header = themeable(
({ localeable(
classnames: cx, ({
className, classnames: cx,
showCloseButton, className,
onClose, showCloseButton,
children, onClose,
classPrefix, children,
...rest classPrefix,
}: ThemeProps & { translate: __,
className?: string; ...rest
showCloseButton?: boolean; }: ThemeProps &
onClose?: () => void; LocaleProps & {
children?: React.ReactNode; className?: string;
} & React.HTMLAttributes<HTMLDivElement>) => ( showCloseButton?: boolean;
<div {...rest} className={cx('Modal-header', className)}> onClose?: () => void;
{showCloseButton !== false ? ( children?: React.ReactNode;
<a } & React.HTMLAttributes<HTMLDivElement>) => (
data-tooltip="关闭弹窗" <div {...rest} className={cx('Modal-header', className)}>
data-position="left" {showCloseButton !== false ? (
onClick={onClose} <a
className={cx('Modal-close')} data-tooltip={__('关闭弹窗')}
> data-position="left"
<Icon icon="close" className="icon" /> onClick={onClose}
</a> className={cx('Modal-close')}
) : null} >
{children} <Icon icon="close" className="icon" />
</div> </a>
) : null}
{children}
</div>
)
) )
); );
@ -216,9 +221,9 @@ export class Modal extends React.Component<ModalProps, ModalState> {
} }
} }
const themedModal = themeable(Modal); const FinalModal = themeable(localeable(Modal));
export default themedModal as typeof themedModal & { export default FinalModal as typeof FinalModal & {
Header: typeof Modal.Header; Header: typeof Modal.Header;
Title: typeof Modal.Title; Title: typeof Modal.Title;
Body: typeof Modal.Body; Body: typeof Modal.Body;

View File

@ -5,10 +5,12 @@ import uncontrollable from 'uncontrollable';
import {Icon} from './icons'; import {Icon} from './icons';
import Input from './Input'; import Input from './Input';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
export interface ResultBoxProps export interface ResultBoxProps
extends ThemeProps, extends ThemeProps,
Omit<InputBoxProps, 'result' | 'prefix' | 'onChange'> { LocaleProps,
Omit<InputBoxProps, 'result' | 'prefix' | 'onChange' | 'translate'> {
onChange?: (value: string) => void; onChange?: (value: string) => void;
onResultClick?: (e: React.MouseEvent<HTMLElement>) => void; onResultClick?: (e: React.MouseEvent<HTMLElement>) => void;
result?: Array<any>; result?: Array<any>;
@ -96,6 +98,8 @@ export class ResultBox extends React.Component<ResultBoxProps> {
onResultChange, onResultChange,
onChange, onChange,
onResultClick, onResultClick,
translate: __,
locale,
...rest ...rest
} = this.props; } = this.props;
const isFocused = this.state.isFocused; const isFocused = this.state.isFocused;
@ -124,7 +128,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
)) ))
) : allowInput ? null : ( ) : allowInput ? null : (
<span className={cx('ResultBox-placeholder')}> <span className={cx('ResultBox-placeholder')}>
{placeholder || '无'} {__(placeholder || '无')}
</span> </span>
)} )}
@ -163,8 +167,10 @@ export class ResultBox extends React.Component<ResultBoxProps> {
} }
export default themeable( export default themeable(
uncontrollable(ResultBox, { localeable(
value: 'onChange', uncontrollable(ResultBox, {
result: 'onResultChange' value: 'onChange',
}) result: 'onResultChange'
})
)
); );

View File

@ -8,8 +8,9 @@ import {Icon} from './icons';
import {autobind, guid} from '../utils/helper'; import {autobind, guid} from '../utils/helper';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import {findDOMNode} from 'react-dom'; import {findDOMNode} from 'react-dom';
import {LocaleProps, localeable} from '../locale';
export interface ResultListProps extends ThemeProps { export interface ResultListProps extends ThemeProps, LocaleProps {
className?: string; className?: string;
value?: Array<Option>; value?: Array<Option>;
onChange?: (value: Array<Option>) => void; onChange?: (value: Array<Option>) => void;
@ -123,7 +124,8 @@ export class ResultList extends React.Component<ResultListProps> {
disabled, disabled,
title, title,
itemClassName, itemClassName,
sortable sortable,
translate: __
} = this.props; } = this.props;
return ( return (
@ -163,11 +165,11 @@ export class ResultList extends React.Component<ResultListProps> {
))} ))}
</div> </div>
) : ( ) : (
<div className={cx('Selections-placeholder')}>{placeholder}</div> <div className={cx('Selections-placeholder')}>{__(placeholder)}</div>
)} )}
</div> </div>
); );
} }
} }
export default themeable(ResultList); export default themeable(localeable(ResultList));

View File

@ -3,8 +3,9 @@ import {ThemeProps, themeable} from '../theme';
import {Icon} from './icons'; import {Icon} from './icons';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
export interface SearchBoxProps extends ThemeProps { export interface SearchBoxProps extends ThemeProps, LocaleProps {
name?: string; name?: string;
onChange?: (text: string) => void; onChange?: (text: string) => void;
placeholder?: string; placeholder?: string;
@ -47,7 +48,8 @@ export class SearchBox extends React.Component<SearchBoxProps> {
active, active,
name, name,
onChange, onChange,
placeholder placeholder,
translate: __
} = this.props; } = this.props;
return ( return (
@ -56,7 +58,7 @@ export class SearchBox extends React.Component<SearchBoxProps> {
name={name} name={name}
onChange={this.handleChange} onChange={this.handleChange}
value={value || ''} value={value || ''}
placeholder={placeholder || '输入关键字'} placeholder={__(placeholder || '输入关键字')}
ref={this.inputRef} ref={this.inputRef}
/> />
@ -75,8 +77,10 @@ export class SearchBox extends React.Component<SearchBoxProps> {
} }
export default themeable( export default themeable(
uncontrollable(SearchBox, { localeable(
active: 'onActiveChange', uncontrollable(SearchBox, {
value: 'onChange' active: 'onActiveChange',
}) value: 'onChange'
})
)
); );

View File

@ -19,10 +19,11 @@ import isPlainObject from 'lodash/isPlainObject';
import union from 'lodash/union'; import union from 'lodash/union';
import {highlight} from '../renderers/Form/Options'; import {highlight} from '../renderers/Form/Options';
import {findDOMNode} from 'react-dom'; import {findDOMNode} from 'react-dom';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import Input from './Input'; import Input from './Input';
import {Api} from '../types'; import {Api} from '../types';
import {LocaleProps, localeable} from '../locale';
export interface Option { export interface Option {
label?: string; label?: string;
@ -273,9 +274,7 @@ export function normalizeOptions(
const DownshiftChangeTypes = Downshift.stateChangeTypes; const DownshiftChangeTypes = Downshift.stateChangeTypes;
interface SelectProps extends OptionProps { interface SelectProps extends OptionProps, ThemeProps, LocaleProps {
classPrefix: string;
classnames: ClassNamesFn;
className?: string; className?: string;
creatable: boolean; creatable: boolean;
createBtnLabel: string; createBtnLabel: string;
@ -629,7 +628,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
placeholder, placeholder,
classPrefix: ns, classPrefix: ns,
labelField, labelField,
disabled disabled,
translate: __
} = this.props; } = this.props;
const selection = this.state.selection; const selection = this.state.selection;
@ -638,7 +638,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
if (!selection.length) { if (!selection.length) {
return ( return (
<div key="placeholder" className={`${ns}Select-placeholder`}> <div key="placeholder" className={`${ns}Select-placeholder`}>
{placeholder} {__(placeholder)}
</div> </div>
); );
} }
@ -693,7 +693,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
searchPromptText, searchPromptText,
editable, editable,
removable, removable,
overlayPlacement overlayPlacement,
translate: __
} = this.props; } = this.props;
const {selection} = this.state; const {selection} = this.state;
@ -732,7 +733,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
onFocus: this.onFocus, onFocus: this.onFocus,
onBlur: this.onBlur, onBlur: this.onBlur,
disabled: disabled, disabled: disabled,
placeholder: searchPromptText, placeholder: __(searchPromptText),
onChange: this.handleInputChange, onChange: this.handleInputChange,
ref: this.inputRef ref: this.inputRef
})} })}
@ -747,7 +748,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
partial={checkedPartial && !checkedAll} partial={checkedPartial && !checkedAll}
onChange={this.toggleCheckAll} onChange={this.toggleCheckAll}
> >
{checkAllLabel} {__(checkAllLabel)}
</Checkbox> </Checkbox>
</div> </div>
) : null} ) : null}
@ -830,13 +831,13 @@ export class Select extends React.Component<SelectProps, SelectState> {
); );
}) })
) : ( ) : (
<div className={cx('Select-noResult')}>{noResultsText}</div> <div className={cx('Select-noResult')}>{__(noResultsText)}</div>
)} )}
{creatable && !disabled ? ( {creatable && !disabled ? (
<a className={cx('Select-addBtn')} onClick={this.handleAddClick}> <a className={cx('Select-addBtn')} onClick={this.handleAddClick}>
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
{createBtnLabel} {__(createBtnLabel)}
</a> </a>
) : null} ) : null}
</div> </div>
@ -942,7 +943,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
export default themeable( export default themeable(
uncontrollable(Select, { localeable(
value: 'onChange' uncontrollable(Select, {
}) value: 'onChange'
})
)
); );

View File

@ -1,12 +1,13 @@
import {Checkboxes, CheckboxesProps} from './Checkboxes'; import {BaseCheckboxes, BaseCheckboxesProps} from './Checkboxes';
import {themeable} from '../theme'; import {themeable} from '../theme';
import React from 'react'; import React from 'react';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import {Option} from './Select'; import {Option} from './Select';
import {resolveVariable} from '../utils/tpl-builtin'; import {resolveVariable} from '../utils/tpl-builtin';
import {localeable} from '../locale';
export interface TableCheckboxesProps extends CheckboxesProps { export interface TableCheckboxesProps extends BaseCheckboxesProps {
columns: Array<{ columns: Array<{
name: string; name: string;
label: string; label: string;
@ -24,9 +25,9 @@ export interface TableCheckboxesProps extends CheckboxesProps {
) => JSX.Element; ) => JSX.Element;
} }
export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> { export class TableCheckboxes extends BaseCheckboxes<TableCheckboxesProps> {
static defaultProps = { static defaultProps = {
...Checkboxes.defaultProps, ...BaseCheckboxes.defaultProps,
cellRender: ( cellRender: (
column: { column: {
name: string; name: string;
@ -51,7 +52,7 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
renderTHead() { renderTHead() {
const {options, classnames: cx, value, option2value} = this.props; const {options, classnames: cx, value, option2value} = this.props;
let columns = this.getColumns(); let columns = this.getColumns();
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
const availableOptions = options.filter(option => !option.disabled); const availableOptions = options.filter(option => !option.disabled);
let partialChecked = false; let partialChecked = false;
let allChecked = !!availableOptions.length; let allChecked = !!availableOptions.length;
@ -94,10 +95,11 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
classnames: cx, classnames: cx,
cellRender, cellRender,
value, value,
option2value option2value,
translate: __
} = this.props; } = this.props;
const columns = this.getColumns(); const columns = this.getColumns();
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
return ( return (
<tbody> <tbody>
@ -123,7 +125,7 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
}) })
) : ( ) : (
<tr> <tr>
<td colSpan={columns.length}>{placeholder}</td> <td colSpan={columns.length}>{__(placeholder)}</td>
</tr> </tr>
)} )}
</tbody> </tbody>
@ -143,7 +145,7 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
itemRender itemRender
} = this.props; } = this.props;
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -187,7 +189,9 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
} }
export default themeable( export default themeable(
uncontrollable(TableCheckboxes, { localeable(
value: 'onChange' uncontrollable(TableCheckboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -10,6 +10,7 @@ import {Options, Option} from './Select';
import Transfer, {TransferProps} from './Transfer'; import Transfer, {TransferProps} from './Transfer';
import {themeable} from '../theme'; import {themeable} from '../theme';
import AssociatedCheckboxes from './AssociatedCheckboxes'; import AssociatedCheckboxes from './AssociatedCheckboxes';
import {localeable} from '../locale';
export interface TabsTransferProps export interface TabsTransferProps
extends Omit< extends Omit<
@ -101,13 +102,14 @@ export class TabsTransfer extends React.Component<TabsTransferProps> {
onSearch: searchable, onSearch: searchable,
option2value, option2value,
onDeferLoad, onDeferLoad,
cellRender cellRender,
translate: __
} = this.props; } = this.props;
if (!Array.isArray(options) || !options.length) { if (!Array.isArray(options) || !options.length) {
return ( return (
<div className={cx('TabsTransfer-placeholder')}> <div className={cx('TabsTransfer-placeholder')}>
{placeholder || '暂无选项'} {__(placeholder || '暂无选项')}
</div> </div>
); );
} }
@ -128,7 +130,7 @@ export class TabsTransfer extends React.Component<TabsTransferProps> {
> >
{searchResult !== null {searchResult !== null
? [ ? [
<Tab title="搜索结果" key={0} eventKey={0}> <Tab title={__('搜索结果')} key={0} eventKey={0}>
{this.renderSearchResult(searchResult)} {this.renderSearchResult(searchResult)}
</Tab> </Tab>
] ]
@ -210,4 +212,4 @@ export class TabsTransfer extends React.Component<TabsTransferProps> {
} }
} }
export default themeable(TabsTransfer); export default themeable(localeable(TabsTransfer));

View File

@ -35,8 +35,9 @@ import 'tinymce/plugins/template';
import 'tinymce/plugins/nonbreaking'; import 'tinymce/plugins/nonbreaking';
import 'tinymce/plugins/emoticons'; import 'tinymce/plugins/emoticons';
import 'tinymce/plugins/emoticons/js/emojis'; import 'tinymce/plugins/emoticons/js/emojis';
import {LocaleProps} from '../locale';
interface TinymceEditorProps { interface TinymceEditorProps extends LocaleProps {
model: string; model: string;
onModelChange?: (value: string) => void; onModelChange?: (value: string) => void;
onFocus?: () => void; onFocus?: () => void;
@ -58,12 +59,14 @@ export default class TinymceEditor extends React.Component<TinymceEditorProps> {
elementRef: React.RefObject<HTMLTextAreaElement> = React.createRef(); elementRef: React.RefObject<HTMLTextAreaElement> = React.createRef();
componentDidMount() { componentDidMount() {
const locale = this.props.locale;
this.config = { this.config = {
inline: false, inline: false,
skin: false, skin: false,
content_css: false, content_css: false,
height: 400, height: 400,
language: 'zh_CN', language: !locale || locale === 'zh-cn' ? 'zh_CN' : 'en',
plugins: [ plugins: [
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker', 'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker',
'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking', 'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking',

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import {ThemeProps, themeable} from '../theme'; import {ThemeProps, themeable} from '../theme';
import {CheckboxesProps, Checkboxes} from './Checkboxes'; import {BaseCheckboxesProps, BaseCheckboxes} from './Checkboxes';
import {Options, Option} from './Select'; import {Options, Option} from './Select';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
import ResultList from './ResultList'; import ResultList from './ResultList';
@ -13,8 +13,12 @@ import {Icon} from './icons';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import ChainedCheckboxes from './ChainedCheckboxes'; import ChainedCheckboxes from './ChainedCheckboxes';
import AssociatedCheckboxes from './AssociatedCheckboxes'; import AssociatedCheckboxes from './AssociatedCheckboxes';
import {LocaleProps, localeable} from '../locale';
export interface TransferProps extends ThemeProps, CheckboxesProps { export interface TransferProps
extends ThemeProps,
LocaleProps,
BaseCheckboxesProps {
inline?: boolean; inline?: boolean;
statistics?: boolean; statistics?: boolean;
showArrow?: boolean; showArrow?: boolean;
@ -98,7 +102,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
@autobind @autobind
toggleAll() { toggleAll() {
const {options, option2value, onChange, value} = this.props; const {options, option2value, onChange, value} = this.props;
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
const availableOptions = flattenTree(options).filter( const availableOptions = flattenTree(options).filter(
(option, index, list) => (option, index, list) =>
!option.disabled && !option.disabled &&
@ -185,7 +189,8 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
onSearch, onSearch,
disabled, disabled,
options, options,
statistics statistics,
translate: __
} = this.props; } = this.props;
if (selectRender) { if (selectRender) {
@ -206,7 +211,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
)} )}
> >
<span> <span>
{selectTitle} {__(selectTitle)}
{statistics !== false ? ( {statistics !== false ? (
<span> <span>
{this.valueArray.length}/{this.availableOptions.length} {this.valueArray.length}/{this.availableOptions.length}
@ -221,7 +226,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
disabled || !options.length ? 'is-disabled' : '' disabled || !options.length ? 'is-disabled' : ''
)} )}
> >
{__('全选')}
</a> </a>
) : null} ) : null}
</div> </div>
@ -231,7 +236,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
<InputBox <InputBox
value={this.state.inputValue} value={this.state.inputValue}
onChange={this.handleSearch} onChange={this.handleSearch}
placeholder="请输入关键字" placeholder={__('请输入关键字')}
clearable={false} clearable={false}
> >
{this.state.searchResult !== null ? ( {this.state.searchResult !== null ? (
@ -393,10 +398,11 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
option2value, option2value,
disabled, disabled,
statistics, statistics,
showArrow showArrow,
translate: __
} = this.props; } = this.props;
this.valueArray = Checkboxes.value2array(value, options, option2value); this.valueArray = BaseCheckboxes.value2array(value, options, option2value);
this.availableOptions = flattenTree(options).filter( this.availableOptions = flattenTree(options).filter(
(option, index, list) => (option, index, list) =>
!option.disabled && !option.disabled &&
@ -419,7 +425,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
<div className={cx('Transfer-result')}> <div className={cx('Transfer-result')}>
<div className={cx('Transfer-title')}> <div className={cx('Transfer-title')}>
<span> <span>
{resultTitle} {__(resultTitle)}
{statistics !== false ? ( {statistics !== false ? (
<span> <span>
{this.valueArray.length}/{this.availableOptions.length} {this.valueArray.length}/{this.availableOptions.length}
@ -433,7 +439,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
disabled || !this.valueArray.length ? 'is-disabled' : '' disabled || !this.valueArray.length ? 'is-disabled' : ''
)} )}
> >
{__('清空')}
</a> </a>
</div> </div>
<ResultList <ResultList
@ -441,7 +447,7 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
sortable={sortable} sortable={sortable}
value={value} value={value}
onChange={onChange} onChange={onChange}
placeholder="请先选择左侧数据" placeholder={__('请先选择左侧数据')}
/> />
</div> </div>
</div> </div>
@ -450,7 +456,9 @@ export class Transfer extends React.Component<TransferProps, TransferState> {
} }
export default themeable( export default themeable(
uncontrollable(Transfer, { localeable(
value: 'onChange' uncontrollable(Transfer, {
}) value: 'onChange'
})
)
); );

View File

@ -14,15 +14,13 @@ import {
createObject createObject
} from '../utils/helper'; } from '../utils/helper';
import {Option, Options, value2array} from './Select'; import {Option, Options, value2array} from './Select';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {highlight} from '../renderers/Form/Options'; import {highlight} from '../renderers/Form/Options';
import {Icon} from './icons'; import {Icon} from './icons';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import {LocaleProps, localeable} from '../locale';
interface TreeSelectorProps { interface TreeSelectorProps extends ThemeProps, LocaleProps {
classPrefix: string;
classnames: ClassNamesFn;
highlightTxt?: string; highlightTxt?: string;
showIcon?: boolean; showIcon?: boolean;
@ -421,7 +419,7 @@ export class TreeSelector extends React.Component<
} }
renderInput(prfix: JSX.Element | null = null) { renderInput(prfix: JSX.Element | null = null) {
const {classnames: cx} = this.props; const {classnames: cx, translate: __} = this.props;
const {inputValue} = this.state; const {inputValue} = this.state;
return ( return (
@ -431,12 +429,12 @@ export class TreeSelector extends React.Component<
<input <input
onChange={this.handleInputChange} onChange={this.handleInputChange}
value={inputValue} value={inputValue}
placeholder="请输入" placeholder={__('请输入')}
/> />
<a data-tooltip="取消" onClick={this.handleCancel}> <a data-tooltip={__('取消')} onClick={this.handleCancel}>
<Icon icon="close" className="icon" /> <Icon icon="close" className="icon" />
</a> </a>
<a data-tooltip="确认" onClick={this.handleConfirm}> <a data-tooltip={__('确认')} onClick={this.handleConfirm}>
<Icon icon="check" className="icon" /> <Icon icon="check" className="icon" />
</a> </a>
</div> </div>
@ -473,7 +471,8 @@ export class TreeSelector extends React.Component<
removable, removable,
createTip, createTip,
editTip, editTip,
removeTip removeTip,
translate: __
} = this.props; } = this.props;
const { const {
unfolded, unfolded,
@ -611,7 +610,7 @@ export class TreeSelector extends React.Component<
{creatable && hasAbility(item, 'creatable') ? ( {creatable && hasAbility(item, 'creatable') ? (
<a <a
onClick={this.handleAdd.bind(this, item)} onClick={this.handleAdd.bind(this, item)}
data-tooltip={createTip} data-tooltip={__(createTip)}
data-position="left" data-position="left"
> >
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
@ -621,7 +620,7 @@ export class TreeSelector extends React.Component<
{removable && hasAbility(item, 'removable') ? ( {removable && hasAbility(item, 'removable') ? (
<a <a
onClick={this.handleRemove.bind(this, item)} onClick={this.handleRemove.bind(this, item)}
data-tooltip={removeTip} data-tooltip={__(removeTip)}
data-position="left" data-position="left"
> >
<Icon icon="minus" className="icon" /> <Icon icon="minus" className="icon" />
@ -631,7 +630,7 @@ export class TreeSelector extends React.Component<
{editable && hasAbility(item, 'editable') ? ( {editable && hasAbility(item, 'editable') ? (
<a <a
onClick={this.handleEdit.bind(this, item)} onClick={this.handleEdit.bind(this, item)}
data-tooltip={editTip} data-tooltip={__(editTip)}
data-position="left" data-position="left"
> >
<Icon icon="pencil" className="icon" /> <Icon icon="pencil" className="icon" />
@ -689,7 +688,8 @@ export class TreeSelector extends React.Component<
creatable, creatable,
rootCreatable, rootCreatable,
rootCreateTip, rootCreateTip,
disabled disabled,
translate: __
} = this.props; } = this.props;
let options = this.props.options; let options = this.props.options;
const {value, isAdding, addingParent, isEditing, inputValue} = this.state; const {value, isAdding, addingParent, isEditing, inputValue} = this.state;
@ -705,7 +705,7 @@ export class TreeSelector extends React.Component<
onClick={this.handleAdd.bind(this, null)} onClick={this.handleAdd.bind(this, null)}
> >
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
<span>{rootCreateTip}</span> <span>{__(rootCreateTip)}</span>
</a> </a>
); );
} }
@ -772,4 +772,4 @@ export class TreeSelector extends React.Component<
} }
} }
export default themeable(TreeSelector); export default themeable(localeable(TreeSelector));

View File

@ -1,4 +1,4 @@
import {Checkboxes, CheckboxesProps} from './Checkboxes'; import {BaseCheckboxes, BaseCheckboxesProps} from './Checkboxes';
import {themeable} from '../theme'; import {themeable} from '../theme';
import React from 'react'; import React from 'react';
import uncontrollable from 'uncontrollable'; import uncontrollable from 'uncontrollable';
@ -6,8 +6,9 @@ import Checkbox from './Checkbox';
import {Option} from './Select'; import {Option} from './Select';
import {autobind, eachTree, everyTree} from '../utils/helper'; import {autobind, eachTree, everyTree} from '../utils/helper';
import Spinner from './Spinner'; import Spinner from './Spinner';
import {localeable} from '../locale';
export interface TreeCheckboxesProps extends CheckboxesProps { export interface TreeCheckboxesProps extends BaseCheckboxesProps {
expand?: 'all' | 'first' | 'root' | 'none'; expand?: 'all' | 'first' | 'root' | 'none';
} }
@ -15,7 +16,7 @@ export interface TreeCheckboxesState {
expanded: Array<string>; expanded: Array<string>;
} }
export class TreeCheckboxes extends Checkboxes< export class TreeCheckboxes extends BaseCheckboxes<
TreeCheckboxesProps, TreeCheckboxesProps,
TreeCheckboxesState TreeCheckboxesState
> { > {
@ -25,7 +26,7 @@ export class TreeCheckboxes extends Checkboxes<
}; };
static defaultProps = { static defaultProps = {
...Checkboxes.defaultProps, ...BaseCheckboxes.defaultProps,
expand: 'first' as 'first' expand: 'first' as 'first'
}; };
@ -83,7 +84,7 @@ export class TreeCheckboxes extends Checkboxes<
return; return;
} }
let valueArray = Checkboxes.value2array(value, options, option2value); let valueArray = BaseCheckboxes.value2array(value, options, option2value);
if ( if (
option.value === void 0 && option.value === void 0 &&
@ -257,10 +258,11 @@ export class TreeCheckboxes extends Checkboxes<
className, className,
placeholder, placeholder,
classnames: cx, classnames: cx,
option2value option2value,
translate: __
} = this.props; } = this.props;
this.valueArray = Checkboxes.value2array(value, options, option2value); this.valueArray = BaseCheckboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -272,7 +274,9 @@ export class TreeCheckboxes extends Checkboxes<
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<div className={cx('TreeCheckboxes-placeholder')}>{placeholder}</div> <div className={cx('TreeCheckboxes-placeholder')}>
{__(placeholder)}
</div>
)} )}
</div> </div>
); );
@ -280,7 +284,9 @@ export class TreeCheckboxes extends Checkboxes<
} }
export default themeable( export default themeable(
uncontrollable(TreeCheckboxes, { localeable(
value: 'onChange' uncontrollable(TreeCheckboxes, {
}) value: 'onChange'
})
)
); );

View File

@ -5,23 +5,24 @@ import Checkbox from './Checkbox';
import {Option} from './Select'; import {Option} from './Select';
import {autobind, eachTree, everyTree} from '../utils/helper'; import {autobind, eachTree, everyTree} from '../utils/helper';
import Spinner from './Spinner'; import Spinner from './Spinner';
import {ListRadiosProps, ListRadios} from './ListRadios'; import {BaseRadiosProps, BaseRadios} from './ListRadios';
import {localeable} from '../locale';
export interface TreeRadiosProps extends ListRadiosProps { export interface TreeRadiosProps extends BaseRadiosProps {
expand?: 'all' | 'first' | 'root' | 'none'; expand: 'all' | 'first' | 'root' | 'none';
} }
export interface TreeRadiosState { export interface TreeRadiosState {
expanded: Array<string>; expanded: Array<string>;
} }
export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> { export class TreeRadios extends BaseRadios<TreeRadiosProps, TreeRadiosState> {
state: TreeRadiosState = { state: TreeRadiosState = {
expanded: [] expanded: []
}; };
static defaultProps = { static defaultProps = {
...ListRadios.defaultProps, ...BaseRadios.defaultProps,
expand: 'first' as 'first' expand: 'first' as 'first'
}; };
@ -168,10 +169,11 @@ export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> {
className, className,
placeholder, placeholder,
classnames: cx, classnames: cx,
option2value option2value,
translate: __
} = this.props; } = this.props;
this.selected = ListRadios.resolveSelected(value, options, option2value); this.selected = BaseRadios.resolveSelected(value, options, option2value);
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) { if (Array.isArray(options) && options.length) {
@ -183,7 +185,7 @@ export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> {
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<div className={cx('TreeRadios-placeholder')}>{placeholder}</div> <div className={cx('TreeRadios-placeholder')}>{__(placeholder)}</div>
)} )}
</div> </div>
); );
@ -191,7 +193,9 @@ export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> {
} }
export default themeable( export default themeable(
uncontrollable(TreeRadios, { localeable(
value: 'onChange' uncontrollable(TreeRadios, {
}) value: 'onChange'
})
)
); );

View File

@ -2,8 +2,9 @@ import moment from 'moment';
// @ts-ignore // @ts-ignore
import DaysView from 'react-datetime/src/DaysView'; import DaysView from 'react-datetime/src/DaysView';
import React from 'react'; import React from 'react';
import {LocaleProps, localeable} from '../../locale';
interface CustomDaysViewProps { interface CustomDaysViewProps extends LocaleProps {
classPrefix?: string; classPrefix?: string;
prevIcon?: string; prevIcon?: string;
nextIcon?: string; nextIcon?: string;
@ -36,7 +37,7 @@ interface CustomDaysViewProps {
handleClickOutside: () => void; handleClickOutside: () => void;
} }
export default class CustomDaysView extends DaysView { export class CustomDaysView extends DaysView {
props: CustomDaysViewProps; props: CustomDaysViewProps;
getDaysOfWeek: (locale: any) => any; getDaysOfWeek: (locale: any) => any;
renderDays: () => JSX.Element; renderDays: () => JSX.Element;
@ -160,6 +161,8 @@ export default class CustomDaysView extends DaysView {
return null; return null;
} }
const __ = this.props.translate;
return ( return (
<tfoot key="tf"> <tfoot key="tf">
<tr> <tr>
@ -168,10 +171,10 @@ export default class CustomDaysView extends DaysView {
{this.props.requiredConfirm ? ( {this.props.requiredConfirm ? (
<div key="button" className="rdtActions"> <div key="button" className="rdtActions">
<a className="rdtBtn rdtBtnConfirm" onClick={this.confirm}> <a className="rdtBtn rdtBtnConfirm" onClick={this.confirm}>
{__('确认')}
</a> </a>
<a className="rdtBtn rdtBtnCancel" onClick={this.cancel}> <a className="rdtBtn rdtBtnCancel" onClick={this.cancel}>
{__('取消')}
</a> </a>
</div> </div>
) : null} ) : null}
@ -185,6 +188,7 @@ export default class CustomDaysView extends DaysView {
const footer = this.renderFooter(); const footer = this.renderFooter();
const date = this.props.viewDate; const date = this.props.viewDate;
const locale = date.localeData(); const locale = date.localeData();
const __ = this.props.translate;
const tableChildren = [ const tableChildren = [
<thead key="th"> <thead key="th">
@ -206,13 +210,13 @@ export default class CustomDaysView extends DaysView {
<div className="rdtCenter"> <div className="rdtCenter">
<a className="rdtSwitch" onClick={this.props.showView('years')}> <a className="rdtSwitch" onClick={this.props.showView('years')}>
{date.format('YYYY年')} {date.format(__('YYYY年'))}
</a> </a>
<a <a
className="rdtSwitch" className="rdtSwitch"
onClick={this.props.showView('months')} onClick={this.props.showView('months')}
> >
{date.format('MM月')} {date.format(__('MMM'))}
</a> </a>
</div> </div>
@ -246,3 +250,7 @@ export default class CustomDaysView extends DaysView {
); );
} }
} }
export default localeable(
(CustomDaysView as any) as React.ComponentClass<CustomDaysViewProps>
);

View File

@ -2,8 +2,9 @@
import MonthsView from 'react-datetime/src/MonthsView'; import MonthsView from 'react-datetime/src/MonthsView';
import moment from 'moment'; import moment from 'moment';
import React from 'react'; import React from 'react';
import {LocaleProps, localeable} from '../../locale';
export default class CustomMonthsView extends MonthsView { export class CustomMonthsView extends MonthsView {
props: { props: {
viewDate: moment.Moment; viewDate: moment.Moment;
subtractTime: ( subtractTime: (
@ -16,7 +17,7 @@ export default class CustomMonthsView extends MonthsView {
type: string, type: string,
toSelected?: moment.Moment toSelected?: moment.Moment
) => () => void; ) => () => void;
}; } & LocaleProps;
renderMonths: () => JSX.Element; renderMonths: () => JSX.Element;
renderMonth = (props: any, month: number) => { renderMonth = (props: any, month: number) => {
var localMoment = this.props.viewDate; var localMoment = this.props.viewDate;
@ -34,6 +35,8 @@ export default class CustomMonthsView extends MonthsView {
); );
}; };
render() { render() {
const __ = this.props.translate;
return ( return (
<div className="rdtMonths"> <div className="rdtMonths">
<table> <table>
@ -45,7 +48,9 @@ export default class CustomMonthsView extends MonthsView {
> >
« «
</th> </th>
<th className="rdtSwitch">{this.props.viewDate.year()}</th> <th className="rdtSwitch">
{this.props.viewDate.format(__('YYYY年'))}
</th>
<th className="rdtNext" onClick={this.props.addTime(1, 'years')}> <th className="rdtNext" onClick={this.props.addTime(1, 'years')}>
» »
</th> </th>
@ -59,3 +64,5 @@ export default class CustomMonthsView extends MonthsView {
); );
} }
} }
export default localeable(CustomMonthsView as any);

View File

@ -2,8 +2,9 @@
import YearsView from 'react-datetime/src/YearsView'; import YearsView from 'react-datetime/src/YearsView';
import moment from 'moment'; import moment from 'moment';
import React from 'react'; import React from 'react';
import {LocaleProps, localeable} from '../../locale';
export default class CustomYearsView extends YearsView { export class CustomYearsView extends YearsView {
props: { props: {
viewDate: moment.Moment; viewDate: moment.Moment;
subtractTime: ( subtractTime: (
@ -17,7 +18,7 @@ export default class CustomYearsView extends YearsView {
toSelected?: moment.Moment toSelected?: moment.Moment
) => () => void; ) => () => void;
showView: (view: string) => () => void; showView: (view: string) => () => void;
}; } & LocaleProps;
renderYears: (year: number) => JSX.Element; renderYears: (year: number) => JSX.Element;
renderYear = (props: any, year: number) => { renderYear = (props: any, year: number) => {
return ( return (
@ -29,6 +30,7 @@ export default class CustomYearsView extends YearsView {
render() { render() {
let year = this.props.viewDate.year(); let year = this.props.viewDate.year();
year = year - (year % 10); year = year - (year % 10);
const __ = this.props.translate;
return ( return (
<div className="rdtYears"> <div className="rdtYears">
@ -42,7 +44,7 @@ export default class CustomYearsView extends YearsView {
« «
</th> </th>
<th className="rdtSwitch"> <th className="rdtSwitch">
{year}-{year + 9} {__('{{from}}年-{{to}}年', {from: year, to: year + 9})}
</th> </th>
<th className="rdtNext" onClick={this.props.addTime(10, 'years')}> <th className="rdtNext" onClick={this.props.addTime(10, 'years')}>
» »
@ -57,3 +59,5 @@ export default class CustomYearsView extends YearsView {
); );
} }
} }
export default localeable(CustomYearsView as any);

View File

@ -24,7 +24,6 @@ import {
SchemaNode, SchemaNode,
Schema, Schema,
Action, Action,
Omit,
RendererData RendererData
} from './types'; } from './types';
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
@ -34,11 +33,24 @@ import omit from 'lodash/omit';
import difference from 'lodash/difference'; import difference from 'lodash/difference';
import isPlainObject from 'lodash/isPlainObject'; import isPlainObject from 'lodash/isPlainObject';
import Scoped from './Scoped'; import Scoped from './Scoped';
import {getTheme, ThemeInstance, ClassNamesFn, ThemeContext} from './theme'; import {
getTheme,
ThemeInstance,
ClassNamesFn,
ThemeContext,
ThemeProps
} from './theme';
import find from 'lodash/find'; import find from 'lodash/find';
import Alert from './components/Alert2'; import Alert from './components/Alert2';
import {LazyComponent} from './components'; import {LazyComponent} from './components';
import ImageGallery from './components/ImageGallery'; import ImageGallery from './components/ImageGallery';
import {
TranslateFn,
getDefaultLocale,
makeTranslator,
LocaleContext,
LocaleProps
} from './locale';
export interface TestFunc { export interface TestFunc {
( (
@ -98,11 +110,9 @@ export interface RendererEnv {
[propName: string]: any; [propName: string]: any;
} }
export interface RendererProps { export interface RendererProps extends ThemeProps, LocaleProps {
render: (region: string, node: SchemaNode, props?: any) => JSX.Element; render: (region: string, node: SchemaNode, props?: any) => JSX.Element;
env: RendererEnv; env: RendererEnv;
classPrefix: string;
classnames: ClassNamesFn;
$path: string; // 当前组件所在的层级信息 $path: string; // 当前组件所在的层级信息
store?: IIRendererStore; store?: IIRendererStore;
syncSuperStore?: boolean; syncSuperStore?: boolean;
@ -193,7 +203,7 @@ export function filterSchema(
} }
export function Renderer(config: RendererBasicConfig) { export function Renderer(config: RendererBasicConfig) {
return function<T extends RendererComponent>(component: T): T { return function <T extends RendererComponent>(component: T): T {
const renderer = registerRenderer({ const renderer = registerRenderer({
...config, ...config,
component: component component: component
@ -215,9 +225,7 @@ export function registerRenderer(config: RendererConfig): RendererConfig {
if (~rendererNames.indexOf(config.name)) { if (~rendererNames.indexOf(config.name)) {
throw new Error( throw new Error(
`The renderer with name "${ `The renderer with name "${config.name}" has already exists, please try another name!`
config.name
}" has already exists, please try another name!`
); );
} }
@ -322,6 +330,8 @@ export interface RootRendererProps {
env: RendererEnv; env: RendererEnv;
theme: string; theme: string;
pathPrefix?: string; pathPrefix?: string;
locale?: string;
translate?: TranslateFn;
[propName: string]: any; [propName: string]: any;
} }
@ -362,6 +372,8 @@ export class RootRenderer extends React.Component<RootRendererProps> {
pathPrefix, pathPrefix,
location, location,
data, data,
locale,
translate,
...rest ...rest
} = this.props; } = this.props;
@ -385,28 +397,32 @@ export class RootRenderer extends React.Component<RootRendererProps> {
return ( return (
<RootStoreContext.Provider value={rootStore}> <RootStoreContext.Provider value={rootStore}>
<ThemeContext.Provider value={this.props.theme || 'default'}> <ThemeContext.Provider value={this.props.theme || 'default'}>
<ImageGallery modalContainer={env.getModalContainer}> <LocaleContext.Provider value={this.props.locale!}>
{ <ImageGallery modalContainer={env.getModalContainer}>
renderChild( {
pathPrefix || '', renderChild(
isPlainObject(schema) pathPrefix || '',
? { isPlainObject(schema)
type: 'page', ? {
...(schema as Schema) type: 'page',
} ...(schema as any)
: schema, }
{ : schema,
...rest, {
resolveDefinitions: this.resolveDefinitions, ...rest,
location: location, resolveDefinitions: this.resolveDefinitions,
data: finalData, location: location,
env, data: finalData,
classnames: theme.classnames, env,
classPrefix: theme.classPrefix classnames: theme.classnames,
} classPrefix: theme.classPrefix,
) as JSX.Element locale,
} translate
</ImageGallery> }
) as JSX.Element
}
</ImageGallery>
</LocaleContext.Provider>
</ThemeContext.Provider> </ThemeContext.Provider>
</RootStoreContext.Provider> </RootStoreContext.Provider>
); );
@ -634,7 +650,7 @@ export function HocStoreFactory(renderer: {
storeType: string; storeType: string;
extendsData?: boolean; extendsData?: boolean;
}): any { }): any {
return function<T extends React.ComponentType<RendererProps>>(Component: T) { return function <T extends React.ComponentType<RendererProps>>(Component: T) {
type Props = Omit< type Props = Omit<
RendererProps, RendererProps,
'store' | 'data' | 'dataUpdatedAt' | 'scope' 'store' | 'data' | 'dataUpdatedAt' | 'scope'
@ -646,8 +662,9 @@ export function HocStoreFactory(renderer: {
@observer @observer
class StoreFactory extends React.Component<Props> { class StoreFactory extends React.Component<Props> {
static displayName = `WithStore(${Component.displayName || static displayName = `WithStore(${
Component.name})`; Component.displayName || Component.name
})`;
static ComposedComponent = Component; static ComposedComponent = Component;
static contextType = RootStoreContext; static contextType = RootStoreContext;
store: IIRendererStore; store: IIRendererStore;
@ -839,7 +856,7 @@ export function HocStoreFactory(renderer: {
return ( return (
<Component <Component
{ {
...rest as any /* todo */ ...(rest as any) /* todo */
} }
{...exprProps} {...exprProps}
ref={this.refFn} ref={this.refFn}
@ -956,6 +973,8 @@ export function render(
const env = getEnv(store); const env = getEnv(store);
const theme = props.theme || options.theme || 'default'; const theme = props.theme || options.theme || 'default';
env.theme = getTheme(theme); env.theme = getTheme(theme);
const locale = props.locale || getDefaultLocale();
const translate = props.translate || makeTranslator(locale);
return ( return (
<ScopedRootRenderer <ScopedRootRenderer
@ -965,6 +984,8 @@ export function render(
rootStore={store} rootStore={store}
env={env} env={env}
theme={theme} theme={theme}
locale={locale}
translate={translate}
/> />
); );
} }

119
src/locale.tsx Normal file
View File

@ -0,0 +1,119 @@
// 多语言支持
import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';
import {resolveVariable} from './utils/tpl-builtin';
export type TranslateFn<T = any> = (str: T, data?: object) => T;
interface LocaleConfig {
[propsName: string]: string;
}
let defaultLocale: string = 'zh-cn';
const locales: {
[propName: string]: LocaleConfig;
} = {};
export function register(name: string, config: LocaleConfig) {
locales[name] = config;
}
const fns: {
[propName: string]: TranslateFn;
} = {};
function format(str: string, data?: object) {
return str.replace(/(\\)?\{\{([\s\S]+?)\}\}/g, (_, escape, key) => {
if (escape) {
return _.substring(1);
}
return resolveVariable(key, data || {});
});
}
export function makeTranslator(locale?: string): TranslateFn {
if (locale && fns[locale]) {
return fns[locale];
}
const fn = (str: any, ...args: any[]) => {
if (!str || typeof str !== 'string') {
return str;
}
const dict = locales[locale!] || locales[defaultLocale];
return format(dict?.[str] || str, ...args);
};
locale && (fns[locale] = fn);
return fn;
}
export function getDefaultLocale() {
return defaultLocale;
}
export function setDefaultLocale(loacle: string) {
defaultLocale = loacle;
}
export interface LocaleProps {
locale: string;
translate: TranslateFn;
}
export const LocaleContext = React.createContext('');
export function localeable<
T extends React.ComponentType<React.ComponentProps<T> & LocaleProps>
>(ComposedComponent: T) {
type OuterProps = JSX.LibraryManagedAttributes<
T,
Omit<React.ComponentProps<T>, keyof LocaleProps>
> & {
locale?: string;
translate?: (str: string, ...args: any[]) => string;
};
const result = hoistNonReactStatic(
class extends React.Component<OuterProps> {
static displayName = `I18N(${
ComposedComponent.displayName || ComposedComponent.name
})`;
static contextType = LocaleContext;
static ComposedComponent = ComposedComponent;
render() {
const locale: string =
this.props.locale || this.context || defaultLocale;
const translate = this.props.translate || makeTranslator(locale);
const injectedProps: {
locale: string;
translate: TranslateFn;
} = {
locale,
translate: translate!
};
return (
<LocaleContext.Provider value={locale}>
<ComposedComponent
{...(this.props as JSX.LibraryManagedAttributes<
T,
React.ComponentProps<T>
>)}
{...injectedProps}
/>
</LocaleContext.Provider>
);
}
},
ComposedComponent
);
return result as typeof result & {
ComposedComponent: T;
};
}

153
src/locale/en.ts Normal file
View File

@ -0,0 +1,153 @@
import {register} from '../locale';
register('en', {
'确认': 'Confirm',
'取消': 'Cancel',
'YYYY年': 'YYYY',
'MM月': 'MMM',
'{{from}}年-{{to}}年': '{{from}} - {{to}}',
'请选择日期': 'Select Date',
'请选择日期以及时间': 'Select Datetime',
'请选择时间': 'Select Time',
'系统消息': 'System Info',
'加载中': 'Loading',
'点击刷新重新加载': 'Click to refresh',
'请先选择左侧数据': 'Select from left first.',
'请选择颜色': 'Select color',
'现在': 'Now',
'今天': 'Today',
'昨天': 'Yesterday',
'本周一': 'Monday',
'本月初': 'Earlier this month',
'上个月初': 'Earlier last month',
'上个季节初': 'Earlier last quarter',
'明天': 'Tomorrow',
'本周日': 'Sunday',
'本月底': 'last day of this month',
'{{hours}}小时前': '{{hours}} hour(s) ago',
'{{hours}}小时后': '{{hours}} hour(s) after',
'{{days}}天前': '{{days}} day(s) ago',
'{{days}}天后': '{{days}} day(s) after',
'{{weeks}}周前': '{{weeks}} week(s) ago',
'{{weeks}}周后': '{{weeks}} week(s) after',
'{{months}}月前': '{{months}} month(s) ago',
'{{months}}月后': '{{months}} month(s) after',
'{{quarters}}季度前': '{{quarters}} quarter(s) ago',
'{{quarters}}季度后': '{{quarters}} quarter(s) after',
' 至 ': ' to ',
'最近1天': 'Last day',
'最近7天': 'Last 7 days',
'最近90天': 'Last 90 days',
'上周': 'Last week',
'本月': 'This month',
'上个月': 'Last month',
'上个季节': 'Last quarter',
'本季度': 'This quarter',
'请选择日期范围': 'Select Daterange',
'关闭': 'Close',
'暂无选项': 'No options',
'请选择位置': 'Pick location',
'无': 'None',
'没有数据': 'No data',
'请先选择数据': 'Select data first',
'请选择': 'Select',
'全选': 'Check all',
'搜索结果': 'Search result',
'清空': 'Clear',
'当前选择': 'Selected',
'添加一级节点': 'Add root node',
'添加孩子节点': 'Add child',
'编辑该节点': 'Edit this node',
'移除该节点': 'Remove this node',
'请输入': 'Enter',
'请输入关键字': 'Enter keywords',
'新增选项': 'New option',
'请输入街道信息': 'Enter street info',
'删除': 'Delete',
'新增': 'New',
'新增一条数据': 'Add a data',
'类型': 'Type',
'拖拽排序': 'Drag to sort',
'删除失败': 'Delete failed',
'确认要删除?': 'Are you sure you want to delete?',
'组合表单成员数量不够,低于设定的最小{{minLenth}}个,请添加更多的成员。':
'The number of combined form members is not enough. It is lower than the minimum {{minlenth}} set. Please add more members.',
'组合表单成员数量超出,超出设定的最大{{maxLength}}个,请删除多余的成员。':
'The number of combined form members exceeds the set maximum of {{MaxLength}}}. Please delete the extra members.',
'子表单验证失败,请仔细检查': 'Validate failed, please check this Subform.',
'成员{{index}}': 'Member {{index}}',
'清空数据': 'Clear data',
'您选择的文件 {{filename}} 大小为 {{actualSize}} 超出了最大为 {{maxSize}} 的限制,请重新选择。':
'The file {{filename}} you selected has a size of {actualsize}} which exceeds the maximum limit of {{maxsize}}. Please select again.',
'您添加的文件{{files}}不符合类型的`{{accept}}`的设定,请仔细检查。':
'The file you added {{files}} does not match the setting of the type `{{accept}}`. Please check it carefully.',
'把文件拖到这,然后松完成添加!':
'Drag the file here, then release to finish adding!',
'把图片拖到这,然后松开完成添加!':
'Drag the picture here, then release to finish adding!',
'重新上传': 'Repick',
'重试上传': 'Retry',
'继续添加': 'Continue add',
'上传文件': 'Upload file',
'移除': 'Remove',
'暂停上传': 'Pause uplaod',
'开始上传': 'Start upload',
'已成功上传{{uploaded}}个文件,{{failed}}个文件上传失败,':
'Successfully uploaded {{uploaded}} files, failed to upload {{failed}} files,',
'失败文件': 'Failed files.',
'高度{{height}}px': 'height: {{height}}px',
'宽度{{width}}px': 'width: {{width}}px',
'尺寸({{width}} x {{height}}': 'size: ({{width}}px x {{height}}px)',
'您选择的图片不符合尺寸要求, 请上传{{info}}的图片':
'The picture you selected does not meet the size requirements. Please upload the picture of {{info}}',
'您选择的图片不符合尺寸要求, 请上传不要超过{{info}}的图片':
'The picture you selected does not meet the size requirements. Please upload a picture that does not exceed {{info}}`.',
'您选择的图片不符合尺寸要求, 请上传不要小于{{info}}的图片':
'The picture you selected does not meet the size requirements. Please upload a picture no less than {{info}}',
'您选择的图片不符合尺寸要求, 请上传尺寸比率为 {{ratio}} 的图片':
'The picture you selected does not meet the size requirements. Please upload the picture with the size ratio of ${ration}',
'文件上传失败请重试': 'File upload failed, please try again',
'文件上传中': 'File uploading',
'查看大图': 'Zoom In',
'裁剪图片': 'Crop picture',
'当前状态支持从剪切板中粘贴图片文件。':
'The current state supports pasting picture files from the clipboard.',
'表单': 'Form',
'提交': 'Submit',
'初始化失败': 'Initialization failed',
'保存成功': 'Saved successfully',
'保存失败': 'Save failed',
'依赖的部分字段没有通过验证,请注意填写!':
'Some of the dependent fields failed to pass the verification, please fill in!',
'请输入名称': 'Please enter a name',
'编辑{{label}}': 'Edit {{label}}',
'每': 'Per',
'编辑详情': 'Detail',
'删除当前行': 'Delete current row',
'操作': 'Operation',
'新增一行': 'Add a row',
'暂无标签': 'No tag yet',
'新增:{{label}}': 'New {{label}}',
'顶级': 'Root',
'点击复制': 'Copy',
'{{page}}/{{lastPage}} 总共:{{total}} 项。':
'{{page}} of {{lastPage}} total: {{total}}.',
'每页显示': 'Per page',
'加载更多': 'Load more',
'筛选': 'Filter',
'搜索': 'Search',
'日期无效': 'Invalid date',
'关闭弹窗': 'Close',
'链接': 'Link',
'当前有 {{modified}} 条记录修改了内容, 但并没有提交。请选择:':
'There are currently {{modified}} records that have modified the contents, but they have not been submitted. Please select:',
'放弃': 'Give up',
'当前有 {{moved}} 条记录修改了顺序, 但并没有提交。请选择:':
'There are currently {{moved}} records that have changed the order, but have not been committed. Please select:',
'点击开始排序': 'Click to start sorting',
'请拖动左边的按钮进行排序': 'Please drag the button on the left to sort',
'排序': 'Sort',
'正序': 'Asc',
'降序': 'Desc'
});

View File

@ -40,7 +40,6 @@ const ActionProps = [
]; ];
import {filterContents} from './Remark'; import {filterContents} from './Remark';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable} from '../theme';
import {Omit} from '../types';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
export interface ActionProps { export interface ActionProps {

View File

@ -1335,16 +1335,20 @@ export default class CRUD extends React.Component<CRUDProps, any> {
} }
renderStatistics() { renderStatistics() {
const {store, classnames: cx} = this.props; const {store, classnames: cx, translate: __} = this.props;
if (store.lastPage <= 1) { if (store.lastPage <= 1) {
return null; return null;
} }
return ( return (
<div className={cx('Crud-statistics')}>{`${ <div className={cx('Crud-statistics')}>
store.page + '/' + store.lastPage {__('{{page}}/{{lastPage}} 总共:{{total}} 项。', {
}${store.total}`}</div> page: store.page,
lastPage: store.lastPage,
total: store.total
})}
</div>
); );
} }
@ -1353,7 +1357,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
store, store,
perPageAvailable, perPageAvailable,
classnames: cx, classnames: cx,
classPrefix: ns classPrefix: ns,
translate: __
} = this.props; } = this.props;
const items = childProps.items; const items = childProps.items;
@ -1371,11 +1376,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
return ( return (
<div className={cx('Crud-pageSwitch')}> <div className={cx('Crud-pageSwitch')}>
{__('每页显示')}
<Select <Select
classPrefix={ns} classPrefix={ns}
searchable={false} searchable={false}
placeholder="请选择.." placeholder={__('请选择')}
options={perPages} options={perPages}
value={store.perPage + ''} value={store.perPage + ''}
onChange={(value: any) => this.handleChangePage(1, value.value)} onChange={(value: any) => this.handleChangePage(1, value.value)}
@ -1386,7 +1391,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
} }
renderLoadMore() { renderLoadMore() {
const {store, classPrefix: ns, classnames: cx} = this.props; const {store, classPrefix: ns, classnames: cx, translate: __} = this.props;
const {page, lastPage} = store; const {page, lastPage} = store;
return page < lastPage ? ( return page < lastPage ? (
@ -1399,7 +1404,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
size="sm" size="sm"
className="btn-primary" className="btn-primary"
> >
{__('加载更多')}
</Button> </Button>
</div> </div>
) : ( ) : (
@ -1408,7 +1413,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
} }
renderFilterToggler() { renderFilterToggler() {
const {store, classnames: cx} = this.props; const {store, classnames: cx, translate: __} = this.props;
if (!store.filterTogggable) { if (!store.filterTogggable) {
return null; return null;
@ -1422,7 +1427,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
})} })}
> >
<i className="fa fa-sliders m-r-sm" /> <i className="fa fa-sliders m-r-sm" />
{__('筛选')}
</button> </button>
); );
} }
@ -1574,7 +1579,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
classnames: cx, classnames: cx,
labelField, labelField,
labelTpl, labelTpl,
primaryField primaryField,
translate: __
} = this.props; } = this.props;
if (!store.selectedItems.length) { if (!store.selectedItems.length) {
@ -1587,7 +1593,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
{store.selectedItems.map((item, index) => ( {store.selectedItems.map((item, index) => (
<div key={index} className={cx(`Crud-value`)}> <div key={index} className={cx(`Crud-value`)}>
<span <span
data-tooltip="删除" data-tooltip={__('删除')}
data-position="bottom" data-position="bottom"
className={cx('Crud-valueIcon')} className={cx('Crud-valueIcon')}
onClick={this.unSelectItem.bind(this, item, index)} onClick={this.unSelectItem.bind(this, item, index)}
@ -1605,7 +1611,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
</div> </div>
))} ))}
<a onClick={this.clearSelection} className={cx('Crud-selectionClear')}> <a onClick={this.clearSelection} className={cx('Crud-selectionClear')}>
{__('清空')}
</a> </a>
</div> </div>
); );
@ -1633,6 +1639,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
keepItemSelectionOnPageChange, keepItemSelectionOnPageChange,
onAction, onAction,
popOverContainer, popOverContainer,
translate: __,
...rest ...rest
} = this.props; } = this.props;
@ -1646,9 +1653,9 @@ export default class CRUD extends React.Component<CRUDProps, any> {
? render( ? render(
'filter', 'filter',
{ {
title: '条件过滤', title: __('条件过滤'),
mode: 'inline', mode: 'inline',
submitText: '搜索', submitText: __('搜索'),
...filter, ...filter,
type: 'form', type: 'form',
api: null api: null

View File

@ -177,9 +177,9 @@ export default class Cards extends React.Component<GridProps, object> {
} }
componentDidMount() { componentDidMount() {
let parent: HTMLElement | Window | null = getScrollParent(findDOMNode( let parent: HTMLElement | Window | null = getScrollParent(
this findDOMNode(this) as HTMLElement
) as HTMLElement); );
if (!parent || parent === document.body) { if (!parent || parent === document.body) {
parent = window; parent = window;
} }
@ -282,9 +282,7 @@ export default class Cards extends React.Component<GridProps, object> {
const afixedDom = dom.querySelector(`.${ns}Cards-fixedTop`) as HTMLElement; const afixedDom = dom.querySelector(`.${ns}Cards-fixedTop`) as HTMLElement;
this.body.offsetWidth && this.body.offsetWidth &&
(afixedDom.style.cssText = `top: ${offsetY}px;width: ${ (afixedDom.style.cssText = `top: ${offsetY}px;width: ${this.body.offsetWidth}px;`);
this.body.offsetWidth
}px;`);
affixed ? afixedDom.classList.add('in') : afixedDom.classList.remove('in'); affixed ? afixedDom.classList.add('in') : afixedDom.classList.remove('in');
// store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY); // store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY);
} }
@ -520,9 +518,7 @@ export default class Cards extends React.Component<GridProps, object> {
<div className={cx('Cards-heading')}> <div className={cx('Cards-heading')}>
{store.modified && !hideQuickSaveBtn ? ( {store.modified && !hideQuickSaveBtn ? (
<span> <span>
{`当前有 ${ {`当前有 ${store.modified} 条记录修改了内容, 但并没有提交。请选择:`}
store.modified
} , :`}
<button <button
type="button" type="button"
className={cx('Button Button--xs Button--success m-l-sm')} className={cx('Button Button--xs Button--success m-l-sm')}
@ -753,7 +749,8 @@ export default class Cards extends React.Component<GridProps, object> {
masonryLayout, masonryLayout,
itemsClassName, itemsClassName,
classnames: cx, classnames: cx,
data data,
translate: __
} = this.props; } = this.props;
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。 this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
@ -840,7 +837,7 @@ export default class Cards extends React.Component<GridProps, object> {
</div> </div>
) : ( ) : (
<div className={cx('Cards-placeholder')}> <div className={cx('Cards-placeholder')}>
{filter(placeholder, data)} {filter(__(placeholder), data)}
</div> </div>
)} )}

View File

@ -38,7 +38,8 @@ export const HocCopyable = () => (Component: React.ComponentType<any>): any => {
className, className,
data, data,
noHoc, noHoc,
classnames: cx classnames: cx,
translate: __
} = this.props; } = this.props;
if (copyable && !noHoc) { if (copyable && !noHoc) {
@ -55,7 +56,7 @@ export const HocCopyable = () => (Component: React.ComponentType<any>): any => {
<Component {...this.props} wrapperComponent={''} noHoc /> <Component {...this.props} wrapperComponent={''} noHoc />
<i <i
key="edit-btn" key="edit-btn"
data-tooltip="点击复制" data-tooltip={__('点击复制')}
className={cx('Field-copyBtn fa fa-clipboard')} className={cx('Field-copyBtn fa fa-clipboard')}
onClick={this.handleClick.bind(this, content)} onClick={this.handleClick.bind(this, content)}
/> />

View File

@ -55,7 +55,8 @@ export class DateField extends React.Component<DateProps, DateState> {
placeholder, placeholder,
fromNow, fromNow,
className, className,
classnames: cx classnames: cx,
translate: __
} = this.props; } = this.props;
let viewValue: React.ReactNode = ( let viewValue: React.ReactNode = (
<span className="text-muted">{placeholder}</span> <span className="text-muted">{placeholder}</span>
@ -77,7 +78,7 @@ export class DateField extends React.Component<DateProps, DateState> {
} }
viewValue = !viewValue ? ( viewValue = !viewValue ? (
<span className="text-danger"></span> <span className="text-danger">{__('日期无效')}</span>
) : ( ) : (
viewValue viewValue
); );

View File

@ -122,7 +122,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
} }
buildActions(): Array<Action> { buildActions(): Array<Action> {
const {actions, confirm} = this.props; const {actions, confirm, translate: __} = this.props;
if (typeof actions !== 'undefined') { if (typeof actions !== 'undefined') {
return actions; return actions;
@ -132,14 +132,14 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
ret.push({ ret.push({
type: 'button', type: 'button',
actionType: 'cancel', actionType: 'cancel',
label: '取消' label: __('取消')
}); });
if (confirm) { if (confirm) {
ret.push({ ret.push({
type: 'button', type: 'button',
actionType: 'confirm', actionType: 'confirm',
label: '确认', label: __('确认'),
primary: true primary: true
}); });
} }
@ -380,7 +380,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
showCloseButton, showCloseButton,
env, env,
classnames: cx, classnames: cx,
classPrefix classPrefix,
translate: __
} = this.props; } = this.props;
// console.log('Render Dialog'); // console.log('Render Dialog');
@ -408,7 +409,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
<div className={cx('Modal-header', headerClassName)}> <div className={cx('Modal-header', headerClassName)}>
{showCloseButton !== false && !store.loading ? ( {showCloseButton !== false && !store.loading ? (
<a <a
data-tooltip="关闭弹窗" data-tooltip={__('关闭弹窗')}
data-position="left" data-position="left"
onClick={this.handleSelfClose} onClick={this.handleSelfClose}
className={cx('Modal-close')} className={cx('Modal-close')}
@ -417,14 +418,14 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
</a> </a>
) : null} ) : null}
<div className={cx('Modal-title')}> <div className={cx('Modal-title')}>
{filter(title, store.formData)} {filter(__(title), store.formData)}
</div> </div>
</div> </div>
) : title ? ( ) : title ? (
<div className={cx('Modal-header', headerClassName)}> <div className={cx('Modal-header', headerClassName)}>
{showCloseButton !== false && !store.loading ? ( {showCloseButton !== false && !store.loading ? (
<a <a
data-tooltip="关闭弹窗" data-tooltip={__('关闭弹窗')}
onClick={this.handleSelfClose} onClick={this.handleSelfClose}
className={cx('Modal-close')} className={cx('Modal-close')}
> >
@ -437,7 +438,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
</div> </div>
) : showCloseButton !== false && !store.loading ? ( ) : showCloseButton !== false && !store.loading ? (
<a <a
data-tooltip="关闭弹窗" data-tooltip={__('关闭弹窗')}
onClick={this.handleSelfClose} onClick={this.handleSelfClose}
className={cx('Modal-close')} className={cx('Modal-close')}
> >

View File

@ -113,7 +113,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
} }
buildActions(): Array<Action> { buildActions(): Array<Action> {
const {actions, confirm} = this.props; const {actions, confirm, translate: __} = this.props;
if (typeof actions !== 'undefined') { if (typeof actions !== 'undefined') {
return actions; return actions;
@ -123,14 +123,14 @@ export default class Drawer extends React.Component<DrawerProps, object> {
ret.push({ ret.push({
type: 'button', type: 'button',
actionType: 'close', actionType: 'close',
label: '取消' label: __('取消')
}); });
if (confirm) { if (confirm) {
ret.push({ ret.push({
type: 'button', type: 'button',
actionType: 'confirm', actionType: 'confirm',
label: '确认', label: __('确认'),
primary: true primary: true
}); });
} }

View File

@ -165,7 +165,8 @@ export default class CheckboxesControl extends React.Component<
labelClassName, labelClassName,
creatable, creatable,
addApi, addApi,
createBtnLabel createBtnLabel,
translate: __
} = this.props; } = this.props;
let body: Array<React.ReactNode> = []; let body: Array<React.ReactNode> = [];
@ -217,13 +218,13 @@ export default class CheckboxesControl extends React.Component<
{body && body.length ? ( {body && body.length ? (
body body
) : ( ) : (
<span className={`Form-placeholder`}>{placeholder}</span> <span className={`Form-placeholder`}>{__(placeholder)}</span>
)} )}
{(creatable || addApi) && !disabled ? ( {(creatable || addApi) && !disabled ? (
<a className={cx('Checkboxes-addBtn')} onClick={this.handleAddClick}> <a className={cx('Checkboxes-addBtn')} onClick={this.handleAddClick}>
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
{createBtnLabel} {__(createBtnLabel)}
</a> </a>
) : null} ) : null}
</div> </div>

View File

@ -5,8 +5,9 @@ import {ClassNamesFn, themeable} from '../../theme';
import {Select} from '../../components'; import {Select} from '../../components';
import {autobind} from '../../utils/helper'; import {autobind} from '../../utils/helper';
import {Option} from './Options'; import {Option} from './Options';
import {localeable, LocaleProps} from '../../locale';
export interface CityPickerProps { export interface CityPickerProps extends LocaleProps {
value: any; value: any;
onChange: (value: any) => void; onChange: (value: any) => void;
extractValue: boolean; extractValue: boolean;
@ -226,7 +227,8 @@ export class CityPicker extends React.Component<
disabled, disabled,
allowCity, allowCity,
allowDistrict, allowDistrict,
allowStreet allowStreet,
translate: __
} = this.props; } = this.props;
const {provinceCode, cityCode, districtCode, street} = this.state; const {provinceCode, cityCode, districtCode, street} = this.state;
@ -290,7 +292,7 @@ export class CityPicker extends React.Component<
value={street} value={street}
onChange={this.handleStreetChange} onChange={this.handleStreetChange}
onBlur={this.handleStreetEnd} onBlur={this.handleStreetEnd}
placeholder="请输入街道信息" placeholder={__('请输入街道信息')}
/> />
) : null} ) : null}
</div> </div>
@ -298,7 +300,7 @@ export class CityPicker extends React.Component<
} }
} }
const ThemedCity = themeable(CityPicker); const ThemedCity = themeable(localeable(CityPicker));
export default ThemedCity; export default ThemedCity;
export interface LocationControlProps extends FormControlProps { export interface LocationControlProps extends FormControlProps {

View File

@ -321,7 +321,8 @@ export default class ComboControl extends React.Component<ComboProps> {
deleteApi, deleteApi,
deleteConfirmText, deleteConfirmText,
data, data,
env env,
translate: __
} = this.props; } = this.props;
if (disabled) { if (disabled) {
@ -333,7 +334,7 @@ export default class ComboControl extends React.Component<ComboProps> {
if (isEffectiveApi(deleteApi, ctx)) { if (isEffectiveApi(deleteApi, ctx)) {
const confirmed = await env.confirm( const confirmed = await env.confirm(
deleteConfirmText ? filter(deleteConfirmText, ctx) : '确认要删除?' deleteConfirmText ? filter(deleteConfirmText, ctx) : __('确认要删除?')
); );
if (!confirmed) { if (!confirmed) {
// 如果不确认,则跳过! // 如果不确认,则跳过!
@ -343,7 +344,7 @@ export default class ComboControl extends React.Component<ComboProps> {
const result = await env.fetcher(deleteApi as Api, ctx); const result = await env.fetcher(deleteApi as Api, ctx);
if (!result.ok) { if (!result.ok) {
env.notify('error', '删除失败'); env.notify('error', __('删除失败'));
return; return;
} }
} }
@ -480,25 +481,34 @@ export default class ComboControl extends React.Component<ComboProps> {
} }
validate(): any { validate(): any {
const {value, minLength, maxLength, messages, nullable} = this.props; const {
value,
minLength,
maxLength,
messages,
nullable,
translate: __
} = this.props;
if (minLength && (!Array.isArray(value) || value.length < minLength)) { if (minLength && (!Array.isArray(value) || value.length < minLength)) {
return ( return __(
(messages && messages.minLengthValidateFailed) || (messages && messages.minLengthValidateFailed) ||
`组合表单成员数量不够,低于设定的最小${minLength}个,请添加更多的成员。` '组合表单成员数量不够,低于设定的最小{{minLenth}}个,请添加更多的成员。',
{minLength}
); );
} else if (maxLength && Array.isArray(value) && value.length > maxLength) { } else if (maxLength && Array.isArray(value) && value.length > maxLength) {
return ( return __(
(messages && messages.maxLengthValidateFailed) || (messages && messages.maxLengthValidateFailed) ||
`组合表单成员数量超出,超出设定的最大${maxLength}个,请删除多余的成员。` '组合表单成员数量超出,超出设定的最大{{maxLength}}个,请删除多余的成员。',
{maxLength}
); );
} else if (this.subForms.length && (!nullable || value)) { } else if (this.subForms.length && (!nullable || value)) {
return Promise.all(this.subForms.map(item => item.validate())).then( return Promise.all(this.subForms.map(item => item.validate())).then(
values => { values => {
if (~values.indexOf(false)) { if (~values.indexOf(false)) {
return ( return __(
(messages && messages.validateFailed) || (messages && messages.validateFailed) ||
'子表单验证失败,请仔细检查' '子表单验证失败,请仔细检查'
); );
} }
@ -698,9 +708,8 @@ export default class ComboControl extends React.Component<ComboProps> {
} }
renderPlaceholder() { renderPlaceholder() {
return ( const {placeholder, translate: __} = this.props;
<span className="text-muted">{this.props.placeholder || '没有数据'}</span> return <span className="text-muted">{__(placeholder || '没有数据')}</span>;
);
} }
renderTabsMode() { renderTabsMode() {
@ -725,7 +734,8 @@ export default class ComboControl extends React.Component<ComboProps> {
deleteIcon, deleteIcon,
tabsLabelTpl, tabsLabelTpl,
conditions, conditions,
changeImmediately changeImmediately,
translate: __
} = this.props; } = this.props;
let controls = this.props.controls; let controls = this.props.controls;
@ -762,7 +772,7 @@ export default class ComboControl extends React.Component<ComboProps> {
{ {
type: 'dropdown-button', type: 'dropdown-button',
icon: addIcon, icon: addIcon,
label: addButtonText || '新增', label: __(addButtonText || '新增'),
level: 'info', level: 'info',
size: 'sm', size: 'sm',
closeOnClick: true closeOnClick: true
@ -781,10 +791,10 @@ export default class ComboControl extends React.Component<ComboProps> {
<a <a
onClick={this.addItem} onClick={this.addItem}
data-position="left" data-position="left"
data-tooltip="新增一条数据" data-tooltip={__('新增一条数据')}
> >
{addIcon ? <i className={cx('m-r-xs', addIcon)} /> : null} {addIcon ? <i className={cx('m-r-xs', addIcon)} /> : null}
<span>{addButtonText || '新增'}</span> <span>{__(addButtonText || '新增')}</span>
</a> </a>
) )
) : null} ) : null}
@ -808,7 +818,7 @@ export default class ComboControl extends React.Component<ComboProps> {
className={cx( className={cx(
`Combo-toolbarBtn ${!store.removable ? 'is-disabled' : ''}` `Combo-toolbarBtn ${!store.removable ? 'is-disabled' : ''}`
)} )}
data-tooltip="删除" data-tooltip={__('删除')}
data-position="bottom" data-position="bottom"
> >
{deleteIcon ? ( {deleteIcon ? (
@ -837,7 +847,11 @@ export default class ComboControl extends React.Component<ComboProps> {
return ( return (
<Tab <Tab
title={filter(tabsLabelTpl || '成员${index|plus}', data)} title={filter(
tabsLabelTpl ||
__('成员{{index}}', {index: (data as any).index + 1}),
data
)}
key={this.keys[index] || (this.keys[index] = guid())} key={this.keys[index] || (this.keys[index] = guid())}
toolbar={toolbar} toolbar={toolbar}
eventKey={index} eventKey={index}
@ -847,7 +861,7 @@ export default class ComboControl extends React.Component<ComboProps> {
> >
{condition && typeSwitchable !== false ? ( {condition && typeSwitchable !== false ? (
<div className={cx('Combo-itemTag')}> <div className={cx('Combo-itemTag')}>
<label></label> <label>{__('类型')}</label>
<Select <Select
onChange={this.handleComboTypeChange.bind(this, index)} onChange={this.handleComboTypeChange.bind(this, index)}
options={(conditions as Array<Condition>).map(item => ({ options={(conditions as Array<Condition>).map(item => ({
@ -888,7 +902,7 @@ export default class ComboControl extends React.Component<ComboProps> {
) )
) : ( ) : (
<Alert2 level="warning" className="m-b-none"> <Alert2 level="warning" className="m-b-none">
{__('数据非法,或者数据已失效,请移除')}
</Alert2> </Alert2>
)} )}
</div> </div>
@ -931,7 +945,8 @@ export default class ComboControl extends React.Component<ComboProps> {
conditions, conditions,
lazyLoad, lazyLoad,
changeImmediately, changeImmediately,
placeholder placeholder,
translate: __
} = this.props; } = this.props;
let controls = this.props.controls; let controls = this.props.controls;
@ -990,7 +1005,7 @@ export default class ComboControl extends React.Component<ComboProps> {
!store.removable ? 'is-disabled' : '' !store.removable ? 'is-disabled' : ''
}` }`
)} )}
data-tooltip="删除" data-tooltip={__('删除')}
data-position="bottom" data-position="bottom"
> >
{deleteIcon ? ( {deleteIcon ? (
@ -1029,7 +1044,7 @@ export default class ComboControl extends React.Component<ComboProps> {
<div className={cx('Combo-itemDrager')}> <div className={cx('Combo-itemDrager')}>
<a <a
key="drag" key="drag"
data-tooltip="拖拽排序" data-tooltip={__('拖拽排序')}
data-position="bottom" data-position="bottom"
> >
{dragIcon ? ( {dragIcon ? (
@ -1042,7 +1057,7 @@ export default class ComboControl extends React.Component<ComboProps> {
) : null} ) : null}
{condition && typeSwitchable !== false ? ( {condition && typeSwitchable !== false ? (
<div className={cx('Combo-itemTag')}> <div className={cx('Combo-itemTag')}>
<label></label> <label>{__('类型')}</label>
<Select <Select
onChange={this.handleComboTypeChange.bind(this, index)} onChange={this.handleComboTypeChange.bind(this, index)}
options={(conditions as Array<Condition>).map(item => ({ options={(conditions as Array<Condition>).map(item => ({
@ -1084,7 +1099,7 @@ export default class ComboControl extends React.Component<ComboProps> {
) )
) : ( ) : (
<Alert2 level="warning" className="m-b-none"> <Alert2 level="warning" className="m-b-none">
{__('数据非法,或者数据已失效,请移除')}
</Alert2> </Alert2>
)} )}
</div> </div>
@ -1107,7 +1122,7 @@ export default class ComboControl extends React.Component<ComboProps> {
{ {
type: 'dropdown-button', type: 'dropdown-button',
icon: addIcon, icon: addIcon,
label: addButtonText || '新增', label: __(addButtonText || '新增'),
level: 'info', level: 'info',
size: 'sm', size: 'sm',
closeOnClick: true closeOnClick: true
@ -1127,18 +1142,20 @@ export default class ComboControl extends React.Component<ComboProps> {
type="button" type="button"
onClick={this.addItem} onClick={this.addItem}
className={cx(`Button Combo-addBtn`, addButtonClassName)} className={cx(`Button Combo-addBtn`, addButtonClassName)}
data-tooltip="新增一条数据" data-tooltip={__('新增一条数据')}
> >
{addIcon ? ( {addIcon ? (
<i className={cx('Button-icon', addIcon)} /> <i className={cx('Button-icon', addIcon)} />
) : null} ) : null}
<span>{addButtonText || '新增'}</span> <span>{__(addButtonText || '新增')}</span>
</button> </button>
) )
) : null} ) : null}
{draggable ? ( {draggable ? (
<span className={cx(`Combo-dragableTip`)} ref={this.dragTipRef}> <span className={cx(`Combo-dragableTip`)} ref={this.dragTipRef}>
{Array.isArray(value) && value.length > 1 ? draggableTip : ''} {Array.isArray(value) && value.length > 1
? __(draggableTip)
: ''}
</span> </span>
) : null} ) : null}
</div> </div>
@ -1159,7 +1176,8 @@ export default class ComboControl extends React.Component<ComboProps> {
noBorder, noBorder,
disabled, disabled,
typeSwitchable, typeSwitchable,
nullable nullable,
translate: __
} = this.props; } = this.props;
let controls = this.props.controls; let controls = this.props.controls;
@ -1183,7 +1201,7 @@ export default class ComboControl extends React.Component<ComboProps> {
<div className={cx(`Combo-item`)}> <div className={cx(`Combo-item`)}>
{condition && typeSwitchable !== false ? ( {condition && typeSwitchable !== false ? (
<div className={cx('Combo-itemTag')}> <div className={cx('Combo-itemTag')}>
<label></label> <label>{__('类型')}</label>
<Select <Select
onChange={this.handleComboTypeChange.bind(this, 0)} onChange={this.handleComboTypeChange.bind(this, 0)}
options={(conditions as Array<Condition>).map(item => ({ options={(conditions as Array<Condition>).map(item => ({
@ -1219,14 +1237,14 @@ export default class ComboControl extends React.Component<ComboProps> {
) )
) : ( ) : (
<Alert2 level="warning" className="m-b-none"> <Alert2 level="warning" className="m-b-none">
{__('数据非法,或者数据已失效,请移除')}
</Alert2> </Alert2>
)} )}
</div> </div>
</div> </div>
{value && nullable ? ( {value && nullable ? (
<a className={cx('Combo-setNullBtn')} href="#" onClick={this.setNull}> <a className={cx('Combo-setNullBtn')} href="#" onClick={this.setNull}>
{__('清空数据')}
</a> </a>
) : null} ) : null}
</div> </div>

View File

@ -101,6 +101,8 @@ export default class DateControl extends React.PureComponent<
defaultValue, defaultValue,
defaultData, defaultData,
classnames: cx, classnames: cx,
minDate,
maxDate,
...rest ...rest
} = this.props; } = this.props;

View File

@ -271,7 +271,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return; return;
} }
const {maxSize, multiple, maxLength} = this.props; const {maxSize, multiple, maxLength, translate: __} = this.props;
let allowed = let allowed =
multiple && maxLength multiple && maxLength
? maxLength - this.state.files.length ? maxLength - this.state.files.length
@ -282,11 +282,14 @@ export default class FileControl extends React.Component<FileProps, FileState> {
[].slice.call(files, 0, allowed).forEach((file: FileX) => { [].slice.call(files, 0, allowed).forEach((file: FileX) => {
if (maxSize && file.size > maxSize) { if (maxSize && file.size > maxSize) {
this.props.env.alert( this.props.env.alert(
`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize( __(
file.size '您选择的文件 {{filename}} 大小为 {{actualSize}} 超出了最大为 {{maxSize}} 的限制,请重新选择。',
)} ${ImageControl.formatFileSize( {
maxSize filename: file.name,
)} ` actualSize: ImageControl.formatFileSize(file.size),
maxSize: ImageControl.formatFileSize(maxSize)
}
)
); );
file.state = 'invalid'; file.state = 'invalid';
} else { } else {
@ -320,7 +323,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
if (evt.type !== 'change' && evt.type !== 'drop') { if (evt.type !== 'change' && evt.type !== 'drop') {
return; return;
} }
const {multiple, env, accept} = this.props; const {multiple, env, accept, translate: __} = this.props;
const files = rejectedFiles.map((file: any) => ({ const files = rejectedFiles.map((file: any) => ({
...file, ...file,
@ -338,9 +341,10 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}); });
env.alert( env.alert(
`您添加的文件${files.map( __('您添加的文件{{files}}不符合类型的`{{accept}}`的设定,请仔细检查。', {
(item: any) => `${item.name}` files: files.map((item: any) => `${item.name}`).join(' '),
)}\`${accept}\`设定,请仔细检查。` accept
})
); );
} }
@ -393,6 +397,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
return; return;
} }
const __ = this.props.translate;
const file = find( const file = find(
this.state.files, this.state.files,
item => item.state === 'pending' item => item.state === 'pending'
@ -463,7 +468,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
if (this.resolve) { if (this.resolve) {
this.resolve( this.resolve(
this.state.files.some(file => file.state === 'error') this.state.files.some(file => file.state === 'error')
? '文件上传失败请重试' ? __('文件上传失败请重试')
: null : null
); );
this.resolve = undefined; this.resolve = undefined;
@ -489,7 +494,8 @@ export default class FileControl extends React.Component<FileProps, FileState> {
finishChunkApi, finishChunkApi,
asBase64, asBase64,
asBlob, asBlob,
data data,
translate: __
} = this.props; } = this.props;
if (asBase64) { if (asBase64) {
@ -545,7 +551,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
) )
.then(ret => { .then(ret => {
if (ret.status || !ret.data) { if (ret.status || !ret.data) {
throw new Error(ret.msg || '上传失败, 请重试'); throw new Error(ret.msg || __('上传失败, 请重试'));
} }
onProgress(1); onProgress(1);
@ -565,7 +571,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
}); });
}) })
.catch(error => { .catch(error => {
cb(error.message || '上传失败, 请重试', file); cb(error.message || __('上传失败, 请重试'), file);
}); });
} }
@ -660,6 +666,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
let startProgress = 0.2; let startProgress = 0.2;
let endProgress = 0.9; let endProgress = 0.9;
let progressArr: Array<number>; let progressArr: Array<number>;
const __ = this.props.translate;
interface ObjectState { interface ObjectState {
key: string; key: string;
@ -692,10 +699,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
} }
); );
self self._send(startApi).then(startChunk).catch(reject);
._send(startApi)
.then(startChunk)
.catch(reject);
function startChunk(ret: Payload) { function startChunk(ret: Payload) {
onProgress(startProgress); onProgress(startProgress);
@ -703,7 +707,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
progressArr = tasks.map(() => 0); progressArr = tasks.map(() => 0);
if (!ret.data) { if (!ret.data) {
throw new Error('接口返回错误,请仔细检查'); throw new Error(__('接口返回错误,请仔细检查'));
} }
state = { state = {
@ -713,7 +717,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
total: tasks.length total: tasks.length
}; };
mapLimit(tasks, 3, uploadPartFile(state, config), function( mapLimit(tasks, 3, uploadPartFile(state, config), function (
err, err,
results results
) { ) {
@ -755,10 +759,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
} }
); );
self self._send(endApi).then(resolve).catch(reject);
._send(endApi)
.then(resolve)
.catch(reject);
} }
function uploadPartFile(state: ObjectState, conf: Partial<FileProps>) { function uploadPartFile(state: ObjectState, conf: Partial<FileProps>) {
@ -853,6 +854,8 @@ export default class FileControl extends React.Component<FileProps, FileState> {
} }
validate(): any { validate(): any {
const __ = this.props.translate;
if ( if (
this.state.uploading || this.state.uploading ||
this.state.files.some(item => item.state === 'pending') this.state.files.some(item => item.state === 'pending')
@ -862,7 +865,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
this.startUpload(); this.startUpload();
}); });
} else if (this.state.files.some(item => item.state === 'error')) { } else if (this.state.files.some(item => item.state === 'error')) {
return '文件上传失败请重试'; return __('文件上传失败请重试');
} }
} }
@ -878,6 +881,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
hideUploadButton, hideUploadButton,
className, className,
classnames: cx, classnames: cx,
translate: __,
render render
} = this.props; } = this.props;
let {files, uploading, error} = this.state; let {files, uploading, error} = this.state;
@ -921,7 +925,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
{isDragActive ? ( {isDragActive ? (
<div className={cx('FileControl-acceptTip')}> <div className={cx('FileControl-acceptTip')}>
{__('把文件拖到这,然后松完成添加!')}
</div> </div>
) : ( ) : (
<> <>
@ -935,10 +939,10 @@ export default class FileControl extends React.Component<FileProps, FileState> {
> >
<Icon icon="upload" className="icon" /> <Icon icon="upload" className="icon" />
{!multiple && files.length {!multiple && files.length
? '重新上传' ? __('重新上传')
: multiple && files.length : multiple && files.length
? '继续添加' ? __('继续添加')
: '上传文件'} : __('上传文件')}
</Button> </Button>
) : null} ) : null}
@ -980,7 +984,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
) : null} ) : null}
{file.state !== 'uploading' ? ( {file.state !== 'uploading' ? (
<a <a
data-tooltip="移除" data-tooltip={__('移除')}
className={cx('FileControl-clear')} className={cx('FileControl-clear')}
onClick={() => this.removeFile(file, index)} onClick={() => this.removeFile(file, index)}
> >
@ -1024,8 +1028,12 @@ export default class FileControl extends React.Component<FileProps, FileState> {
{failed ? ( {failed ? (
<div className={cx('FileControl-sum')}> <div className={cx('FileControl-sum')}>
{uploaded}{failed} {__('已成功上传{{uploaded}}个文件,{{failed}}个文件上传失败,', {
<a onClick={this.retry}></a> uploaded,
failed
})}
<a onClick={this.retry}>{__('重试上传')}</a>
{__('失败文件。')}
</div> </div>
) : null} ) : null}
@ -1036,7 +1044,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
className={cx('FileControl-uploadBtn')} className={cx('FileControl-uploadBtn')}
onClick={this.toggleUpload} onClick={this.toggleUpload}
> >
{uploading ? '暂停上传' : '开始上传'} {__(uploading ? '暂停上传' : '开始上传')}
</Button> </Button>
) : null} ) : null}
</div> </div>

View File

@ -201,7 +201,8 @@ export default class IconPickerControl extends React.PureComponent<
classnames: cx, classnames: cx,
name, name,
value, value,
noDataTip noDataTip,
translate: __
} = this.props; } = this.props;
const options = this.formatOptions(); const options = this.formatOptions();
const vendors = this.getVendors(); const vendors = this.getVendors();
@ -311,7 +312,7 @@ export default class IconPickerControl extends React.PureComponent<
: 'IconPickerControl-singleVendor' : 'IconPickerControl-singleVendor'
)} )}
> >
{noDataTip} {__(noDataTip)}
</div> </div>
)} )}
</div> </div>

View File

@ -15,6 +15,7 @@ import Button from '../../components/Button';
import accepts from 'attr-accept'; import accepts from 'attr-accept';
import {getNameFromUrl} from './File'; import {getNameFromUrl} from './File';
import ImageComponent, {ImageThumbProps} from '../Image'; import ImageComponent, {ImageThumbProps} from '../Image';
import {TranslateFn} from '../../locale';
let preventEvent = (e: any) => e.stopPropagation(); let preventEvent = (e: any) => e.stopPropagation();
@ -139,14 +140,18 @@ export default class ImageControl extends React.Component<
: undefined; : undefined;
} }
static sizeInfo(width?: number, height?: number): string { static sizeInfo(
width: number | undefined,
height: number | undefined,
__: TranslateFn
): string {
if (!width) { if (!width) {
return `高度${height}px`; return __('高度{{height}}px', {height: height});
} else if (!height) { } else if (!height) {
return `宽度${width}px`; return __('宽度{{width}}px', {width: width});
} }
return `尺寸(${width} x ${height}`; return __('尺寸({{width}} x {{height}}', {width, height});
} }
state: ImageState = { state: ImageState = {
@ -268,11 +273,12 @@ export default class ImageControl extends React.Component<
buildCrop(props: ImageProps) { buildCrop(props: ImageProps) {
let crop = props.crop; let crop = props.crop;
const __ = this.props.translate;
if (crop && props.multiple) { if (crop && props.multiple) {
props.env && props.env &&
props.env.alert && props.env.alert &&
props.env.alert('图片多选配置和裁剪配置冲突,目前不能二者都支持!'); props.env.alert(__('图片多选配置和裁剪配置冲突,目前不能二者都支持!'));
return null; return null;
} }
@ -299,7 +305,7 @@ export default class ImageControl extends React.Component<
if (evt.type !== 'change' && evt.type !== 'drop') { if (evt.type !== 'change' && evt.type !== 'drop') {
return; return;
} }
const {multiple, env, accept} = this.props; const {multiple, env, accept, translate: __} = this.props;
const files = rejectedFiles.map((file: any) => ({ const files = rejectedFiles.map((file: any) => ({
...file, ...file,
@ -317,9 +323,10 @@ export default class ImageControl extends React.Component<
// }); // });
env.alert( env.alert(
`您添加的文件${files.map( __('您添加的文件{{files}}不符合类型的`{{accept}}`的设定,请仔细检查。', {
(item: any) => `${item.name}` files: files.map((file: any) => `${file.name}`).join(' '),
)}\`${accept}\`设定,请仔细检查。` accept
})
); );
} }
@ -365,6 +372,7 @@ export default class ImageControl extends React.Component<
} }
const env = this.props.env; const env = this.props.env;
const __ = this.props.translate;
const file = find(this.files, item => item.state === 'pending') as FileX; const file = find(this.files, item => item.state === 'pending') as FileX;
if (file) { if (file) {
this.current = file; this.current = file;
@ -405,7 +413,7 @@ export default class ImageControl extends React.Component<
); );
} }
env.notify('error', error || '图片上传失败,请重试'); env.notify('error', error || __('图片上传失败,请重试'));
} else { } else {
newFile = { newFile = {
...obj, ...obj,
@ -449,7 +457,7 @@ export default class ImageControl extends React.Component<
if (this.resolve) { if (this.resolve) {
this.resolve( this.resolve(
this.files.some(file => file.state === 'error') this.files.some(file => file.state === 'error')
? '文件上传失败请重试' ? __('文件上传失败请重试')
: null : null
); );
this.resolve = undefined; this.resolve = undefined;
@ -631,7 +639,7 @@ export default class ImageControl extends React.Component<
return; return;
} }
const {multiple, maxLength, maxSize, accept} = this.props; const {multiple, maxLength, maxSize, accept, translate: __} = this.props;
let currentFiles = this.files; let currentFiles = this.files;
if (!multiple && currentFiles.length) { if (!multiple && currentFiles.length) {
@ -649,11 +657,14 @@ export default class ImageControl extends React.Component<
[].slice.call(files, 0, allowed).forEach((file: FileX) => { [].slice.call(files, 0, allowed).forEach((file: FileX) => {
if (maxSize && file.size > maxSize) { if (maxSize && file.size > maxSize) {
alert( alert(
`您选择的文件 ${file.name} 大小为 ${ImageControl.formatFileSize( __(
file.size '您选择的文件 {{filename}} 大小为 {{actualSize}} 超出了最大为 {{maxSize}} 的限制,请重新选择。',
)} ${ImageControl.formatFileSize( {
maxSize filename: file.name,
)} ` actualSize: ImageControl.formatFileSize(file.size),
maxSize: ImageControl.formatFileSize(maxSize)
}
)
); );
return; return;
} }
@ -691,7 +702,7 @@ export default class ImageControl extends React.Component<
cb: (error: null | string, file: FileX, obj?: FileValue) => void, cb: (error: null | string, file: FileX, obj?: FileValue) => void,
onProgress: (progress: number) => void onProgress: (progress: number) => void
) { ) {
const {limit} = this.props; const {limit, translate: __} = this.props;
if (!limit) { if (!limit) {
return this._upload(file, cb, onProgress); return this._upload(file, cb, onProgress);
@ -707,32 +718,33 @@ export default class ImageControl extends React.Component<
(limit.width && limit.width != width) || (limit.width && limit.width != width) ||
(limit.height && limit.height != height) (limit.height && limit.height != height)
) { ) {
error = `您选择的图片不符合尺寸要求, 请上传${ImageControl.sizeInfo( error = __('您选择的图片不符合尺寸要求, 请上传{{info}}的图片', {
limit.width, info: ImageControl.sizeInfo(limit.width, limit.height, __)
limit.height });
)}`;
} else if ( } else if (
(limit.maxWidth && limit.maxWidth < width) || (limit.maxWidth && limit.maxWidth < width) ||
(limit.maxHeight && limit.maxHeight < height) (limit.maxHeight && limit.maxHeight < height)
) { ) {
error = `您选择的图片不符合尺寸要求, 请上传不要超过${ImageControl.sizeInfo( error = __('您选择的图片不符合尺寸要求, 请上传不要超过{{info}}的图片', {
limit.maxWidth, info: ImageControl.sizeInfo(limit.maxWidth, limit.maxHeight, __)
limit.maxHeight });
)}`;
} else if ( } else if (
(limit.minWidth && limit.minWidth > width) || (limit.minWidth && limit.minWidth > width) ||
(limit.minHeight && limit.minHeight > height) (limit.minHeight && limit.minHeight > height)
) { ) {
error = `您选择的图片不符合尺寸要求, 请上传不要小于${ImageControl.sizeInfo( error = __('您选择的图片不符合尺寸要求, 请上传不要小于{{info}}的图片', {
limit.minWidth, info: ImageControl.sizeInfo(limit.minWidth, limit.minHeight, __)
limit.minHeight });
)}`;
} else if ( } else if (
limit.aspectRatio && limit.aspectRatio &&
Math.abs(width / height - limit.aspectRatio) > 0.01 Math.abs(width / height - limit.aspectRatio) > 0.01
) { ) {
error = `您选择的图片不符合尺寸要求, 请上传尺寸比率为 ${limit.aspectRatioLabel || error = __(
limit.aspectRatio} `; '您选择的图片不符合尺寸要求, 请上传尺寸比率为 {{ratio}} 的图片',
{
ratio: limit.aspectRatioLabel || limit.aspectRatio
}
);
} }
if (error) { if (error) {
@ -750,10 +762,11 @@ export default class ImageControl extends React.Component<
cb: (error: null | string, file: Blob, obj?: FileValue) => void, cb: (error: null | string, file: Blob, obj?: FileValue) => void,
onProgress: (progress: number) => void onProgress: (progress: number) => void
) { ) {
const __ = this.props.translate;
this._send(file, this.props.reciever as string, {}, onProgress) this._send(file, this.props.reciever as string, {}, onProgress)
.then((ret: Payload) => { .then((ret: Payload) => {
if (ret.status) { if (ret.status) {
throw new Error(ret.msg || '上传失败, 请重试'); throw new Error(ret.msg || __('上传失败, 请重试'));
} }
const obj: FileValue = { const obj: FileValue = {
@ -764,7 +777,7 @@ export default class ImageControl extends React.Component<
cb(null, file, obj); cb(null, file, obj);
}) })
.catch(error => cb(error.message || '上传失败,请重试', file)); .catch(error => cb(error.message || __('上传失败,请重试'), file));
} }
_send( _send(
@ -854,6 +867,8 @@ export default class ImageControl extends React.Component<
} }
validate(): any { validate(): any {
const __ = this.props.translate;
if (this.state.locked && this.state.lockedReason) { if (this.state.locked && this.state.lockedReason) {
return this.state.lockedReason; return this.state.lockedReason;
} else if (this.state.cropFile) { } else if (this.state.cropFile) {
@ -870,7 +885,7 @@ export default class ImageControl extends React.Component<
this.startUpload(); this.startUpload();
}); });
} else if (this.files.some(item => item.state === 'error')) { } else if (this.files.some(item => item.state === 'error')) {
return '文件上传失败请重试'; return __('文件上传失败请重试');
} }
} }
@ -887,7 +902,8 @@ export default class ImageControl extends React.Component<
hideUploadButton, hideUploadButton,
thumbMode, thumbMode,
thumbRatio, thumbRatio,
reCropable reCropable,
translate: __
} = this.props; } = this.props;
const {files, error, crop, uploading, cropFile} = this.state; const {files, error, crop, uploading, cropFile} = this.state;
@ -902,7 +918,7 @@ export default class ImageControl extends React.Component<
<a <a
className={cx('ImageControl-cropCancel')} className={cx('ImageControl-cropCancel')}
onClick={this.cancelCrop} onClick={this.cancelCrop}
data-tooltip="取消" data-tooltip={__('取消')}
data-position="left" data-position="left"
> >
<Icon icon="close" className="icon" /> <Icon icon="close" className="icon" />
@ -910,7 +926,7 @@ export default class ImageControl extends React.Component<
<a <a
className={cx('ImageControl-cropConfirm')} className={cx('ImageControl-cropConfirm')}
onClick={this.handleCrop} onClick={this.handleCrop}
data-tooltip="确认" data-tooltip={__('确认')}
data-position="left" data-position="left"
> >
<Icon icon="check" className="icon" /> <Icon icon="check" className="icon" />
@ -954,7 +970,7 @@ export default class ImageControl extends React.Component<
'is-reject': isDragReject 'is-reject': isDragReject
})} })}
> >
{__('把图片拖到这,然后松开完成添加!')}
</div> </div>
) : ( ) : (
<> <>
@ -974,7 +990,7 @@ export default class ImageControl extends React.Component<
<> <>
<a <a
className={cx('ImageControl-itemClear')} className={cx('ImageControl-itemClear')}
data-tooltip="移除" data-tooltip={__('移除')}
data-position="bottom" data-position="bottom"
onClick={this.removeFile.bind( onClick={this.removeFile.bind(
this, this,
@ -993,7 +1009,7 @@ export default class ImageControl extends React.Component<
> >
<Icon icon="retry" className="icon" /> <Icon icon="retry" className="icon" />
<p className="ImageControl-itemInfoError"> <p className="ImageControl-itemInfoError">
{__('重新上传')}
</p> </p>
</a> </a>
</> </>
@ -1007,7 +1023,7 @@ export default class ImageControl extends React.Component<
)} )}
key="clear" key="clear"
className={cx('ImageControl-itemClear')} className={cx('ImageControl-itemClear')}
data-tooltip="移除" data-tooltip={__('移除')}
> >
<Icon icon="close" className="icon" /> <Icon icon="close" className="icon" />
</a> </a>
@ -1015,7 +1031,7 @@ export default class ImageControl extends React.Component<
key="info" key="info"
className={cx('ImageControl-itemInfo')} className={cx('ImageControl-itemInfo')}
> >
<p></p> <p>{__('文件上传中')}</p>
<div className={cx('ImageControl-progress')}> <div className={cx('ImageControl-progress')}>
<span <span
style={{ style={{
@ -1067,7 +1083,7 @@ export default class ImageControl extends React.Component<
)} )}
<a <a
data-tooltip="查看大图" data-tooltip={__('查看大图')}
data-position="bottom" data-position="bottom"
target="_blank" target="_blank"
href={file.url || file.preview} href={file.url || file.preview}
@ -1084,7 +1100,7 @@ export default class ImageControl extends React.Component<
reCropable !== false && reCropable !== false &&
!disabled ? ( !disabled ? (
<a <a
data-tooltip="裁剪图片" data-tooltip={__('裁剪图片')}
data-position="bottom" data-position="bottom"
onClick={this.editImage.bind(this, key)} onClick={this.editImage.bind(this, key)}
> >
@ -1093,7 +1109,7 @@ export default class ImageControl extends React.Component<
) : null} ) : null}
{!disabled ? ( {!disabled ? (
<a <a
data-tooltip="移除" data-tooltip={__('移除')}
data-position="bottom" data-position="bottom"
onClick={this.removeFile.bind( onClick={this.removeFile.bind(
this, this,
@ -1128,14 +1144,14 @@ export default class ImageControl extends React.Component<
'is-disabled': disabled 'is-disabled': disabled
})} })}
onClick={this.handleSelect} onClick={this.handleSelect}
data-tooltip={placeholder} data-tooltip={__(placeholder)}
data-position="right" data-position="right"
> >
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
{isFocused ? ( {isFocused ? (
<span className={cx('ImageControl-pasteTip')}> <span className={cx('ImageControl-pasteTip')}>
{__('当前状态支持从剪切板中粘贴图片文件。')}
</span> </span>
) : null} ) : null}
</label> </label>
@ -1148,7 +1164,7 @@ export default class ImageControl extends React.Component<
disabled={!hasPending} disabled={!hasPending}
onClick={this.toggleUpload} onClick={this.toggleUpload}
> >
{uploading ? '暂停上传' : '开始上传'} {__(uploading ? '暂停上传' : '开始上传')}
</Button> </Button>
) : null} ) : null}

View File

@ -131,7 +131,7 @@ export default class MatrixCheckbox extends React.Component<
} }
async reload() { async reload() {
const {source, data, env, onChange} = this.props; const {source, data, env, onChange, translate: __} = this.props;
if (!isEffectiveApi(source, data) || this.state.loading) { if (!isEffectiveApi(source, data) || this.state.loading) {
return; return;
@ -159,7 +159,7 @@ export default class MatrixCheckbox extends React.Component<
.fetcher(source, data) .fetcher(source, data)
.then(ret => { .then(ret => {
if (!ret.ok) { if (!ret.ok) {
throw new Error(ret.msg || '数据请求错误'); throw new Error(ret.msg || __('数据请求错误'));
} }
if (!this.mounted) { if (!this.mounted) {
return resolve(); return resolve();

View File

@ -393,7 +393,8 @@ export default class NestedSelectControl extends React.Component<
options, options,
disabled, disabled,
searchable, searchable,
searchPromptText searchPromptText,
translate: __
} = this.props; } = this.props;
const stack = this.state.stack; const stack = this.state.stack;
@ -410,7 +411,7 @@ export default class NestedSelectControl extends React.Component<
onFocus={this.onFocus} onFocus={this.onFocus}
onBlur={this.onBlur} onBlur={this.onBlur}
disabled={disabled!!} disabled={disabled!!}
placeholder={searchPromptText} placeholder={__(searchPromptText)}
onChange={this.handleInputChange} onChange={this.handleInputChange}
ref={this.inputRef} ref={this.inputRef}
/> />

View File

@ -539,7 +539,8 @@ export function registerOptionsControl(config: OptionsConfig) {
valueField, valueField,
formItem: model, formItem: model,
createBtnLabel, createBtnLabel,
env env,
translate: __
} = this.props; } = this.props;
// 禁用或者没有配置 name // 禁用或者没有配置 name
@ -554,7 +555,7 @@ export function registerOptionsControl(config: OptionsConfig) {
type: 'text', type: 'text',
name: labelField || 'label', name: labelField || 'label',
label: false, label: false,
placeholder: '请输入名称' placeholder: __('请输入名称')
} }
]; ];
} }
@ -650,7 +651,8 @@ export function registerOptionsControl(config: OptionsConfig) {
source, source,
data, data,
formItem: model, formItem: model,
optionLabel optionLabel,
translate: __
} = this.props; } = this.props;
if (disabled || !model) { if (disabled || !model) {
@ -663,7 +665,7 @@ export function registerOptionsControl(config: OptionsConfig) {
type: 'text', type: 'text',
name: labelField || 'label', name: labelField || 'label',
label: false, label: false,
placeholder: '请输入名称' placeholder: __('请输入名称')
} }
]; ];
} }
@ -673,7 +675,9 @@ export function registerOptionsControl(config: OptionsConfig) {
: await onOpenDialog( : await onOpenDialog(
{ {
type: 'dialog', type: 'dialog',
title: `编辑${optionLabel || '选项'}`, title: __('编辑{{label}}', {
label: optionLabel || '选项'
}),
body: { body: {
type: 'form', type: 'form',
api: editApi, api: editApi,
@ -695,7 +699,7 @@ export function registerOptionsControl(config: OptionsConfig) {
); );
if (!payload.ok) { if (!payload.ok) {
env.notify('error', payload.msg || '保存失败,请仔细检查'); env.notify('error', payload.msg || __('保存失败,请仔细检查'));
result = null; result = null;
} else { } else {
result = payload.data || result; result = payload.data || result;
@ -738,7 +742,8 @@ export function registerOptionsControl(config: OptionsConfig) {
env, env,
formItem: model, formItem: model,
source, source,
valueField valueField,
translate: __
} = this.props; } = this.props;
if (disabled || !model) { if (disabled || !model) {
@ -758,7 +763,7 @@ export function registerOptionsControl(config: OptionsConfig) {
// 通过 deleteApi 删除。 // 通过 deleteApi 删除。
try { try {
if (!deleteApi) { if (!deleteApi) {
throw new Error('请配置 deleteApi'); throw new Error(__('请配置 deleteApi'));
} }
const result = await env.fetcher(deleteApi!, ctx, { const result = await env.fetcher(deleteApi!, ctx, {
@ -766,7 +771,7 @@ export function registerOptionsControl(config: OptionsConfig) {
}); });
if (!result.ok) { if (!result.ok) {
env.notify('error', result.msg || '删除失败,请重试'); env.notify('error', result.msg || __('删除失败,请重试'));
} else if (source) { } else if (source) {
this.reload(); this.reload();
} else { } else {

View File

@ -382,7 +382,8 @@ export default class PickerControl extends React.PureComponent<
placeholder, placeholder,
embed, embed,
value, value,
selectedOptions selectedOptions,
translate: __
} = this.props; } = this.props;
return ( return (
<div className={cx(`PickerControl`, className)}> <div className={cx(`PickerControl`, className)}>
@ -399,7 +400,9 @@ export default class PickerControl extends React.PureComponent<
> >
<div onClick={this.handleClick} className={cx('Picker-input')}> <div onClick={this.handleClick} className={cx('Picker-input')}>
{!selectedOptions.length && placeholder ? ( {!selectedOptions.length && placeholder ? (
<div className={cx('Picker-placeholder')}>{placeholder}</div> <div className={cx('Picker-placeholder')}>
{__(placeholder)}
</div>
) : null} ) : null}
<div className={cx('Picker-valueWrap')}> <div className={cx('Picker-valueWrap')}>
@ -427,7 +430,7 @@ export default class PickerControl extends React.PureComponent<
{render( {render(
'modal', 'modal',
{ {
title: '请选择', title: __('请选择'),
size: size, size: size,
type: modalMode, type: modalMode,
body: { body: {

View File

@ -63,7 +63,8 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
classPrefix, classPrefix,
itemClassName, itemClassName,
labelClassName, labelClassName,
labelField labelField,
translate: __
} = this.props; } = this.props;
return ( return (
@ -78,7 +79,7 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
delimiter={delimiter!} delimiter={delimiter!}
labelClassName={labelClassName} labelClassName={labelClassName}
labelField={labelField} labelField={labelField}
placeholder={placeholder} placeholder={__(placeholder)}
options={options} options={options}
columnsCount={columnsCount} columnsCount={columnsCount}
classPrefix={classPrefix} classPrefix={classPrefix}

View File

@ -4,7 +4,6 @@
* *
* *
*/ */
/* eslint fecs-indent: [0, "space", 2, 2] */
import React from 'react'; import React from 'react';
import cx from 'classnames'; import cx from 'classnames';
@ -59,7 +58,13 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
renderInput() { renderInput() {
const value = this.props.value; const value = this.props.value;
const parts = value ? value.split(':') : []; const parts = value ? value.split(':') : [];
let {options, placeholder, disabled, classPrefix: ns} = this.props; let {
options,
placeholder,
disabled,
classPrefix: ns,
translate: __
} = this.props;
let optionsArray: Array<Option> = []; let optionsArray: Array<Option> = [];
@ -69,7 +74,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
})); }));
optionsArray.unshift({ optionsArray.unshift({
label: placeholder as string, label: __(placeholder as string),
value: '' value: ''
}); });
@ -182,7 +187,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
<div className="repeat-control hbox"> <div className="repeat-control hbox">
{input ? ( {input ? (
<div className="col v-middle" style={{width: 30}}> <div className="col v-middle" style={{width: 30}}>
<span></span> <span>{__('每')}</span>
</div> </div>
) : null} ) : null}
@ -193,7 +198,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
classPrefix={ns} classPrefix={ns}
className={input ? 'pull-right' : ''} className={input ? 'pull-right' : ''}
options={optionsArray} options={optionsArray}
placeholder={placeholder} placeholder={__(placeholder)}
onChange={this.handleOptionChange} onChange={this.handleOptionChange}
value={parts[0]} value={parts[0]}
clearable={false} clearable={false}

View File

@ -34,7 +34,6 @@ export default class RichTextControl extends React.Component<
videoReciever: '/api/upload/video', videoReciever: '/api/upload/video',
placeholder: '请输入', placeholder: '请输入',
options: { options: {
language: 'zh_cn',
toolbarButtonsSM: [ toolbarButtonsSM: [
'paragraphFormat', 'paragraphFormat',
'quote', 'quote',
@ -156,7 +155,9 @@ export default class RichTextControl extends React.Component<
...(props.options && props.options.events), ...(props.options && props.options.events),
'froalaEditor.focus': this.handleFocus, 'froalaEditor.focus': this.handleFocus,
'froalaEditor.blur': this.handleBlur 'froalaEditor.blur': this.handleBlur
} },
language:
!this.props.locale || this.props.locale === 'zh-cn' ? 'zh_cn' : ''
}; };
if (props.buttons) { if (props.buttons) {
@ -178,10 +179,15 @@ export default class RichTextControl extends React.Component<
formData.append('file', blobInfo.blob(), blobInfo.filename()); formData.append('file', blobInfo.blob(), blobInfo.filename());
try { try {
const response = await fetcher(props.reciever, formData, { const response = await fetcher(props.reciever, formData, {
method: 'post', method: 'post'
}); });
if (response.ok) { if (response.ok) {
ok(response.data?.link || response.data?.url || response.data?.value || (response as any).link); ok(
response.data?.link ||
response.data?.url ||
response.data?.value ||
(response as any).link
);
} }
} catch (e) { } catch (e) {
fail(e); fail(e);
@ -212,7 +218,9 @@ export default class RichTextControl extends React.Component<
disabled, disabled,
size, size,
vendor, vendor,
env env,
locale,
translate
} = this.props; } = this.props;
const finnalVendor = vendor || (env.richTextToken ? 'froala' : 'tinymce'); const finnalVendor = vendor || (env.richTextToken ? 'froala' : 'tinymce');
@ -232,6 +240,8 @@ export default class RichTextControl extends React.Component<
onBlur={this.handleBlur} onBlur={this.handleBlur}
config={this.config} config={this.config}
disabled={disabled} disabled={disabled}
locale={locale}
translate={translate}
/> />
</div> </div>
); );

View File

@ -151,7 +151,8 @@ export default class SubFormControl extends React.PureComponent<
value, value,
btnLabel, btnLabel,
render, render,
data data,
translate: __
} = this.props; } = this.props;
return [ return [
@ -169,7 +170,7 @@ export default class SubFormControl extends React.PureComponent<
key={key} key={key}
> >
<span <span
data-tooltip="删除" data-tooltip={__('删除')}
data-position="bottom" data-position="bottom"
className={`${ns}Select-valueIcon`} className={`${ns}Select-valueIcon`}
onClick={this.removeItem.bind(this, key)} onClick={this.removeItem.bind(this, key)}
@ -179,7 +180,7 @@ export default class SubFormControl extends React.PureComponent<
<span <span
onClick={this.open.bind(this, key)} onClick={this.open.bind(this, key)}
className={`${ns}SubForm-valueLabel`} className={`${ns}SubForm-valueLabel`}
data-tooltip="编辑详情" data-tooltip={__('编辑详情')}
data-position="bottom" data-position="bottom"
> >
{(value && {(value &&
@ -190,7 +191,7 @@ export default class SubFormControl extends React.PureComponent<
'label', 'label',
{ {
type: 'tpl', type: 'tpl',
tpl: btnLabel tpl: __(btnLabel)
}, },
{ {
data: createObject(data, value) data: createObject(data, value)
@ -207,10 +208,10 @@ export default class SubFormControl extends React.PureComponent<
onClick={this.addItem} onClick={this.addItem}
className={cx(`${ns}Button ${ns}SubForm-addBtn`, addButtonClassName)} className={cx(`${ns}Button ${ns}SubForm-addBtn`, addButtonClassName)}
disabled={disabled} disabled={disabled}
data-tooltip="新增一条数据" data-tooltip={__('新增一条数据')}
> >
<i className="fa fa-plus m-r-xs" /> <i className="fa fa-plus m-r-xs" />
<span></span> <span>{__('新增')}</span>
</button> </button>
]; ];
} }
@ -224,7 +225,8 @@ export default class SubFormControl extends React.PureComponent<
labelField, labelField,
btnLabel, btnLabel,
render, render,
data data,
translate: __
} = this.props; } = this.props;
return ( return (
@ -238,7 +240,7 @@ export default class SubFormControl extends React.PureComponent<
btnClassName btnClassName
)} )}
onClick={this.open.bind(this, 0)} onClick={this.open.bind(this, 0)}
data-tooltip="编辑详情" data-tooltip={__('编辑详情')}
data-position="bottom" data-position="bottom"
> >
<span className={`${ns}SubForm-valueLabel`}> <span className={`${ns}SubForm-valueLabel`}>
@ -250,7 +252,7 @@ export default class SubFormControl extends React.PureComponent<
'label', 'label',
{ {
type: 'tpl', type: 'tpl',
tpl: btnLabel tpl: __(btnLabel)
}, },
{ {
data: createObject(data, value) data: createObject(data, value)

View File

@ -10,7 +10,7 @@ import omit from 'lodash/omit';
import {dataMapping} from '../../utils/tpl-builtin'; import {dataMapping} from '../../utils/tpl-builtin';
import findIndex from 'lodash/findIndex'; import findIndex from 'lodash/findIndex';
import memoize from 'lodash/memoize'; import memoize from 'lodash/memoize';
import { SimpleMap } from '../../utils/SimpleMap'; import {SimpleMap} from '../../utils/SimpleMap';
export interface TableProps extends FormControlProps { export interface TableProps extends FormControlProps {
placeholder?: string; placeholder?: string;
@ -103,12 +103,18 @@ export default class FormTable extends React.Component<TableProps, TableState> {
} }
validate(): any { validate(): any {
const {value, minLength, maxLength} = this.props; const {value, minLength, maxLength, translate: __} = this.props;
if (minLength && (!Array.isArray(value) || value.length < minLength)) { if (minLength && (!Array.isArray(value) || value.length < minLength)) {
return `组合表单成员数量不够,低于最小的设定${minLength}个,请添加更多的成员。`; return __(
'组合表单成员数量不够,低于设定的最小{{minLenth}}个,请添加更多的成员。',
{minLength}
);
} else if (maxLength && Array.isArray(value) && value.length > maxLength) { } else if (maxLength && Array.isArray(value) && value.length > maxLength) {
return `组合表单成员数量超出,超出最大的设定${maxLength}个,请删除多余的成员。`; return __(
'组合表单成员数量超出,超出设定的最大{{maxLength}}个,请删除多余的成员。',
{maxLength}
);
} else { } else {
const subForms: Array<any> = []; const subForms: Array<any> = [];
Object.keys(this.subForms).forEach( Object.keys(this.subForms).forEach(
@ -118,7 +124,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
return Promise.all(subForms.map(item => item.validate())).then( return Promise.all(subForms.map(item => item.validate())).then(
values => { values => {
if (~values.indexOf(false)) { if (~values.indexOf(false)) {
return '内部表单验证失败'; return __('内部表单验证失败');
} }
return; return;
@ -129,7 +135,15 @@ export default class FormTable extends React.Component<TableProps, TableState> {
} }
doAction(action: Action, ctx: RendererData, ...rest: Array<any>) { doAction(action: Action, ctx: RendererData, ...rest: Array<any>) {
const {onAction, value, valueField, env, onChange, editable} = this.props; const {
onAction,
value,
valueField,
env,
onChange,
editable,
translate: __
} = this.props;
if (action.actionType === 'add') { if (action.actionType === 'add') {
const rows = Array.isArray(value) ? value.concat() : []; const rows = Array.isArray(value) ? value.concat() : [];
@ -166,9 +180,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
action.actionType === 'delete' action.actionType === 'delete'
) { ) {
if (!valueField) { if (!valueField) {
return env.alert('请配置 valueField'); return env.alert(__('请配置 valueField'));
} else if (!action.payload) { } else if (!action.payload) {
return env.alert('action 上请配置 payload, 否则不清楚要删除哪个'); return env.alert(__('action 上请配置 payload, 否则不清楚要删除哪个'));
} }
const rows = Array.isArray(value) ? value.concat() : []; const rows = Array.isArray(value) ? value.concat() : [];
@ -226,7 +240,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
addApi, addApi,
updateApi, updateApi,
data, data,
env env,
translate: __
} = this.props; } = this.props;
// form 是 lazyChange 的,先让他们 flush, 即把未提交的数据提交。 // form 是 lazyChange 的,先让他们 flush, 即把未提交的数据提交。
@ -252,11 +267,13 @@ export default class FormTable extends React.Component<TableProps, TableState> {
} }
if (remote && !remote.ok) { if (remote && !remote.ok) {
env.notify('error', remote.msg || '保存失败'); env.notify('error', remote.msg || __('保存失败'));
return; return;
} else if (remote && remote.ok) { } else if (remote && remote.ok) {
item = { item = {
...((isNew ? addApi : updateApi) as ApiObject).replaceData ? {} : item, ...(((isNew ? addApi : updateApi) as ApiObject).replaceData
? {}
: item),
...remote.data ...remote.data
}; };
} }
@ -291,7 +308,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
deleteApi, deleteApi,
deleteConfirmText, deleteConfirmText,
env, env,
data data,
translate: __
} = this.props; } = this.props;
let newValue = Array.isArray(value) ? value.concat() : []; let newValue = Array.isArray(value) ? value.concat() : [];
const item = newValue[index]; const item = newValue[index];
@ -303,7 +321,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
const ctx = createObject(data, item); const ctx = createObject(data, item);
if (isEffectiveApi(deleteApi, ctx)) { if (isEffectiveApi(deleteApi, ctx)) {
const confirmed = await env.confirm( const confirmed = await env.confirm(
deleteConfirmText ? filter(deleteConfirmText, ctx) : '确认要删除?' deleteConfirmText ? filter(deleteConfirmText, ctx) : __('确认要删除?')
); );
if (!confirmed) { if (!confirmed) {
// 如果不确认,则跳过! // 如果不确认,则跳过!
@ -313,7 +331,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
const result = await env.fetcher(deleteApi, ctx); const result = await env.fetcher(deleteApi, ctx);
if (!result.ok) { if (!result.ok) {
env.notify('error', '删除失败'); env.notify('error', __('删除失败'));
return; return;
} }
} }
@ -339,6 +357,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
? props.columns.concat() ? props.columns.concat()
: []; : [];
const ns = this.props.classPrefix; const ns = this.props.classPrefix;
const __ = this.props.translate;
let btns = []; let btns = [];
if (props.addable && props.showAddBtn !== false) { if (props.addable && props.showAddBtn !== false) {
@ -350,7 +369,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
size="sm" size="sm"
key={key} key={key}
level="link" level="link"
tooltip="新增一行" tooltip={__('新增一行')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -399,7 +418,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
size="sm" size="sm"
key={key} key={key}
level="link" level="link"
tooltip="编辑当前行" tooltip={__('编辑当前行')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -423,7 +442,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
size="sm" size="sm"
key={key} key={key}
level="link" level="link"
tooltip="保存" tooltip={__('保存')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -447,7 +466,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
size="sm" size="sm"
key={key} key={key}
level="link" level="link"
tooltip="取消" tooltip={__('取消')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -481,7 +500,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
size="sm" size="sm"
key={key} key={key}
level="link" level="link"
tooltip="删除当前行" tooltip={__('删除当前行')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -503,7 +522,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
type: 'operation', type: 'operation',
buttons: btns, buttons: btns,
width: 150, width: 150,
label: '操作' label: __('操作')
}); });
} }
@ -576,7 +595,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
buildItems = memoize( buildItems = memoize(
(value: Array<any>, editIndex: number) => { (value: Array<any>, editIndex: number) => {
return value.map((value: any, index: number) => return value.map((value: any, index: number) =>
index === editIndex ? this.state.editting : value) index === editIndex ? this.state.editting : value
);
}, },
(...args: Array<any>) => JSON.stringify(args) (...args: Array<any>) => JSON.stringify(args)
); );
@ -592,7 +612,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
draggable, draggable,
addable, addable,
columnsTogglable, columnsTogglable,
combineNum combineNum,
translate: __
} = this.props; } = this.props;
return ( return (
@ -601,7 +622,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
'body', 'body',
{ {
type: 'table', type: 'table',
placeholder, placeholder: __(placeholder),
columns: this.state.columns, columns: this.state.columns,
affixHeader: false affixHeader: false
}, },
@ -610,12 +631,14 @@ export default class FormTable extends React.Component<TableProps, TableState> {
saveImmediately: true, saveImmediately: true,
disabled, disabled,
draggable: draggable && !~this.state.editIndex, draggable: draggable && !~this.state.editIndex,
items: this.buildItems((Array.isArray(value) && value.length items: this.buildItems(
? value Array.isArray(value) && value.length
: addable && showAddBtn !== false ? value
? [{__isPlaceholder: true}] : addable && showAddBtn !== false
: [] ? [{__isPlaceholder: true}]
), this.state.editIndex), : [],
this.state.editIndex
),
getEntryId: this.getEntryId, getEntryId: this.getEntryId,
onSave: this.handleTableSave, onSave: this.handleTableSave,
onSaveOrder: this.handleSaveTableOrder, onSaveOrder: this.handleSaveTableOrder,

View File

@ -2,7 +2,7 @@ import {OptionsControlProps, OptionsControl} from './Options';
import React from 'react'; import React from 'react';
import {Api} from '../../types'; import {Api} from '../../types';
import Spinner from '../../components/Spinner'; import Spinner from '../../components/Spinner';
import {TransferRenderer} from './Transfer'; import {BaseTransferRenderer} from './Transfer';
import TabsTransfer from '../../components/TabsTransfer'; import TabsTransfer from '../../components/TabsTransfer';
export interface TabsTransferProps extends OptionsControlProps { export interface TabsTransferProps extends OptionsControlProps {
@ -16,7 +16,9 @@ export interface TabsTransferProps extends OptionsControlProps {
@OptionsControl({ @OptionsControl({
type: 'tabs-transfer' type: 'tabs-transfer'
}) })
export class TabsTransferRenderer extends TransferRenderer<TabsTransferProps> { export class TabsTransferRenderer extends BaseTransferRenderer<
TabsTransferProps
> {
render() { render() {
const { const {
className, className,

View File

@ -271,7 +271,8 @@ export default class TagControl extends React.PureComponent<
popOverContainer, popOverContainer,
dropdown, dropdown,
options, options,
optionsTip optionsTip,
translate: __
} = this.props; } = this.props;
const finnalOptions = Array.isArray(options) const finnalOptions = Array.isArray(options)
@ -300,7 +301,7 @@ export default class TagControl extends React.PureComponent<
{...getInputProps({ {...getInputProps({
name, name,
ref: this.input, ref: this.input,
placeholder: placeholder || '暂无标签', placeholder: __(placeholder || '暂无标签'),
value: this.state.inputValue, value: this.state.inputValue,
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
onFocus: this.handleFocus, onFocus: this.handleFocus,
@ -348,7 +349,9 @@ export default class TagControl extends React.PureComponent<
// 保留原来的展现方式,不推荐 // 保留原来的展现方式,不推荐
<div className={cx('TagControl-sug')}> <div className={cx('TagControl-sug')}>
{optionsTip ? ( {optionsTip ? (
<div className={cx('TagControl-sugTip')}>{optionsTip}</div> <div className={cx('TagControl-sugTip')}>
{__(optionsTip)}
</div>
) : null} ) : null}
{options.map((item, index) => ( {options.map((item, index) => (
<div <div

View File

@ -415,7 +415,8 @@ export default class TextControl extends React.PureComponent<
labelField, labelField,
valueField, valueField,
multiple, multiple,
spinnerClassName spinnerClassName,
translate: __
} = this.props; } = this.props;
return ( return (
@ -542,7 +543,7 @@ export default class TextControl extends React.PureComponent<
> >
{option.isNew ? ( {option.isNew ? (
<span> <span>
{option.label} {__('新增:{{label}}', {label: option.label})}
<Icon icon="enter" className="icon" /> <Icon icon="enter" className="icon" />
</span> </span>
) : ( ) : (

View File

@ -15,7 +15,7 @@ import find from 'lodash/find';
import {optionValueCompare} from '../../components/Select'; import {optionValueCompare} from '../../components/Select';
import {resolveVariable} from '../../utils/tpl-builtin'; import {resolveVariable} from '../../utils/tpl-builtin';
export interface TransferProps extends OptionsControlProps { export interface BaseTransferProps extends OptionsControlProps {
showArrow?: boolean; showArrow?: boolean;
sortable?: boolean; sortable?: boolean;
selectMode?: 'table' | 'list' | 'tree' | 'chained' | 'associated'; selectMode?: 'table' | 'list' | 'tree' | 'chained' | 'associated';
@ -29,8 +29,8 @@ export interface TransferProps extends OptionsControlProps {
searchApi?: Api; searchApi?: Api;
} }
export class TransferRenderer< export class BaseTransferRenderer<
T extends OptionsControlProps = TransferProps T extends OptionsControlProps = BaseTransferProps
> extends React.Component<T> { > extends React.Component<T> {
@autobind @autobind
handleChange(value: Array<Option>) { handleChange(value: Array<Option>) {
@ -212,6 +212,9 @@ export class TransferRenderer<
} }
} }
// ts 3.9 里面非得这样才不报错,鬼知道为何。
export class TransferRender extends BaseTransferRenderer {}
export default OptionsControl({ export default OptionsControl({
type: 'transfer' type: 'transfer'
})(TransferRenderer); })(TransferRender);

View File

@ -69,7 +69,8 @@ export default class TreeControl extends React.Component<TreeProps> {
removeTip, removeTip,
onDelete, onDelete,
rootCreatable, rootCreatable,
rootCreateTip rootCreateTip,
translate: __
} = this.props; } = this.props;
return ( return (
@ -84,7 +85,7 @@ export default class TreeControl extends React.Component<TreeProps> {
joinValues={joinValues} joinValues={joinValues}
extractValue={extractValue} extractValue={extractValue}
delimiter={delimiter} delimiter={delimiter}
placeholder={placeholder} placeholder={__(placeholder)}
options={options} options={options}
multiple={multiple} multiple={multiple}
initiallyOpen={initiallyOpen} initiallyOpen={initiallyOpen}
@ -92,7 +93,7 @@ export default class TreeControl extends React.Component<TreeProps> {
withChildren={withChildren} withChildren={withChildren}
onlyChildren={onlyChildren} onlyChildren={onlyChildren}
hideRoot={hideRoot} hideRoot={hideRoot}
rootLabel={rootLabel} rootLabel={__(rootLabel)}
rootValue={rootValue} rootValue={rootValue}
showIcon={showIcon} showIcon={showIcon}
showRadio={showRadio} showRadio={showRadio}

View File

@ -132,15 +132,21 @@ export default class TreeSelectControl extends React.Component<
} }
validate(): any { validate(): any {
const {value, minLength, maxLength, delimiter} = this.props; const {value, minLength, maxLength, delimiter, translate: __} = this.props;
let curValue = Array.isArray(value) let curValue = Array.isArray(value)
? value ? value
: (value ? String(value) : '').split(delimiter || ','); : (value ? String(value) : '').split(delimiter || ',');
if (minLength && curValue.length < minLength) { if (minLength && curValue.length < minLength) {
return `已选择数量低于设定的最小个数${minLength},请选择更多的选项。`; return __(
'已选择数量低于设定的最小个数${minLength},请选择更多的选项。',
{minLength}
);
} else if (maxLength && curValue.length > maxLength) { } else if (maxLength && curValue.length > maxLength) {
return `已选择数量超出设定的最大个数${maxLength},请取消选择超出的选项。`; return __(
'已选择数量超出设定的最大个数{{maxLength}},请取消选择超出的选项。',
{maxLength}
);
} }
} }
@ -321,7 +327,8 @@ export default class TreeSelectControl extends React.Component<
labelField, labelField,
disabled, disabled,
placeholder, placeholder,
classnames: cx classnames: cx,
translate: __
} = this.props; } = this.props;
if ((!multiple || !selectedOptions.length) && this.state.inputValue) { if ((!multiple || !selectedOptions.length) && this.state.inputValue) {
@ -355,7 +362,7 @@ export default class TreeSelectControl extends React.Component<
) )
) : ( ) : (
<span key="placeholder" className={cx('TreeSelect-placeholder')}> <span key="placeholder" className={cx('TreeSelect-placeholder')}>
{placeholder} {__(placeholder)}
</span> </span>
); );
} }
@ -386,7 +393,8 @@ export default class TreeSelectControl extends React.Component<
searchable, searchable,
autoComplete, autoComplete,
maxLength, maxLength,
minLength minLength,
translate: __
} = this.props; } = this.props;
let filtedOptions = let filtedOptions =
@ -420,14 +428,14 @@ export default class TreeSelectControl extends React.Component<
joinValues={joinValues} joinValues={joinValues}
extractValue={extractValue} extractValue={extractValue}
delimiter={delimiter} delimiter={delimiter}
placeholder={optionsPlaceholder} placeholder={__(optionsPlaceholder)}
options={filtedOptions} options={filtedOptions}
highlightTxt={this.state.inputValue} highlightTxt={this.state.inputValue}
multiple={multiple} multiple={multiple}
initiallyOpen={initiallyOpen} initiallyOpen={initiallyOpen}
unfoldedLevel={unfoldedLevel} unfoldedLevel={unfoldedLevel}
withChildren={withChildren} withChildren={withChildren}
rootLabel={rootLabel} rootLabel={__(rootLabel)}
rootValue={rootValue} rootValue={rootValue}
showIcon={showIcon} showIcon={showIcon}
showRadio={showRadio} showRadio={showRadio}

View File

@ -460,13 +460,13 @@ export default class Form extends React.Component<FormProps, object> {
} }
submit(fn?: (values: object) => Promise<any>): Promise<any> { submit(fn?: (values: object) => Promise<any>): Promise<any> {
const {store, messages} = this.props; const {store, messages, translate: __} = this.props;
this.flush(); this.flush();
return store.submit( return store.submit(
fn, fn,
this.hooks['validate' || []], this.hooks['validate' || []],
messages && messages.validateFailed __(messages && messages.validateFailed)
); );
} }
@ -563,7 +563,8 @@ export default class Form extends React.Component<FormProps, object> {
env, env,
onChange, onChange,
clearPersistDataAfterSubmit, clearPersistDataAfterSubmit,
trimValues trimValues,
translate: __
} = this.props; } = this.props;
// 做动作之前,先把数据同步一下。 // 做动作之前,先把数据同步一下。
@ -581,7 +582,7 @@ export default class Form extends React.Component<FormProps, object> {
if (Array.isArray(action.required) && action.required.length) { if (Array.isArray(action.required) && action.required.length) {
return store.validateFields(action.required).then(result => { return store.validateFields(action.required).then(result => {
if (!result) { if (!result) {
env.notify('error', '依赖的部分字段没有通过验证,请注意填写!'); env.notify('error', __('依赖的部分字段没有通过验证,请注意填写!'));
} else { } else {
this.handleAction( this.handleAction(
e, e,
@ -706,15 +707,17 @@ export default class Form extends React.Component<FormProps, object> {
} else if (action.actionType === 'ajax') { } else if (action.actionType === 'ajax') {
store.setCurrentAction(action); store.setCurrentAction(action);
if (!isEffectiveApi(action.api)) { if (!isEffectiveApi(action.api)) {
return env.alert(`当 actionType 为 ajax 时,请设置 api 属性`); return env.alert(__(`当 actionType 为 ajax 时,请设置 api 属性`));
} }
return store return store
.saveRemote(action.api as Api, data, { .saveRemote(action.api as Api, data, {
successMessage: successMessage: __(
(action.messages && action.messages.success) || saveSuccess, (action.messages && action.messages.success) || saveSuccess
errorMessage: ),
errorMessage: __(
(action.messages && action.messages.failed) || saveFailed (action.messages && action.messages.failed) || saveFailed
)
}) })
.then(async response => { .then(async response => {
response && response &&
@ -833,7 +836,7 @@ export default class Form extends React.Component<FormProps, object> {
} }
buildActions() { buildActions() {
const {actions, submitText, controls} = this.props; const {actions, submitText, controls, translate: __} = this.props;
if ( if (
typeof actions !== 'undefined' || typeof actions !== 'undefined' ||
@ -852,7 +855,7 @@ export default class Form extends React.Component<FormProps, object> {
return [ return [
{ {
type: 'submit', type: 'submit',
label: submitText, label: __(submitText),
primary: true primary: true
} }
]; ];
@ -1126,7 +1129,8 @@ export default class Form extends React.Component<FormProps, object> {
bodyClassName, bodyClassName,
classnames: cx, classnames: cx,
affixFooter, affixFooter,
lazyLoad lazyLoad,
translate: __
} = this.props; } = this.props;
let body: JSX.Element = this.renderBody(); let body: JSX.Element = this.renderBody();
@ -1136,7 +1140,7 @@ export default class Form extends React.Component<FormProps, object> {
'body', 'body',
{ {
type: 'panel', type: 'panel',
title: title title: __(title)
}, },
{ {
className: cx(panelClassName, 'Panel--form'), className: cx(panelClassName, 'Panel--form'),

View File

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import {Renderer, RendererProps} from '../factory'; import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl'; import {filter} from '../utils/tpl';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {autobind} from '../utils/helper'; import {autobind} from '../utils/helper';
import {Icon} from '../components/icons'; import {Icon} from '../components/icons';
import {LocaleProps, localeable} from '../locale';
export interface ImageThumbProps { export interface ImageThumbProps extends LocaleProps, ThemeProps {
src: string; src: string;
originalSrc?: string; // 原图 originalSrc?: string; // 原图
enlargeAble?: boolean; enlargeAble?: boolean;
@ -19,8 +20,6 @@ export interface ImageThumbProps {
caption?: string; caption?: string;
thumbMode?: 'w-full' | 'h-full' | 'contain' | 'cover'; thumbMode?: 'w-full' | 'h-full' | 'contain' | 'cover';
thumbRatio?: '1:1' | '4:3' | '16:9'; thumbRatio?: '1:1' | '4:3' | '16:9';
classnames: ClassNamesFn;
classPrefix: string;
onLoad?: React.EventHandler<any>; onLoad?: React.EventHandler<any>;
} }
@ -43,7 +42,8 @@ export class ImageThumb extends React.Component<ImageThumbProps> {
title, title,
caption, caption,
onLoad, onLoad,
enlargeAble enlargeAble,
translate: __
} = this.props; } = this.props;
return ( return (
@ -65,7 +65,7 @@ export class ImageThumb extends React.Component<ImageThumbProps> {
{enlargeAble ? ( {enlargeAble ? (
<div key="overlay" className={cx('Image-overlay')}> <div key="overlay" className={cx('Image-overlay')}>
<a <a
data-tooltip="查看大图" data-tooltip={__('查看大图')}
data-position="bottom" data-position="bottom"
target="_blank" target="_blank"
onClick={this.handleEnlarge} onClick={this.handleEnlarge}
@ -87,7 +87,7 @@ export class ImageThumb extends React.Component<ImageThumbProps> {
); );
} }
} }
const ThemedImageThumb = themeable(ImageThumb); const ThemedImageThumb = themeable(localeable(ImageThumb));
export default ThemedImageThumb; export default ThemedImageThumb;
export interface ImageFieldProps extends RendererProps { export interface ImageFieldProps extends RendererProps {

View File

@ -24,7 +24,8 @@ export class LinkField extends React.Component<LinkProps, object> {
blank, blank,
htmlTarget, htmlTarget,
data, data,
render render,
translate: __
} = this.props; } = this.props;
let value = this.props.value; let value = this.props.value;
@ -36,7 +37,7 @@ export class LinkField extends React.Component<LinkProps, object> {
target={htmlTarget || (blank ? '_blank' : '_self')} target={htmlTarget || (blank ? '_blank' : '_self')}
className={cx('Link', className)} className={cx('Link', className)}
> >
{body ? render('body', body) : finnalHref || value || '链接'} {body ? render('body', body) : finnalHref || value || __('链接')}
</a> </a>
); );
} }

View File

@ -167,9 +167,9 @@ export default class List extends React.Component<ListProps, object> {
} }
componentDidMount() { componentDidMount() {
let parent: HTMLElement | Window | null = getScrollParent(findDOMNode( let parent: HTMLElement | Window | null = getScrollParent(
this findDOMNode(this) as HTMLElement
) as HTMLElement); );
if (!parent || parent === document.body) { if (!parent || parent === document.body) {
parent = window; parent = window;
} }
@ -251,9 +251,7 @@ export default class List extends React.Component<ListProps, object> {
const affixed = clip.top < offsetY && clip.top + clip.height - 40 > offsetY; const affixed = clip.top < offsetY && clip.top + clip.height - 40 > offsetY;
this.body.offsetWidth && this.body.offsetWidth &&
(afixedDom.style.cssText = `top: ${offsetY}px;width: ${ (afixedDom.style.cssText = `top: ${offsetY}px;width: ${this.body.offsetWidth}px;`);
this.body.offsetWidth
}px;`);
affixed ? afixedDom.classList.add('in') : afixedDom.classList.remove('in'); affixed ? afixedDom.classList.add('in') : afixedDom.classList.remove('in');
// store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY); // store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY);
} }
@ -494,9 +492,7 @@ export default class List extends React.Component<ListProps, object> {
<div className={cx('List-heading')}> <div className={cx('List-heading')}>
{store.modified && !hideQuickSaveBtn ? ( {store.modified && !hideQuickSaveBtn ? (
<span> <span>
{`当前有 ${ {`当前有 ${store.modified} 条记录修改了内容, 但并没有提交。请选择:`}
store.modified
} , :`}
<button <button
type="button" type="button"
className={cx('Button Button--xs Button--success m-l-sm')} className={cx('Button Button--xs Button--success m-l-sm')}
@ -732,7 +728,8 @@ export default class List extends React.Component<ListProps, object> {
checkOnItemClick, checkOnItemClick,
affixHeader, affixHeader,
classnames: cx, classnames: cx,
size size,
translate: __
} = this.props; } = this.props;
this.renderedToolbars = []; this.renderedToolbars = [];
@ -791,7 +788,7 @@ export default class List extends React.Component<ListProps, object> {
)} )}
</div> </div>
) : ( ) : (
<div className={cx('List-placeholder')}>{placeholder}</div> <div className={cx('List-placeholder')}>{__(placeholder)}</div>
)} )}
{this.renderFooter()} {this.renderFooter()}

View File

@ -128,7 +128,7 @@ export class Navigation extends React.Component<
return this.receive(query); return this.receive(query);
} }
const {data, env, source} = this.props; const {data, env, source, translate: __} = this.props;
const finalData = values ? createObject(data, values) : data; const finalData = values ? createObject(data, values) : data;
if (!isEffectiveApi(source, data)) { if (!isEffectiveApi(source, data)) {
@ -144,7 +144,7 @@ export class Navigation extends React.Component<
if (!payload.ok) { if (!payload.ok) {
this.setState({ this.setState({
error: payload.msg || '获取链接错误' error: payload.msg || __('获取链接错误')
}); });
} else { } else {
const links = Array.isArray(payload.data) const links = Array.isArray(payload.data)

View File

@ -91,7 +91,7 @@ export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (
} }
buildSchema() { buildSchema() {
const {popOver, name, label} = this.props; const {popOver, name, label, translate: __} = this.props;
let schema; let schema;
@ -108,7 +108,7 @@ export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (
type: popOver.mode, type: popOver.mode,
actions: [ actions: [
{ {
label: '关闭', label: __('关闭'),
type: 'button', type: 'button',
actionType: 'cancel' actionType: 'cancel'
} }

View File

@ -304,7 +304,7 @@ export const HocQuickEdit = (config: Partial<QuickEditConfig> = {}) => (
} }
buildSchema() { buildSchema() {
const {quickEdit, name, label} = this.props; const {quickEdit, name, label, translate: __} = this.props;
let schema; let schema;
@ -372,12 +372,12 @@ export const HocQuickEdit = (config: Partial<QuickEditConfig> = {}) => (
: [ : [
{ {
type: 'button', type: 'button',
label: '取消', label: __('取消'),
actionType: 'cancel' actionType: 'cancel'
}, },
{ {
label: '确认', label: __('确认'),
type: 'submit', type: 'submit',
primary: true primary: true
} }

View File

@ -213,14 +213,14 @@ export default class Service extends React.Component<ServiceProps> {
throwErrors: boolean = false, throwErrors: boolean = false,
delegate?: IScopedContext delegate?: IScopedContext
) { ) {
const {onAction, store, env, api} = this.props; const {onAction, store, env, api, translate: __} = this.props;
if (api && action.actionType === 'ajax') { if (api && action.actionType === 'ajax') {
store.setCurrentAction(action); store.setCurrentAction(action);
store store
.saveRemote(action.api as string, data, { .saveRemote(action.api as string, data, {
successMessage: action.messages && action.messages.success, successMessage: __(action.messages && action.messages.success),
errorMessage: action.messages && action.messages.failed errorMessage: __(action.messages && action.messages.failed)
}) })
.then(async () => { .then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) { if (action.feedback && isVisible(action.feedback, store.data)) {

View File

@ -959,7 +959,8 @@ export default class Table extends React.Component<TableProps, object> {
data, data,
classnames: cx, classnames: cx,
saveImmediately, saveImmediately,
headingClassName headingClassName,
translate: __
} = this.props; } = this.props;
if ( if (
@ -971,14 +972,19 @@ export default class Table extends React.Component<TableProps, object> {
<div className={cx('Table-heading', headingClassName)} key="heading"> <div className={cx('Table-heading', headingClassName)} key="heading">
{!saveImmediately && store.modified && !hideQuickSaveBtn ? ( {!saveImmediately && store.modified && !hideQuickSaveBtn ? (
<span> <span>
{`当前有 ${store.modified} 条记录修改了内容, 但并没有提交。请选择:`} {__(
'当前有 {{modified}} 条记录修改了内容, 但并没有提交。请选择:',
{
modified: store.modified
}
)}
<button <button
type="button" type="button"
className={cx('Button Button--xs Button--success m-l-sm')} className={cx('Button Button--xs Button--success m-l-sm')}
onClick={this.handleSave} onClick={this.handleSave}
> >
<i className="fa fa-check m-r-xs" /> <i className="fa fa-check m-r-xs" />
{__('提交')}
</button> </button>
<button <button
type="button" type="button"
@ -986,19 +992,21 @@ export default class Table extends React.Component<TableProps, object> {
onClick={this.reset} onClick={this.reset}
> >
<i className="fa fa-times m-r-xs" /> <i className="fa fa-times m-r-xs" />
{__('放弃')}
</button> </button>
</span> </span>
) : store.moved ? ( ) : store.moved ? (
<span> <span>
{`当前有 ${store.moved} 条记录修改了顺序, 但并没有提交。请选择:`} {__('当前有 {{moved}} 条记录修改了顺序, 但并没有提交。请选择:', {
moved: store.moved
})}
<button <button
type="button" type="button"
className={cx('Button Button--xs Button--success m-l-sm')} className={cx('Button Button--xs Button--success m-l-sm')}
onClick={this.handleSaveOrder} onClick={this.handleSaveOrder}
> >
<i className="fa fa-check m-r-xs" /> <i className="fa fa-check m-r-xs" />
{__('提交')}
</button> </button>
<button <button
type="button" type="button"
@ -1006,7 +1014,7 @@ export default class Table extends React.Component<TableProps, object> {
onClick={this.reset} onClick={this.reset}
> >
<i className="fa fa-times m-r-xs" /> <i className="fa fa-times m-r-xs" />
{__('放弃')}
</button> </button>
</span> </span>
) : title ? ( ) : title ? (
@ -1497,7 +1505,14 @@ export default class Table extends React.Component<TableProps, object> {
} }
renderDragToggler() { renderDragToggler() {
const {store, env, draggable, classPrefix: ns, dragIcon} = this.props; const {
store,
env,
draggable,
classPrefix: ns,
dragIcon,
translate: __
} = this.props;
if (!draggable || store.isNested) { if (!draggable || store.isNested) {
return null; return null;
@ -1508,7 +1523,7 @@ export default class Table extends React.Component<TableProps, object> {
disabled={!!store.modified} disabled={!!store.modified}
classPrefix={ns} classPrefix={ns}
key="dragging-toggle" key="dragging-toggle"
tooltip="点击开始排序" tooltip={__('点击开始排序')}
tooltipContainer={ tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined env && env.getModalContainer ? env.getModalContainer : undefined
} }
@ -1587,7 +1602,8 @@ export default class Table extends React.Component<TableProps, object> {
showHeader, showHeader,
store, store,
classnames: cx, classnames: cx,
data data,
translate: __
} = this.props; } = this.props;
if (showHeader === false) { if (showHeader === false) {
@ -1625,7 +1641,7 @@ export default class Table extends React.Component<TableProps, object> {
{child} {child}
{store.dragging ? ( {store.dragging ? (
<div className={cx('Table-dragTip')} ref={this.dragTipRef}> <div className={cx('Table-dragTip')} ref={this.dragTipRef}>
{__('请拖动左边的按钮进行排序')}
</div> </div>
) : null} ) : null}
</div> </div>
@ -1767,7 +1783,7 @@ export default class Table extends React.Component<TableProps, object> {
? filter(rowClassNameExpr, item.data) ? filter(rowClassNameExpr, item.data)
: rowClassName : rowClassName
)} )}
columns={columns} columns={store.footableColumns}
renderCell={this.renderCell} renderCell={this.renderCell}
render={render} render={render}
onAction={onAction} onAction={onAction}
@ -2160,7 +2176,7 @@ export class HeadCellSearchDropDown extends React.Component<
} }
buildSchema() { buildSchema() {
const {searchable, sortable, name, label} = this.props; const {searchable, sortable, name, label, translate: __} = this.props;
let schema; let schema;
@ -2207,14 +2223,14 @@ export class HeadCellSearchDropDown extends React.Component<
{ {
type: 'button-group', type: 'button-group',
name: 'orderDir', name: 'orderDir',
label: '排序', label: __('排序'),
options: [ options: [
{ {
label: '正序', label: __('正序'),
value: 'asc' value: 'asc'
}, },
{ {
label: '降序', label: __('降序'),
value: 'desc' value: 'desc'
} }
] ]
@ -2230,12 +2246,12 @@ export class HeadCellSearchDropDown extends React.Component<
actions: [ actions: [
{ {
type: 'button', type: 'button',
label: '取消', label: __('取消'),
actionType: 'cancel' actionType: 'cancel'
}, },
{ {
label: '搜索', label: __('搜索'),
type: 'submit', type: 'submit',
primary: true primary: true
} }

View File

@ -2,7 +2,6 @@
* @file video * @file video
* @author fex * @author fex
*/ */
/* eslint fecs-indent: [0, "space", 2, 2] */
import React from 'react'; import React from 'react';

View File

@ -650,7 +650,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
actionNextLabel, actionNextLabel,
actionNextSaveLabel, actionNextSaveLabel,
actionFinishLabel, actionFinishLabel,
render render,
translate: __
} = this.props; } = this.props;
if (!Array.isArray(steps)) { if (!Array.isArray(steps)) {
@ -698,7 +699,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
`prev-btn`, `prev-btn`,
{ {
type: 'button', type: 'button',
label: actionPrevLabel, label: __(actionPrevLabel),
actionType: 'prev', actionType: 'prev',
className: actionClassName className: actionClassName
}, },
@ -713,10 +714,10 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
{ {
type: 'button', type: 'button',
label: !nextStep label: !nextStep
? actionFinishLabel ? __(actionFinishLabel)
: !step.api : !step.api
? actionNextLabel ? __(actionNextLabel)
: actionNextSaveLabel, : __(actionNextSaveLabel),
actionType: 'next', actionType: 'next',
primary: !nextStep || !!step.api, primary: !nextStep || !!step.api,
className: actionClassName className: actionClassName

View File

@ -2,7 +2,6 @@
import cx from 'classnames'; import cx from 'classnames';
import React from 'react'; import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics'; import hoistNonReactStatic from 'hoist-non-react-statics';
import {ExtractProps, Omit} from './types';
export type ClassValue = export type ClassValue =
| string | string
@ -117,17 +116,19 @@ export const ThemeContext = React.createContext('theme');
export let defaultTheme: string = 'default'; export let defaultTheme: string = 'default';
export function themeable< export function themeable<
T extends React.ComponentType<ThemeProps & ExtractProps<T>> T extends React.ComponentType<React.ComponentProps<T> & ThemeProps>
>(ComposedComponent: T) { >(ComposedComponent: T) {
type ComposedProps = JSX.LibraryManagedAttributes<T, ExtractProps<T>>; type OuterProps = JSX.LibraryManagedAttributes<
type Props = Omit<ComposedProps, keyof ThemeProps> & { T,
Omit<React.ComponentProps<T>, keyof ThemeProps>
> & {
theme?: string; theme?: string;
classPrefix?: string; classPrefix?: string;
classnames?: ClassNamesFn; classnames?: ClassNamesFn;
}; };
const result = hoistNonReactStatic( const result = hoistNonReactStatic(
class extends React.Component<Props> { class extends React.Component<OuterProps> {
static displayName = `Themeable(${ static displayName = `Themeable(${
ComposedComponent.displayName || ComposedComponent.name ComposedComponent.displayName || ComposedComponent.name
})`; })`;
@ -150,9 +151,10 @@ export function themeable<
return ( return (
<ThemeContext.Provider value={theme}> <ThemeContext.Provider value={theme}>
<ComposedComponent <ComposedComponent
{ {...(this.props as JSX.LibraryManagedAttributes<
...(this.props as any) /* todo, 解决这个类型问题 */ T,
} React.ComponentProps<T>
>)}
{...injectedProps} {...injectedProps}
/> />
</ThemeContext.Provider> </ThemeContext.Provider>

View File

@ -140,15 +140,15 @@ export interface RendererData {
type RendererDataAlis = RendererData; type RendererDataAlis = RendererData;
export type FunctionPropertyNames<T> = { export type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T]; }[keyof T];
export interface JSONSchema { export interface JSONSchema {
[propsName: string]: any; [propsName: string]: any;
} }
export type Omit<T, K extends keyof T & any> = Pick<T, Exclude<keyof T, K>>; // export type Omit<T, K extends keyof T & any> = Pick<T, Exclude<keyof T, K>>;
export type Override<M, N> = Omit<M, Extract<keyof M, keyof N>> & N; // export type Override<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
export type ExtractProps< // export type ExtractProps<
TComponentOrTProps // TComponentOrTProps
> = TComponentOrTProps extends React.ComponentType<infer P> ? P : never; // > = TComponentOrTProps extends React.ComponentType<infer P> ? P : never;

View File

@ -607,7 +607,7 @@ export function isBreakpoint(str: string): boolean {
const breaks = str.split(/\s*,\s*|\s+/); const breaks = str.split(/\s*,\s*|\s+/);
if (window.matchMedia) { if ((window as any).matchMedia) {
return breaks.some( return breaks.some(
item => item =>
item === '*' || item === '*' ||
@ -1095,7 +1095,7 @@ export function sortArray<T extends any>(
field: string, field: string,
dir: -1 | 1 dir: -1 | 1
): Array<T> { ): Array<T> {
return items.sort((a, b) => { return items.sort((a: any, b: any) => {
let ret: number; let ret: number;
const a1 = a[field]; const a1 = a[field];
const b1 = b[field]; const b1 = b[field];

View File

@ -2,7 +2,6 @@
* @file resize-sensor.js. * @file resize-sensor.js.
* @author fex * @author fex
*/ */
/* eslint-disable */
class EventQueue { class EventQueue {
q: Array<Function> = []; q: Array<Function> = [];
@ -70,7 +69,7 @@ function attachResizeEvent(element: HTMLElement, resized: Function) {
let lastWidth: number, lastHeight: number; let lastWidth: number, lastHeight: number;
const reset = function() { const reset = function () {
expandChild.style.width = expand.offsetWidth + 10 + 'px'; expandChild.style.width = expand.offsetWidth + 10 + 'px';
expandChild.style.height = expand.offsetHeight + 10 + 'px'; expandChild.style.height = expand.offsetHeight + 10 + 'px';
expand.scrollLeft = expand.scrollWidth; expand.scrollLeft = expand.scrollWidth;
@ -83,13 +82,13 @@ function attachResizeEvent(element: HTMLElement, resized: Function) {
reset(); reset();
let changed = function() { let changed = function () {
if ((element as any).resizedAttached) { if ((element as any).resizedAttached) {
(element as any).resizedAttached.call(); (element as any).resizedAttached.call();
} }
}; };
let addEvent = function(el: HTMLElement, name: string, cb: Function) { let addEvent = function (el: HTMLElement, name: string, cb: Function) {
if ((el as any).attachEvent) { if ((el as any).attachEvent) {
(el as any).attachEvent('on' + name, cb); (el as any).attachEvent('on' + name, cb);
} else { } else {
@ -97,7 +96,7 @@ function attachResizeEvent(element: HTMLElement, resized: Function) {
} }
}; };
let onScroll = function(e: Event) { let onScroll = function (e: Event) {
if ( if (
element.offsetWidth != lastWidth || element.offsetWidth != lastWidth ||
element.offsetHeight != lastHeight element.offsetHeight != lastHeight
@ -132,7 +131,7 @@ export function resizeSensor(
once: boolean = false once: boolean = false
) { ) {
if (once) { if (once) {
attachResizeEvent(element, function(this: any) { attachResizeEvent(element, function (this: any) {
callback.apply(this, arguments); callback.apply(this, arguments);
detach(element); detach(element);
}); });
@ -142,7 +141,7 @@ export function resizeSensor(
attachResizeEvent(element, callback); attachResizeEvent(element, callback);
let detached = false; let detached = false;
return function() { return function () {
if (detached) return; if (detached) return;
detached = true; detached = true;
detach(element); detach(element);

View File

@ -1,7 +1,10 @@
declare module 'uncontrollable' { declare module 'uncontrollable' {
function uncontrollable<T extends React.ComponentType<any>, P extends { function uncontrollable<
[propName:string]: any T extends Raect.ComponentType<any>,
}>(arg:T, config:P):T; P extends {
[propName: string]: any;
}
>(arg: T, config: P): T;
export = uncontrollable; export = uncontrollable;
} }