Merge remote-tracking branch 'origin/dev/2025b' into dev/2025b

dev/2025b
Lei OT 2 months ago
commit b1ce681ed0

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.5.1", "@ant-design/icons": "^5.5.1",
"@react-pdf/renderer": "^3.4.0", "@react-pdf/renderer": "^3.4.0",
"antd": "^5.17.2", "antd": "^5.25.2",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"docx": "^8.5.0", "docx": "^8.5.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@ -22,11 +22,11 @@
"i18next-http-backend": "^2.5.2", "i18next-http-backend": "^2.5.2",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.30.1",
"react-i18next": "^14.1.2", "react-i18next": "^14.1.2",
"react-router-dom": "^6.10.0",
"react-to-pdf": "^1.0.1", "react-to-pdf": "^1.0.1",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz", "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz",
"zustand": "^4.5.2" "zustand": "^4.5.7"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.28", "@types/react": "^18.0.28",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

@ -61,14 +61,19 @@ const initRouter = async () => {
{ path: 'account/profile', element: <AccountProfile />}, { path: 'account/profile', element: <AccountProfile />},
{ path: 'account/management', element: <RequireAuth subject={PERM_ACCOUNT_MANAGEMENT} result={true}><AccountManagement /></RequireAuth>}, { 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: '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/newest', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReservationNewest /></RequireAuth>},
{ path: 'reservation/:reservationId', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReservationDetail /></RequireAuth>}, { path: 'reservation/:reservationId', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReservationDetail /></RequireAuth>},
//
{ path: 'feedback', element: <RequireAuth subject={PERM_OVERSEA} result={true}><FeedbackIndex /></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/: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/:GRI_SN/:RefNo', element: <RequireAuth subject={PERM_OVERSEA} result={true}><FeedbackDetail /></RequireAuth>},
//
{ path: 'report', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReportIndex /></RequireAuth>}, { path: 'report', element: <RequireAuth subject={PERM_OVERSEA} result={true}><ReportIndex /></RequireAuth>},
//
{ path: 'notice', element: <NoticeIndex />}, { path: 'notice', element: <NoticeIndex />},
{ path: 'notice/:CCP_BLID', element: <NoticeDetail />}, { path: 'notice/:CCP_BLID', element: <NoticeDetail />},
//
{ path: 'invoice',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoiceIndex /></RequireAuth>}, { 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/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/history/:GMDSN/:GSN',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoiceHistory /></RequireAuth>},
@ -78,20 +83,18 @@ const initRouter = async () => {
{ path: 'airticket/plan/:coli_sn/:gri_sn',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketPlan /></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>}, { path: 'airticket/invoice',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketInvoice /></RequireAuth>},
{ path: 'airticket/invoicepaid',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketInvoicePaid /></RequireAuth>}, { path: 'airticket/invoicepaid',element:<RequireAuth subject={PERM_AIR_TICKET} result={true}><AirticketInvoicePaid /></RequireAuth>},
//
{ path: 'trainticket',element: <RequireAuth subject={PERM_TRAIN_TICKET} result={true}><Trainticket /></RequireAuth>}, { path: 'trainticket',element: <RequireAuth subject={PERM_TRAIN_TICKET} result={true}><Trainticket /></RequireAuth>},
{ path: 'trainticket/plan/:coli_sn/:gri_sn',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketPlan /></RequireAuth>}, { path: 'trainticket/plan/:coli_sn/:gri_sn',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketPlan /></RequireAuth>},
{ path: 'trainticket/invoice',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketInvoice /></RequireAuth>}, { path: 'trainticket/invoice',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketInvoice /></RequireAuth>},
{ path: 'trainticket/invoicepaid',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketInvoicePaid /></RequireAuth>}, { path: 'trainticket/invoicepaid',element:<RequireAuth subject={PERM_TRAIN_TICKET} result={true}><TrainticketInvoicePaid /></RequireAuth>},
//
{ path: "products",element: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT} result={true}><ProductsManage /></RequireAuth>}, { path: "products",element: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT} result={true}><ProductsManage /></RequireAuth>},
{ path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT} result={true}><ProductsAudit /></RequireAuth>}, { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT} result={true}><ProductsAudit /></RequireAuth>},
{ path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsDetail /></RequireAuth>}, { path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsDetail /></RequireAuth>},
{ path: "products/audit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsAudit /></RequireAuth>}, { path: "products/audit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsAudit /></RequireAuth>},
{ path: "products/edit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsDetail /></RequireAuth>}, { path: "products/edit",element:<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT} result={true}><ProductsDetail /></RequireAuth>},
//
] ]
}, },
{ {

@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'
import { Layout, Menu, ConfigProvider, theme, Dropdown, message, FloatButton, Space, Row, Col, Badge, App as AntApp } from 'antd' import { Layout, Menu, ConfigProvider, theme, Dropdown, message, FloatButton, Space, Row, Col, Badge, App as AntApp } from 'antd'
import { DownOutlined } from '@ant-design/icons' import { DownOutlined } from '@ant-design/icons'
import 'antd/dist/reset.css' import 'antd/dist/reset.css'
import AppLogo from '@/assets/logo-gh.png' import AppLogo from '@/assets/highlights_travel_600_550.png'
import { isEmpty } from '@/utils/commons' import { isEmpty } from '@/utils/commons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import zhLocale from 'antd/locale/zh_CN' import zhLocale from 'antd/locale/zh_CN'
@ -103,7 +103,7 @@ function App() {
<Row gutter={{ md: 24 }} justify='end' align='middle'> <Row gutter={{ md: 24 }} justify='end' align='middle'>
<Col span={15}> <Col span={15}>
<NavLink to='/'> <NavLink to='/'>
<img src={AppLogo} className='float-left h-9 my-4 mr-6 ml-0 bg-white/30' alt='App logo' /> <img src={AppLogo} className='float-left h-12 my-2 mr-6 ml-0' alt='App logo' />
</NavLink> </NavLink>
<Menu <Menu
theme='dark' theme='dark'

@ -1,6 +1,6 @@
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useEffect } from 'react' import { useEffect } from 'react'
import { Button, Form, Input, Row, Radio, App } from 'antd' import { Button, Form, Input, Row, Radio, App, Typography } from 'antd'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useAuthStore from '@/stores/Auth' import useAuthStore from '@/stores/Auth'
import { appendRequestParams } from '@/utils/request' import { appendRequestParams } from '@/utils/request'
@ -46,6 +46,8 @@ function Login() {
} }
return ( return (
<>
<Typography.Title className="text-center" level={3}>Global Highlights Hub</Typography.Title>
<Row justify='center' align='middle' className='min-h-96'> <Row justify='center' align='middle' className='min-h-96'>
<Form <Form
name='login' name='login'
@ -103,6 +105,7 @@ function Login() {
</Form.Item> </Form.Item>
</Form> </Form>
</Row> </Row>
</>
) )
} }

@ -1,7 +1,7 @@
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { Layout, ConfigProvider, theme, Row, Col, App as AntApp } from "antd"; import { Layout, ConfigProvider, theme, Row, Col, App as AntApp } from "antd";
import "antd/dist/reset.css"; import "antd/dist/reset.css";
import AppLogo from "@/assets/logo-gh.png"; import AppLogo from "@/assets/highlights_travel_600_550.png";
import { useThemeContext } from "@/stores/ThemeContext"; import { useThemeContext } from "@/stores/ThemeContext";
import { BUILD_VERSION } from "@/config"; import { BUILD_VERSION } from "@/config";
@ -20,9 +20,8 @@ function Standlone() {
}}> }}>
<AntApp> <AntApp>
<Layout className="min-h-screen"> <Layout className="min-h-screen">
<Header className="sticky top-0 z-10 w-full"> <Header className="sticky top-0 z-10 w-full text-center">
<img src={AppLogo} className="float-left h-9 my-4 mr-6 ml-0 bg-white/30" alt="App logo" /> <img src={AppLogo} className="h-12 my-2 mr-6 ml-0" alt="App logo" />
<p className="text-white text-center">Global Highlights Hub</p>
</Header> </Header>
<Content className="p-6 m-0 min-h-72 bg-white"> <Content className="p-6 m-0 min-h-72 bg-white">
<Outlet /> <Outlet />

@ -140,6 +140,8 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
const [isQuotationModalOpen, setQuotationModalOpen] = useState(false) const [isQuotationModalOpen, setQuotationModalOpen] = useState(false)
const [isBatchSetupModalOpen, setBatchSetupModalOpen] = useState(false) const [isBatchSetupModalOpen, setBatchSetupModalOpen] = useState(false)
const [groupSizeUnlimit, setGroupSizeUnlimit] = useState(false)
const [groupMaxUnlimit, setGroupMaxUnlimit] = useState(false)
const { modal, notification } = App.useApp() const { modal, notification } = App.useApp()
const [quotationForm] = Form.useForm() const [quotationForm] = Form.useForm()
const [batchSetupForm] = Form.useForm() const [batchSetupForm] = Form.useForm()
@ -446,6 +448,18 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
<Radio value='1'>每团</Radio> <Radio value='1'>每团</Radio>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Checkbox onChange={e => {
if (e.target.checked) {
quotationForm.setFieldValue('group_size_min', 0)
quotationForm.setFieldValue('group_size_max', 1000)
setGroupSizeUnlimit(true)
} else {
quotationForm.setFieldValue('group_size_min', 1)
if (!groupMaxUnlimit) quotationForm.setFieldValue('group_size_max', 999)
setGroupSizeUnlimit(false)
}
}}>不分人等(0~1000)</Checkbox>
<Form.Item <Form.Item
label={t('products:group_size')} label={t('products:group_size')}
name='group_size_min' name='group_size_min'
@ -456,8 +470,18 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
}, },
]} ]}
> >
<InputNumber style={{ width: '100%' }} /> <InputNumber disabled={groupSizeUnlimit} min='0' max='999' style={{ width: '100%' }} />
</Form.Item> </Form.Item>
<Checkbox disabled={groupSizeUnlimit} onChange={e => {
if (e.target.checked) {
quotationForm.setFieldValue('group_size_max', 1000)
setGroupMaxUnlimit(true)
} else {
quotationForm.setFieldValue('group_size_max', 999)
setGroupMaxUnlimit(false)
}
}}>不限(1000)</Checkbox>
<Form.Item <Form.Item
label={t('products:group_size')} label={t('products:group_size')}
name='group_size_max' name='group_size_max'
@ -468,7 +492,7 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
}, },
]} ]}
> >
<InputNumber style={{ width: '100%' }} /> <InputNumber disabled={groupSizeUnlimit || groupMaxUnlimit} min='0' max='999' style={{ width: '100%' }} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t('products:use_dates')} label={t('products:use_dates')}

Loading…
Cancel
Save