diff --git a/doc/RBAC 权限.sql b/doc/RBAC 权限.sql
index 489d8f4..5f6befb 100644
--- a/doc/RBAC 权限.sql
+++ b/doc/RBAC 权限.sql
@@ -59,6 +59,19 @@ VALUES ('管理角色', '/account/role-new', 'system')
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
VALUES ('所有海外功能', '/oversea/all', 'oversea')
+
+INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
+VALUES ('团预订', '/reservation/all', 'oversea')
+INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
+VALUES ('团预订(客服)', '/reservation/most', 'oversea')
+
+INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
+VALUES ('账单', '/invoice/all', 'oversea')
+INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
+VALUES ('反馈表', '/feedback/all', 'oversea')
+INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
+VALUES ('质量评分', '/report/all', 'oversea')
+
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
VALUES ('所有国内功能', '/domestic/all', 'domestic')
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
diff --git a/src/components/SearchInput.jsx b/src/components/SearchInput.jsx
index 134c301..19724a5 100644
--- a/src/components/SearchInput.jsx
+++ b/src/components/SearchInput.jsx
@@ -31,6 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
showSearch
allowClear
maxTagCount={1}
+ loading={fetching}
dropdownStyle={{width: '20rem'}}
{...props}
onSearch={debounceFetcher}
diff --git a/src/config.js b/src/config.js
index 0d4e825..848b932 100644
--- a/src/config.js
+++ b/src/config.js
@@ -29,7 +29,12 @@ export const PERM_ROLE_NEW = '/account/role-new'
// 海外供应商
// category: oversea
-export const PERM_OVERSEA = '/oversea/all'
+export const PERM_OVERSEA = '/oversea/all' // @Deprecated 准备作废...
+export const PERM_RESERVATION_ALL = '/reservation/all' // 供应商使用,只能搜索自己的数据
+export const PERM_RESERVATION_MOST = '/reservation/most' // 客服使用,可是选择其他供应商搜索
+export const PERM_INVOICE_ALL = '/invoice/all'
+export const PERM_FEEDBACK_ALL = '/feedback/all'
+export const PERM_REPORT_ALL = '/report/all'
// 国内供应商
// category: domestic
diff --git a/src/main.jsx b/src/main.jsx
index d1fee90..af56f23 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -49,7 +49,10 @@ import ImageViewer from '@/views/ImageViewer';
import CustomerImageViewer from '@/views/CustomerImageViewer';
import PickYear from './views/products/PickYear'
-import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA,PERM_TRAIN_TICKET, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_PUT } from '@/config'
+import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW,
+ PERM_TRAIN_TICKET, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_PUT,
+ PERM_RESERVATION_ALL, PERM_FEEDBACK_ALL, PERM_INVOICE_ALL, PERM_REPORT_ALL
+} from '@/config'
import './i18n'
@@ -66,23 +69,24 @@ const initRouter = async () => {
{ path: 'account/management', element: },
{ path: 'account/role-list', element: },
//
- { path: 'reservation/newest', element: },
- { path: 'reservation/:reservationId', element: },
+ { path: 'reservation/newest', element: },
+ { path: 'reservation/:reservationId', element: },
//
- { path: 'feedback', element: },
- { path: 'feedback/:GRI_SN/:CII_SN/:RefNo', element: },
- { path: 'feedback/:GRI_SN/:RefNo', element: },
+ { path: 'feedback', element: },
+ { path: 'feedback/:GRI_SN/:CII_SN/:RefNo', element: },
+ { path: 'feedback/:GRI_SN/:RefNo', element: },
//
- { path: 'report', element: },
+ { path: 'report', element: },
//
{ path: 'notice', element: },
{ path: 'notice/:CCP_BLID', element: },
//
- { path: 'invoice',element:},
- { path: 'invoice/detail/:GMDSN/:GSN',element:},
- { path: 'invoice/history/:GMDSN/:GSN',element:},
- { path: 'invoice/paid',element:},
- { path: 'invoice/paid/detail/:flid', element: },
+ { path: 'invoice',element:},
+ { path: 'invoice/detail/:GMDSN/:GSN',element:},
+ { path: 'invoice/history/:GMDSN/:GSN',element:},
+ { path: 'invoice/paid',element:},
+ { path: 'invoice/paid/detail/:flid', element: },
+
{ path: 'airticket',element: },
{ path: 'airticket/plan/:coli_sn/:gri_sn',element:},
{ path: 'airticket/invoice',element:},
diff --git a/src/stores/Reservation.js b/src/stores/Reservation.js
index e28ea15..670fe18 100644
--- a/src/stores/Reservation.js
+++ b/src/stores/Reservation.js
@@ -38,7 +38,7 @@ export const fetchAttachList = async (reservationId) => {
const useReservationStore = create(devtools((set, get) => ({
cityList: [],
-
+ selectedAgencyId: -1,
selectedReservation: null,
selectedConfirmation: null,
arrivalDateRange: [],
@@ -60,12 +60,12 @@ const useReservationStore = create(devtools((set, get) => ({
],
getCityListByReservationId: async (reservationId) => {
- const { travelAgencyId } = usingStorage()
+ const { selectedAgencyId } = get()
set(() => ({
cityList: []
}))
- const cityListJson = await fetchCityList(travelAgencyId, reservationId)
+ const cityListJson = await fetchCityList(selectedAgencyId, reservationId)
const mapCityList = cityListJson.map((data) => {
return {
key: data.CII_SN,
@@ -93,12 +93,14 @@ const useReservationStore = create(devtools((set, get) => ({
},
fetchReservationList: (formValues, current=1) => {
- const { travelAgencyId } = usingStorage()
+ set(() => ({
+ selectedAgencyId: formValues.selectedAgencyId
+ }))
const { reservationPage } = get()
// 设置为 0,后端会重新计算总数,当跳转第 X 页时可用原来的总数。
const totalNum = current == 1 ? 0 : reservationPage.total
const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanSearchList')
- .append('VEI_SN', travelAgencyId)
+ .append('VEI_SN', formValues.selectedAgencyId)
.append('GroupNo', formValues.referenceNo)
.append('DateStart', formValues.startdate)
.append('DateEnd', formValues.enddate)
@@ -138,8 +140,7 @@ const useReservationStore = create(devtools((set, get) => ({
})
},
- fetchAllGuideList: () => {
- const { travelAgencyId } = usingStorage()
+ fetchAgencyGuideList: (travelAgencyId) => {
const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/PTGetGuideList')
.append('VEI_SN', travelAgencyId)
.build()
@@ -161,8 +162,7 @@ const useReservationStore = create(devtools((set, get) => ({
})
},
- getReservationDetail: async (reservationId) => {
- const { travelAgencyId } = usingStorage()
+ getReservationDetail: async (travelAgencyId, reservationId) => {
const { planDetail, planChangeList } = await fetchPlanDetail(travelAgencyId, reservationId)
const attachListJson = await fetchAttachList(reservationId)
diff --git a/src/views/App.jsx b/src/views/App.jsx
index 2ac855e..567fc5c 100644
--- a/src/views/App.jsx
+++ b/src/views/App.jsx
@@ -22,7 +22,9 @@ import { useDefaultLgc } from '@/i18n/LanguageSwitcher'
import { appendRequestParams } from '@/utils/request'
import LogUploader from '@/components/LogUploader'
-import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT,PERM_TRAIN_TICKET } from '@/config'
+import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT,PERM_TRAIN_TICKET,
+ PERM_RESERVATION_ALL, PERM_FEEDBACK_ALL, PERM_INVOICE_ALL, PERM_REPORT_ALL
+ } from '@/config'
const { Header, Content, Footer } = Layout
@@ -98,10 +100,10 @@ function App() {
mode='horizontal'
selectedKeys={[defaultPath]}
items={[
- isPermitted(PERM_OVERSEA) ? { key: 'reservation', label: {t('menu.Reservation')} } : null,
- isPermitted(PERM_OVERSEA) ? { key: 'invoice', label: {t('menu.Invoice')} } : null,
- isPermitted(PERM_OVERSEA) ? { key: 'feedback', label: {t('menu.Feedback')} } : null,
- isPermitted(PERM_OVERSEA) ? { key: 'report', label: {t('menu.Report')} } : null,
+ isPermitted(PERM_RESERVATION_ALL) ? { key: 'reservation', label: {t('menu.Reservation')} } : null,
+ isPermitted(PERM_INVOICE_ALL) ? { key: 'invoice', label: {t('menu.Invoice')} } : null,
+ isPermitted(PERM_FEEDBACK_ALL) ? { key: 'feedback', label: {t('menu.Feedback')} } : null,
+ isPermitted(PERM_REPORT_ALL) ? { key: 'report', label: {t('menu.Report')} } : null,
isPermitted(PERM_AIR_TICKET) ? { key: 'airticket', label: {t('menu.Airticket')} } : null,
isPermitted(PERM_TRAIN_TICKET) ? { key: 'trainticket', label: {t('menu.Trainticket')} } : null,
isProductPermitted ? { key: 'products', label: {t('menu.Products')} } : null,
diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx
index e972632..8c9196f 100644
--- a/src/views/account/Management.jsx
+++ b/src/views/account/Management.jsx
@@ -3,7 +3,7 @@ import useAccountStore, { fetchRoleList, fetchTravelAgencyByName, genRandomPassw
import useFormStore from '@/stores/Form'
import { isEmpty, debounce } 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 { App, Button, Col, Form, Input, Modal, Row, Select, Space, Table, Typography, Switch, Spin } from 'antd'
import dayjs from 'dayjs'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -306,7 +306,7 @@ function Management() {
showSearch
filterOption={false}
onSearch={debounce(handleTravelAgencySearch, 800)}
- notFoundContent={null}
+ notFoundContent={dataLoading ? : null}
>
diff --git a/src/views/reservation/Detail.jsx b/src/views/reservation/Detail.jsx
index 52f7f1c..697241c 100644
--- a/src/views/reservation/Detail.jsx
+++ b/src/views/reservation/Detail.jsx
@@ -52,7 +52,7 @@ function Detail() {
}
function attachmentRender(_, confirm) {
- const attachmentKey = `GHH/${travelAgencyId}/${reservationId}/PCISN${confirm.key}`;
+ const attachmentKey = `GHH/${selectedAgencyId}/${reservationId}/PCISN${confirm.key}`;
return (
<>
@@ -77,19 +77,19 @@ function Detail() {
const { notification } = App.useApp();
const { reservationId } = useParams();
- const { travelAgencyId, loginToken } = usingStorage()
+ const { loginToken } = usingStorage()
- const [getReservationDetail, reservationDetail, confirmationList, selectConfirmation, submitConfirmation] =
+ const [getReservationDetail, reservationDetail, confirmationList, selectConfirmation, submitConfirmation, selectedAgencyId] =
useReservationStore((state) =>
- [state.getReservationDetail, state.reservationDetail, state.confirmationList, state.selectConfirmation, state.submitConfirmation])
+ [state.getReservationDetail, state.reservationDetail, state.confirmationList, state.selectConfirmation, state.submitConfirmation, state.selectedAgencyId])
const randomString = new Date().getTime()
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=${travelAgencyId}&token=${loginToken}&FileType=1&v=${randomString}`
+ `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${selectedAgencyId}&token=${loginToken}&FileType=1&v=${randomString}`
const nameCardUrl =
- `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${travelAgencyId}&token=${loginToken}&FileType=2&v=${randomString}`
+ `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${reservationId}&VEI_SN=${selectedAgencyId}&token=${loginToken}&FileType=2&v=${randomString}`
const showConfirmModal = (confirm) => {
setIsModalOpen(true);
@@ -117,7 +117,7 @@ function Detail() {
setReservationPreviewUrl(officeWebViewerUrl + encodeURIComponent(reservationUrl))
setNameCardPreviewUrl(officeWebViewerUrl + encodeURIComponent(nameCardUrl))
- getReservationDetail(reservationId)
+ getReservationDetail(selectedAgencyId, reservationId)
.catch(ex => {
notification.error({
message: `Notification`,
diff --git a/src/views/reservation/Newest.jsx b/src/views/reservation/Newest.jsx
index 32b2630..5579e22 100644
--- a/src/views/reservation/Newest.jsx
+++ b/src/views/reservation/Newest.jsx
@@ -6,7 +6,10 @@ import { isEmpty } from '@/utils/commons'
import { useTranslation } from 'react-i18next'
import useFormStore from '@/stores/Form'
import useReservationStore from '@/stores/Reservation'
+import useAuthStore from '@/stores/Auth'
import SearchForm from '@/components/SearchForm'
+import { usingStorage } from '@/hooks/usingStorage'
+import { PERM_RESERVATION_MOST } from '@/config';
const { Title } = Typography
@@ -94,25 +97,18 @@ function Newest() {
const [dataLoading, setDataLoading] = useState(false)
const [guideSelectOptions, setGuideSelectOptions] = useState([])
+ const { travelAgencyId } = usingStorage()
+ const isPermitted = useAuthStore((state) => state.isPermitted)
const formValuesToSub = useFormStore((state) => state.formValuesToSub)
- const [fetchAllGuideList, fetchReservationList, reservationList, reservationPage, cityList, selectReservation, getCityListByReservationId, setupCityGuide, updateReservationGuide] =
+ const [fetchAgencyGuideList, fetchReservationList, reservationList, reservationPage, cityList, selectReservation, getCityListByReservationId, setupCityGuide, updateReservationGuide] =
useReservationStore((state) =>
- [state.fetchAllGuideList, state.fetchReservationList, state.reservationList, state.reservationPage, state.cityList, state.selectReservation, state.getCityListByReservationId, state.setupCityGuide, state.updateReservationGuide])
+ [state.fetchAgencyGuideList, state.fetchReservationList, state.reservationList, state.reservationPage, state.cityList, state.selectReservation, state.getCityListByReservationId, state.setupCityGuide, state.updateReservationGuide])
const { notification } = App.useApp()
useEffect (() => {
- fetchAllGuideList()
- .then((guideList) => {
- const selectOptions = guideList.map((data) => {
- return {
- value: data.guideId,
- label: data.guideName
- }
- })
- setGuideSelectOptions(selectOptions)
- })
- }, [fetchAllGuideList])
+ initAgencyGuideList(travelAgencyId)
+ }, [])
const showCityGuideModal = (reservation) => {
setDataLoading(true)
@@ -145,10 +141,46 @@ function Newest() {
setDataLoading(false);
}
+ const initAgencyGuideList = (agencyId) => {
+ fetchAgencyGuideList(agencyId)
+ .then((guideList) => {
+ const selectOptions = guideList.map((data) => {
+ return {
+ value: data.guideId,
+ label: data.guideName
+ }
+ })
+ setGuideSelectOptions(selectOptions)
+ })
+ }
+
// 默认重新搜索第一页,所有状态的计划
const searchReservation = (submitValues, current=1) => {
setDataLoading(true)
- fetchReservationList(submitValues, current)
+
+ const getSelectedAgencyId = () => {
+
+ if (isPermitted(PERM_RESERVATION_MOST)) {
+ return isEmpty(submitValues.agency) ? travelAgencyId : parseInt(submitValues.agency, 10)
+ } else {
+ return travelAgencyId
+ }
+ }
+ const selectedAgencyId = getSelectedAgencyId()
+
+ initAgencyGuideList(selectedAgencyId)
+
+ if (isEmpty(selectedAgencyId)) {
+ notification.error({
+ message: `Notification`,
+ description: 'Agency is required',
+ placement: 'top',
+ duration: 4,
+ });
+ return
+ }
+ const formValues = {...submitValues, ...{ selectedAgencyId }};
+ fetchReservationList(formValues, current)
.catch(ex => {
notification.error({
message: `Notification`,
@@ -199,10 +231,14 @@ function Newest() {
{
searchReservation(initialValue)