Merge remote-tracking branch 'origin/main'

feature/price_manager
Lei OT 1 year ago
commit c7f29f4999

@ -50,5 +50,11 @@
"Feedback": "Feedback", "Feedback": "Feedback",
"Notice": "Notice", "Notice": "Notice",
"Report": "Report" "Report": "Report"
},
"Validation": {
"Title": "Notification",
"LoginFailed": "Incorrect password, Login failed.",
"UsernameIsEmpty": "Please input your username",
"PasswordIsEmpty": "Please input your password"
} }
} }

@ -20,7 +20,7 @@
"preview": "预览", "preview": "预览",
"Login": "登录", "Login": "登录",
"Username": "账户名", "Username": "账",
"Password": "密码", "Password": "密码",
"ChangePassword": "修改密码", "ChangePassword": "修改密码",
@ -50,5 +50,15 @@
"Feedback": "反馈表", "Feedback": "反馈表",
"Notice": "通知", "Notice": "通知",
"Report": "质量评分" "Report": "质量评分"
},
"Notification": {
"Title": "温馨提示",
"LoginFailed": "密码错误,登陆失败。"
},
"Validation": {
"Title": "温馨提示",
"LoginFailed": "密码错误,登陆失败。",
"UsernameIsEmpty": "请输入账号",
"PasswordIsEmpty": "请输入密码"
} }
} }

@ -44,7 +44,7 @@ const useAuthStore = create((set, get) => ({
}, },
valdateUserPassword: async (usr, pwd) => { validateUserPassword: async (usr, pwd) => {
const formData = new FormData() const formData = new FormData()
formData.append('username', usr) formData.append('username', usr)
formData.append('Password', pwd) formData.append('Password', pwd)
@ -183,124 +183,6 @@ const useAuthStore = create((set, get) => ({
export default useAuthStore export default useAuthStore
export class Auth { export class Auth {
constructor(root) {
makeAutoObservable(this, { rootStore: false });
this.root = root;
this.login.token = root.getSession(KEY_LOGIN_TOKEN);
this.login.userId = root.getSession(KEY_USER_ID);
this.login.travelAgencyId = root.getSession(KEY_TRAVEL_AGENCY_ID);
if (isNotEmpty(this.login.token)) {
appendRequestParams('token', this.login.token);
this.fetchUserDetail();
}
}
valdateUserPassword(usr, pwd) {
const formData = new FormData();
formData.append('username', usr);
formData.append('Password', pwd);
const postUrl = HT_HOST + '/service-CooperateSOA/Login';
return postForm(postUrl, formData)
.then(json => {
if (json.errcode == 0) {
runInAction(() => {
this.login.token = json.Result.token;
this.login.timeout = false;
});
this.root.putSession(KEY_LOGIN_TOKEN, json.Result.token);
appendRequestParams('token', json.Result.token);
return json.Result.WU_LMI_SN;
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
}
});
}
fetchUserDetail() {
const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLinkManInfo')
.append('token', this.login.token)
.build();
return fetchJSON(fetchUrl)
.then(json => {
if (json.errcode == 0) {
runInAction(() => {
this.login.userId = json.Result.LMI_SN;
this.login.username = json.Result.LoginName;
this.login.travelAgencyId = json.Result.LMI_VEI_SN;
this.login.travelAgencyName = json.Result.VName;
this.login.telephone = json.Result.LkPhone;
this.login.emailAddress = json.Result.LMI_listmail;
this.login.cityId = json.Result.citysn;
this.root.putSession(KEY_TRAVEL_AGENCY_ID, this.login.travelAgencyId);
this.root.putSession(KEY_USER_ID, this.login.userId);
});
loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`)
this.startTokenInterval();
return this.login;
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
}
});
}
startTokenInterval() {
const authStore = this;
async function fetchLastRequet() {
const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLastReqDate')
.append('token', authStore.login.token)
.build();
const json = await fetchJSON(fetchUrl)
if (json.errcode == 0 && isNotEmpty(json.result)) {
return json.result.LastReqDate;
} else {
return 0;
}
}
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();
}
}
this.tokenInterval = setInterval(() => checkTokenTimeout(), 1000*60*20);
}
logout() {
this.root.clearSession();
runInAction(() => {
this.login.timeout = true;
});
}
tokenInterval = null;
changeUserPassword(password, newPassword) {
const formData = new FormData();
formData.append('UserID', this.login.userId);
formData.append('Password', password);
formData.append('NewPassword', newPassword);
formData.append('token', this.login.token);
const postUrl = HT_HOST + '/service-CooperateSOA/SetPassword';
return postForm(postUrl, formData)
.then(json => {
if (json.errcode == 0) {
return json;
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
}
});
}
login = { login = {
token: '', token: '',
userId: 0, // LMI_SN userId: 0, // LMI_SN
@ -313,5 +195,3 @@ export class Auth {
timeout: false timeout: false
} }
} }
// export Auth;

@ -28,7 +28,7 @@ const useReservationStore = create((set, get) => ({
confirmationList: [ confirmationList: [
], ],
fetchReservationList: (travelAgencyId, current, status=null) => { fetchReservationList: (travelAgencyId, formVal, current=1) => {
const fromDate = null //this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[0].format('YYYY-MM-DD'); const fromDate = null //this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[0].format('YYYY-MM-DD');
const thruDate = null //this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[1].format('YYYY-MM-DD'); const thruDate = null //this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[1].format('YYYY-MM-DD');
// this.reservationPage.current = current; // this.reservationPage.current = current;
@ -36,34 +36,18 @@ const useReservationStore = create((set, get) => ({
const totalNum = 0//current == 1 ? 0 : this.reservationPage.total; const totalNum = 0//current == 1 ? 0 : this.reservationPage.total;
const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanSearchList') const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanSearchList')
.append('VEI_SN', travelAgencyId) .append('VEI_SN', travelAgencyId)
.append('GroupNo', '')//this.referenceNo) .append('GroupNo', formVal.referenceNo)
.append('DateStart', '')//fromDate) .append('DateStart', formVal.fromDate)
.append('DateEnd', '')//thruDate) .append('DateEnd', formVal.thruDate)
.append('NotConfirm', '')//status) .append('NotConfirm', '')//status)
.append('TotalNum', totalNum) .append('TotalNum', totalNum)
.append('PageSize', 5)//this.reservationPage.size) .append('PageSize', 5)//this.reservationPage.size)
.append('PageIndex', 1)//this.reservationPage.current) .append('PageIndex', current)
.build(); .build();
return fetchJSON(fetchUrl) return fetchJSON(fetchUrl)
.then(json => { .then(json => {
if (json.errcode == 0) { if (json.errcode == 0) {
// runInAction(() => {
// this.reservationList = (json?.Result??[]).map((data, index) => {
// return {
// key: data.vas_gri_sn,
// reservationId: data.vas_gri_sn,
// referenceNumber: data.GriName,
// arrivalDate: data.GetGDate,
// pax: data.PersonNum,
// status: data.GState,
// reservationDate: data.SendDate,
// guide: data.Guide
// }
// });
// this.reservationPage.total = (json?.Result??[{RsTotal: 0}])[0].RsTotal;
// });
const mapReservationList = (json?.Result??[]).map((data, index) => { const mapReservationList = (json?.Result??[]).map((data, index) => {
return { return {
key: data.vas_gri_sn, key: data.vas_gri_sn,

@ -1,15 +1,11 @@
import { Outlet, Link, useHref, useNavigate, useLocation, NavLink } from "react-router-dom"; import { Outlet, Link, useHref, useNavigate, useLocation, NavLink } from 'react-router-dom';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { observer } from "mobx-react"; import { Layout, Menu, ConfigProvider, theme, Dropdown, Space, Row, Col, Badge, Typography, Modal, Input, Button, App as AntApp } from 'antd';
import { toJS } from "mobx"; import { DownOutlined } from '@ant-design/icons';
import { Layout, Menu, ConfigProvider, theme, Dropdown, Space, Row, Col, Badge, Typography, Modal, Input, Button, App as AntApp } from "antd"; import 'antd/dist/reset.css';
import { DownOutlined } from "@ant-design/icons"; import AppLogo from '@/assets/logo-gh.png';
import "antd/dist/reset.css"; import { isEmpty } from '@/utils/commons';
import AppLogo from "@/assets/logo-gh.png"; import Language from '../i18n/LanguageSwitcher';
import { isEmpty } from "@/utils/commons";
import { useStore } from "@/stores/StoreContext.js";
import * as config from "@/config";
import Language from "../i18n/LanguageSwitcher";
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import zhLocale from 'antd/locale/zh_CN'; import zhLocale from 'antd/locale/zh_CN';
import enLocale from 'antd/locale/en_US'; import enLocale from 'antd/locale/en_US';
@ -25,12 +21,10 @@ function App() {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const { authStore } = useStore();
const { notification } = AntApp.useApp(); const { notification } = AntApp.useApp();
const loginUser = useAuthStore((state) => state.loginUser) const loginUser = useAuthStore((state) => state.loginUser)
// const login = toJS(authStore.login);
const noticeUnRead = useNoticeStore((state) => state.noticeUnRead); const noticeUnRead = useNoticeStore((state) => state.noticeUnRead);
const href = useHref(); const href = useHref();
const loginToken = loginUser.token; const loginToken = loginUser.token;
@ -71,8 +65,8 @@ function App() {
setPassword(''); setPassword('');
}; };
const splitPath = href.split("/"); const splitPath = href.split('/');
let defaultPath = "reservation"; let defaultPath = 'reservation';
if (splitPath.length > 1) { if (splitPath.length > 1) {
defaultPath = splitPath[1]; defaultPath = splitPath[1];
@ -90,7 +84,7 @@ function App() {
<ConfigProvider locale={antdLng} <ConfigProvider locale={antdLng}
theme={{ theme={{
token: { token: {
colorPrimary: "#00b96b", colorPrimary: '#00b96b',
}, },
algorithm: theme.defaultAlgorithm, algorithm: theme.defaultAlgorithm,
}}> }}>
@ -104,7 +98,7 @@ function App() {
> >
<Title level={3}>{t('LoginTimeout')}</Title> <Title level={3}>{t('LoginTimeout')}</Title>
<div>{t('LoginTimeoutTip')}</div> <div>{t('LoginTimeoutTip')}</div>
<Space direction="horizontal"> <Space direction='horizontal'>
<Input.Password value={password} <Input.Password value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
onPressEnter={() => onSubmit()} onPressEnter={() => onSubmit()}
@ -116,29 +110,29 @@ function App() {
</Modal> </Modal>
<Layout <Layout
style={{ style={{
minHeight: "100vh", minHeight: '100vh',
}}> }}>
<Header className="header" style={{ position: "sticky", top: 0, zIndex: 1, width: "100%" }}> <Header className='header' style={{ position: 'sticky', top: 0, zIndex: 1, width: '100%' }}>
<Row gutter={{ md: 24 }} justify="end" align="middle"> <Row gutter={{ md: 24 }} justify='end' align='middle'>
<Col span={16}> <Col span={16}>
<NavLink to="/"> <NavLink to='/'>
<img src={AppLogo} className="logo" alt="App logo" /> <img src={AppLogo} className='logo' alt='App logo' />
</NavLink> </NavLink>
<Menu <Menu
theme="dark" theme='dark'
mode="horizontal" mode='horizontal'
selectedKeys={[defaultPath]} selectedKeys={[defaultPath]}
items={[ items={[
{ key: "reservation", label: <Link to="/reservation/newest">{t('menu.Reservation')}</Link> }, { key: 'reservation', label: <Link to='/reservation/newest'>{t('menu.Reservation')}</Link> },
{ key: "invoice", label: <Link to="/invoice">{t('menu.Invoice')}</Link> }, { key: 'invoice', label: <Link to='/invoice'>{t('menu.Invoice')}</Link> },
{ key: "feedback", label: <Link to="/feedback">{t('menu.Feedback')}</Link> }, { key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</Link> },
{ key: "report", label: <Link to="/report">{t('menu.Report')}</Link> }, { key: 'report', label: <Link to='/report'>{t('menu.Report')}</Link> },
{ {
key: "notice", key: 'notice',
label: ( label: (
<Link to="/notice"> <Link to='/notice'>
{t('menu.Notice')} {t('menu.Notice')}
{noticeUnRead ? <Badge dot /> : ""} {noticeUnRead ? <Badge dot /> : ''}
</Link> </Link>
), ),
}, },
@ -146,7 +140,7 @@ function App() {
/> />
</Col> </Col>
<Col span={4}> <Col span={4}>
<Title level={3} style={{ color: "white", marginBottom: "0", display: "flex", justifyContent: "end" }}> <Title level={3} style={{ color: 'white', marginBottom: '0', display: 'flex', justifyContent: 'end' }}>
{loginUser.travelAgencyName} {loginUser.travelAgencyName}
</Title> </Title>
</Col> </Col>
@ -194,4 +188,4 @@ function App() {
); );
} }
export default observer(App); export default App

@ -1,125 +1,109 @@
import { useNavigate, useLocation } from "react-router-dom"; import { useNavigate, useLocation } from 'react-router-dom';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Button, Checkbox, Form, Input, Row, App } from 'antd'; import { Button, Checkbox, Form, Input, Row, App } from 'antd';
import { useStore } from '@/stores/StoreContext.js';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import useAuthStore from '@/stores/Auth' import useAuthStore from '@/stores/Auth'
import useNoticeStore from '@/stores/Notice'; import useNoticeStore from '@/stores/Notice';
function Login() { function Login() {
const { t, i18n } = useTranslation(); const [validateUserPassword, loginStatus, logout] =
useAuthStore((state) => [state.validateUserPassword, state.loginStatus])
const getBulletinUnReadCount = useNoticeStore((state) => state.getBulletinUnReadCount)
const { authStore } = useStore(); const { t, i18n } = useTranslation()
const [valdateUserPassword, fetchUserDetail, loginStatus] =
useAuthStore((state) => [state.valdateUserPassword, state.fetchUserDetail, state.loginStatus])
const { notification } = App.useApp(); const { notification } = App.useApp();
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const [form] = Form.useForm() const [form] = Form.useForm()
const getBulletinUnReadCount = useNoticeStore((state) => state.getBulletinUnReadCount);
useEffect (() => { useEffect (() => {
if (location.search === '?out') { if (location.search === '?out') {
authStore.logout(); logout();
navigate('/login'); navigate('/login')
} }
return () => { }, [])
// unmount...
};
}, []);
useEffect (() => { useEffect (() => {
if (loginStatus === 302) { if (loginStatus === 302) {
navigate("/reservation/newest") navigate('/reservation/newest')
} }
}, [loginStatus]); }, [loginStatus])
const onFinish = (values) => { const onFinish = (values) => {
valdateUserPassword(values.username, values.password) validateUserPassword(values.username, values.password)
.then((userId) => {
// noticeStore.getBulletinUnReadCount(userId);
console.info('valdateUserPassword')
//
})
.catch(ex => { .catch(ex => {
notification.error({ notification.error({
message: `Notification`, message: t('Validation.Title'),
description: 'Login failed. Incorrect username or password.', description: t('Validation.LoginFailed'),
placement: 'top', placement: 'top',
duration: 4, duration: 4,
}); })
}); })
}; }
const onFinishFailed = (errorInfo) => { const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo); console.log('Failed:', errorInfo);
} }
console.info('loginStatus: ' + loginStatus)
if (loginStatus === 302) { return (
} else { <Row justify='center' align='middle' style={{ minHeight: 500 }}>
return ( <Form
<Row justify="center" align="middle" style={{ minHeight: 500 }}> name='basic'
<Form form={form}
name="basic" size='large'
// layout="vertical" labelCol={{
form={form} span: 8,
size="large" }}
labelCol={{ wrapperCol={{
span: 8, span: 16,
}} }}
style={{
maxWidth: 600,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete='off'
>
<Form.Item
label={t('Username')}
name='username'
rules={[
{
required: true,
message: t('Validation.UsernameIsEmpty'),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t('Password')}
name='password'
rules={[
{
required: true,
message: t('Validation.PasswordIsEmpty'),
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
wrapperCol={{ wrapperCol={{
offset: 8,
span: 16, span: 16,
}} }}
style={{
maxWidth: 600,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
> >
<Form.Item <Button type='primary' htmlType='submit' style={{width: '100%'}}>
label={t("Username")} {t('Login')}
name="username" </Button>
rules={[ </Form.Item>
{ </Form>
required: true, </Row>
message: 'Please input your username!', )
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("Password")}
name="password"
rules={[
{
required: true,
message: 'Please input your password!',
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
wrapperCol={{
offset: 8,
span: 16,
}}
>
<Button type="primary" htmlType="submit" style={{width: "100%"}}>
{t('Login')}
</Button>
</Form.Item>
</Form>
</Row>
);
}
} }
export default Login; export default Login

@ -113,10 +113,10 @@ function Newest() {
const { reservationStore } = useStore(); const { reservationStore } = useStore();
const loginUser = useAuthStore((state) => state.loginUser) const loginUser = useAuthStore((state) => state.loginUser)
const [fetchAllGuideList, fetchReservationList] = const [fetchAllGuideList, fetchReservationList, reservationList] =
useReservationStore((state) => [state.fetchAllGuideList, state.fetchReservationList]) useReservationStore((state) => [state.fetchAllGuideList, state.fetchReservationList, state.reservationList])
const { reservationList, reservationPage, referenceNo, arrivalDateRange, cityList } = reservationStore; const { reservationPage, cityList } = reservationStore;
const { notification } = App.useApp(); const { notification } = App.useApp();
useEffect (() => { useEffect (() => {
@ -170,7 +170,7 @@ function Newest() {
// //
const onSearchClick = (current=1, status=null) => { const onSearchClick = (current=1, status=null) => {
setDataLoading(true); setDataLoading(true)
fetchReservationList(loginUser.travelAgencyId, current, status) fetchReservationList(loginUser.travelAgencyId, current, status)
.catch(ex => { .catch(ex => {
notification.error({ notification.error({
@ -181,8 +181,8 @@ function Newest() {
}); });
}) })
.finally(() => { .finally(() => {
setDataLoading(false); setDataLoading(false)
}); })
} }
return ( return (
@ -231,33 +231,21 @@ function Newest() {
}, },
}} }}
onSubmit={(err, formVal, filedsVal) => { onSubmit={(err, formVal, filedsVal) => {
const _ff = objectMapper(formVal, { startdate: 'DateStart', enddate: 'DateEnd', referenceNo: 'GroupNo' }) setDataLoading(true)
console.log('from form', formVal, 'mappered for search', _ff); fetchReservationList(loginUser.travelAgencyId, formVal)
.catch(ex => {
notification.error({
message: `Notification`,
description: ex.message,
placement: 'top',
duration: 4,
});
})
.finally(() => {
setDataLoading(false)
})
}} }}
/> />
<Row gutter={16}>
<Col md={24} lg={6} xxl={4}>
<Input placeholder={t('group:RefNo')} value={referenceNo} onChange={(e) => { reservationStore.updatePropertyValue('referenceNo', e.target.value)} } />
</Col>
<Col md={24} lg={8} xxl={6}>
<Space direction="horizontal">
{t('group:ArrivalDate')}
<DatePicker.RangePicker
allowClear={true}
inputReadOnly={true}
presets={presets}
defaultValue={toJS(arrivalDateRange)}
placeholder={['From', 'Thru']}
onChange={(dateRange) => {
reservationStore.updatePropertyValue('arrivalDateRange', dateRange == null ? [] : dateRange)
}}
/>
</Space>
</Col>
<Col md={24} lg={4} xxl={4}>
<Button type='primary' onClick={() => onSearchClick()} loading={dataLoading}>{t('Search')}</Button>
</Col>
</Row>
<Title level={3}></Title> <Title level={3}></Title>
<Row> <Row>
<Col span={24}> <Col span={24}>
@ -272,7 +260,7 @@ function Newest() {
total: reservationPage.total, total: reservationPage.total,
simple: true simple: true
}} }}
onChange={(pagination, filters, sorter, extra) => {onSearchClick(pagination.current);}} onChange={(pagination, filters, sorter, extra) => {onSearchClick(pagination.current)}}
columns={reservationListColumns} dataSource={reservationList} columns={reservationListColumns} dataSource={reservationList}
/> />
</Col> </Col>

Loading…
Cancel
Save