fix: languages config, update-password

This commit is contained in:
jialin
2024-06-25 11:50:55 +08:00
parent bd6b3dcbe3
commit 9edce7116e
21 changed files with 435 additions and 128 deletions
+4 -2
View File
@@ -1,3 +1,4 @@
import { useIntl } from '@umijs/max';
import { Button, Space } from 'antd'; import { Button, Space } from 'antd';
type FormButtonsProps = { type FormButtonsProps = {
@@ -18,6 +19,7 @@ const FormButtons: React.FC<FormButtonsProps> = ({
showOk = true, showOk = true,
htmlType = 'button' htmlType = 'button'
}) => { }) => {
const intl = useIntl();
return ( return (
<Space size={40} style={{ marginTop: '80px' }}> <Space size={40} style={{ marginTop: '80px' }}>
{showOk && ( {showOk && (
@@ -27,12 +29,12 @@ const FormButtons: React.FC<FormButtonsProps> = ({
style={{ width: '120px' }} style={{ width: '120px' }}
htmlType={htmlType} htmlType={htmlType}
> >
{okText || '保存'} {okText || intl.formatMessage({ id: 'common.button.save' })}
</Button> </Button>
)} )}
{showCancel && ( {showCancel && (
<Button onClick={onCancel} style={{ width: '98px' }}> <Button onClick={onCancel} style={{ width: '98px' }}>
{cancelText || '取消'} {cancelText || intl.formatMessage({ id: 'common.button.cancel' })}
</Button> </Button>
)} )}
</Space> </Space>
@@ -0,0 +1,68 @@
import {
digitReg,
lowercaseReg,
specialCharacterReg,
uppercaseReg
} from '@/config';
import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Space } from 'antd';
const PasswordValidate: React.FC<{ value: string }> = ({ value = '' }) => {
const intl = useIntl();
const renderIcon = ({ valid, text }: { valid: boolean; text: string }) => {
return (
<>
{valid ? (
<CheckCircleFilled style={{ color: 'green' }} />
) : (
<CloseCircleFilled style={{ color: 'red' }} />
)}
<span
className="m-l-5"
style={{ color: 'var(--ant-color-text-description)' }}
>
{text}
</span>
</>
);
};
return (
<Space direction="vertical" style={{ paddingTop: '10px' }}>
<span>
{renderIcon({
valid: uppercaseReg.test(value),
text: intl.formatMessage({ id: 'users.password.uppcase' })
})}
</span>
<span>
{renderIcon({
valid: lowercaseReg.test(value),
text: intl.formatMessage({ id: 'users.password.lowercase' })
})}
</span>
<span>
{renderIcon({
valid: digitReg.test(value),
text: intl.formatMessage({ id: 'users.password.number' })
})}
</span>
<span>
{renderIcon({
valid: value.length >= 6 && value.length <= 12,
text: intl.formatMessage({ id: 'users.password.length' })
})}
</span>
<span>
{renderIcon({
valid: specialCharacterReg.test(value),
text: intl.formatMessage({ id: 'users.password.special' })
})}
</span>
</Space>
);
};
export default PasswordValidate;
+15
View File
@@ -43,3 +43,18 @@ export const WatchEventType = {
UPDATE: 'MODIFIED', UPDATE: 'MODIFIED',
DELETE: 'DELETED' DELETE: 'DELETED'
}; };
export const PasswordReg =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?=\S+$).{6,12}$/;
export const uppercaseReg = /(?=.*[A-Z])/;
export const lowercaseReg = /(?=.*[a-z])/;
export const digitReg = /(?=.*\d)/;
export const specialCharacterReg = /(?=.*[\W_])/;
export const noSpaceReg = /(?=\S+$)/;
export const lengthReg = /^.{6,12}$/;
+3 -1
View File
@@ -4,6 +4,7 @@ import menu from './en-US/menu';
import models from './en-US/models'; import models from './en-US/models';
import playground from './en-US/playground'; import playground from './en-US/playground';
import resources from './en-US/resources'; import resources from './en-US/resources';
import users from './en-US/users';
export default { export default {
...common, ...common,
@@ -11,5 +12,6 @@ export default {
...models, ...models,
...playground, ...playground,
...resources, ...resources,
...apikeys ...apikeys,
...users
}; };
+4 -1
View File
@@ -1,6 +1,9 @@
export default { export default {
'apikeys.title': 'API Keys', 'apikeys.title': 'API Keys',
'apikeys.table.apikeys': 'keys',
'apikeys.button.create': 'New API Key', 'apikeys.button.create': 'New API Key',
'apikeys.form.expiretime': 'Expiration', 'apikeys.form.expiretime': 'Expiration',
'apikeys.table.name': 'Key Name' 'apikeys.table.name': 'Key Name',
'apikeys.table.save.tips':
'Make sure to copy your key immediately. You will not be able to see it again.'
}; };
+4 -1
View File
@@ -179,5 +179,8 @@ export default {
'common.settings.language': 'Language', 'common.settings.language': 'Language',
'common.delete.confirm': 'common.delete.confirm':
'Are you sure you want to delete the selected {type}?', 'Are you sure you want to delete the selected {type}?',
'common.filter.name': 'Filter by name' 'common.filter.name': 'Filter by name',
'common.form.password': 'Password',
'common.form.username': 'Username',
'common.login.rember': 'Remember me'
}; };
+22
View File
@@ -0,0 +1,22 @@
export default {
'users.title': 'Users',
'users.button.create': 'Create User',
'users.form.edit': 'Edit User',
'users.form.create': 'Create User',
'users.table.username': 'User Name',
'users.table.role': 'Role',
'users.form.fullname': 'Full Name',
'users.table.user': 'users',
'users.form.admin': 'Admin',
'users.form.user': 'User',
'users.form.newpassword': 'New Password',
'users.form.currentpassword': 'Current Password',
'users.form.updatepassword': 'Modify Password',
'users.form.rule.password':
'Contains uppercase and lowercase letters, numbers, and special characters, 6 to 12 characters in length, no spaces allowed.',
'users.password.uppcase': 'At least one uppercase letter',
'users.password.lowercase': 'At least one lowercase letter',
'users.password.number': 'At least one number',
'users.password.special': 'At least one special character',
'users.password.length': 'Length between 6 and 12 characters'
};
+4 -2
View File
@@ -1,9 +1,10 @@
import menu from './en-US/menu';
import apikeys from './zh-CN/apikeys'; import apikeys from './zh-CN/apikeys';
import common from './zh-CN/common'; import common from './zh-CN/common';
import menu from './zh-CN/menu';
import models from './zh-CN/models'; import models from './zh-CN/models';
import playground from './zh-CN/playground'; import playground from './zh-CN/playground';
import resources from './zh-CN/resources'; import resources from './zh-CN/resources';
import users from './zh-CN/users';
export default { export default {
...common, ...common,
@@ -11,5 +12,6 @@ export default {
...models, ...models,
...playground, ...playground,
...resources, ...resources,
...apikeys ...apikeys,
...users
}; };
+3 -1
View File
@@ -1,6 +1,8 @@
export default { export default {
'apikeys.title': 'API 密钥', 'apikeys.title': 'API 密钥',
'apikeys.table.apikeys': '密钥',
'apikeys.button.create': '新建 API 密钥', 'apikeys.button.create': '新建 API 密钥',
'apikeys.form.expiretime': '过期时间', 'apikeys.form.expiretime': '过期时间',
'apikeys.table.name': '密钥名称' 'apikeys.table.name': '密钥名称',
'apikeys.table.save.tips': '确保立即复制您的密钥。您将无法再次看到它!'
}; };
+4 -1
View File
@@ -174,5 +174,8 @@ export default {
'common.settings.instructions': '操作指引', 'common.settings.instructions': '操作指引',
'common.settings.language': '语言', 'common.settings.language': '语言',
'common.delete.confirm': '确定删除选中的{type}吗?', 'common.delete.confirm': '确定删除选中的{type}吗?',
'common.filter.name': '名称查询' 'common.filter.name': '名称查询',
'common.form.password': '密码',
'common.form.username': '用户名',
'common.login.rember': '记住我'
}; };
+22
View File
@@ -0,0 +1,22 @@
export default {
'users.title': '用户',
'users.button.create': '新建用户',
'users.form.edit': '编辑用户',
'users.form.create': '新建用户',
'users.table.username': '用户名',
'users.table.role': '角色',
'users.form.fullname': '全名',
'users.table.user': '用户',
'users.form.admin': '管理员',
'users.form.user': '普通用户',
'users.form.newpassword': '新密码',
'users.form.currentpassword': '当前密码',
'users.form.updatepassword': '修改密码',
'users.form.rule.password':
'包含大小写字母、数字和特殊字符,6至12个字符,不允许有空格',
'users.password.uppcase': '至少包含一个大写字母',
'users.password.lowercase': '至少包含一个小写字母',
'users.password.number': '至少包含一个数字',
'users.password.special': '至少包含一个特殊字符',
'users.password.length': '长度在6至12个字符之间'
};
+38 -5
View File
@@ -3,6 +3,7 @@ import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select'; import SealSelect from '@/components/seal-form/seal-select';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { SyncOutlined } from '@ant-design/icons'; import { SyncOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form, Modal } from 'antd'; import { Form, Modal } from 'antd';
import { expirationOptions } from '../config'; import { expirationOptions } from '../config';
import { FormData } from '../config/types'; import { FormData } from '../config/types';
@@ -23,6 +24,7 @@ const AddModal: React.FC<AddModalProps> = ({
onCancel onCancel
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const intl = useIntl();
const Suffix = ( const Suffix = (
<SyncOutlined <SyncOutlined
style={{ style={{
@@ -53,18 +55,49 @@ const AddModal: React.FC<AddModalProps> = ({
} }
> >
<Form name="addAPIKey" form={form} onFinish={onOk} preserve={false}> <Form name="addAPIKey" form={form} onFinish={onOk} preserve={false}>
<Form.Item<FormData> name="name" rules={[{ required: true }]}> <Form.Item<FormData>
<SealInput.Input label="Name" required></SealInput.Input> name="name"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.table.name' })
}
)
}
]}
>
<SealInput.Input
label={intl.formatMessage({ id: 'common.table.name' })}
required
></SealInput.Input>
</Form.Item> </Form.Item>
<Form.Item<FormData> name="expires_in" rules={[{ required: true }]}> <Form.Item<FormData>
name="expires_in"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.select' },
{
name: intl.formatMessage({ id: 'apikeys.form.expiretime' })
}
)
}
]}
>
<SealSelect <SealSelect
label="Expiration" label={intl.formatMessage({ id: 'apikeys.form.expiretime' })}
required required
options={expirationOptions} options={expirationOptions}
></SealSelect> ></SealSelect>
</Form.Item> </Form.Item>
<Form.Item<FormData> name="description" rules={[{ required: false }]}> <Form.Item<FormData> name="description" rules={[{ required: false }]}>
<SealInput.TextArea label="Description"></SealInput.TextArea> <SealInput.TextArea
label={intl.formatMessage({ id: 'common.table.description' })}
></SealInput.TextArea>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>
+13 -13
View File
@@ -128,7 +128,7 @@ const Models: React.FC = () => {
}; };
const res = await createApisKey({ data: params }); const res = await createApisKey({ data: params });
setOpenAddModal(false); setOpenAddModal(false);
message.success('successfully!'); message.success(intl.formatMessage({ id: 'common.message.success' }));
setDataSource([res, ...dataSource]); setDataSource([res, ...dataSource]);
setTotal(total + 1); setTotal(total + 1);
} catch (error) { } catch (error) {
@@ -144,11 +144,14 @@ const Models: React.FC = () => {
const handleDelete = (row: ListItem) => { const handleDelete = (row: ListItem) => {
Modal.confirm({ Modal.confirm({
title: '', title: '',
content: 'Are you sure you want to delete the selected keys?', content: intl.formatMessage(
{ id: 'common.delete.confirm' },
{ type: intl.formatMessage({ id: 'apikeys.table.apikeys' }) }
),
async onOk() { async onOk() {
console.log('OK'); console.log('OK');
await deleteApisKey(row.id); await deleteApisKey(row.id);
message.success('successfully!'); message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData(); fetchData();
}, },
onCancel() { onCancel() {
@@ -160,10 +163,13 @@ const Models: React.FC = () => {
const handleDeleteBatch = () => { const handleDeleteBatch = () => {
Modal.confirm({ Modal.confirm({
title: '', title: '',
content: 'Are you sure you want to delete the selected keys?', content: intl.formatMessage(
{ id: 'common.delete.confirm' },
{ type: intl.formatMessage({ id: 'apikeys.table.apikeys' }) }
),
async onOk() { async onOk() {
await handleBatchRequest(rowSelection.selectedRowKeys, deleteApisKey); await handleBatchRequest(rowSelection.selectedRowKeys, deleteApisKey);
message.success('successfully!'); message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData(); fetchData();
}, },
onCancel() { onCancel() {
@@ -172,12 +178,6 @@ const Models: React.FC = () => {
}); });
}; };
const handleEditUser = () => {
setOpenAddModal(true);
setAction(PageAction.EDIT);
setTitle('Edit User');
};
const renderSecrectKey = (text: string, record: ListItem) => { const renderSecrectKey = (text: string, record: ListItem) => {
const { value } = record; const { value } = record;
@@ -187,7 +187,7 @@ const Models: React.FC = () => {
{value && ( {value && (
<span> <span>
<Tag color="error" style={{ padding: '10px 12px' }}> <Tag color="error" style={{ padding: '10px 12px' }}>
访 {intl.formatMessage({ id: 'apikeys.table.save.tips' })}
</Tag> </Tag>
<span className="flex-center"> <span className="flex-center">
<Tooltip <Tooltip
@@ -299,7 +299,7 @@ const Models: React.FC = () => {
}} }}
/> />
<Column <Column
title="Operation" title={intl.formatMessage({ id: 'common.table.operation' })}
key="operation" key="operation"
render={(text, record: ListItem) => { render={(text, record: ListItem) => {
return ( return (
+7
View File
@@ -27,3 +27,10 @@ export const accessToken = async () => {
method: 'POST' method: 'POST'
}); });
}; };
export const updatePassword = async (params: any) => {
return request(`${AUTH_API}/update-password`, {
method: 'POST',
data: params
});
};
+58 -39
View File
@@ -1,7 +1,7 @@
import LogoIcon from '@/assets/images/logo.png'; import LogoIcon from '@/assets/images/logo.png';
import SealInput from '@/components/seal-form/seal-input'; import SealInput from '@/components/seal-form/seal-input';
import { LockOutlined, UserOutlined } from '@ant-design/icons'; import { GlobalOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
import { history, useModel } from '@umijs/max'; import { SelectLang, history, useIntl, useModel } from '@umijs/max';
import { Button, Checkbox, Form } from 'antd'; import { Button, Checkbox, Form } from 'antd';
import { flushSync } from 'react-dom'; import { flushSync } from 'react-dom';
import { login } from './apis'; import { login } from './apis';
@@ -24,7 +24,7 @@ const renderLogo = () => {
}; };
const Login = () => { const Login = () => {
const { initialState, setInitialState } = useModel('@@initialState'); const { initialState, setInitialState } = useModel('@@initialState');
const intl = useIntl();
const [form] = Form.useForm(); const [form] = Form.useForm();
const gotoDefaultPage = (userInfo: any) => { const gotoDefaultPage = (userInfo: any) => {
@@ -61,44 +61,63 @@ const Login = () => {
}; };
return ( return (
<Form <div>
form={form} <div style={{ position: 'fixed', right: 0, top: 0, padding: '0 20px' }}>
style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }} <SelectLang icon={<GlobalOutlined />} reload={false} />
onFinish={handleLogin} </div>
> <Form
<div>{renderLogo()}</div> form={form}
<Form.Item style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }}
name="username" onFinish={handleLogin}
rules={[
{
required: true,
message: 'Please input your Username!'
}
]}
> >
<SealInput.Input label="Username" prefix={<UserOutlined />} /> <div>{renderLogo()}</div>
</Form.Item> <Form.Item
name="username"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{ name: intl.formatMessage({ id: 'common.form.username' }) }
)
}
]}
>
<SealInput.Input
label={intl.formatMessage({ id: 'common.form.username' })}
prefix={<UserOutlined />}
/>
</Form.Item>
<Form.Item <Form.Item
name="password" name="password"
rules={[ rules={[
{ {
required: true, required: true,
message: 'Please input your Password!' message: intl.formatMessage(
} { id: 'common.form.rule.input' },
]} { name: intl.formatMessage({ id: 'common.form.password' }) }
> )
<SealInput.Password prefix={<LockOutlined />} label="Password" /> }
</Form.Item> ]}
<Form.Item name="autoLogin"> >
<div style={{ paddingLeft: 10 }}> <SealInput.Password
<Checkbox>Remember me</Checkbox> prefix={<LockOutlined />}
</div> label={intl.formatMessage({ id: 'common.form.password' })}
</Form.Item> />
<Button htmlType="submit" type="primary" block> </Form.Item>
Login <Form.Item name="autoLogin">
</Button> <div style={{ paddingLeft: 10 }}>
</Form> <Checkbox>
{intl.formatMessage({ id: 'common.login.rember' })}
</Checkbox>
</div>
</Form.Item>
<Button htmlType="submit" type="primary" block>
{intl.formatMessage({ id: 'menu.login' })}
</Button>
</Form>
</div>
); );
}; };
+2 -2
View File
@@ -41,8 +41,8 @@ export const receiveChatStream = async (
} }
let chunk = decoder.decode(value, { stream: true }); let chunk = decoder.decode(value, { stream: true });
if (chunk.startsWith('data: ')) { if (chunk.startsWith('data:')) {
chunk = chunk.substring('data: '.length); chunk = chunk.substring('data:'.length);
} }
const item = JSON.parse(chunk?.trim()); const item = JSON.parse(chunk?.trim());
callback(item); callback(item);
@@ -43,9 +43,6 @@ const MessageList: React.FC<MessageProps> = (props) => {
const systemRef = useRef<any>(null); const systemRef = useRef<any>(null);
const contentRef = useRef<any>(''); const contentRef = useRef<any>('');
const handleReceiveMessage = (data: any) => {
console.log('event source message: ', { data });
};
const handleSystemMessageChange = (e: any) => { const handleSystemMessageChange = (e: any) => {
setSystemMessage(e.target.value); setSystemMessage(e.target.value);
}; };
+58 -23
View File
@@ -1,21 +1,29 @@
import FormButtons from '@/components/form-buttons'; import FormButtons from '@/components/form-buttons';
import SealInput from '@/components/seal-form/seal-input'; import SealInput from '@/components/seal-form/seal-input';
import { PasswordReg } from '@/config';
import { INPUT_WIDTH } from '@/constants'; import { INPUT_WIDTH } from '@/constants';
import { updatePassword } from '@/pages/login/apis';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { Form } from 'antd'; import { useIntl } from '@umijs/max';
import { Form, message } from 'antd';
import { StrictMode } from 'react'; import { StrictMode } from 'react';
interface ProfileProps { interface ProfileProps {
name: string; username?: string;
password: string; new_password: string;
originalPassword: string; current_password: string;
email: string;
} }
const Profile: React.FC = () => { const Profile: React.FC = () => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const intl = useIntl();
const handleOnFinish = (values: any) => { const newpasswordValue = Form.useWatch('new_password', form);
console.log('handleOnFinish', values);
const handleOnFinish = async (values: any) => {
try {
await updatePassword(values);
message.success(intl.formatMessage({ id: 'common.message.success' }));
} catch (error) {}
}; };
const handleOnFinishFailed = (errorInfo: any) => { const handleOnFinishFailed = (errorInfo: any) => {
@@ -30,7 +38,7 @@ const Profile: React.FC = () => {
<PageContainer <PageContainer
ghost ghost
header={{ header={{
title: 'Profile' title: intl.formatMessage({ id: 'users.form.updatepassword' })
}} }}
extra={[]} extra={[]}
> >
@@ -41,33 +49,60 @@ const Profile: React.FC = () => {
onFinish={handleOnFinish} onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed} onFinishFailed={handleOnFinishFailed}
> >
<Form.Item<ProfileProps> name="name" rules={[{ required: true }]}> {/* <Form.Item<ProfileProps>
name="username"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.form.username' })
}
)
}
]}
>
<SealInput.Input <SealInput.Input
label="Name" label={intl.formatMessage({ id: 'common.form.username' })}
required required
style={{ width: INPUT_WIDTH.default }} style={{ width: INPUT_WIDTH.default }}
></SealInput.Input> ></SealInput.Input>
</Form.Item> </Form.Item> */}
<Form.Item<ProfileProps> name="email" rules={[{ required: true }]}>
<SealInput.Input
label="Email"
required
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ProfileProps> <Form.Item<ProfileProps>
name="originalPassword" name="current_password"
rules={[{ required: true }]} rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({
id: 'users.form.currentpassword'
})
}
)
}
]}
> >
<SealInput.Password <SealInput.Password
label="Original Password" label={intl.formatMessage({ id: 'users.form.currentpassword' })}
required required
style={{ width: INPUT_WIDTH.default }} style={{ width: INPUT_WIDTH.default }}
></SealInput.Password> ></SealInput.Password>
</Form.Item> </Form.Item>
<Form.Item<ProfileProps> name="password" rules={[{ required: true }]}> <Form.Item<ProfileProps>
name="new_password"
rules={[
{
required: true,
pattern: PasswordReg,
message: intl.formatMessage({ id: 'users.form.rule.password' })
}
]}
>
<SealInput.Password <SealInput.Password
label="Password" label={intl.formatMessage({ id: 'users.form.newpassword' })}
required required
style={{ width: INPUT_WIDTH.default }} style={{ width: INPUT_WIDTH.default }}
></SealInput.Password> ></SealInput.Password>
+63 -8
View File
@@ -3,8 +3,13 @@ import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select'; import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { SyncOutlined } from '@ant-design/icons'; import {
import { Form, Modal } from 'antd'; SyncOutlined,
UserOutlined,
UserSwitchOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form, Modal, Select } from 'antd';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { UserRoles, UserRolesOptions } from '../config'; import { UserRoles, UserRolesOptions } from '../config';
import { FormData, ListItem } from '../config/types'; import { FormData, ListItem } from '../config/types';
@@ -26,6 +31,7 @@ const AddModal: React.FC<AddModalProps> = ({
onCancel onCancel
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const intl = useIntl();
const suffix = ( const suffix = (
<SyncOutlined <SyncOutlined
style={{ style={{
@@ -68,18 +74,67 @@ const AddModal: React.FC<AddModalProps> = ({
} }
> >
<Form name="addUserForm" form={form} onFinish={onOk} preserve={false}> <Form name="addUserForm" form={form} onFinish={onOk} preserve={false}>
<Form.Item<FormData> name="username" rules={[{ required: true }]}> <Form.Item<FormData>
<SealInput.Input label="Name" required></SealInput.Input> name="username"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.table.name' })
}
)
}
]}
>
<SealInput.Input
label={intl.formatMessage({ id: 'common.table.name' })}
required
></SealInput.Input>
</Form.Item> </Form.Item>
<Form.Item<FormData> name="full_name" rules={[{ required: false }]}> <Form.Item<FormData> name="full_name" rules={[{ required: false }]}>
<SealInput.Input label="FullName"></SealInput.Input> <SealInput.Input
label={intl.formatMessage({ id: 'users.form.fullname' })}
></SealInput.Input>
</Form.Item> </Form.Item>
<Form.Item<FormData> name="is_admin" rules={[{ required: false }]}> <Form.Item<FormData> name="is_admin" rules={[{ required: false }]}>
<SealSelect label="Role" options={UserRolesOptions}></SealSelect> <SealSelect label={intl.formatMessage({ id: 'users.table.role' })}>
{UserRolesOptions.map((item) => {
return (
<Select.Option value={item.value} key={item.value}>
{item.value === UserRoles.ADMIN ? (
<UserSwitchOutlined className="size-16" />
) : (
<UserOutlined className="size-16" />
)}
<span className="m-l-5">
{intl.formatMessage({ id: item.label })}
</span>
</Select.Option>
);
})}
</SealSelect>
</Form.Item> </Form.Item>
<Form.Item<FormData> name="password" rules={[{ required: true }]}> <Form.Item<FormData>
<SealInput.Password label="Password" required></SealInput.Password> name="password"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.form.password' })
}
)
}
]}
>
<SealInput.Password
label={intl.formatMessage({ id: 'common.form.password' })}
required
></SealInput.Password>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>
+2 -2
View File
@@ -4,6 +4,6 @@ export const UserRoles = {
}; };
export const UserRolesOptions = [ export const UserRolesOptions = [
{ label: '管理员', value: UserRoles.ADMIN }, { label: 'users.form.admin', value: UserRoles.ADMIN },
{ label: '普通用户', value: UserRoles.USER } { label: 'users.form.user', value: UserRoles.USER }
]; ];
+41 -24
View File
@@ -13,6 +13,7 @@ import {
UserSwitchOutlined UserSwitchOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd'; import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
@@ -27,6 +28,7 @@ const Models: React.FC = () => {
const { sortOrder, setSortOrder } = useTableSort({ const { sortOrder, setSortOrder } = useTableSort({
defaultSortOrder: 'descend' defaultSortOrder: 'descend'
}); });
const intl = useIntl();
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [openAddModal, setOpenAddModal] = useState(false); const [openAddModal, setOpenAddModal] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -93,11 +95,7 @@ const Models: React.FC = () => {
const handleAddUser = () => { const handleAddUser = () => {
setOpenAddModal(true); setOpenAddModal(true);
setAction(PageAction.CREATE); setAction(PageAction.CREATE);
setTitle('Add User'); setTitle(intl.formatMessage({ id: 'users.form.create' }));
};
const handleClickMenu = (e: any) => {
console.log('click', e);
}; };
const handleModalOk = async (data: FormData) => { const handleModalOk = async (data: FormData) => {
@@ -119,7 +117,7 @@ const Models: React.FC = () => {
} }
fetchData(); fetchData();
setOpenAddModal(false); setOpenAddModal(false);
message.success('successfully!'); message.success('');
} catch (error) { } catch (error) {
setOpenAddModal(false); setOpenAddModal(false);
} }
@@ -133,11 +131,14 @@ const Models: React.FC = () => {
const handleDelete = (row: ListItem) => { const handleDelete = (row: ListItem) => {
Modal.confirm({ Modal.confirm({
title: '', title: '',
content: 'Are you sure you want to delete the selected users?', content: intl.formatMessage(
{ id: 'common.delete.confirm' },
{ type: intl.formatMessage({ id: 'users.table.user' }) }
),
async onOk() { async onOk() {
console.log('OK'); console.log('OK');
await deleteUser(row.id); await deleteUser(row.id);
message.success('successfully!'); message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData(); fetchData();
}, },
onCancel() { onCancel() {
@@ -149,10 +150,13 @@ const Models: React.FC = () => {
const handleDeleteBatch = () => { const handleDeleteBatch = () => {
Modal.confirm({ Modal.confirm({
title: '', title: '',
content: 'Are you sure you want to delete the selected users?', content: intl.formatMessage(
{ id: 'common.delete.confirm' },
{ type: intl.formatMessage({ id: 'users.table.user' }) }
),
async onOk() { async onOk() {
await handleBatchRequest(rowSelection.selectedRowKeys, deleteUser); await handleBatchRequest(rowSelection.selectedRowKeys, deleteUser);
message.success('successfully!'); message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData(); fetchData();
}, },
onCancel() { onCancel() {
@@ -165,7 +169,7 @@ const Models: React.FC = () => {
setCurrentData(row); setCurrentData(row);
setOpenAddModal(true); setOpenAddModal(true);
setAction(PageAction.EDIT); setAction(PageAction.EDIT);
setTitle('Edit User'); setTitle(intl.formatMessage({ id: 'users.form.edit' }));
}; };
useEffect(() => { useEffect(() => {
@@ -177,7 +181,7 @@ const Models: React.FC = () => {
<PageContainer <PageContainer
ghost ghost
header={{ header={{
title: 'Users' title: intl.formatMessage({ id: 'users.title' })
}} }}
extra={[]} extra={[]}
> >
@@ -186,7 +190,7 @@ const Models: React.FC = () => {
left={ left={
<Space> <Space>
<Input <Input
placeholder="名称查询" placeholder={intl.formatMessage({ id: 'common.filter.name' })}
style={{ width: 300 }} style={{ width: 300 }}
onChange={handleNameChange} onChange={handleNameChange}
></Input> ></Input>
@@ -205,7 +209,7 @@ const Models: React.FC = () => {
type="primary" type="primary"
onClick={handleAddUser} onClick={handleAddUser}
> >
Add User {intl.formatMessage({ id: 'users.button.create' })}
</Button> </Button>
<Button <Button
icon={<DeleteOutlined />} icon={<DeleteOutlined />}
@@ -213,7 +217,7 @@ const Models: React.FC = () => {
onClick={handleDeleteBatch} onClick={handleDeleteBatch}
disabled={!rowSelection.selectedRowKeys.length} disabled={!rowSelection.selectedRowKeys.length}
> >
Delete {intl.formatMessage({ id: 'common.button.delete' })}
</Button> </Button>
</Space> </Space>
} }
@@ -234,9 +238,14 @@ const Models: React.FC = () => {
onChange: handlePageChange onChange: handlePageChange
}} }}
> >
<Column title="Name" dataIndex="username" key="name" width={200} />
<Column <Column
title="Create Time" title={intl.formatMessage({ id: 'users.table.username' })}
dataIndex="username"
key="name"
width={200}
/>
<Column
title={intl.formatMessage({ id: 'common.table.createTime' })}
dataIndex="created_at" dataIndex="created_at"
key="createTime" key="createTime"
defaultSortOrder="descend" defaultSortOrder="descend"
@@ -248,25 +257,29 @@ const Models: React.FC = () => {
}} }}
/> />
<Column <Column
title="Role" title={intl.formatMessage({ id: 'users.table.role' })}
dataIndex="role" dataIndex="role"
key="role" key="role"
render={(text, record: ListItem) => { render={(text, record: ListItem) => {
return record.is_admin ? ( return record.is_admin ? (
<> <>
<UserSwitchOutlined className="size-16" /> <UserSwitchOutlined className="size-16" />
<span className="m-l-5"></span> <span className="m-l-5">
{intl.formatMessage({ id: 'users.form.admin' })}
</span>
</> </>
) : ( ) : (
<> <>
<UserOutlined className="size-16" /> <UserOutlined className="size-16" />
<span className="m-l-5"></span> <span className="m-l-5">
{intl.formatMessage({ id: 'users.form.user' })}
</span>
</> </>
); );
}} }}
/> />
<Column <Column
title="Update Time" title={intl.formatMessage({ id: 'common.table.updateTime' })}
dataIndex="updated_at" dataIndex="updated_at"
key="updateTime" key="updateTime"
defaultSortOrder="descend" defaultSortOrder="descend"
@@ -278,13 +291,15 @@ const Models: React.FC = () => {
}} }}
/> />
<Column <Column
title="Operation" title={intl.formatMessage({ id: 'common.table.operation' })}
key="operation" key="operation"
width={200} width={200}
render={(text, record: ListItem) => { render={(text, record: ListItem) => {
return ( return (
<Space size={20}> <Space size={20}>
<Tooltip title="编辑"> <Tooltip
title={intl.formatMessage({ id: 'common.button.edit' })}
>
<Button <Button
size="small" size="small"
type="primary" type="primary"
@@ -292,7 +307,9 @@ const Models: React.FC = () => {
icon={<EditOutlined></EditOutlined>} icon={<EditOutlined></EditOutlined>}
></Button> ></Button>
</Tooltip> </Tooltip>
<Tooltip title="删除"> <Tooltip
title={intl.formatMessage({ id: 'common.button.delete' })}
>
<Button <Button
size="small" size="small"
type="primary" type="primary"