From 0499b70218a38c3700cdb1ac6070e9be5ce8f3f7 Mon Sep 17 00:00:00 2001 From: zuiidea Date: Wed, 24 Oct 2018 18:52:56 +0800 Subject: [PATCH] Translation user and post page. --- mock/_utils.js | 9 +- mock/user.js | 8 +- src/locales/en/messages.json | 42 +++++- src/locales/zh/messages.json | 42 +++++- src/pages/post/components/List.js | 122 ++++++++-------- src/pages/post/components/List.less | 16 -- src/pages/post/index.js | 14 +- src/pages/request/index.js | 4 +- src/pages/user/$id/index.js | 5 +- src/pages/user/components/Filter.js | 187 +++++++++++++----------- src/pages/user/components/List.js | 185 ++++++++++++----------- src/pages/user/components/List.less | 14 -- src/pages/user/components/Modal.js | 218 ++++++++++++++-------------- src/pages/user/index.js | 9 +- src/utils/model.js | 1 - 15 files changed, 495 insertions(+), 381 deletions(-) delete mode 100644 src/pages/post/components/List.less delete mode 100644 src/pages/user/components/List.less diff --git a/mock/_utils.js b/mock/_utils.js index 012a580..91e0a69 100644 --- a/mock/_utils.js +++ b/mock/_utils.js @@ -17,17 +17,24 @@ export function randomNumber(min, max) { } export function randomAvatar() { + // https://uifaces.co const avatarList = [ 'https://randomuser.me/api/portraits/men/32.jpg', 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?h=350&auto=compress&cs=tinysrgb', 'https://d3iw72m71ie81c.cloudfront.net/female-17.jpg', 'https://randomuser.me/api/portraits/men/35.jpg', 'https://pbs.twimg.com/profile_images/835224725815246848/jdMBCxHS.jpg', - 'https://pbs.twimg.com/profile_images/1010888664732200962/OmFg5TO0.jpg', 'https://pbs.twimg.com/profile_images/584098247641300992/N25WgvW_.png', 'https://d3iw72m71ie81c.cloudfront.net/male-5.jpg', 'https://images.pexels.com/photos/413723/pexels-photo-413723.jpeg?h=350&auto=compress&cs=tinysrgb', 'https://images.pexels.com/photos/61100/pexels-photo-61100.jpeg?h=350&auto=compress&cs=tinysrgb', + 'https://randomuser.me/api/portraits/women/44.jpg', + 'https://randomuser.me/api/portraits/women/68.jpg', + 'https://randomuser.me/api/portraits/women/65.jpg', + 'https://randomuser.me/api/portraits/men/43.jpg', + 'https://tinyfac.es/data/avatars/475605E3-69C5-4D2B-8727-61B7BB8C4699-500w.jpeg', + 'https://pbs.twimg.com/profile_images/943227488292962306/teiNNAiy.jpg', + 'https://randomuser.me/api/portraits/men/46.jpg' ] return avatarList[randomNumber(0, avatarList.length - 1)] } diff --git a/mock/user.js b/mock/user.js index 3801f8d..c0260c5 100644 --- a/mock/user.js +++ b/mock/user.js @@ -15,13 +15,7 @@ let usersListData = Mock.mock({ email: '@email', createTime: '@datetime', avatar() { - return Mock.Random.image( - '100x100', - Mock.Random.color(), - '#757575', - 'png', - this.nickName.substr(0, 1) - ) + return randomAvatar() }, }, ], diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index ccff8a7..4e8253a 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1,14 +1,54 @@ { "/dashboard": "/dashboard", + "Add Param": "Add Param", + "Address": "Address", + "Age": "Age", + "Are you sure delete this record?": "Are you sure delete this record?", + "Author": "Author", + "Avatar": "Avatar", + "Categories": "Categories", "Clear notifications": "Clear notifications", + "Comments": "Comments", + "Create": "Create", + "Create User": "Create User", + "CreateTime": "CreateTime", "Dark": "Dark", + "Delete": "Delete", + "Email": "Email", + "Female": "Female", + "Gender": "Gender", "Hi,": "Hi,", + "Image": "Image", "Light": "Light", + "Male": "Male", + "Name": "Name", + "NickName": "NickName", "Not Found": "Not Found", + "Operation": "Operation", + "Params": "Params", "Password": "Password", + "Phone": "Phone", + "Pick an address": "Pick an address", + "Please pick an address": "Please pick an address", + "Publised": "Publised", + "Publish Date": "Publish Date", + "Reset": "Reset", + "Search": "Search", + "Search Name": "Search Name", + "Send": "Send", "Sign in": "Sign in", "Sign out": "Sign out", "Switch Theme": "Switch Theme", + "Tags": "Tags", + "The input is not valid E-mail!": "The input is not valid E-mail!", + "The input is not valid phone!": "The input is not valid phone!", + "Title": "Title", + "Total {total} Items": "Total {total} Items", + "Unpublished": "Unpublished", + "Update": "Update", + "Update User": "Update User", "Username": "Username", + "Views": "Views", + "Visibility": "Visibility", "You have viewed all notifications.": "You have viewed all notifications." -} +} \ No newline at end of file diff --git a/src/locales/zh/messages.json b/src/locales/zh/messages.json index 972a3e5..fcf3a47 100644 --- a/src/locales/zh/messages.json +++ b/src/locales/zh/messages.json @@ -1,14 +1,54 @@ { "/dashboard": "/zh/dashboard", + "Add Param": "添加参数", + "Address": "地址", + "Age": "年龄", + "Are you sure delete this record?": "您确定要删除这条记录吗?", + "Author": "作者", + "Avatar": "头像", + "Categories": "类别", "Clear notifications": "清空消息", + "Comments": "评论数", + "Create": "创建", + "Create User": "创建用户", + "CreateTime": "创建时间", "Dark": "暗", + "Delete": "删除", + "Email": "电子邮件", + "Female": "女", + "Gender": "性别", "Hi,": "你好,", + "Image": "图像", "Light": "明", + "Male": "男性", + "Name": "名字", + "NickName": "昵称", "Not Found": "未找到", + "Operation": "操作", + "Params": "参数", "Password": "密码", + "Phone": "电话", + "Pick an address": "选择地址", + "Please pick an address": "选择地址", + "Publised": "已发布", + "Publish Date": "发布日期", + "Reset": "重置", + "Search": "搜索", + "Search Name": "搜索名字", + "Send": "发送", "Sign in": "登录", "Sign out": "退出登录", "Switch Theme": "切换主题", + "Tags": "标签", + "The input is not valid E-mail!": "输入的电子邮件无效!", + "The input is not valid phone!": "输入无效的手机!", + "Title": "标题", + "Total {total} Items": "总共 {total} 条记录", + "Unpublished": "未发布", + "Update": "更新", + "Update User": "更新用户", "Username": "用户名", + "Views": "浏览数", + "Visibility": "可见性", "You have viewed all notifications.": "您已查看所有通知" -} +} \ No newline at end of file diff --git a/src/pages/post/components/List.js b/src/pages/post/components/List.js index a3567da..cecaee2 100644 --- a/src/pages/post/components/List.js +++ b/src/pages/post/components/List.js @@ -1,63 +1,69 @@ -import React from 'react' -import { Table } from 'antd' -import styles from './List.less' +import React, { PureComponent } from 'react' +import { Table, Avatar } from 'antd' +import { withI18n } from '@lingui/react' -const List = ({ ...tableProps }) => { - const columns = [ - { - title: 'Image', - dataIndex: 'image', - className: styles.image, - width: 64, - render: text => Feture, - }, - { - title: 'Title', - dataIndex: 'title', - }, - { - title: 'Author', - dataIndex: 'author', - }, - { - title: 'Categories', - dataIndex: 'categories', - }, - { - title: 'Tags', - dataIndex: 'tags', - }, - { - title: 'Visibility', - dataIndex: 'visibility', - }, - { - title: 'Comments', - dataIndex: 'comments', - }, - { - title: 'Views', - dataIndex: 'views', - }, - { - title: 'Date', - dataIndex: 'date', - }, - ] +@withI18n() +class List extends PureComponent { + render() { + const { i18n, ...tableProps } = this.props + const columns = [ + { + title: i18n.t`Image`, + dataIndex: 'image', + width: 100, + render: text => , + }, + { + title: i18n.t`Title`, + dataIndex: 'title', + }, + { + title: i18n.t`Author`, + dataIndex: 'author', + }, + { + title: i18n.t`Categories`, + dataIndex: 'categories', + }, + { + title: i18n.t`Tags`, + dataIndex: 'tags', + }, + { + title: i18n.t`Visibility`, + dataIndex: 'visibility', + }, + { + title: i18n.t`Comments`, + dataIndex: 'comments', + }, + { + title: i18n.t`Views`, + dataIndex: 'views', + }, + { + title: i18n.t`Publish Date`, + dataIndex: 'date', + }, + ] - return ( -
- record.id} - /> - - ) + return ( +
+
i18n.t`Total ${total} Items`, + }} + bordered + scroll={{ x: 1200 }} + columns={columns} + simple + rowKey={record => record.id} + /> + + ) + } } export default List diff --git a/src/pages/post/components/List.less b/src/pages/post/components/List.less deleted file mode 100644 index 941c0fc..0000000 --- a/src/pages/post/components/List.less +++ /dev/null @@ -1,16 +0,0 @@ -.table { - td.image { - line-height: 1; - - img { - border-radius: 2px; - } - } - - :global { - .ant-table-tbody > tr > td, - .ant-table-thead > tr > th { - height: 60px; - } - } -} diff --git a/src/pages/post/index.js b/src/pages/post/index.js index 6516595..e71a90a 100644 --- a/src/pages/post/index.js +++ b/src/pages/post/index.js @@ -4,6 +4,7 @@ import { connect } from 'dva' import { Tabs } from 'antd' import { router } from 'utils' import { stringify } from 'qs' +import { withI18n } from '@lingui/react' import { Page } from 'components' import List from './components/List' @@ -14,10 +15,11 @@ const EnumPostStatus = { PUBLISHED: 2, } +@withI18n() @connect(({ post, loading }) => ({ post, loading })) class Post extends PureComponent { render() { - const { post, loading, location } = this.props + const { post, loading, location, i18n } = this.props const { list, pagination } = post const { query, pathname } = location @@ -56,10 +58,16 @@ class Post extends PureComponent { } onTabClick={handleTabClick} > - + - + diff --git a/src/pages/request/index.js b/src/pages/request/index.js index a34bb1c..911e101 100644 --- a/src/pages/request/index.js +++ b/src/pages/request/index.js @@ -237,7 +237,9 @@ class RequestPage extends React.Component { ))} - + diff --git a/src/pages/user/$id/index.js b/src/pages/user/$id/index.js index 0b4a44a..8924db3 100644 --- a/src/pages/user/$id/index.js +++ b/src/pages/user/$id/index.js @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import { connect } from 'dva' +import { Page } from 'components' import styles from './index.less' @connect(({ userDetail }) => ({ userDetail })) @@ -20,9 +21,9 @@ class UserDetail extends PureComponent { } } return ( -
+
{content}
-
+ ) } } diff --git a/src/pages/user/components/Filter.js b/src/pages/user/components/Filter.js index ea66c96..c031d30 100644 --- a/src/pages/user/components/Filter.js +++ b/src/pages/user/components/Filter.js @@ -1,8 +1,9 @@ /* global document */ -import React from 'react' +import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import moment from 'moment' import { FilterItem } from 'components' +import { Trans, withI18n } from '@lingui/react' import { Form, Button, Row, Col, DatePicker, Input, Cascader } from 'antd' import city from 'utils/city' @@ -22,13 +23,10 @@ const TwoColProps = { xl: 96, } -const Filter = ({ - onAdd, - onFilterChange, - filter, - form: { getFieldDecorator, getFieldsValue, setFieldsValue }, -}) => { - const handleFields = fields => { +@withI18n() +@Form.create() +class Filter extends PureComponent { + handleFields = fields => { const { createTime } = fields if (createTime.length) { fields.createTime = [ @@ -39,13 +37,19 @@ const Filter = ({ return fields } - const handleSubmit = () => { + handleSubmit = () => { + const { onFilterChange, form } = this.props + const { getFieldsValue } = form + let fields = getFieldsValue() - fields = handleFields(fields) + fields = this.handleFields(fields) onFilterChange(fields) } - const handleReset = () => { + handleReset = () => { + const { form } = this.props + const { getFieldsValue, setFieldsValue } = form + const fields = getFieldsValue() for (let item in fields) { if ({}.hasOwnProperty.call(fields, item)) { @@ -57,86 +61,107 @@ const Filter = ({ } } setFieldsValue(fields) - handleSubmit() + this.handleSubmit() } + handleChange = (key, values) => { + const { form, onFilterChange } = this.props + const { getFieldsValue } = form - const handleChange = (key, values) => { let fields = getFieldsValue() fields[key] = values - fields = handleFields(fields) + fields = this.handleFields(fields) onFilterChange(fields) } - const { name, address } = filter - let initialCreateTime = [] - if (filter.createTime && filter.createTime[0]) { - initialCreateTime[0] = moment(filter.createTime[0]) - } - if (filter.createTime && filter.createTime[1]) { - initialCreateTime[1] = moment(filter.createTime[1]) - } + render() { + const { onAdd, filter, form, i18n } = this.props + const { getFieldDecorator } = form + const { name, address } = filter - return ( - -
- {getFieldDecorator('name', { initialValue: name })( - - )} - - - {getFieldDecorator('address', { initialValue: address })( - document.getElementById('addressCascader')} - /> - )} - - - - {getFieldDecorator('createTime', { initialValue: initialCreateTime })( - { - return document.getElementById('createTimeRangePicker') - }} + let initialCreateTime = [] + if (filter.createTime && filter.createTime[0]) { + initialCreateTime[0] = moment(filter.createTime[0]) + } + if (filter.createTime && filter.createTime[1]) { + initialCreateTime[1] = moment(filter.createTime[1]) + } + + return ( + + + {getFieldDecorator('name', { initialValue: name })( + )} - - - - -
-
+ {getFieldDecorator('address', { initialValue: address })( + + document.getElementById('addressCascader') + } + /> + )} + + + + {getFieldDecorator('createTime', { + initialValue: initialCreateTime, + })( + { + return document.getElementById('createTimeRangePicker') + }} + /> + )} + + + + +
+ + +
+ - - - -
- - - ) + + + + ) + } } Filter.propTypes = { @@ -146,4 +171,4 @@ Filter.propTypes = { onFilterChange: PropTypes.func, } -export default Form.create()(Filter) +export default Filter diff --git a/src/pages/user/components/List.js b/src/pages/user/components/List.js index 2779dca..60e69f7 100644 --- a/src/pages/user/components/List.js +++ b/src/pages/user/components/List.js @@ -1,19 +1,22 @@ -import React from 'react' +import React, { PureComponent } from 'react' import PropTypes from 'prop-types' -import { Table, Modal } from 'antd' +import { Table, Modal, Avatar } from 'antd' import { DropOption } from 'components' +import { Trans, withI18n } from '@lingui/react' import Link from 'umi/link' -import styles from './List.less' const { confirm } = Modal -const List = ({ onDeleteItem, onEditItem, location, ...tableProps }) => { - const handleMenuClick = (record, e) => { +@withI18n() +class List extends PureComponent { + handleMenuClick = (record, e) => { + const { onDeleteItem, onEditItem, i18n } = this.props + if (e.key === '1') { onEditItem(record) } else if (e.key === '2') { confirm({ - title: 'Are you sure delete this record?', + title: i18n.t`Are you sure delete this record?`, onOk() { onDeleteItem(record.id) }, @@ -21,86 +24,98 @@ const List = ({ onDeleteItem, onEditItem, location, ...tableProps }) => { } } - const columns = [ - { - title: 'Avatar', - dataIndex: 'avatar', - key: 'avatar', - width: 64, - className: styles.avatar, - render: text => avatar, - }, - { - title: 'Name', - dataIndex: 'name', - key: 'name', - render: (text, record) => {text}, - }, - { - title: 'NickName', - dataIndex: 'nickName', - key: 'nickName', - }, - { - title: 'Age', - dataIndex: 'age', - key: 'age', - }, - { - title: 'Gender', - dataIndex: 'isMale', - key: 'isMale', - render: text => {text ? 'Male' : 'Female'}, - }, - { - title: 'Phone', - dataIndex: 'phone', - key: 'phone', - }, - { - title: 'Email', - dataIndex: 'email', - key: 'email', - }, - { - title: 'Address', - dataIndex: 'address', - key: 'address', - }, - { - title: 'CreateTime', - dataIndex: 'createTime', - key: 'createTime', - }, - { - title: 'Operation', - key: 'operation', - width: 100, - render: (text, record) => { - return ( - handleMenuClick(record, e)} - menuOptions={[ - { key: '1', name: 'Update' }, - { key: '2', name: 'Delete' }, - ]} - /> - ) - }, - }, - ] + render() { + const { + handleMenuClick, + onDeleteItem, + onEditItem, + i18n, + ...tableProps + } = this.props - return ( -
record.id} - /> - ) + const columns = [ + { + title: Avatar, + dataIndex: 'avatar', + key: 'avatar', + width: 100, + render: text => , + }, + { + title: Name, + dataIndex: 'name', + key: 'name', + render: (text, record) => {text}, + }, + { + title: NickName, + dataIndex: 'nickName', + key: 'nickName', + }, + { + title: Age, + dataIndex: 'age', + key: 'age', + }, + { + title: Gender, + dataIndex: 'isMale', + key: 'isMale', + render: text => {text ? 'Male' : 'Female'}, + }, + { + title: Phone, + dataIndex: 'phone', + key: 'phone', + }, + { + title: Email, + dataIndex: 'email', + key: 'email', + }, + { + title: Address, + dataIndex: 'address', + key: 'address', + }, + { + title: CreateTime, + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: Operation, + key: 'operation', + width: 100, + render: (text, record) => { + return ( + handleMenuClick(record, e)} + menuOptions={[ + { key: '1', name: i18n.t`Update` }, + { key: '2', name: i18n.t`Delete` }, + ]} + /> + ) + }, + }, + ] + + return ( +
i18n.t`Total ${total} Items`, + }} + bordered + scroll={{ x: 1250 }} + columns={columns} + simple + rowKey={record => record.id} + /> + ) + } } List.propTypes = { diff --git a/src/pages/user/components/List.less b/src/pages/user/components/List.less deleted file mode 100644 index f2bc5ba..0000000 --- a/src/pages/user/components/List.less +++ /dev/null @@ -1,14 +0,0 @@ -.table { - td.avatar { - img { - border-radius: 50%; - } - } - - :global { - .ant-table-tbody > tr > td, - .ant-table-thead > tr > th { - height: 62px; - } - } -} diff --git a/src/pages/user/components/Modal.js b/src/pages/user/components/Modal.js index 21266e3..8f2a193 100644 --- a/src/pages/user/components/Modal.js +++ b/src/pages/user/components/Modal.js @@ -1,6 +1,7 @@ -import React from 'react' +import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import { Form, Input, InputNumber, Radio, Modal, Cascader } from 'antd' +import { Trans, withI18n } from '@lingui/react' import city from 'utils/city' const FormItem = Form.Item @@ -13,14 +14,13 @@ const formItemLayout = { span: 14, }, } +@withI18n() +@Form.create() +class UserModal extends PureComponent { + handleOk = () => { + const { item = {}, onOk, form } = this.props + const { validateFields, getFieldsValue } = form -const modal = ({ - item = {}, - onOk, - form: { getFieldDecorator, validateFields, getFieldsValue }, - ...modalProps -}) => { - const handleOk = () => { validateFields(errors => { if (errors) { return @@ -34,111 +34,115 @@ const modal = ({ }) } - const modalOpts = { - ...modalProps, - onOk: handleOk, - } + render() { + const { item = {}, onOk, form, i18n, ...modalProps } = this.props + const { getFieldDecorator } = form - return ( - -
- - {getFieldDecorator('name', { - initialValue: item.name, - rules: [ - { - required: true, - }, - ], - })()} - - - {getFieldDecorator('nickName', { - initialValue: item.nickName, - rules: [ - { - required: true, - }, - ], - })()} - - - {getFieldDecorator('isMale', { - initialValue: item.isMale, - rules: [ - { - required: true, - type: 'boolean', - }, - ], - })( - - Male - Female - - )} - - - {getFieldDecorator('age', { - initialValue: item.age, - rules: [ - { - required: true, - type: 'number', - }, - ], - })()} - - - {getFieldDecorator('phone', { - initialValue: item.phone, - rules: [ - { - required: true, - pattern: /^1[34578]\d{9}$/, - message: 'The input is not valid phone!', - }, - ], - })()} - - - {getFieldDecorator('email', { - initialValue: item.email, - rules: [ - { - required: true, - pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/, - message: 'The input is not valid E-mail!', - }, - ], - })()} - - - {getFieldDecorator('address', { - initialValue: item.address && item.address.split(' '), - rules: [ - { - required: true, - }, - ], - })( - - )} - - -
- ) + return ( + +
+ + {getFieldDecorator('name', { + initialValue: item.name, + rules: [ + { + required: true, + }, + ], + })()} + + + {getFieldDecorator('nickName', { + initialValue: item.nickName, + rules: [ + { + required: true, + }, + ], + })()} + + + {getFieldDecorator('isMale', { + initialValue: item.isMale, + rules: [ + { + required: true, + type: 'boolean', + }, + ], + })( + + + Male + + + Female + + + )} + + + {getFieldDecorator('age', { + initialValue: item.age, + rules: [ + { + required: true, + type: 'number', + }, + ], + })()} + + + {getFieldDecorator('phone', { + initialValue: item.phone, + rules: [ + { + required: true, + pattern: /^1[34578]\d{9}$/, + message: i18n.t`The input is not valid phone!`, + }, + ], + })()} + + + {getFieldDecorator('email', { + initialValue: item.email, + rules: [ + { + required: true, + pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/, + message: i18n.t`The input is not valid E-mail!`, + }, + ], + })()} + + + {getFieldDecorator('address', { + initialValue: item.address && item.address.split(' '), + rules: [ + { + required: true, + }, + ], + })( + + )} + + +
+ ) + } } -modal.propTypes = { +UserModal.propTypes = { form: PropTypes.object.isRequired, type: PropTypes.string, item: PropTypes.object, onOk: PropTypes.func, } -export default Form.create()(modal) +export default UserModal diff --git a/src/pages/user/index.js b/src/pages/user/index.js index 15c1061..c89273d 100644 --- a/src/pages/user/index.js +++ b/src/pages/user/index.js @@ -3,16 +3,18 @@ import PropTypes from 'prop-types' import { router } from 'utils' import { connect } from 'dva' import { Row, Col, Button, Popconfirm } from 'antd' +import { withI18n } from '@lingui/react' import { Page } from 'components' import { stringify } from 'qs' import List from './components/List' import Filter from './components/Filter' import Modal from './components/Modal' +@withI18n() @connect(({ user, loading }) => ({ user, loading })) class User extends PureComponent { render() { - const { location, dispatch, user, loading } = this.props + const { location, dispatch, user, loading, i18n } = this.props const { query, pathname } = location const { list, @@ -41,7 +43,9 @@ class User extends PureComponent { visible: modalVisible, maskClosable: false, confirmLoading: loading.effects[`user/${modalType}`], - title: `${modalType === 'create' ? 'Create User' : 'Update User'}`, + title: `${ + modalType === 'create' ? i18n.t`Create User` : i18n.t`Update User` + }`, wrapClassName: 'vertical-center-modal', onOk(data) { dispatch({ @@ -62,7 +66,6 @@ class User extends PureComponent { dataSource: list, loading: loading.effects['user/query'], pagination, - location, onChange(page) { handleRefresh({ page: page.current, diff --git a/src/utils/model.js b/src/utils/model.js index f967262..6bc8b9b 100644 --- a/src/utils/model.js +++ b/src/utils/model.js @@ -17,7 +17,6 @@ export const pageModel = modelExtend(model, { pagination: { showSizeChanger: true, showQuickJumper: true, - showTotal: total => `Total ${total} Items`, current: 1, total: 0, pageSize: 10,