feat:客服角色可搜索每个供应商的团预订

main
LiaoYijun 6 months ago
parent a18ce37de7
commit 0dfc56056e

@ -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])

@ -31,6 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
showSearch
allowClear
maxTagCount={1}
loading={fetching}
dropdownStyle={{width: '20rem'}}
{...props}
onSearch={debounceFetcher}

@ -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

@ -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: <RequireAuth subject={PERM_ACCOUNT_MANAGEMENT} result={true}><AccountManagement /></RequireAuth>},
{ path: 'account/role-list', element: <RequireAuth subject={PERM_ROLE_NEW} result={true}><RoleList /></RequireAuth>},
//
{ path: 'reservation/newest', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReservationNewest /></RequireAuth>},
{ path: 'reservation/:reservationId', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReservationDetail /></RequireAuth>},
{ path: 'reservation/newest', element: <RequireAuth subject={PERM_RESERVATION_ALL} result={true}><ReservationNewest /></RequireAuth>},
{ path: 'reservation/:reservationId', element: <RequireAuth subject={PERM_RESERVATION_ALL} result={true}><ReservationDetail /></RequireAuth>},
//
{ path: 'feedback', element: <RequireAuth subject={PERM_OVERSEA} result={true}><FeedbackIndex /></RequireAuth>},
{ path: 'feedback/:GRI_SN/:CII_SN/:RefNo', element: <RequireAuth subject={PERM_OVERSEA} result={true}><FeedbackCustomerDetail /></RequireAuth>},
{ path: 'feedback/:GRI_SN/:RefNo', element: <RequireAuth subject={PERM_OVERSEA} result={true}><FeedbackDetail /></RequireAuth>},
{ path: 'feedback', element: <RequireAuth subject={PERM_FEEDBACK_ALL} result={true}><FeedbackIndex /></RequireAuth>},
{ path: 'feedback/:GRI_SN/:CII_SN/:RefNo', element: <RequireAuth subject={PERM_FEEDBACK_ALL} result={true}><FeedbackCustomerDetail /></RequireAuth>},
{ path: 'feedback/:GRI_SN/:RefNo', element: <RequireAuth subject={PERM_FEEDBACK_ALL} result={true}><FeedbackDetail /></RequireAuth>},
//
{ path: 'report', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReportIndex /></RequireAuth>},
{ path: 'report', element: <RequireAuth subject={PERM_REPORT_ALL} result={true}><ReportIndex /></RequireAuth>},
//
{ path: 'notice', element: <NoticeIndex />},
{ path: 'notice/:CCP_BLID', element: <NoticeDetail />},
//
{ path: 'invoice',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoiceIndex /></RequireAuth>},
{ path: 'invoice/detail/:GMDSN/:GSN',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoiceDetail /></RequireAuth>},
{ path: 'invoice/history/:GMDSN/:GSN',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoiceHistory /></RequireAuth>},
{ path: 'invoice/paid',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoicePaid /></RequireAuth>},
{ path: 'invoice/paid/detail/:flid', element: <RequireAuth subject={PERM_OVERSEA} result={true}><InvoicePaidDetail /></RequireAuth>},
{ path: 'invoice',element:<RequireAuth subject={PERM_INVOICE_ALL} result={true}><InvoiceIndex /></RequireAuth>},
{ path: 'invoice/detail/:GMDSN/:GSN',element:<RequireAuth subject={PERM_INVOICE_ALL} result={true}><InvoiceDetail /></RequireAuth>},
{ path: 'invoice/history/:GMDSN/:GSN',element:<RequireAuth subject={PERM_INVOICE_ALL} result={true}><InvoiceHistory /></RequireAuth>},
{ path: 'invoice/paid',element:<RequireAuth subject={PERM_INVOICE_ALL} result={true}><InvoicePaid /></RequireAuth>},
{ path: 'invoice/paid/detail/:flid', element: <RequireAuth subject={PERM_INVOICE_ALL} result={true}><InvoicePaidDetail /></RequireAuth>},
{ path: 'airticket',element: <RequireAuth subject={PERM_AIR_TICKET} result={true}><Airticket /></RequireAuth>},
{ path: 'airticket/plan/:coli_sn/:gri_sn',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketPlan /></RequireAuth>},
{ path: 'airticket/invoice',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketInvoice /></RequireAuth>},

@ -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)

@ -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: <Link to='/reservation/newest'>{t('menu.Reservation')}</Link> } : null,
isPermitted(PERM_OVERSEA) ? { key: 'invoice', label: <Link to='/invoice'>{t('menu.Invoice')}</Link> } : null,
isPermitted(PERM_OVERSEA) ? { key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</Link> } : null,
isPermitted(PERM_OVERSEA) ? { key: 'report', label: <Link to='/report'>{t('menu.Report')}</Link> } : null,
isPermitted(PERM_RESERVATION_ALL) ? { key: 'reservation', label: <Link to='/reservation/newest'>{t('menu.Reservation')}</Link> } : null,
isPermitted(PERM_INVOICE_ALL) ? { key: 'invoice', label: <Link to='/invoice'>{t('menu.Invoice')}</Link> } : null,
isPermitted(PERM_FEEDBACK_ALL) ? { key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</Link> } : null,
isPermitted(PERM_REPORT_ALL) ? { key: 'report', label: <Link to='/report'>{t('menu.Report')}</Link> } : null,
isPermitted(PERM_AIR_TICKET) ? { key: 'airticket', label: <Link to='/airticket'>{t('menu.Airticket')}</Link> } : null,
isPermitted(PERM_TRAIN_TICKET) ? { key: 'trainticket', label: <Link to='/trainticket'>{t('menu.Trainticket')}</Link> } : null,
isProductPermitted ? { key: 'products', label: <Link to={productLink}>{t('menu.Products')}</Link> } : null,

@ -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 ? <Spin size='small' /> : null}
>
</Select>
</Form.Item>

@ -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 (
<>
<ImageUploader osskey={attachmentKey} ignore_case={false} deletable={false} />
@ -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`,

@ -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() {
<SearchForm
initialValue={{unconfirmed: true}}
fieldsConfig={{
shows: ['referenceNo', 'dates', 'unconfirmed'],
shows: isPermitted(PERM_RESERVATION_MOST) ? ['agency', 'referenceNo', 'dates', 'unconfirmed'] : ['referenceNo', 'dates', 'unconfirmed'],
sort: { agency: 0, },
fieldProps: {
dates: { label: t('group:ArrivalDate') },
},
fieldComProps: {
agency: {mode: null, allowClear: false}
}
}}
onMounted={(initialValue) => {
searchReservation(initialValue)

Loading…
Cancel
Save