diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index ffeabc2..3d600bf 100644 Binary files a/doc/价格管理平台.bmpr and b/doc/价格管理平台.bmpr differ diff --git a/src/hooks/useStorage.js b/src/hooks/useStorage.js new file mode 100644 index 0000000..16da146 --- /dev/null +++ b/src/hooks/useStorage.js @@ -0,0 +1,50 @@ +export function useStorage() { + + const setSession = (key, value) => { + if (window.sessionStorage) { + const sessionStorage = window.sessionStorage + return sessionStorage.setItem(key, value) + } else { + console.error('browser not support sessionStorage.') + } + } + + const camelCasedWords = (string) => { + if (typeof string !== 'string' || string.length === 0) { + return string; + } + return string.split('_').map((word, index) => { + if (index === 0) { + return '' + } else if (index === 1) { + return word.toLowerCase() + } else { + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() + } + }).join('') + } + + const storageObject = {} + + if (window.sessionStorage) { + + const sessionStorage = window.sessionStorage + + for (let i = 0; i < sessionStorage.length; i++) { + const key = sessionStorage.key(i) + const value = sessionStorage.getItem(key) + const propName = camelCasedWords(key) + storageObject[propName] = value + } + + return { + ...storageObject, + setStorage: (key, value) => { + setSession(key, value) + } + } + } else { + console.error('browser not support sessionStorage.') + return {} + } +} diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 2655979..525bb4a 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -1,13 +1,15 @@ 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 { create } from 'zustand' +import { useStorage } from '@/hooks/useStorage' + -const KEY_LOGIN_TOKEN = 'KEY_LOGIN_TOKEN'; -const KEY_TRAVEL_AGENCY_ID = 'KEY_TRAVEL_AGENCY_ID'; -const KEY_USER_ID = 'KEY_USER_ID'; +const KEY_LOGIN_TOKEN = 'KEY_LOGIN_TOKEN' +const KEY_TRAVEL_AGENCY_ID = 'KEY_TRAVEL_AGENCY_ID' +const KEY_USER_ID = 'KEY_USER_ID' const useAuthStore = create((set, get) => ({ @@ -45,11 +47,12 @@ const useAuthStore = create((set, get) => ({ }, validateUserPassword: async (usr, pwd) => { + const { setStorage } = useStorage() + 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) @@ -59,8 +62,9 @@ const useAuthStore = create((set, get) => ({ return 0; } } - const token = await fetchLoginToken() - appendRequestParams('token', token) + const loginToken = await fetchLoginToken() + setStorage(KEY_LOGIN_TOKEN, loginToken) + appendRequestParams('token', loginToken) const fetchUrl = prepareUrl(HT_HOST + '/service-CooperateSOA/GetLinkManInfo').build() @@ -69,7 +73,7 @@ const useAuthStore = create((set, get) => ({ if (json.errcode == 0) { set(() => ({ loginUser: { - token: token, + token: loginToken, timeout: false, userId: json.Result.LMI_SN, username: json.Result.LoginName, @@ -81,6 +85,9 @@ const useAuthStore = create((set, get) => ({ }, loginStatus: 302 })) + + setStorage(KEY_USER_ID, json.Result.LMI_SN) + setStorage(KEY_TRAVEL_AGENCY_ID, json.Result.LMI_VEI_SN) // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) // this.startTokenInterval() } else { diff --git a/src/stores/Reservation.js b/src/stores/Reservation.js index 96c3da9..dd50987 100644 --- a/src/stores/Reservation.js +++ b/src/stores/Reservation.js @@ -1,8 +1,39 @@ -import { makeAutoObservable, runInAction } from "mobx"; -import { fetchJSON, postForm } from '@/utils/request'; -import { HT_HOST } from "@/config"; -import { prepareUrl } from '@/utils/commons'; +import { makeAutoObservable, runInAction } from "mobx" import { create } from 'zustand' +import { fetchJSON, postForm } from '@/utils/request' +import { HT_HOST } from "@/config" +import { prepareUrl } from '@/utils/commons' +import { useStorage } from '@/hooks/useStorage' + +export const fetchCityList = async (travelAgencyId, reservationId) => { + const { errcode, Result } = await fetchJSON( + `${HT_HOST}/service-cusservice/PTGetCityGuide`, + { VEI_SN: travelAgencyId, GRI_SN: reservationId, LGC: 1 }) + return errcode !== 0 ? {} : Result; +} + +export const fetchPlanDetail = async (travelAgencyId, reservationId) => { + const json = await fetchJSON( + `${HT_HOST}/service-cusservice/GetPlanInfo`, + { VEI_SN: travelAgencyId, GRI_SN: reservationId }) + + if (json.errcode == 0) { + return { + planDetail: json.PlanDetail[0], + planChangeList: json.PlanChange??[] + } + } else { + return {} + } +} + +export const fetchAttachList = async (reservationId) => { + + const { errcode, result } = await fetchJSON( + `${HT_HOST}/service-fileServer/PlanChangeFileList`, + { GRI_SN: reservationId }) + return errcode !== 0 ? {} : result; +} const useReservationStore = create((set, get) => ({ @@ -28,7 +59,41 @@ const useReservationStore = create((set, get) => ({ confirmationList: [ ], - fetchReservationList: (travelAgencyId, formVal, current=1) => { + getCityListByReservationId: async (reservationId) => { + const { travelAgencyId } = useStorage() + set(() => ({ + cityList: [] + })) + + const cityListJson = await fetchCityList(travelAgencyId, reservationId) + const mapCityList = cityListJson.map((data) => { + return { + key: data.CII_SN, + cityId: data.CII_SN, + cityName: data.CityName, + tourGuideId: data.TGI_SN, + tourGuide: data.GuideName + } + }) + set(() => ({ + cityList: mapCityList + })) + }, + + selectReservation: (reservation) => { + set(() => ({ + selectedReservation: reservation + })) + }, + + selectConfirmation: (confirmation) => { + set(() => ({ + selectedConfirmation: confirmation + })) + }, + + fetchReservationList: (formVal, current=1) => { + const { travelAgencyId } = useStorage() const { reservationPage } = get() // 设置为 0,后端会重新计算总数,当跳转第 X 页时可用原来的总数。 const totalNum = current == 1 ? 0 : reservationPage.total; @@ -37,7 +102,7 @@ const useReservationStore = create((set, get) => ({ .append('GroupNo', formVal.referenceNo) .append('DateStart', formVal.startdate) .append('DateEnd', formVal.enddate) - .append('NotConfirm', '')//status) + .append('NotConfirm', '')//status)// Todo: 待解决 .append('TotalNum', totalNum) .append('PageSize', reservationPage.size) .append('PageIndex', current) @@ -73,7 +138,8 @@ const useReservationStore = create((set, get) => ({ }); }, - fetchAllGuideList: (travelAgencyId) => { + fetchAllGuideList: () => { + const { userId, travelAgencyId } = useStorage() const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/PTGetGuideList') .append('VEI_SN', travelAgencyId) .build(); @@ -94,149 +160,66 @@ const useReservationStore = create((set, get) => ({ } }); }, -})) - -export default useReservationStore -export class Reservation { + getReservationDetail: async (travelAgencyId, reservationId) => { + const { planDetail, planChangeList } = await fetchPlanDetail(travelAgencyId, reservationId) + const attachListJson = await fetchAttachList(reservationId) - constructor(root) { - makeAutoObservable(this, { rootStore: false }); - this.root = root; - } - - fetchReservationList(current, status=null) { - const fromDate = this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[0].format('YYYY-MM-DD'); - const thruDate = this.arrivalDateRange.length == 0 ? null : this.arrivalDateRange[1].format('YYYY-MM-DD'); - this.reservationPage.current = current; - // 设置为 0,后端会重新计算总数,当跳转第 X 页时可用原来的总数。 - const totalNum = current == 1 ? 0 : this.reservationPage.total; - const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanSearchList') - .append('VEI_SN', this.root.authStore.login.travelAgencyId) - .append('GroupNo', this.referenceNo) - .append('DateStart', fromDate) - .append('DateEnd', thruDate) - .append('NotConfirm', status) - .append('TotalNum', totalNum) - .append('PageSize', this.reservationPage.size) - .append('PageIndex', this.reservationPage.current) - .append("token", this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl) - .then(json => { - 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; - }); - } else { - throw new Error(json.errmsg + ': ' + json.errcode); - } + const mapConfirmationList = planChangeList.map((data) => { + const filterAttchList = attachListJson.filter(attch => { + return attch.PCI_SN === data.PCI_SN; }); - } + return { + key: data.PCI_SN, + PCI_Changetext: data.PCI_Changetext, + PCI_SendDate: data.PCI_SendDate, + ConfirmPerson: data.ConfirmPerson, + PCI_ConfirmText: data.PCI_ConfirmText, + PCI_ConfirmDate: data.PCI_ConfirmDate, + VAS_SN: data.PCI_VAS_SN, + attachmentList: filterAttchList + } + }) + + set(() => ({ + reservationDetail: { + referenceNumber: planDetail.GRI_Name, + tourGuide: planDetail.Guide, + arrivalDate: planDetail.eoi_getdate, + reservationId: reservationId + }, + confirmationList: mapConfirmationList + })) + }, - fetchReservation(reservationId) { - const fetchDetailUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanInfo') - .append('VEI_SN', this.root.authStore.login.travelAgencyId) - .append('GRI_SN', reservationId) - .append("token", this.root.authStore.login.token) - .build(); - // https://p9axztuwd7x8a7.mycht.cn/service-fileServer/PlanChangeFileList - const fetchAttachmentUrl = prepareUrl(HT_HOST + '/service-fileServer/PlanChangeFileList') - .append('GRI_SN', reservationId) - .append("token", this.root.authStore.login.token) - .build(); + submitConfirmation: (userId, travelAgencyId, confirmText) => { + const { selectedConfirmation, getReservationDetail, reservationDetail } = get() + const formData = new FormData() + formData.append('PCI_SN', selectedConfirmation.key) + formData.append('OPSN', userId) + formData.append('ConfirmText', confirmText) + formData.append('VAS_SN', selectedConfirmation.VAS_SN) - const attachmentPromise = fetchJSON(fetchAttachmentUrl) - .then(json => { - if (json.errcode == 0) { - return json; - } else { - throw new Error(json.errmsg + ': ' + json.errcode); - } - }); + const postUrl = HT_HOST + '/service-cusservice/PTConfirmPlanChange' - const detailPromise = fetchJSON(fetchDetailUrl) + return postForm(postUrl, formData) .then(json => { - if (json.errcode == 0) { - return json; - } else { - throw new Error(json.errmsg + ': ' + json.errcode); + if (json.errcode == 0 && json.Result.length == 1) { + // this.fetchReservation(this.reservationDetail.reservationId); + getReservationDetail(travelAgencyId, reservationDetail.reservationId) + return json } }); - - return Promise.all([attachmentPromise, detailPromise]) - .then(results => { - const attachList = results[0].result; - const planDetail = results[1].PlanDetail[0]; - const planChange = results[1]?.PlanChange??[]; - runInAction(() => { - this.reservationDetail = { - referenceNumber: planDetail.GRI_Name, - tourGuide: planDetail.Guide, - arrivalDate: planDetail.eoi_getdate, - reservationId: reservationId - }; - this.confirmationList = planChange.map((data, index) => { - const filterAttchList = attachList.filter(attch => { - return attch.PCI_SN === data.PCI_SN; - }); - return { - key: data.PCI_SN, - PCI_Changetext: data.PCI_Changetext, - PCI_SendDate: data.PCI_SendDate, - ConfirmPerson: data.ConfirmPerson, - PCI_ConfirmText: data.PCI_ConfirmText, - PCI_ConfirmDate: data.PCI_ConfirmDate, - VAS_SN: data.PCI_VAS_SN, - attachmentList: filterAttchList - } - }); - }); - }); } +})) - fetchCityList(reservationId) { - const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/PTGetCityGuide') - .append('VEI_SN', this.root.authStore.login.travelAgencyId) - .append('GRI_SN', reservationId) - .append('LGC', 1) - .append("token", this.root.authStore.login.token) - .build(); +export default useReservationStore - runInAction(() => { - this.cityList = []; - }); - return fetchJSON(fetchUrl) - .then(json => { - runInAction(() => { - if (json.errcode == 0) { - this.cityList = (json?.Result??[]).map((data, index) => { - return { - key: data.CII_SN, - cityId: data.CII_SN, - cityName: data.CityName, - tourGuideId: data.TGI_SN, - tourGuide: data.GuideName - } - }); - } else { - throw new Error(json.errmsg + ': ' + json.errcode); - } - }); - }); +export class Reservation { + + constructor(root) { + makeAutoObservable(this, { rootStore: false }); + this.root = root; } updateReservationGuide() { @@ -284,33 +267,6 @@ export class Reservation { }); } - submitConfirmation(confirmText) { - let formData = new FormData(); - formData.append('PCI_SN', this.selectedConfirmation.key); - formData.append('OPSN', this.root.authStore.login.userId); - formData.append('ConfirmText', confirmText); - formData.append('VAS_SN', this.selectedConfirmation.VAS_SN); - formData.append("token", this.root.authStore.login.token); - - const postUrl = HT_HOST + '/service-cusservice/PTConfirmPlanChange'; - - return postForm(postUrl, formData) - .then(json => { - if (json.errcode == 0 && json.Result.length == 1) { - this.fetchReservation(this.reservationDetail.reservationId); - return json; - } - }); - } - - editReservation(reservation) { - this.selectedReservation = reservation; - } - - editConfirmation(confirmation) { - this.selectedConfirmation = confirmation; - } - updatePropertyValue(name, value) { runInAction(() => { this[name] = value; diff --git a/src/utils/request.js b/src/utils/request.js index c83a272..68d80a6 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -45,7 +45,7 @@ function checkStatus(response) { } } -function checkBizCode(responseJson){ +function checkBizCode(responseJson) { if (responseJson.errcode === 0) { return responseJson; } else { diff --git a/src/views/reservation/Detail.jsx b/src/views/reservation/Detail.jsx index 8ce7b1b..52fe4f9 100644 --- a/src/views/reservation/Detail.jsx +++ b/src/views/reservation/Detail.jsx @@ -6,8 +6,9 @@ import { Row, Col, Space, Button, Table, Input, Typography, Modal, Tag, App } fr import { FileOutlined } from '@ant-design/icons'; -import { useStore } from '@/stores/StoreContext.js'; +import useAuthStore from '@/stores/Auth' import { useTranslation } from 'react-i18next'; +import useReservationStore from '@/stores/Reservation' const { Title, Paragraph } = Typography; const { TextArea } = Input; @@ -77,16 +78,19 @@ function Detail() { const [dataLoading, setDataLoading] = useState(false); const { notification } = App.useApp(); const { reservationId } = useParams(); - const { authStore, reservationStore } = useStore(); - const { reservationDetail, confirmationList } = reservationStore; - const { login } = authStore; + + const loginUser = useAuthStore((state) => state.loginUser) + const [getReservationDetail, reservationDetail, confirmationList, selectConfirmation, submitConfirmation] = + useReservationStore((state) => + [state.getReservationDetail, state.reservationDetail, state.confirmationList, state.selectConfirmation, state.submitConfirmation]) + const officeWebViewerUrl = 'https://view.officeapps.live.com/op/embed.aspx?wdPrint=1&wdHideGridlines=0&wdHideComments=1&wdEmbedCode=0&src='; // 测试文档:https://www.chinahighlights.com/public/reservationW220420009.doc const reservationUrl = - `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${login.travelAgencyId}&token=${login.token}&FileType=1`; + `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${loginUser.travelAgencyId}&token=${loginUser.token}&FileType=1`; const nameCardUrl = - `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${login.travelAgencyId}&token=${login.token}&FileType=2`; + `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${loginUser.travelAgencyId}&token=${loginUser.token}&FileType=2`; const reservationPreviewUrl = officeWebViewerUrl + encodeURIComponent(reservationUrl); const nameCardPreviewUrl = officeWebViewerUrl + encodeURIComponent(nameCardUrl); @@ -95,12 +99,12 @@ function Detail() { setIsModalOpen(true); const formattedText = confirm.PCI_ConfirmText;//.replace(/\;/g, "\n——————————————————————\n"); setConfirmText(formattedText); - reservationStore.editConfirmation(confirm); + selectConfirmation(confirm); }; const handleOk = () => { setConfirmLoading(true); - reservationStore.submitConfirmation(confirmText + '\n——————————————————————\n' +newConfirmText) + submitConfirmation(loginUser.userId, loginUser.travelAgencyId, confirmText + '\n——————————————————————\n' +newConfirmText) .finally(() => { setNewConfirmText(''); setIsModalOpen(false); @@ -113,7 +117,7 @@ function Detail() { useEffect(() => { setDataLoading(true); - reservationStore.fetchReservation(reservationId) + getReservationDetail(loginUser.travelAgencyId, reservationId) .catch(ex => { notification.error({ message: `Notification`, @@ -187,4 +191,4 @@ function Detail() { ); } -export default observer(Detail); +export default Detail; diff --git a/src/views/reservation/Newest.jsx b/src/views/reservation/Newest.jsx index fedf909..6857b17 100644 --- a/src/views/reservation/Newest.jsx +++ b/src/views/reservation/Newest.jsx @@ -4,7 +4,6 @@ import { Row, Col, Space, Button, Table, Typography, Modal, App, Select } from ' import dayjs from 'dayjs' import { isEmpty } from '@/utils/commons' import { useTranslation } from 'react-i18next' -import useAuthStore from '@/stores/Auth' import useFormStore from '@/stores/Form' import useReservationStore from '@/stores/Reservation' import SearchForm from '@/components/SearchForm' @@ -97,7 +96,7 @@ function Newest() { filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) } - options={toJS(guideSelectOptions)} + options={guideSelectOptions} /> ) } @@ -107,20 +106,20 @@ function Newest() { const [dataLoading, setDataLoading] = useState(false) const [guideSelectOptions, setGuideSelectOptions] = useState([]) - const formValues = useFormStore((state) => state.formValues); - const loginUser = useAuthStore((state) => state.loginUser) - const [fetchAllGuideList, fetchReservationList, reservationList, reservationPage, cityList] = + const formValues = useFormStore((state) => state.formValues) + + const [fetchAllGuideList, fetchReservationList, reservationList, reservationPage, cityList, selectReservation, getCityListByReservationId] = useReservationStore((state) => - [state.fetchAllGuideList, state.fetchReservationList, state.reservationList, state.reservationPage, state.cityList]) + [state.fetchAllGuideList, state.fetchReservationList, state.reservationList, state.reservationPage, state.cityList, state.selectReservation, state.getCityListByReservationId]) - const { notification } = App.useApp(); + const { notification } = App.useApp() useEffect (() => { if (location.search !== '?back') { // 第一页,未确认计划 onSearchClick(1, 1) } - fetchAllGuideList(loginUser.travelAgencyId) + fetchAllGuideList() .then((guideList) => { const selectOptions = guideList.map((data, index) => { return { @@ -130,19 +129,16 @@ function Newest() { }) setGuideSelectOptions(selectOptions) }) - return () => { - // unmount... - }; }, []) const showCityGuideModal = (reservation) => { setDataLoading(true) setIsModalOpen(true) - reservationStore.editReservation(reservation); - reservationStore.fetchCityList(reservation.reservationId) + selectReservation(reservation); + getCityListByReservationId(reservation.reservationId) .catch(ex => { notification.error({ - message: `Notification`, + message: 'Notification', description: ex.message, placement: 'top', duration: 4, @@ -151,14 +147,14 @@ function Newest() { .finally(() => { setDataLoading(false); }) - }; + } const handleOk = () => { reservationStore.updateReservationGuide() .finally(() => { setIsModalOpen(false); setDataLoading(false); }) - }; + } const handleCancel = () => { setIsModalOpen(false); setDataLoading(false); @@ -167,7 +163,7 @@ function Newest() { // 默认重新搜索第一页,所有状态的计划 const onSearchClick = (current=1, status=null) => { setDataLoading(true) - fetchReservationList(loginUser.travelAgencyId, formValues, current) + fetchReservationList(formValues, current) .catch(ex => { notification.error({ message: `Notification`, @@ -228,14 +224,14 @@ function Newest() { }} onSubmit={(err, formVal, filedsVal) => { setDataLoading(true) - fetchReservationList(loginUser.travelAgencyId, formVal) + fetchReservationList(travelAgencyId, formVal) .catch(ex => { notification.error({ message: 'Notification', description: ex.message, placement: 'top', duration: 4, - }); + }) }) .finally(() => { setDataLoading(false) @@ -256,7 +252,7 @@ function Newest() { total: reservationPage.total, simple: true }} - onChange={(pagination, filters, sorter, extra) => {onSearchClick(pagination.current)}} + onChange={(pagination) => {onSearchClick(pagination.current)}} columns={reservationListColumns} dataSource={reservationList} />