Merge remote-tracking branch 'origin/main'

feature/price_manager
Lei OT 1 year ago
commit af2ce43b2a

@ -0,0 +1,12 @@
{
"CurrentPassword": "Current password",
"NewPassword": "New password",
"ReenterPassword": "Reenter password",
"Validation": {
"Success": "Your password has been successfully updated.",
"Fail": "Failed to change password. Please try again.",
"CurrentPassword": "Please input your password.",
"NewPassword": "Please input your new password.",
"ReenterPassword": "Please reenter your password."
}
}

@ -0,0 +1,12 @@
{
"CurrentPassword": "当前密码",
"NewPassword": "新密码",
"ReenterPassword": "重复输入密码",
"Validation": {
"Success": "密码更新成功",
"Fail": "密码更新失败",
"CurrentPassword": "请输入密码。",
"NewPassword": "请输入新密码。",
"ReenterPassword": "请重复输入密码。"
}
}

@ -51,10 +51,6 @@
"Notice": "通知",
"Report": "质量评分"
},
"Notification": {
"Title": "温馨提示",
"LoginFailed": "密码错误,登陆失败。"
},
"Validation": {
"Title": "温馨提示",
"LoginFailed": "密码错误,登陆失败。",

@ -6,7 +6,7 @@ const persistObject = {}
* G-JSON:LOGIN_USER -> loginUser = { username: 'test-username' }
*/
export function usingStorage() {
// TODO: rename storageUse()???webStorage()???
const getStorage = () => {
if (import.meta.env.DEV && window.localStorage) {
return window.localStorage

@ -17,7 +17,7 @@ i18n
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
ns: ['common', 'group', 'vendor'],
ns: ['common', 'group', 'vendor', 'account'],
defaultNS: 'common',
detection: {
// convertDetectedLanguage: 'Iso15897',

@ -1,33 +1,51 @@
import { makeAutoObservable, runInAction } from "mobx";
import { create } from 'zustand'
import { appendRequestParams, fetchJSON, postForm } from '@/utils/request'
import { HT_HOST } from "@/config"
import { isNotEmpty, prepareUrl } from '@/utils/commons'
import { loadPageSpy } from '@/pageSpy'
import { usingStorage } from '@/hooks/usingStorage'
const KEY_LOGIN_TOKEN = 'G-STR:LOGIN_TOKEN'
const KEY_TRAVEL_AGENCY_ID = 'G-INT:TRAVEL_AGENCY_ID'
const KEY_USER_ID = 'G-INT:USER_ID'
const KEY_USER_NAME = 'G-STR:USERNAME'
const KEY_USER_DETAIL = 'G-JSON:USER_DETAIL'
export const fetchLoginToken = async (username, password) => {
const formData = new FormData()
formData.append('username', username)
formData.append('Password', password)
const { errcode, Result } = await postForm(
`${HT_HOST}/service-CooperateSOA/Login`,
formData)
return errcode !== 0 ? {} : Result
}
export const fetchUserDetail = async (loginToken) => {
const { errcode, Result } = await fetchJSON(
`${HT_HOST}/service-CooperateSOA/GetLinkManInfo`, { token: loginToken})
return errcode !== 0 ? {} : Result
}
async function fetchLastRequet() {
const { errcode, result } = await fetchJSON(`${HT_HOST}/service-CooperateSOA/GetLastReqDate`)
return errcode !== 0 ? {} : result
}
const useAuthStore = create((set, get) => ({
tokenInterval: null,
tokenTimeout: false,
loginStatus: 0,
loginUser: {
token: '',
userId: 0, // LMI_SN
username: '0',
travelAgencyId: 0, // VEI_SN
travelAgencyName: '',
telephone: '',
emailAddress: '',
cityId: 0,
timeout: false,
permissionList: [],
},
@ -48,137 +66,81 @@ const useAuthStore = create((set, get) => ({
},
validateUserPassword: async (usr, pwd) => {
const { startTokenInterval } = get()
const { setStorage } = usingStorage()
const formData = new FormData()
formData.append('username', usr)
formData.append('Password', pwd)
async function fetchLoginToken() {
const postUrl = HT_HOST + '/service-CooperateSOA/Login'
const json = await postForm(postUrl, formData)
if (json.errcode == 0 && isNotEmpty(json.Result)) {
return json.Result.token;
} else {
return 0;
}
}
const loginToken = await fetchLoginToken()
setStorage(KEY_LOGIN_TOKEN, loginToken)
appendRequestParams('token', loginToken)
const { token: loginToken } = await fetchLoginToken(usr, pwd)
const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLinkManInfo').build()
const userDetail = await fetchUserDetail(loginToken)
return fetchJSON(fetchUrl)
.then(json => {
if (json.errcode == 0) {
set(() => ({
loginUser: {
token: loginToken,
timeout: false,
userId: json.Result.LMI_SN,
username: json.Result.LoginName,
travelAgencyId: json.Result.LMI_VEI_SN,
travelAgencyName: json.Result.VName,
telephone: json.Result.LkPhone,
emailAddress: json.Result.LMI_listmail,
cityId: json.Result.citysn,
},
loginStatus: 302
}))
setStorage(KEY_USER_ID, json.Result.LMI_SN)
setStorage(KEY_TRAVEL_AGENCY_ID, json.Result.LMI_VEI_SN)
setStorage(KEY_USER_NAME, json.Result.LoginName)
// loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`)
// this.startTokenInterval()
} else {
throw new Error(json.errmsg + ': ' + json.errcode)
}
})
set(() => ({
loginUser: {
telephone: userDetail.LkPhone,
emailAddress: userDetail.LMI_listmail,
cityId: userDetail.citysn,
},
tokenTimeout: false,
loginStatus: 302
}))
setStorage(KEY_LOGIN_TOKEN, loginToken)
setStorage(KEY_USER_ID, userDetail.LMI_SN)
setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN)
setStorage(KEY_USER_DETAIL, {username: userDetail.LoginName, travelAgencyName: userDetail.VName})
appendRequestParams('token', loginToken)
// loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`)
startTokenInterval()
},
logout: () => {
const { tokenInterval } = get()
const { clearStorage } = usingStorage()
clearStorage()
clearInterval(tokenInterval)
set(() => ({
loginUser: {
token: '',
timeout: true
},
loginStatus: 0
loginStatus: 0,
tokenInterval: null,
tokenTimeout: true
}))
},
fetchUserDetail: () => {
const { loginUser } = get()
const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLinkManInfo')
.append('token', loginUser.token)
.build();
return fetchJSON(fetchUrl)
.then(json => {
if (json.errcode == 0) {
set((state) => ({
loginUser: {
...state.loginUser,
userId: json.Result.LMI_SN,
username: json.Result.LoginName,
travelAgencyId: json.Result.LMI_VEI_SN,
travelAgencyName: json.Result.VName,
telephone: json.Result.LkPhone,
emailAddress: json.Result.LMI_listmail,
cityId: json.Result.citysn,
}
}))
// loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`)
// this.startTokenInterval()
return loginUser
} else {
throw new Error(json.errmsg + ': ' + json.errcode)
}
});
},
startTokenInterval: () => {
const { loginUser } = get()
async function fetchLastRequet() {
const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLastReqDate')
.append('token', loginUser.token)
.build();
const json = await fetchJSON(fetchUrl)
if (json.errcode == 0 && isNotEmpty(json.result)) {
return json.result.LastReqDate;
} else {
return 0;
}
}
const { loginTimeout } = get()
async function checkTokenTimeout() {
const lastRequest = await fetchLastRequet();
const lastReqDate = new Date(lastRequest);
const now = new Date();
const diffTime = now.getTime() - lastReqDate.getTime();
const diffHours = diffTime/1000/60/60;
if (diffHours > 4) {
authStore.logout();
const { LastReqDate } = await fetchLastRequet()
const lastReqDate = new Date(LastReqDate)
const now = new Date()
const diffTime = now.getTime() - lastReqDate.getTime()
const diffHours = diffTime/1000/60//60
if (diffHours > 1) {
loginTimeout()
}
}
const interval = setInterval(() => checkTokenTimeout(), 1000*60*20)
const interval = setInterval(() => checkTokenTimeout(), 1000)//*60)//*20)
set(() => ({
tokenInterval: interval
}))
},
loginTimeout: () => {
const { tokenInterval } = get()
// TODO: 这里没有清理 token刷新后可以正常使用系统
clearInterval(tokenInterval)
set(() => ({
tokenTimeout: true
}))
},
changeUserPassword: (password, newPassword) => {
const { loginUser } = get()
const { userId } = usingStorage()
const formData = new FormData();
formData.append('UserID', loginUser.userId);
formData.append('UserID', userId);
formData.append('Password', password);
formData.append('NewPassword', newPassword);
formData.append('token', loginUser.token);
const postUrl = HT_HOST + '/service-CooperateSOA/SetPassword';
return postForm(postUrl, formData)
@ -195,6 +157,7 @@ const useAuthStore = create((set, get) => ({
export default useAuthStore
export class Auth {
// TODO: 等待所有获取用户信息修改完后删除
login = {
token: '',
userId: 0, // LMI_SN

@ -23,15 +23,15 @@ const { Title } = Typography;
function App() {
const { t, i18n } = useTranslation();
const [password, setPassword] = useState('');
const { notification } = AntApp.useApp();
const [password, setPassword] = useState('')
const [loginUser, validateUserPassword] = useAuthStore((state) => [state.loginUser, state.validateUserPassword])
const [validateUserPassword, tokenTimeout] = useAuthStore(
(state) => [state.validateUserPassword, state.tokenTimeout])
const { loginToken, username } = usingStorage()
const { loginToken, userDetail } = usingStorage()
const noticeUnRead = useNoticeStore((state) => state.noticeUnRead)
const href = useHref();
const href = useHref()
const navigate = useNavigate()
const location = useLocation()
@ -53,16 +53,12 @@ function App() {
}, [location]);
const onSubmit = () => {
validateUserPassword(username, password)
validateUserPassword(userDetail?.username, password)
.catch(ex => {
notification.error({
message: `Notification`,
description: ex.message,
placement: 'top',
duration: 4,
});
});
setPassword('');
console.error(ex)
alert(t('Validation.LoginFailed'))
})
setPassword('')
};
const splitPath = href.split('/');
@ -95,17 +91,17 @@ function App() {
closable={false}
maskClosable={false}
footer={null}
open={loginUser.timeout}
open={tokenTimeout}
>
<Title level={3}>{t('LoginTimeout')}</Title>
<div>{t('LoginTimeoutTip')}</div>
<Space direction='horizontal'>
<Input.Password value={password}
onChange={(e) => setPassword(e.target.value)}
onPressEnter={() => onSubmit()}
addonBefore={loginUser.username} />
onPressEnter={onSubmit}
addonBefore={userDetail?.username} />
<Button
onClick={() => onSubmit()}
onClick={onSubmit}
>{t('Submit')}</Button></Space>
</Modal>
@ -142,7 +138,7 @@ function App() {
</Col>
<Col span={4}>
<Title level={3} style={{ color: 'white', marginBottom: '0', display: 'flex', justifyContent: 'end' }}>
{loginUser.travelAgencyName}
{userDetail?.travelAgencyName}
</Title>
</Col>
<Col span={2}>
@ -162,7 +158,7 @@ function App() {
>
<a onClick={e => e.preventDefault()}>
<Space>
{username}
{userDetail?.username}
<DownOutlined />
</Space>
</a>

@ -24,6 +24,7 @@ function Login() {
const onFinish = (values) => {
validateUserPassword(values.username, values.password)
.catch(ex => {
console.error(ex)
notification.error({
message: t('Validation.Title'),
description: t('Validation.LoginFailed'),

@ -1,44 +1,47 @@
import { useNavigate } from "react-router-dom";
import { Button, Space, Form, Input, Row, Typography, App } from 'antd';
import { useStore } from '@/stores/StoreContext.js';
import { useNavigate } from "react-router-dom"
import { Button, Space, Form, Input, Row, Typography, App } from 'antd'
import { useTranslation } from 'react-i18next'
import useAuthStore from '@/stores/Auth'
const { Title } = Typography;
const { Title } = Typography
function ChangePassword() {
const navigate = useNavigate();
const { authStore } = useStore();
const { notification } = App.useApp();
const [form] = Form.useForm();
const { t } = useTranslation()
const navigate = useNavigate()
const changeUserPassword = useAuthStore((state) => state.changeUserPassword)
const { notification } = App.useApp()
const [form] = Form.useForm()
const onFinish = (values) => {
authStore.changeUserPassword(values.currentPassword, values.newPassword)
changeUserPassword(values.currentPassword, values.newPassword)
.then(() => {
notification.success({
message: `Notification`,
description: 'Your password has been successfully updated.',
message: t('Validation.Title'),
description: t('account:Validation.Success'),
placement: 'top',
duration: 4,
});
})
form.resetFields()
})
.catch(() => {
notification.error({
message: `Notification`,
description: 'Failed to change password. Please try again.',
message: t('Validation.Title'),
description: t('account:Validation.Fail'),
placement: 'top',
duration: 4,
});
});
};
})
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
// form.resetFields();
};
console.log('Failed:', errorInfo)
// form.resetFields()
}
return (
<>
<Row justify="center" align="middle" style={{ minHeight: 500 }}>
<Row justify="center" align="middle" style={{ minHeight: 500 }}>
<Form
name="basic"
form={form}
@ -51,38 +54,38 @@ function ChangePassword() {
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item><Title level={2}>Change your password</Title></Form.Item>
<Form.Item><Title level={2}>{t('ChangePassword')}</Title></Form.Item>
<Form.Item
label="Current password"
label={t('account:CurrentPassword')}
name="currentPassword"
rules={[
{
required: true,
message: 'Please input your password!',
message: t('account:Validation.CurrentPassword'),
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="New password"
label={t('account:NewPassword')}
name="newPassword"
rules={[
{
required: true,
message: 'Please input your password!',
message: t('account:Validation.NewPassword'),
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="Reenter password"
label={t('account:ReenterPassword')}
name="reenterPassword"
rules={[
{
required: true,
message: 'Please reenter your password!',
message: t('account:Validation.ReenterPassword'),
},
]}
>
@ -91,17 +94,17 @@ function ChangePassword() {
<Form.Item>
<Space size="middle">
<Button type="primary" htmlType="submit">
Save
{t('Submit')}
</Button>
<Button onClick={() => navigate('/reservation/newest')}>
Cancel
{t('Cancel')}
</Button>
</Space>
</Form.Item>
</Form>
</Row>
</>
);
)
}
export default ChangePassword;
export default ChangePassword
Loading…
Cancel
Save