|
|
import SearchForm from '@/components/SearchForm'
|
|
|
import useAccountStore, { fetchRoleList, fetchTravelAgencyByName } from '@/stores/Account'
|
|
|
import useFormStore from '@/stores/Form'
|
|
|
import { isEmpty } from '@/utils/commons'
|
|
|
import { ExclamationCircleFilled } from '@ant-design/icons'
|
|
|
import { App, Button, Col, Form, Input, Modal, Row, Select, Space, Table, Typography, Switch } from 'antd'
|
|
|
import dayjs from 'dayjs'
|
|
|
import { useEffect, useState } from 'react'
|
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
|
|
const { Title } = Typography
|
|
|
|
|
|
function Management() {
|
|
|
const { t } = useTranslation()
|
|
|
|
|
|
const accountListColumns = [
|
|
|
{
|
|
|
title: t('account:username'),
|
|
|
dataIndex: 'username',
|
|
|
render: accountRender
|
|
|
},
|
|
|
{
|
|
|
title: t('account:realname'),
|
|
|
dataIndex: 'realname',
|
|
|
},
|
|
|
{
|
|
|
title: t('account:travelAgencyName'),
|
|
|
dataIndex: 'travelAgencyName',
|
|
|
},
|
|
|
{
|
|
|
title: t('account:email'),
|
|
|
dataIndex: 'email',
|
|
|
},
|
|
|
{
|
|
|
title: t('account:roleName'),
|
|
|
dataIndex: 'role'
|
|
|
},
|
|
|
{
|
|
|
title: t('account:lastLogin'),
|
|
|
dataIndex: 'lastLogin',
|
|
|
render: (text) => (isEmpty(text) ? '' : dayjs(text).format('YYYY-MM-DD HH:mm:ss'))
|
|
|
},
|
|
|
{
|
|
|
title: t('account:action.edit'),
|
|
|
dataIndex: 'account:action',
|
|
|
render: actionRender
|
|
|
},
|
|
|
]
|
|
|
|
|
|
function accountRender(text, account) {
|
|
|
return (
|
|
|
<Button type='link' onClick={() => onAccountSeleted(account)}>{text}</Button>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
function actionRender(_, account) {
|
|
|
return (
|
|
|
<Space key='actionRenderSpace' size='middle'>
|
|
|
<Switch checkedChildren={t('account:action.enable')} unCheckedChildren={t('account:action.disable')} checked={account.disabled==0} onChange={(checked) => {
|
|
|
showDisableConfirm(account, checked)
|
|
|
}} />
|
|
|
<Button type='link' key='resetPassword' onClick={() => showResetPasswordConfirm(account)}>{t('account:action.resetPassword')}</Button>
|
|
|
</Space>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
const [isAccountModalOpen, setAccountModalOpen] = useState(false)
|
|
|
const [dataLoading, setDataLoading] = useState(false)
|
|
|
const [roleAllList, setRoleAllList] = useState([])
|
|
|
const [travelAgencyList, setTravelAgencyList] = useState([])
|
|
|
const [currentTravelAgency, setCurrentTravelAgency] = useState(null)
|
|
|
|
|
|
const [accountForm] = Form.useForm()
|
|
|
const [searchAccountByCriteria, accountList, toggleAccountStatus, saveOrUpdateAccount, resetAccountPassword] =
|
|
|
useAccountStore((state) =>
|
|
|
[state.searchAccountByCriteria, state.accountList, state.toggleAccountStatus, state.saveOrUpdateAccount, state.resetAccountPassword])
|
|
|
|
|
|
const formValues = useFormStore(state => state.formValues)
|
|
|
const { notification, modal } = App.useApp()
|
|
|
|
|
|
useEffect(() => {
|
|
|
fetchRoleList()
|
|
|
.then((roleList) => {
|
|
|
setRoleAllList(roleList.map(r => {
|
|
|
return {
|
|
|
value: r.role_id,
|
|
|
label: r.role_name,
|
|
|
disabled: r.role_id === 1
|
|
|
}
|
|
|
}))
|
|
|
})
|
|
|
}, [])
|
|
|
|
|
|
const handelAccountSearch = () => {
|
|
|
setDataLoading(true)
|
|
|
searchAccountByCriteria(formValues)
|
|
|
.catch(ex => {
|
|
|
notification.error({
|
|
|
message: 'Notification',
|
|
|
description: ex.message,
|
|
|
placement: 'top',
|
|
|
duration: 4,
|
|
|
})
|
|
|
})
|
|
|
.finally(() => {
|
|
|
setDataLoading(false)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const onAccountSeleted = async (account) => {
|
|
|
setTravelAgencyList([{
|
|
|
label: account.travelAgencyName,
|
|
|
value: account.travelAgencyId
|
|
|
}])
|
|
|
accountForm.setFieldsValue(account)
|
|
|
setCurrentTravelAgency(account.travelAgencyId)
|
|
|
setAccountModalOpen(true)
|
|
|
}
|
|
|
|
|
|
const onAccountFinish = (values) => {
|
|
|
saveOrUpdateAccount(values)
|
|
|
.then(() => {
|
|
|
handelAccountSearch()
|
|
|
})
|
|
|
.catch(ex => {
|
|
|
notification.error({
|
|
|
message: 'Notification',
|
|
|
description: ex.message,
|
|
|
placement: 'top',
|
|
|
duration: 4,
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const onAccountFailed = (error) => {
|
|
|
console.log('Failed:', error)
|
|
|
// form.resetFields()
|
|
|
}
|
|
|
|
|
|
const handleTravelAgencySearch = (newValue) => {
|
|
|
setDataLoading(true)
|
|
|
fetchTravelAgencyByName(newValue)
|
|
|
.then(result => {
|
|
|
setTravelAgencyList(result.map(r => {
|
|
|
return {
|
|
|
label: r.travel_agency_name,
|
|
|
value: r.travel_agency_id
|
|
|
}
|
|
|
}))
|
|
|
})
|
|
|
.finally(() => {
|
|
|
setDataLoading(false)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const handleTravelAgencyChange = (newValue) => {
|
|
|
setCurrentTravelAgency(newValue)
|
|
|
}
|
|
|
|
|
|
const showDisableConfirm = (account, status) => {
|
|
|
|
|
|
const confirmTitle = status ? t('account:action.enable.title') : t('account:action.disable.title')
|
|
|
|
|
|
modal.confirm({
|
|
|
title: confirmTitle,
|
|
|
icon: <ExclamationCircleFilled />,
|
|
|
content: t('account:username') + ': ' + account.username + ', ' + t('account:realname') + ': ' + account.realname,
|
|
|
onOk() {
|
|
|
toggleAccountStatus(account.userId, status)
|
|
|
.then(() => {
|
|
|
handelAccountSearch()
|
|
|
})
|
|
|
.catch(ex => {
|
|
|
notification.error({
|
|
|
message: 'Notification',
|
|
|
description: ex.message,
|
|
|
placement: 'top',
|
|
|
duration: 4,
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
onCancel() {
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const showResetPasswordConfirm = (account) => {
|
|
|
const randomPassword = account.username + '@' + (Math.floor(Math.random() * 900) + 100)
|
|
|
modal.confirm({
|
|
|
title: 'Do you want to reset password?',
|
|
|
icon: <ExclamationCircleFilled />,
|
|
|
content: `Username: ${account.username}, Realname: ${account.realname}`,
|
|
|
onOk() {
|
|
|
resetAccountPassword(account.userId, randomPassword)
|
|
|
.then(() => {
|
|
|
notification.info({
|
|
|
message: `请复制新密码给 [${account.realname}]`,
|
|
|
description: '新密码:' + randomPassword,
|
|
|
placement: 'top',
|
|
|
duration: 60,
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
onCancel() {
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
<>
|
|
|
<Modal
|
|
|
centered
|
|
|
okButtonProps={{
|
|
|
autoFocus: true,
|
|
|
htmlType: 'submit',
|
|
|
}}
|
|
|
title={t('account:detail')}
|
|
|
open={isAccountModalOpen} onOk={() => setAccountModalOpen(false)} onCancel={() => setAccountModalOpen(false)}
|
|
|
destroyOnClose={true}
|
|
|
clearOnDestroy={true}
|
|
|
modalRender={(dom) => (
|
|
|
<Form
|
|
|
name='AccountForm'
|
|
|
form={accountForm}
|
|
|
layout='vertical'
|
|
|
size='large'
|
|
|
style={{
|
|
|
maxWidth: 600,
|
|
|
}}
|
|
|
onFinish={onAccountFinish}
|
|
|
onFinishFailed={onAccountFailed}
|
|
|
autoComplete='off'
|
|
|
>
|
|
|
{dom}
|
|
|
</Form>
|
|
|
)}
|
|
|
>
|
|
|
<Form.Item name='accountId' className='hidden' ><Input /></Form.Item>
|
|
|
<Form.Item name='userId' className='hidden' ><Input /></Form.Item>
|
|
|
<Form.Item name='lmi2_sn' className='hidden' ><Input /></Form.Item>
|
|
|
<Form.Item
|
|
|
label={t('account:username')}
|
|
|
name='username'
|
|
|
rules={[
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('account:Validation.username'),
|
|
|
},
|
|
|
]}
|
|
|
>
|
|
|
<Input />
|
|
|
</Form.Item>
|
|
|
<Form.Item
|
|
|
label={t('account:realname')}
|
|
|
name='realname'
|
|
|
rules={[
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('account:Validation.realname'),
|
|
|
},
|
|
|
]}
|
|
|
>
|
|
|
<Input />
|
|
|
</Form.Item>
|
|
|
<Form.Item
|
|
|
label={t('account:email')}
|
|
|
name='email'
|
|
|
rules={[
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('account:Validation.email'),
|
|
|
},
|
|
|
]}
|
|
|
>
|
|
|
<Input />
|
|
|
</Form.Item>
|
|
|
<Form.Item
|
|
|
label={t('account:travelAgencyName')}
|
|
|
name='travelAgencyId'
|
|
|
rules={[
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('account:Validation.travelAgency'),
|
|
|
},
|
|
|
]}
|
|
|
>
|
|
|
<Select
|
|
|
options={travelAgencyList}
|
|
|
value={currentTravelAgency}
|
|
|
onChange={handleTravelAgencyChange}
|
|
|
loading={dataLoading}
|
|
|
showSearch
|
|
|
filterOption={false}
|
|
|
onSearch={handleTravelAgencySearch}
|
|
|
notFoundContent={null}
|
|
|
>
|
|
|
</Select>
|
|
|
</Form.Item>
|
|
|
<Form.Item
|
|
|
label={t('account:roleName')}
|
|
|
name='roleId'
|
|
|
rules={[
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('account:Validation.role'),
|
|
|
},
|
|
|
]}
|
|
|
>
|
|
|
<Select
|
|
|
options={roleAllList}
|
|
|
filterOption={false}
|
|
|
notFoundContent={null}
|
|
|
>
|
|
|
</Select>
|
|
|
</Form.Item>
|
|
|
</Modal>
|
|
|
<Space direction='vertical' style={{ width: '100%' }}>
|
|
|
<Title level={3}>{t('account:accountList')}</Title>
|
|
|
<SearchForm
|
|
|
fieldsConfig={{
|
|
|
shows: ['username', 'realname'],
|
|
|
fieldProps: {
|
|
|
username: { label: t('account:username') },
|
|
|
realname: { label: t('account:realname') },
|
|
|
},
|
|
|
}}
|
|
|
onSubmit={() => {
|
|
|
handelAccountSearch()
|
|
|
}}
|
|
|
/>
|
|
|
<Row>
|
|
|
<Col span={24}>
|
|
|
<Space>
|
|
|
<Button onClick={() => setAccountModalOpen(true)}>{t('account:newAccount')}</Button>
|
|
|
</Space>
|
|
|
</Col>
|
|
|
</Row>
|
|
|
<Row>
|
|
|
<Col span={24}>
|
|
|
<Table
|
|
|
bordered
|
|
|
loading={dataLoading}
|
|
|
rowKey='username'
|
|
|
pagination={{
|
|
|
showQuickJumper: true,
|
|
|
showLessItems: true,
|
|
|
showSizeChanger: true,
|
|
|
showTotal: (total) => { return t('Total') + `:${total}` }
|
|
|
}}
|
|
|
onChange={(pagination) => { onSearchClick(pagination.current) }}
|
|
|
columns={accountListColumns} dataSource={accountList}
|
|
|
/>
|
|
|
</Col>
|
|
|
</Row>
|
|
|
</Space>
|
|
|
</>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
export default Management
|