Locale 支持

This commit is contained in:
2betop 2020-06-02 20:41:51 +08:00
parent ae4fb3afbf
commit f4ac5e2a6e
72 changed files with 1087 additions and 624 deletions

View File

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

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import ChainedCheckboxes from './ChainedCheckboxes';
import Spinner from './Spinner';
import TreeRadios from './TreeRadios';
import {Icon} from './icons';
import {localeable} from '../locale';
export interface AssociatedCheckboxesProps extends CheckboxesProps {
leftOptions: Options;
@ -113,6 +114,7 @@ export class AssociatedCheckboxes extends Checkboxes<
options,
(option: Option) => option.ref
);
const __ = this.props.translate;
return (
<div className={cx('AssociatedCheckboxes', className)}>
@ -155,9 +157,9 @@ export class AssociatedCheckboxes extends Checkboxes<
</div>
{selectdOption.loading ? (
<p></p>
<p>{__('加载中')}</p>
) : (
<p></p>
<p>{__('点击刷新重新加载')}</p>
)}
</div>
) : rightMode === 'table' ? (
@ -193,12 +195,12 @@ export class AssociatedCheckboxes extends Checkboxes<
)
) : (
<div className={cx('AssociatedCheckboxes-box')}>
{__('配置错误,选项无法与左侧选项对应')}
</div>
)
) : (
<div className={cx('AssociatedCheckboxes-box')}>
{__('请先选择左侧数据')}
</div>
)}
</div>
@ -208,7 +210,9 @@ export class AssociatedCheckboxes extends Checkboxes<
}
export default themeable(
localeable(
uncontrollable(AssociatedCheckboxes, {
value: 'onChange'
})
)
);

View File

@ -10,6 +10,7 @@ import {Option} from './Select';
import {getTreeDepth} from '../utils/helper';
import times from 'lodash/times';
import Spinner from './Spinner';
import {localeable} from '../locale';
export interface ChainedCheckboxesProps extends CheckboxesProps {
defaultSelectedIndex?: string;
@ -188,13 +189,15 @@ export class ChainedCheckboxes extends Checkboxes<
);
}
const __ = this.props.translate;
return (
<div className={cx('ChainedCheckboxes', className)}>
{body && body.length ? (
body
) : (
<div className={cx('ChainedCheckboxes-placeholder')}>
{placeholder}
{__(placeholder)}
</div>
)}
</div>
@ -203,7 +206,9 @@ export class ChainedCheckboxes extends Checkboxes<
}
export default themeable(
localeable(
uncontrollable(ChainedCheckboxes, {
value: 'onChange'
})
)
);

View File

@ -13,9 +13,10 @@ import {Option, value2array, Options} from './Select';
import find from 'lodash/find';
import {autobind, findTree} from '../utils/helper';
import isEqual from 'lodash/isEqual';
import {LocaleProps, localeable} from '../locale';
// import isPlainObject from 'lodash/isPlainObject';
export interface CheckboxesProps extends ThemeProps {
export interface CheckboxesProps extends ThemeProps, LocaleProps {
options: Options;
className?: string;
placeholder?: string;
@ -115,6 +116,8 @@ export class Checkboxes<
itemRender
} = this.props;
const __ = this.props.translate;
let valueArray = Checkboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = [];
@ -142,14 +145,16 @@ export class Checkboxes<
inline ? 'Checkboxes--inline' : ''
)}
>
{body && body.length ? body : <div>{placeholder}</div>}
{body && body.length ? body : <div>{__(placeholder)}</div>}
</div>
);
}
}
export default themeable(
localeable(
uncontrollable(Checkboxes, {
value: 'onChange'
})
)
);

View File

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

View File

@ -11,10 +11,11 @@ import 'moment/locale/zh-cn';
import {Icon} from './icons';
import PopOver from './PopOver';
import Overlay from './Overlay';
import {ClassNamesFn, themeable} from '../theme';
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {PlainObject} from '../types';
import Calendar from './calendar/Calendar';
import 'react-datetime/css/react-datetime.css';
import {localeable, LocaleProps, TranslateFn} from '../locale';
const availableShortcuts: {[propName: string]: any} = {
now: {
@ -97,9 +98,9 @@ const availableShortcuts: {[propName: string]: any} = {
const advancedShortcuts = [
{
regexp: /^(\d+)hoursago$/,
resolve: (_: string, hours: string) => {
resolve: (_: string, hours: string, __: TranslateFn) => {
return {
label: `${hours}小时前`,
label: __('${hours}小时前', {hours}),
date: (now: moment.Moment) => {
return now.subtract(hours, 'hours');
}
@ -108,9 +109,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)hourslater$/,
resolve: (_: string, hours: string) => {
resolve: (_: string, hours: string, __: TranslateFn) => {
return {
label: `${hours}小时后`,
label: __('${hours}小时后', {hours}),
date: (now: moment.Moment) => {
return now.add(hours, 'hours');
}
@ -119,9 +120,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)daysago$/,
resolve: (_: string, days: string) => {
resolve: (_: string, days: string, __: TranslateFn) => {
return {
label: `${days}天前`,
label: __('${days}天前', {days}),
date: (now: moment.Moment) => {
return now.subtract(days, 'days');
}
@ -130,9 +131,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)dayslater$/,
resolve: (_: string, days: string) => {
resolve: (_: string, days: string, __: TranslateFn) => {
return {
label: `${days}天后`,
label: __('${days}天后', {days}),
date: (now: moment.Moment) => {
return now.add(days, 'days');
}
@ -141,9 +142,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)weeksago$/,
resolve: (_: string, weeks: string) => {
resolve: (_: string, weeks: string, __: TranslateFn) => {
return {
label: `${weeks}周前`,
label: __('${weeks}周前', {weeks}),
date: (now: moment.Moment) => {
return now.subtract(weeks, 'weeks');
}
@ -152,9 +153,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)weekslater$/,
resolve: (_: string, weeks: string) => {
resolve: (_: string, weeks: string, __: TranslateFn) => {
return {
label: `${weeks}周后`,
label: __('${weeks}周后', {weeks}),
date: (now: moment.Moment) => {
return now.add(weeks, 'weeks');
}
@ -163,9 +164,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)monthsago$/,
resolve: (_: string, months: string) => {
resolve: (_: string, months: string, __: TranslateFn) => {
return {
label: `${months}月前`,
label: __('${months}月前', {months}),
date: (now: moment.Moment) => {
return now.subtract(months, 'months');
}
@ -174,9 +175,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)monthslater$/,
resolve: (_: string, months: string) => {
resolve: (_: string, months: string, __: TranslateFn) => {
return {
label: `${months}月后`,
label: __('${months}月后', {months}),
date: (now: moment.Moment) => {
return now.add(months, 'months');
}
@ -185,9 +186,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)quartersago$/,
resolve: (_: string, quarters: string) => {
resolve: (_: string, quarters: string, __: TranslateFn) => {
return {
label: `${quarters}季度前`,
label: __('${quarters}季度前', {quarters}),
date: (now: moment.Moment) => {
return now.subtract(quarters, 'quarters');
}
@ -196,9 +197,9 @@ const advancedShortcuts = [
},
{
regexp: /^(\d+)quarterslater$/,
resolve: (_: string, quarters: string) => {
resolve: (_: string, quarters: string, __: TranslateFn) => {
return {
label: `${quarters}季度后`,
label: __('${quarters}季度后', {quarters}),
date: (now: moment.Moment) => {
return now.add(quarters, 'quarters');
}
@ -226,17 +227,15 @@ export type ShortCuts =
| ShortCutDate
| ShortCutDateRange;
export interface DateProps {
export interface DateProps extends LocaleProps, ThemeProps {
viewMode: 'years' | 'months' | 'days' | 'time';
className?: string;
classPrefix: string;
classnames: ClassNamesFn;
placeholder?: string;
inputFormat?: string;
timeFormat?: string;
format?: string;
timeConstrainst?: object;
closeOnSelect?: boolean;
closeOnSelect: boolean;
disabled?: boolean;
minDate?: moment.Moment;
maxDate?: moment.Moment;
@ -247,7 +246,14 @@ export interface DateProps {
value: any;
shortcuts: string | Array<ShortCuts>;
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 {
@ -257,11 +263,8 @@ export interface DatePickerState {
}
export class DatePicker extends React.Component<DateProps, DatePickerState> {
static defaultProps: Pick<
DateProps,
'viewMode' | 'shortcuts' | 'closeOnSelect' | 'overlayPlacement'
> = {
viewMode: 'days',
static defaultProps = {
viewMode: 'days' as 'years' | 'months' | 'days' | 'time',
shortcuts: '',
closeOnSelect: true,
overlayPlacement: 'auto'
@ -429,12 +432,14 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
return availableShortcuts[key];
}
const __ = this.props.locale;
for (let i = 0, len = advancedShortcuts.length; i < len; i++) {
let item = advancedShortcuts[i];
const m = item.regexp.exec(key);
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 {
shortcutArr = shortcuts;
}
const __ = this.props.translate;
return (
<ul className={`${ns}DatePicker-shortcuts`}>
{shortcutArr.map(item => {
@ -474,7 +481,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
onClick={() => this.selectRannge(shortcut)}
key={shortcut.key || shortcut.label}
>
<a>{shortcut.label}</a>
<a>{__(shortcut.label)}</a>
</li>
);
})}
@ -498,9 +505,11 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
clearable,
shortcuts,
utc,
overlayPlacement
overlayPlacement,
locale
} = this.props;
const __ = this.props.translate;
const isOpened = this.state.isOpened;
let date: moment.Moment | undefined = this.state.value;
@ -526,7 +535,9 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
{date.format(inputFormat)}
</span>
) : (
<span className={`${ns}DatePicker-placeholder`}>{placeholder}</span>
<span className={`${ns}DatePicker-placeholder`}>
{__(placeholder)}
</span>
)}
{clearable && !disabled && value ? (
@ -565,6 +576,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
timeConstraints={timeConstraints}
input={false}
onClose={this.close}
locale={locale}
// utc={utc}
/>
</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 Calendar from './calendar/Calendar';
import PopOver from './PopOver';
import {ClassNamesFn, themeable} from '../theme';
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {PlainObject} from '../types';
import {noop} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
export interface DateRangePickerProps {
export interface DateRangePickerProps extends ThemeProps, LocaleProps {
className?: string;
classPrefix: string;
classnames: ClassNamesFn;
placeholder?: string;
theme?: any;
format: string;
@ -39,7 +38,10 @@ export interface DateRangePickerProps {
disabled?: boolean;
closeOnSelect?: boolean;
overlayPlacement: string;
[propName: string]: any;
timeFormat?: string;
resetValue?: any;
popOverContainer?: any;
dateFormat?: string;
}
export interface DateRangePickerState {
@ -106,10 +108,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('week').add(-1, 'weeks');
},
endDate: (now: moment.Moment) => {
return now
.startOf('week')
.add(-1, 'days')
.endOf('day');
return now.startOf('week').add(-1, 'days').endOf('day');
}
},
@ -129,10 +128,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('month').add(-1, 'month');
},
endDate: (now: moment.Moment) => {
return now
.startOf('month')
.add(-1, 'day')
.endOf('day');
return now.startOf('month').add(-1, 'day').endOf('day');
}
},
@ -142,10 +138,7 @@ const availableRanges: {[propName: string]: any} = {
return now.startOf('quarter').add(-1, 'quarter');
},
endDate: (now: moment.Moment) => {
return now
.startOf('quarter')
.add(-1, 'day')
.endOf('day');
return now.startOf('quarter').add(-1, 'day').endOf('day');
}
},
@ -416,6 +409,8 @@ export class DateRangePicker extends React.Component<
} else {
rangeArr = ranges;
}
const __ = this.props.translate;
return (
<ul className={`${ns}DateRangePicker-rangers`}>
{rangeArr.map(item => {
@ -442,7 +437,7 @@ export class DateRangePicker extends React.Component<
onClick={() => this.selectRannge(range)}
key={range.key || range.label}
>
<a>{range.label}</a>
<a>{__(range.label)}</a>
</li>
);
})}
@ -551,6 +546,7 @@ export class DateRangePicker extends React.Component<
const arr = [];
startViewValue && arr.push(startViewValue);
endViewValue && arr.push(endViewValue);
const __ = this.props.translate;
return (
<div
@ -571,11 +567,11 @@ export class DateRangePicker extends React.Component<
>
{arr.length ? (
<span className={`${ns}DateRangePicker-value`}>
{arr.join(' 至 ')}
{arr.join(__(' 至 '))}
</span>
) : (
<span className={`${ns}DateRangePicker-placeholder`}>
{placeholder}
{__(placeholder)}
</span>
)}
@ -646,10 +642,10 @@ export class DateRangePicker extends React.Component<
})}
onClick={this.confirm}
>
{__('确认')}
</a>
<a className="rdtBtn rdtBtnCancel" onClick={this.close}>
{__('取消')}
</a>
</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 {themeable, ClassNamesFn} from '../theme';
import {themeable, ClassNamesFn, ThemeProps} from '../theme';
import {autobind} from '../utils/helper';
import Modal from './Modal';
import {Icon} from './icons';
import {LocaleProps, localeable} from '../locale';
export interface ImageGalleryProps {
classnames: ClassNamesFn;
classPrefix: string;
export interface ImageGalleryProps extends ThemeProps, LocaleProps {
children: React.ReactNode;
modalContainer?: () => HTMLElement;
}
@ -87,6 +86,7 @@ export class ImageGallery extends React.Component<
render() {
const {children, classnames: cx, modalContainer} = this.props;
const {index, items} = this.state;
const __ = this.props.translate;
return (
<>
@ -103,7 +103,7 @@ export class ImageGallery extends React.Component<
container={modalContainer}
>
<a
data-tooltip="关闭"
data-tooltip={__('关闭')}
data-position="left"
className={cx('ImageGallery-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

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

View File

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

View File

@ -6,8 +6,9 @@ import Checkbox from './Checkbox';
import {Option, Options} from './Select';
import {findTree, autobind} from '../utils/helper';
import isEqual from 'lodash/isEqual';
import {LocaleProps, localeable} from '../locale';
export interface ListRadiosProps extends ThemeProps {
export interface ListRadiosProps extends ThemeProps, LocaleProps {
options: Options;
className?: string;
placeholder: string;
@ -120,6 +121,7 @@ export class ListRadios<
classnames: cx,
option2value
} = this.props;
const __ = this.props.translate;
this.selected = ListRadios.resolveSelected(value, options, option2value);
let body: Array<React.ReactNode> = [];
@ -133,7 +135,7 @@ export class ListRadios<
{body && body.length ? (
body
) : (
<div className={cx('ListRadios-placeholder')}>{placeholder}</div>
<div className={cx('ListRadios-placeholder')}>{__(placeholder)}</div>
)}
</div>
);
@ -141,9 +143,11 @@ export class ListRadios<
}
const themedListRadios = themeable(
localeable(
uncontrollable(ListRadios, {
value: 'onChange'
})
)
);
themedListRadios.resolveSelected = ListRadios.resolveSelected;

View File

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

View File

@ -14,8 +14,9 @@ import {Portal} from 'react-overlays';
import {current, addModal, removeModal} from './ModalManager';
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {Icon} from './icons';
import {LocaleProps, localeable} from '../locale';
export interface ModalProps extends ThemeProps {
export interface ModalProps extends ThemeProps, LocaleProps {
className?: string;
contentClassName?: string;
size?: any;
@ -44,6 +45,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
};
static Header = themeable(
localeable(
({
classnames: cx,
className,
@ -51,8 +53,10 @@ export class Modal extends React.Component<ModalProps, ModalState> {
onClose,
children,
classPrefix,
translate: __,
...rest
}: ThemeProps & {
}: ThemeProps &
LocaleProps & {
className?: string;
showCloseButton?: boolean;
onClose?: () => void;
@ -61,7 +65,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
<div {...rest} className={cx('Modal-header', className)}>
{showCloseButton !== false ? (
<a
data-tooltip="关闭弹窗"
data-tooltip={__('关闭弹窗')}
data-position="left"
onClick={onClose}
className={cx('Modal-close')}
@ -72,6 +76,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
{children}
</div>
)
)
);
static Title = themeable(
@ -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;
Title: typeof Modal.Title;
Body: typeof Modal.Body;

View File

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

View File

@ -8,8 +8,9 @@ import {Icon} from './icons';
import {autobind, guid} from '../utils/helper';
import Sortable from 'sortablejs';
import {findDOMNode} from 'react-dom';
import {LocaleProps, localeable} from '../locale';
export interface ResultListProps extends ThemeProps {
export interface ResultListProps extends ThemeProps, LocaleProps {
className?: string;
value?: Array<Option>;
onChange?: (value: Array<Option>) => void;
@ -123,7 +124,8 @@ export class ResultList extends React.Component<ResultListProps> {
disabled,
title,
itemClassName,
sortable
sortable,
translate: __
} = this.props;
return (
@ -163,11 +165,11 @@ export class ResultList extends React.Component<ResultListProps> {
))}
</div>
) : (
<div className={cx('Selections-placeholder')}>{placeholder}</div>
<div className={cx('Selections-placeholder')}>{__(placeholder)}</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 uncontrollable from 'uncontrollable';
import {autobind} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
export interface SearchBoxProps extends ThemeProps {
export interface SearchBoxProps extends ThemeProps, LocaleProps {
name?: string;
onChange?: (text: string) => void;
placeholder?: string;
@ -47,7 +48,8 @@ export class SearchBox extends React.Component<SearchBoxProps> {
active,
name,
onChange,
placeholder
placeholder,
translate: __
} = this.props;
return (
@ -56,7 +58,7 @@ export class SearchBox extends React.Component<SearchBoxProps> {
name={name}
onChange={this.handleChange}
value={value || ''}
placeholder={placeholder || '输入关键字'}
placeholder={__(placeholder || '输入关键字')}
ref={this.inputRef}
/>
@ -75,8 +77,10 @@ export class SearchBox extends React.Component<SearchBoxProps> {
}
export default themeable(
localeable(
uncontrollable(SearchBox, {
active: 'onActiveChange',
value: 'onChange'
})
)
);

View File

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

View File

@ -5,6 +5,7 @@ import uncontrollable from 'uncontrollable';
import Checkbox from './Checkbox';
import {Option} from './Select';
import {resolveVariable} from '../utils/tpl-builtin';
import {localeable} from '../locale';
export interface TableCheckboxesProps extends CheckboxesProps {
columns: Array<{
@ -94,7 +95,8 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
classnames: cx,
cellRender,
value,
option2value
option2value,
translate: __
} = this.props;
const columns = this.getColumns();
let valueArray = Checkboxes.value2array(value, options, option2value);
@ -123,7 +125,7 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
})
) : (
<tr>
<td colSpan={columns.length}>{placeholder}</td>
<td colSpan={columns.length}>{__(placeholder)}</td>
</tr>
)}
</tbody>
@ -187,7 +189,9 @@ export class TableCheckboxes extends Checkboxes<TableCheckboxesProps> {
}
export default themeable(
localeable(
uncontrollable(TableCheckboxes, {
value: 'onChange'
})
)
);

View File

@ -10,6 +10,7 @@ import {Options, Option} from './Select';
import Transfer, {TransferProps} from './Transfer';
import {themeable} from '../theme';
import AssociatedCheckboxes from './AssociatedCheckboxes';
import {localeable} from '../locale';
export interface TabsTransferProps
extends Omit<
@ -101,13 +102,14 @@ export class TabsTransfer extends React.Component<TabsTransferProps> {
onSearch: searchable,
option2value,
onDeferLoad,
cellRender
cellRender,
translate: __
} = this.props;
if (!Array.isArray(options) || !options.length) {
return (
<div className={cx('TabsTransfer-placeholder')}>
{placeholder || '暂无选项'}
{__(placeholder || '暂无选项')}
</div>
);
}
@ -128,7 +130,7 @@ export class TabsTransfer extends React.Component<TabsTransferProps> {
>
{searchResult !== null
? [
<Tab title="搜索结果" key={0} eventKey={0}>
<Tab title={__('搜索结果')} key={0} eventKey={0}>
{this.renderSearchResult(searchResult)}
</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/emoticons';
import 'tinymce/plugins/emoticons/js/emojis';
import {LocaleProps} from '../locale';
interface TinymceEditorProps {
interface TinymceEditorProps extends LocaleProps {
model: string;
onModelChange?: (value: string) => void;
onFocus?: () => void;
@ -58,12 +59,14 @@ export default class TinymceEditor extends React.Component<TinymceEditorProps> {
elementRef: React.RefObject<HTMLTextAreaElement> = React.createRef();
componentDidMount() {
const locale = this.props.locale;
this.config = {
inline: false,
skin: false,
content_css: false,
height: 400,
language: 'zh_CN',
language: !locale || locale === 'zh-cn' ? 'zh_CN' : '',
plugins: [
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker',
'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking',

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import Checkbox from './Checkbox';
import {Option} from './Select';
import {autobind, eachTree, everyTree} from '../utils/helper';
import Spinner from './Spinner';
import {localeable} from '../locale';
export interface TreeCheckboxesProps extends CheckboxesProps {
expand?: 'all' | 'first' | 'root' | 'none';
@ -257,7 +258,8 @@ export class TreeCheckboxes extends Checkboxes<
className,
placeholder,
classnames: cx,
option2value
option2value,
translate: __
} = this.props;
this.valueArray = Checkboxes.value2array(value, options, option2value);
@ -272,7 +274,9 @@ export class TreeCheckboxes extends Checkboxes<
{body && body.length ? (
body
) : (
<div className={cx('TreeCheckboxes-placeholder')}>{placeholder}</div>
<div className={cx('TreeCheckboxes-placeholder')}>
{__(placeholder)}
</div>
)}
</div>
);
@ -280,7 +284,9 @@ export class TreeCheckboxes extends Checkboxes<
}
export default themeable(
localeable(
uncontrollable(TreeCheckboxes, {
value: 'onChange'
})
)
);

View File

@ -168,7 +168,8 @@ export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> {
className,
placeholder,
classnames: cx,
option2value
option2value,
translate: __
} = this.props;
this.selected = ListRadios.resolveSelected(value, options, option2value);
@ -183,7 +184,7 @@ export class TreeRadios extends ListRadios<TreeRadiosProps, TreeRadiosState> {
{body && body.length ? (
body
) : (
<div className={cx('TreeRadios-placeholder')}>{placeholder}</div>
<div className={cx('TreeRadios-placeholder')}>{__(placeholder)}</div>
)}
</div>
);

View File

@ -2,8 +2,9 @@ import moment from 'moment';
// @ts-ignore
import DaysView from 'react-datetime/src/DaysView';
import React from 'react';
import {LocaleProps, localeable} from '../../locale';
interface CustomDaysViewProps {
interface CustomDaysViewProps extends LocaleProps {
classPrefix?: string;
prevIcon?: string;
nextIcon?: string;
@ -36,7 +37,7 @@ interface CustomDaysViewProps {
handleClickOutside: () => void;
}
export default class CustomDaysView extends DaysView {
export class CustomDaysView extends DaysView {
props: CustomDaysViewProps;
getDaysOfWeek: (locale: any) => any;
renderDays: () => JSX.Element;
@ -160,6 +161,8 @@ export default class CustomDaysView extends DaysView {
return null;
}
const __ = this.props.translate;
return (
<tfoot key="tf">
<tr>
@ -168,10 +171,10 @@ export default class CustomDaysView extends DaysView {
{this.props.requiredConfirm ? (
<div key="button" className="rdtActions">
<a className="rdtBtn rdtBtnConfirm" onClick={this.confirm}>
{__('确认')}
</a>
<a className="rdtBtn rdtBtnCancel" onClick={this.cancel}>
{__('取消')}
</a>
</div>
) : null}
@ -185,6 +188,7 @@ export default class CustomDaysView extends DaysView {
const footer = this.renderFooter();
const date = this.props.viewDate;
const locale = date.localeData();
const __ = this.props.translate;
const tableChildren = [
<thead key="th">
@ -206,13 +210,13 @@ export default class CustomDaysView extends DaysView {
<div className="rdtCenter">
<a className="rdtSwitch" onClick={this.props.showView('years')}>
{date.format('YYYY年')}
{date.format(__('YYYY年'))}
</a>
<a
className="rdtSwitch"
onClick={this.props.showView('months')}
>
{date.format('MM月')}
{date.format(__('MMM'))}
</a>
</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 moment from 'moment';
import React from 'react';
import {LocaleProps, localeable} from '../../locale';
export default class CustomMonthsView extends MonthsView {
export class CustomMonthsView extends MonthsView {
props: {
viewDate: moment.Moment;
subtractTime: (
@ -16,7 +17,7 @@ export default class CustomMonthsView extends MonthsView {
type: string,
toSelected?: moment.Moment
) => () => void;
};
} & LocaleProps;
renderMonths: () => JSX.Element;
renderMonth = (props: any, month: number) => {
var localMoment = this.props.viewDate;
@ -34,6 +35,8 @@ export default class CustomMonthsView extends MonthsView {
);
};
render() {
const __ = this.props.translate;
return (
<div className="rdtMonths">
<table>
@ -45,7 +48,9 @@ export default class CustomMonthsView extends MonthsView {
>
«
</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>
@ -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 moment from 'moment';
import React from 'react';
import {LocaleProps, localeable} from '../../locale';
export default class CustomYearsView extends YearsView {
export class CustomYearsView extends YearsView {
props: {
viewDate: moment.Moment;
subtractTime: (
@ -17,7 +18,7 @@ export default class CustomYearsView extends YearsView {
toSelected?: moment.Moment
) => () => void;
showView: (view: string) => () => void;
};
} & LocaleProps;
renderYears: (year: number) => JSX.Element;
renderYear = (props: any, year: number) => {
return (
@ -29,6 +30,7 @@ export default class CustomYearsView extends YearsView {
render() {
let year = this.props.viewDate.year();
year = year - (year % 10);
const __ = this.props.translate;
return (
<div className="rdtYears">
@ -42,7 +44,7 @@ export default class CustomYearsView extends YearsView {
«
</th>
<th className="rdtSwitch">
{year}-{year + 9}
{__('${from}年-${to}年', {from: year, to: year + 9})}
</th>
<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,
Schema,
Action,
Omit,
RendererData
} from './types';
import {observer} from 'mobx-react';
@ -34,11 +33,24 @@ import omit from 'lodash/omit';
import difference from 'lodash/difference';
import isPlainObject from 'lodash/isPlainObject';
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 Alert from './components/Alert2';
import {LazyComponent} from './components';
import ImageGallery from './components/ImageGallery';
import {
TranslateFn,
getDefaultLocale,
makeTranslator,
LocaleContext,
LocaleProps
} from './locale';
export interface TestFunc {
(
@ -98,11 +110,9 @@ export interface RendererEnv {
[propName: string]: any;
}
export interface RendererProps {
export interface RendererProps extends ThemeProps, LocaleProps {
render: (region: string, node: SchemaNode, props?: any) => JSX.Element;
env: RendererEnv;
classPrefix: string;
classnames: ClassNamesFn;
$path: string; // 当前组件所在的层级信息
store?: IIRendererStore;
syncSuperStore?: boolean;
@ -193,7 +203,7 @@ export function filterSchema(
}
export function Renderer(config: RendererBasicConfig) {
return function<T extends RendererComponent>(component: T): T {
return function <T extends RendererComponent>(component: T): T {
const renderer = registerRenderer({
...config,
component: component
@ -215,9 +225,7 @@ export function registerRenderer(config: RendererConfig): RendererConfig {
if (~rendererNames.indexOf(config.name)) {
throw new Error(
`The renderer with name "${
config.name
}" has already exists, please try another name!`
`The renderer with name "${config.name}" has already exists, please try another name!`
);
}
@ -322,6 +330,8 @@ export interface RootRendererProps {
env: RendererEnv;
theme: string;
pathPrefix?: string;
locale?: string;
translate?: TranslateFn;
[propName: string]: any;
}
@ -362,6 +372,8 @@ export class RootRenderer extends React.Component<RootRendererProps> {
pathPrefix,
location,
data,
locale,
translate,
...rest
} = this.props;
@ -385,6 +397,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
return (
<RootStoreContext.Provider value={rootStore}>
<ThemeContext.Provider value={this.props.theme || 'default'}>
<LocaleContext.Provider value={this.props.locale!}>
<ImageGallery modalContainer={env.getModalContainer}>
{
renderChild(
@ -402,11 +415,14 @@ export class RootRenderer extends React.Component<RootRendererProps> {
data: finalData,
env,
classnames: theme.classnames,
classPrefix: theme.classPrefix
classPrefix: theme.classPrefix,
locale,
translate
}
) as JSX.Element
}
</ImageGallery>
</LocaleContext.Provider>
</ThemeContext.Provider>
</RootStoreContext.Provider>
);
@ -634,7 +650,7 @@ export function HocStoreFactory(renderer: {
storeType: string;
extendsData?: boolean;
}): any {
return function<T extends React.ComponentType<RendererProps>>(Component: T) {
return function <T extends React.ComponentType<RendererProps>>(Component: T) {
type Props = Omit<
RendererProps,
'store' | 'data' | 'dataUpdatedAt' | 'scope'
@ -646,8 +662,9 @@ export function HocStoreFactory(renderer: {
@observer
class StoreFactory extends React.Component<Props> {
static displayName = `WithStore(${Component.displayName ||
Component.name})`;
static displayName = `WithStore(${
Component.displayName || Component.name
})`;
static ComposedComponent = Component;
static contextType = RootStoreContext;
store: IIRendererStore;
@ -839,7 +856,7 @@ export function HocStoreFactory(renderer: {
return (
<Component
{
...rest as any /* todo */
...(rest as any) /* todo */
}
{...exprProps}
ref={this.refFn}
@ -956,6 +973,8 @@ export function render(
const env = getEnv(store);
const theme = props.theme || options.theme || 'default';
env.theme = getTheme(theme);
const locale = props.locale || getDefaultLocale();
const translate = props.translate || makeTranslator(locale);
return (
<ScopedRootRenderer
@ -965,6 +984,8 @@ export function render(
rootStore={store}
env={env}
theme={theme}
locale={locale}
translate={translate}
/>
);
}

View File

@ -2,15 +2,16 @@
import cx from 'classnames';
import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';
import {ExtractProps, Omit} from './types';
import {isObject} from './utils/helper';
import {resolveVariable, resolveVariableAndFilter} from './utils/tpl-builtin';
type translateFn = (str: string, ...args: any[]) => string;
export type TranslateFn<T = any> = (str: T, data?: object) => T;
interface LocaleConfig {
[propsName: string]: string;
}
let defaultLocale: string = '';
let defaultLocale: string = 'zh-cn';
const locales: {
[propName: string]: LocaleConfig;
@ -21,54 +22,68 @@ export function register(name: string, config: LocaleConfig) {
}
const fns: {
[propName: string]: translateFn;
[propName: string]: TranslateFn;
} = {};
// todo 支持参数
function format(str: string, ...args: any[]) {
return str;
function format(str: string, data?: object) {
return str.replace(
/(\\)?\$([a-z0-9_.]+?)|\$\{([\s\S]+?)\}/g,
(_, escape, key1, key2) => {
if (escape) {
return _.substring(1);
}
return resolveVariable(key1 || key2, data || {});
}
);
}
export function makeTranslator(locale?: string): translateFn {
export function makeTranslator(locale?: string): TranslateFn {
if (locale && fns[locale]) {
return fns[locale];
}
const fn = (str: string, ...args: any[]) => {
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);
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;
translate: TranslateFn;
}
export const LocaleContext = React.createContext('locale');
export const LocaleContext = React.createContext('');
export function localeable<
T extends React.ComponentType<LocaleProps & ExtractProps<T>>
T extends React.ComponentType<React.ComponentProps<T> & LocaleProps>
>(ComposedComponent: T) {
type ComposedProps = JSX.LibraryManagedAttributes<T, ExtractProps<T>>;
type Props = Omit<ComposedProps, keyof LocaleProps> & {
type OuterProps = JSX.LibraryManagedAttributes<
T,
Omit<React.ComponentProps<T>, keyof LocaleProps>
> & {
locale?: string;
translate: (str: string, ...args: any[]) => string;
translate?: (str: string, ...args: any[]) => string;
};
const result = hoistNonReactStatic(
class extends React.Component<Props> {
class extends React.Component<OuterProps> {
static displayName = `I18N(${
ComposedComponent.displayName || ComposedComponent.name
})`;
@ -78,21 +93,22 @@ export function localeable<
render() {
const locale: string =
this.props.locale || this.context || defaultLocale;
const translate = makeTranslator(locale);
const translate = this.props.translate || makeTranslator(locale);
const injectedProps: {
locale: string;
translate: translateFn;
translate: TranslateFn;
} = {
locale,
translate
translate: translate!
};
return (
<LocaleContext.Provider value={locale}>
<ComposedComponent
{
...(this.props as any) /* todo, 解决这个类型问题 */
}
{...(this.props as JSX.LibraryManagedAttributes<
T,
React.ComponentProps<T>
>)}
{...injectedProps}
/>
</LocaleContext.Provider>

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',
'本周一': 'This monday',
'本月初': 'Early this month',
'上个月初': 'Early prev month',
'上个季节初': 'Early this quarter',
'明天': 'Tomorrow',
'本周日': 'Last day of this week',
'本月底': '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 week',
'最近90天': 'Last 90 days',
'上周': 'Prev week',
'本月': 'This month',
'上个月': 'Prev month',
'上个季节': 'Prev 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',
'新增一条数据': 'Create a new data',
'类型': 'Type',
'拖拽排序': 'Drag to sort',
'删除失败': 'Delete success',
'确认要删除?': 'Are u sure to delete?',
'组合表单成员数量不够,低于设定的最小${minLenth}个,请添加更多的成员。':
'The count of members is less than ${minLenth}.',
'组合表单成员数量超出,超出设定的最大${maxLength}个,请删除多余的成员。':
'The count of members is more than ${maxLength}.',
'子表单验证失败,请仔细检查': 'Validate failed, please check this sub-form.',
'成员${index}': 'Member ${index}',
'清空数据': 'Clear data',
'您选择的文件 ${filename} 大小为 ${actualSize} 超出了最大为 ${maxSize} 的限制,请重新选择。':
'The size of ${filename} ${actualSize} is great than ${maxSize}.',
'您添加的文件${files}不符合类型的`${accept}`的设定,请仔细检查。':
'The files ${files} are not match `${accept}`, please retry.',
'把文件拖到这,然后松完成添加!':
'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}个文件上传失败,':
'${uploaded} files success, ${failed} failed, ',
'失败文件': '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',
'裁剪图片': '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 new 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -226,7 +226,8 @@ export class CityPicker extends React.Component<
disabled,
allowCity,
allowDistrict,
allowStreet
allowStreet,
translate: __
} = this.props;
const {provinceCode, cityCode, districtCode, street} = this.state;
@ -290,7 +291,7 @@ export class CityPicker extends React.Component<
value={street}
onChange={this.handleStreetChange}
onBlur={this.handleStreetEnd}
placeholder="请输入街道信息"
placeholder={__('请输入街道信息')}
/>
) : null}
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,7 +58,13 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
renderInput() {
const value = this.props.value;
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> = [];
@ -68,7 +74,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
}));
optionsArray.unshift({
label: placeholder as string,
label: __(placeholder as string),
value: ''
});
@ -181,7 +187,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
<div className="repeat-control hbox">
{input ? (
<div className="col v-middle" style={{width: 30}}>
<span></span>
<span>{__('每')}</span>
</div>
) : null}
@ -192,7 +198,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
classPrefix={ns}
className={input ? 'pull-right' : ''}
options={optionsArray}
placeholder={placeholder}
placeholder={__(placeholder)}
onChange={this.handleOptionChange}
value={parts[0]}
clearable={false}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -321,7 +321,8 @@ export default class TreeSelectControl extends React.Component<
labelField,
disabled,
placeholder,
classnames: cx
classnames: cx,
translate: __
} = this.props;
if ((!multiple || !selectedOptions.length) && this.state.inputValue) {
@ -355,7 +356,7 @@ export default class TreeSelectControl extends React.Component<
)
) : (
<span key="placeholder" className={cx('TreeSelect-placeholder')}>
{placeholder}
{__(placeholder)}
</span>
);
}
@ -386,7 +387,8 @@ export default class TreeSelectControl extends React.Component<
searchable,
autoComplete,
maxLength,
minLength
minLength,
translate: __
} = this.props;
let filtedOptions =
@ -420,14 +422,14 @@ export default class TreeSelectControl extends React.Component<
joinValues={joinValues}
extractValue={extractValue}
delimiter={delimiter}
placeholder={optionsPlaceholder}
placeholder={__(optionsPlaceholder)}
options={filtedOptions}
highlightTxt={this.state.inputValue}
multiple={multiple}
initiallyOpen={initiallyOpen}
unfoldedLevel={unfoldedLevel}
withChildren={withChildren}
rootLabel={rootLabel}
rootLabel={__(rootLabel)}
rootValue={rootValue}
showIcon={showIcon}
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> {
const {store, messages} = this.props;
const {store, messages, translate: __} = this.props;
this.flush();
return store.submit(
fn,
this.hooks['validate' || []],
messages && messages.validateFailed
__(messages && messages.validateFailed)
);
}
@ -563,7 +563,8 @@ export default class Form extends React.Component<FormProps, object> {
env,
onChange,
clearPersistDataAfterSubmit,
trimValues
trimValues,
translate: __
} = this.props;
// 做动作之前,先把数据同步一下。
@ -581,7 +582,7 @@ export default class Form extends React.Component<FormProps, object> {
if (Array.isArray(action.required) && action.required.length) {
return store.validateFields(action.required).then(result => {
if (!result) {
env.notify('error', '依赖的部分字段没有通过验证,请注意填写!');
env.notify('error', __('依赖的部分字段没有通过验证,请注意填写!'));
} else {
this.handleAction(
e,
@ -706,15 +707,17 @@ export default class Form extends React.Component<FormProps, object> {
} else if (action.actionType === 'ajax') {
store.setCurrentAction(action);
if (!isEffectiveApi(action.api)) {
return env.alert(`当 actionType 为 ajax 时,请设置 api 属性`);
return env.alert(__(`当 actionType 为 ajax 时,请设置 api 属性`));
}
return store
.saveRemote(action.api as Api, data, {
successMessage:
(action.messages && action.messages.success) || saveSuccess,
errorMessage:
successMessage: __(
(action.messages && action.messages.success) || saveSuccess
),
errorMessage: __(
(action.messages && action.messages.failed) || saveFailed
)
})
.then(async response => {
response &&
@ -833,7 +836,7 @@ export default class Form extends React.Component<FormProps, object> {
}
buildActions() {
const {actions, submitText, controls} = this.props;
const {actions, submitText, controls, translate: __} = this.props;
if (
typeof actions !== 'undefined' ||
@ -852,7 +855,7 @@ export default class Form extends React.Component<FormProps, object> {
return [
{
type: 'submit',
label: submitText,
label: __(submitText),
primary: true
}
];
@ -1126,7 +1129,8 @@ export default class Form extends React.Component<FormProps, object> {
bodyClassName,
classnames: cx,
affixFooter,
lazyLoad
lazyLoad,
translate: __
} = this.props;
let body: JSX.Element = this.renderBody();
@ -1136,7 +1140,7 @@ export default class Form extends React.Component<FormProps, object> {
'body',
{
type: 'panel',
title: title
title: __(title)
},
{
className: cx(panelClassName, 'Panel--form'),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -140,15 +140,15 @@ export interface RendererData {
type RendererDataAlis = RendererData;
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];
export interface JSONSchema {
[propsName: string]: any;
}
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 ExtractProps<
TComponentOrTProps
> = TComponentOrTProps extends React.ComponentType<infer P> ? P : never;
// 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 ExtractProps<
// TComponentOrTProps
// > = TComponentOrTProps extends React.ComponentType<infer P> ? P : never;