Compare commits

...

60 Commits
v2.0.4 ... main

Author SHA1 Message Date
Lei OT cf47dd44a8 Merge remote-tracking branch 'origin/main' 4 days ago
Lei OT 40ca0bdf8d perf: 滚动条 4 days ago
LiaoYijun 87d1e0d3ae fix: 解决预览 Doc 重复刷新 4 days ago
Lei OT 5694081269 perf: 产品管理: 编辑: 允许不选语种提交 4 days ago
LiaoYijun 2b01fc01ea 2.0.20 6 days ago
LiaoYijun 1e3e9b9942 Merge remote-tracking branch 'origin/火车出票模块' 6 days ago
Ycc 534dd60140 站点更新说明 7 days ago
Ycc 0a6a630441 添加站点英文名 7 days ago
Ycc 7e135f349e 优化账单查询 2 weeks ago
Ycc 894f6e8173 sync 3 weeks ago
Ycc b845280151 车票信息编辑 1 month ago
Ycc 4174f33f08 车票信息编辑 1 month ago
Ycc 093fb9833a 火车计划列表 2 months ago
Ycc eda2b2ac2d 查询火车计划列表 2 months ago
Ycc 7acc8a91d7 初始化火车出票模块 2 months ago
LiaoYijun 062b6e21ee 2.0.19 2 months ago
Lei OT f79b4d4caa perf: 产品管理: 多语言描述限制输入字数2000 2 months ago
LiaoYijun 660bec8594 perf: 增加火车票供应商权限 2 months ago
Lei OT d803964131 2.0.18 3 months ago
Lei OT 80f9bb1b48 fix: 人等排序; fix: 海纳是甲方 3 months ago
LiaoYijun f2f8f0216e 2.0.17 5 months ago
LiaoYijun 1fd49cd587 2.0.16 5 months ago
赵鹏 102774dd64 虚拟订单更换一个新订单,旧虚拟订单用新页面显示。 5 months ago
LiaoYijun 7d4b4d3546 2.0.15 6 months ago
LiaoYijun 0602962a83 fix: enddate 增加本月最后一天和时间 23:59 6 months ago
Lei OT 115f913c7f 2.0.14 7 months ago
Lei OT 813258881f perf: 导出合同: 车费, 增加时段列, 显示价格行的时段信息; 景点的绑定: 仅显示`项目名称-类型`; 包价: 全年的时段不显示, 与车费一样按时段分行 7 months ago
Lei OT 9adf82de12 2.0.13 7 months ago
Lei OT e963322eb0 chore: 更新pagespy 前端sdk 7 months ago
LiaoYijun f2b344cae9 2.0.12 7 months ago
eddie 581d9d1b20 update export doc 7 months ago
eddie 563a744629 包价路线,全年的价格无需展示时间 7 months ago
eddie b7f3f2ae14 导出合同文档备注栏按照序号换行 7 months ago
Jimmy Liow 09755c3665 2.0.11 7 months ago
Jimmy Liow 0bf5d43599 perf: 删除无用的文件 7 months ago
eddie a35b7cf037 移除lodash 7 months ago
eddie 48acdad5b7 移除lodash 7 months ago
eddie 87e7c0acc4 使用1120版本合同导出模版文件 7 months ago
eddie 7b15478640 添加新版本导出模版文件 7 months ago
Eddie 1f85a341f6 Merge branch 'main' of github.com:hainatravel/GHHub 7 months ago
Eddie 78834f8f11 添加新的导出文档的class 类 7 months ago
Ycc df1eae2b86 2.0.10 8 months ago
Ycc fc494b57fc 账单审核状态查询,统计勾选记录的价格,修复城市不完整问题 8 months ago
Jimmy Liow b2df11beb3 2.0.9 8 months ago
Lei OT a2ce2534b6 Merge remote-tracking branch 'origin/main' 8 months ago
Lei OT be3aea599b fix: 报价信显示类型 8 months ago
Jimmy Liow 4b2669227e 2.0.8 8 months ago
Lei OT 503547de78 perf: 所有类型增加【报价信显示】; 默认 `计划和报价信都要显示` 8 months ago
Lei OT 202ba82042 perf: 在【餐费、车费、导游】增加【报价信显示】 8 months ago
Jimmy Liow be0b3d2766 2.0.7 8 months ago
Ycc 0f50b5b482 Merge branch 'main' of github.com:hainatravel/GHHub 8 months ago
Jimmy Liow 2f7e8a9f0f 2.0.6 8 months ago
Jimmy Liow 9a80374e24 feat: 上传发票图片使用 CDN 地址 8 months ago
Ycc aee8f94b40 2.0.6 9 months ago
Ycc 2d3d23e10f 自适应手机端 9 months ago
Ycc e4093fcce7 添加提醒 9 months ago
Ycc e462485057 首次进入页面搜索 9 months ago
Ycc 46235a43ac 2.0.5 9 months ago
Ycc 575e792481 显示审核进度 9 months ago
Ycc 8330244008 机票汇款记录查询界面 9 months ago

1
.gitignore vendored

@ -24,3 +24,4 @@ dist-ssr
*.sw? *.sw?
/package-lock.json /package-lock.json
pnpm-lock.yaml

@ -61,6 +61,8 @@ INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
VALUES ('所有国内功能', '/domestic/all', 'domestic') VALUES ('所有国内功能', '/domestic/all', 'domestic')
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
VALUES ('所有机票功能', '/air-ticket/all', 'air-ticket') VALUES ('所有机票功能', '/air-ticket/all', 'air-ticket')
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])
VALUES ('所有火车票功能', '/train-ticket/all', 'train-ticket')
-- 价格管理 -- 价格管理
INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category])

@ -1,7 +1,7 @@
{ {
"name": "global-highlights-hub", "name": "global-highlights-hub",
"private": true, "private": true,
"version": "2.0.4", "version": "2.0.20",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@ -11,8 +11,10 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.5.1",
"@react-pdf/renderer": "^3.4.0", "@react-pdf/renderer": "^3.4.0",
"antd": "^5.17.2", "antd": "^5.17.2",
"dayjs": "^1.11.13",
"docx": "^8.5.0", "docx": "^8.5.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^23.11.5", "i18next": "^23.11.5",
@ -23,8 +25,8 @@
"react-i18next": "^14.1.2", "react-i18next": "^14.1.2",
"react-router-dom": "^6.10.0", "react-router-dom": "^6.10.0",
"react-to-pdf": "^1.0.1", "react-to-pdf": "^1.0.1",
"zustand": "^4.5.2", "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"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.28", "@types/react": "^18.0.28",

@ -88,6 +88,7 @@
"Notice": "Notice", "Notice": "Notice",
"Report": "Report", "Report": "Report",
"Airticket": "AirTicket", "Airticket": "AirTicket",
"Trainticket": "TrainTicket",
"Products": "Products" "Products": "Products"
}, },
"Validation": { "Validation": {

@ -88,6 +88,7 @@
"Notice": "通知", "Notice": "通知",
"Report": "质量评分", "Report": "质量评分",
"Airticket": "机票订票", "Airticket": "机票订票",
"Trainticket": "火车订票",
"Products": "产品管理" "Products": "产品管理"
}, },
"Validation": { "Validation": {

@ -39,11 +39,12 @@ const SearchForm = ({ initialValue, onSubmit, onReset, onMounted, confirmText, f
referenceNo: { key: "referenceNo", transform: value => value || "" }, referenceNo: { key: "referenceNo", transform: value => value || "" },
dates: [ dates: [
{ key: "startdate", transform: arrVal => (arrVal ? arrVal[0].format(DATE_FORMAT) : "") }, { key: "startdate", transform: arrVal => (arrVal ? arrVal[0].format(DATE_FORMAT) : "") },
{ key: "enddate", transform: arrVal => (arrVal ? arrVal[1].format(DATE_FORMAT) : "") }, { key: "enddate", transform: arrVal => (arrVal ? arrVal[1].endOf('month').format(SMALL_DATETIME_FORMAT) : "") },
{ key: "starttime", transform: arrVal => (arrVal ? arrVal[0].format(DATE_FORMAT) : "") }, { key: "starttime", transform: arrVal => (arrVal ? arrVal[0].format(DATE_FORMAT) : "") },
{ key: "endtime", transform: arrVal => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : "") }, { key: "endtime", transform: arrVal => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : "") },
], ],
invoiceStatus: { key: "invoiceStatus", transform: value => value?.value || value?.key || "", default: "" }, invoiceStatus: { key: "invoiceStatus", transform: value => value?.value || value?.key || "", default: "" },
invoiceCheckStatus: { key: "invoiceCheckStatus", transform: value => value?.value || value?.key || "", default: "" },
audit_state: { key: "audit_state", transform: value => value?.value || value?.key || "", default: "" }, audit_state: { key: "audit_state", transform: value => value?.value || value?.key || "", default: "" },
agency: { agency: {
key: "agency", key: "agency",
@ -222,6 +223,22 @@ function getFields(props) {
</Form.Item>, </Form.Item>,
fieldProps?.invoiceStatus?.col || 3 fieldProps?.invoiceStatus?.col || 3
), ),
item(
"invoiceCheckStatus", //
99,
<Form.Item name={`invoiceCheckStatus`} initialValue={at(props, "initialValue.invoiceCheckStatus")[0] || { value: "0", label: "全部"}}>
<Select
labelInValue
options={[
{ value: "0", label: "全部" },
{ value: "1", label: "待提交" },
{ value: "2", label: "已提交待审核" },
{ value: "3", label: "已完成" },
]}
/>
</Form.Item>,
fieldProps?.invoiceCheckStatus?.col || 3
),
item( item(
"dates", // "dates", //
99, 99,

@ -10,8 +10,8 @@ const HeaderWrapper = ({ children, header, loading, backTo, ...props }) => {
} = theme.useToken(); } = theme.useToken();
return ( return (
<> <>
<Spin spinning={loading || false}> <Spin spinning={loading || false} wrapperClassName='h-full [&_.ant-spin-container]:h-full' >
<Layout className=' bg-white'> <Layout className=' bg-white h-full'>
<Header className='header px-6 h-10 ' style={{ background: 'white' }}> <Header className='header px-6 h-10 ' style={{ background: 'white' }}>
<Flex justify={'space-between'} align={'center'} className='h-full'> <Flex justify={'space-between'} align={'center'} className='h-full'>
{/* {header} */} {/* {header} */}
@ -20,7 +20,7 @@ const HeaderWrapper = ({ children, header, loading, backTo, ...props }) => {
</Flex> </Flex>
</Header> </Header>
<Divider className='my-2' /> <Divider className='my-2' />
<Content className='' style={{ backgroundColor: colorBgContainer }}> <Content className='overflow-auto' style={{ backgroundColor: colorBgContainer }}>
{children || <Outlet />} {children || <Outlet />}
</Content> </Content>
</Layout> </Layout>

@ -3,7 +3,7 @@ export const PROJECT_NAME = "GHHub";
// mode: test内部测试使用 // mode: test内部测试使用
export const HT_HOST = import.meta.env.MODE === 'test' ? 'http://120.79.9.217:10024' : import.meta.env.PROD ? 'https://p9axztuwd7x8a7.mycht.cn' : 'http://202.103.68.144:890' export const HT_HOST = import.meta.env.MODE === 'test' ? 'http://120.79.9.217:10024' : import.meta.env.PROD ? 'https://p9axztuwd7x8a7.mycht.cn' : 'http://202.103.68.144:890'
export const OVERSEA_HOST = 'https://pam5yninqgpzw4.mycht.cn' export const OVERSEA_HOST = 'https://ht20-p9axztuwd7x8a7.mycht.cn'
export const DATE_FORMAT = "YYYY-MM-DD"; export const DATE_FORMAT = "YYYY-MM-DD";
export const DATE_FORMAT_MONTH = "YYYY-MM"; export const DATE_FORMAT_MONTH = "YYYY-MM";
@ -34,6 +34,10 @@ export const PERM_DOMESTIC = '/domestic/all'
// category: air-ticket // category: air-ticket
export const PERM_AIR_TICKET = '/air-ticket/all' export const PERM_AIR_TICKET = '/air-ticket/all'
// 火车票供应商
// category: train-ticket
export const PERM_TRAIN_TICKET = '/train-ticket/all'
// 价格管理 // 价格管理
export const PERM_PRODUCTS_MANAGEMENT = '/products/*'; // 管理 export const PERM_PRODUCTS_MANAGEMENT = '/products/*'; // 管理
export const PERM_PRODUCTS_NEW = '/products/new'; // 新增产品 export const PERM_PRODUCTS_NEW = '/products/new'; // 新增产品

@ -105,14 +105,14 @@ export const useProductsTypesFieldsets = (type) => {
const infoDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['display_to_c'] : []; const infoDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['display_to_c'] : [];
const infoRecDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['recommends_rate'] : []; const infoRecDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['recommends_rate'] : [];
const infoTypesMap = { const infoTypesMap = {
'6': [[], []], '6': [[...infoDisplay], []],
'B': [['km'], []], 'B': [['km', ...infoDisplay], []],
'J': [[...infoRecDisplay, 'duration', ], ['description']], 'J': [[...infoRecDisplay, 'duration', ...infoDisplay], ['description']],
'Q': [[...infoRecDisplay, 'duration', ], ['description']], 'Q': [[...infoRecDisplay, 'duration', ...infoDisplay], ['description']],
'D': [[...infoRecDisplay, 'duration', ...infoDisplay], ['description']], 'D': [[...infoRecDisplay, 'duration', ...infoDisplay], ['description']],
'7': [[...infoRecDisplay, 'duration', 'open_weekdays'], ['description']], '7': [[...infoRecDisplay, 'duration', 'open_weekdays', ...infoDisplay], ['description']],
'R': [[], ['description']], 'R': [[...infoDisplay], ['description']],
'8': [[], []], '8': [[...infoDisplay], []],
}; };
const thisTypeFieldset = (_type) => { const thisTypeFieldset = (_type) => {
if (isEmpty(_type)) { if (isEmpty(_type)) {
@ -143,7 +143,7 @@ export const useNewProductRecord = () => {
'recommends_rate': 0, 'recommends_rate': 0,
'dept_id': 0, 'dept_id': 0,
'dept_name': '', 'dept_name': '',
'display_to_c': 0, 'display_to_c': '0',
'km': 0, 'km': 0,
'city_id': 0, 'city_id': 0,
'city_name': '', 'city_name': '',

@ -24,11 +24,19 @@ import NoticeIndex from '@/views/notice/Index'
import NoticeDetail from '@/views/notice/Detail' import NoticeDetail from '@/views/notice/Detail'
import InvoiceIndex from '@/views/invoice/Index' import InvoiceIndex from '@/views/invoice/Index'
import InvoiceDetail from '@/views/invoice/Detail' import InvoiceDetail from '@/views/invoice/Detail'
import InvoiceHistory from '@/views/invoice/History'
import InvoicePaid from '@/views/invoice/Paid' import InvoicePaid from '@/views/invoice/Paid'
import InvoicePaidDetail from '@/views/invoice/PaidDetail' import InvoicePaidDetail from '@/views/invoice/PaidDetail'
import Airticket from '@/views/airticket/Index' import Airticket from '@/views/airticket/Index'
import AirticketPlan from '@/views/airticket/Plan' import AirticketPlan from '@/views/airticket/Plan'
import AirticketInvoice from '@/views/airticket/Invoice' import AirticketInvoice from '@/views/airticket/Invoice'
import AirticketInvoicePaid from '@/views/airticket/InvoicePaid'
import Trainticket from '@/views/trainticket/index'
import TrainticketPlan from '@/views/trainticket/plan'
import TrainticketInvoice from '@/views/trainticket/invoice'
import TrainticketInvoicePaid from '@/views/trainticket/invoicePaid'
import { ThemeContext } from '@/stores/ThemeContext' import { ThemeContext } from '@/stores/ThemeContext'
import { usingStorage } from '@/hooks/usingStorage' import { usingStorage } from '@/hooks/usingStorage'
import useAuthStore from './stores/Auth' import useAuthStore from './stores/Auth'
@ -37,7 +45,7 @@ import { isNotEmpty } from '@/utils/commons'
import ProductsManage from '@/views/products/Manage'; import ProductsManage from '@/views/products/Manage';
import ProductsDetail from '@/views/products/Detail'; import ProductsDetail from '@/views/products/Detail';
import ProductsAudit from '@/views/products/Audit'; import ProductsAudit from '@/views/products/Audit';
import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_PUT } from '@/config' 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 './i18n' import './i18n'
@ -63,11 +71,19 @@ const initRouter = async () => {
{ 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/paid',element:<RequireAuth subject={PERM_OVERSEA} result={true}><InvoicePaid /></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/paid/detail/:flid', element: <RequireAuth subject={PERM_OVERSEA} result={true}><InvoicePaidDetail /></RequireAuth>},
{ path: 'airticket',element: <RequireAuth subject={PERM_AIR_TICKET} result={true}><Airticket /></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/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: '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/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: "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>},

@ -1,26 +1,36 @@
import { loadScript } from '@/utils/commons'; import { loadScript } from '@/utils/commons';
import { PROJECT_NAME } from '@/config'; import { PROJECT_NAME, BUILD_VERSION } from '@/config';
export const loadPageSpy = (title) => { export const loadPageSpy = (title) => {
if (import.meta.env.DEV || window.$pageSpy) return if (import.meta.env.DEV || window.$pageSpy) return
const PageSpyConfig = { api: 'page-spy.mycht.cn', project: PROJECT_NAME, title: title, autoRender: false };
const PageSpySrc = [ const PageSpySrc = [
'https://page-spy.mycht.cn/page-spy/index.min.js', 'https://page-spy.mycht.cn/page-spy/index.min.js'+`?${BUILD_VERSION}`,
'https://page-spy.mycht.cn/plugin/data-harbor/index.min.js', 'https://page-spy.mycht.cn/plugin/data-harbor/index.min.js'+`?${BUILD_VERSION}`,
'https://page-spy.mycht.cn/plugin/rrweb/index.min.js', 'https://page-spy.mycht.cn/plugin/rrweb/index.min.js'+`?${BUILD_VERSION}`,
]; ];
Promise.all(PageSpySrc.map((src) => loadScript(src))).then(() => { Promise.all(PageSpySrc.map((src) => loadScript(src))).then(() => {
// //
PageSpy.registerPlugin(new DataHarborPlugin({ maximum: 2 * 1024 * 1024 })); window.$harbor = new DataHarborPlugin();
// PageSpy window.$rrweb = new RRWebPlugin();
window.$pageSpy = new PageSpy({ api: 'page-spy.mycht.cn', project: PROJECT_NAME, title: title, autoRender: false }); [window.$harbor, window.$rrweb].forEach(p => {
PageSpy.registerPlugin(p)
})
window.$pageSpy = new PageSpy(PageSpyConfig);
}); });
}; };
export const uploadPageSpyLog = () => { export const uploadPageSpyLog = async () => {
window.$pageSpy.triggerPlugins('onOfflineLog', 'upload'); // window.$pageSpy.triggerPlugins('onOfflineLog', 'upload');
if (window.$pageSpy) {
await window.$harbor.upload() // { clearCache: true, remark: '' }
alert('Success')
} else {
alert('Failure')
}
} }
export const PageSpyLog = () => { export const PageSpyLog = () => {

@ -14,7 +14,7 @@ const airTicketStore = create((set, get) => ({
setVeiPlanChangeTxt: veiPlanChangeTxt => set({ veiPlanChangeTxt }), setVeiPlanChangeTxt: veiPlanChangeTxt => set({ veiPlanChangeTxt }),
setAirPortList: airPortList => set({ airPortList }), setAirPortList: airPortList => set({ airPortList }),
async getPlanList(vei_sn, GRI_Name, TimeStart, TimeEnd,plan_state,airticket_state) { async getPlanList(vei_sn, GRI_Name, TimeStart, TimeEnd, plan_state, airticket_state) {
const { setLoading, setPlanList } = get(); const { setLoading, setPlanList } = get();
setLoading(true); setLoading(true);
const searchParams = { const searchParams = {
@ -169,10 +169,12 @@ const airTicketStore = create((set, get) => ({
}); });
}, },
//通知顾问查看机票信息 //通知顾问查看机票信息
async ticketIssuedNotifications(CLF_SN, OPI_SN) { async ticketIssuedNotifications(LMI_SN, CLF_SN, OPI_SN, FlightMemo_messages) {
const searchParams = { const searchParams = {
CLF_SN: CLF_SN, CLF_SN: CLF_SN,
OPI_SN: OPI_SN, OPI_SN: OPI_SN,
LMI_SN: LMI_SN,
FlightMemo_messages: FlightMemo_messages,
}; };
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/TicketIssuedNotifications`, searchParams); const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/TicketIssuedNotifications`, searchParams);
const _result = errcode !== 0 ? [] : result; const _result = errcode !== 0 ? [] : result;

@ -87,7 +87,7 @@ const useAuthStore = create(devtools((set, get) => ({
} }
})) }))
loadPageSpy(userJson.real_name) loadPageSpy(`${userJson.real_name}-${userJson.VName}`)
}, },
authenticate: async (usr, pwd) => { authenticate: async (usr, pwd) => {

@ -0,0 +1,209 @@
import { create } from "zustand";
import { fetchJSON, postForm } from "@/utils/request";
import { prepareUrl, isNotEmpty } from "@/utils/commons";
import { HT_HOST, DATE_FORMAT } from "@/config";
import dayjs from "dayjs";
const trainTicketStore = create((set, get) => ({
loading: false,
setLoading: loading => set({ loading }),
setPlanList: planList => set({ planList }),
setPlanDetail: planDetail => set({ planDetail }),
setGuestList: guestList => set({ guestList }),
setVEIFlightBill: vEIFlightBill => set({ vEIFlightBill }),
setVeiPlanChangeTxt: veiPlanChangeTxt => set({ veiPlanChangeTxt }),
async getPlanList(vei_sn, GRI_Name, TimeStart, TimeEnd, plan_state, airticket_state) {
const { setLoading, setPlanList } = get();
setLoading(true);
const searchParams = {
vei_sn: vei_sn,
FlightDate1: TimeStart,
FlightDate2: TimeEnd,
GRI_Name: GRI_Name,
FlightStatus: plan_state,
TicketIssued: airticket_state,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetTrainPlan`, searchParams);
const _result = errcode !== 0 ? [] : result;
setPlanList(_result);
setLoading(false);
},
async getPlanDetail(vei_sn, gri_sn) {
const { setPlanDetail } = get();
const searchParams = {
vei_sn: vei_sn,
gri_sn: gri_sn,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetTrainPlanDetail`, searchParams);
const _result = errcode !== 0 ? [] : result;
setPlanDetail(_result);
//return _result.filter(item => isNotEmpty(item.GRI_No));
return "dsadsd";
},
async postFlightDetail(CLF_SN, GRI_SN, VEI_SN, original_values, info_object) {
const formData = new FormData();
formData.append("CLF_SN", CLF_SN ? CLF_SN : "");
formData.append("GRI_SN", GRI_SN);
formData.append("VEI_SN", VEI_SN);
for (const [key, value] of Object.entries(original_values)) {
formData.append(key, value); //先用原始数据填充一遍,确保复制了全部数据到新表
}
for (const [key, value] of Object.entries(info_object)) {
formData.set(key, value); //再用新值覆盖
}
formData.set("StartDate", dayjs(info_object.StartDate).format(DATE_FORMAT)); //再用新值覆盖
//是否出票的值true、false变为1或0
formData.set("TicketIssued", info_object.TicketIssued ? 1 : 0);
const postUrl = HT_HOST + "/Service_BaseInfoWeb/edit_or_new_flight_info";
return postForm(postUrl, formData).then(json => {
if (json.errcode == 0) {
return json;
} else {
throw new Error(json.errmsg + ": " + json.errcode);
}
});
},
//删除火车信息
async delete_flight_info(CLF_SN) {
const searchParams = {
CLF_SN: CLF_SN,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/Delete_flight_info`, searchParams);
const _result = errcode !== 0 ? [] : result;
return _result;
},
async getGuestList(coli_sn) {
const { setGuestList } = get();
const searchParams = {
COLI_SN: coli_sn,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetFlightGuestInfo`, searchParams);
const _result = errcode !== 0 ? [] : result;
setGuestList(_result);
},
//获取账单列表
async getVEIFlightBill(VEI_SN, GRI_Name, CheckStatus, FlightDate1, FlightDate2) {
const { setLoading, setVEIFlightBill } = get();
setLoading(true);
const searchParams = {
VEI_SN: VEI_SN,
GRI_Name: GRI_Name,
CheckStatus: CheckStatus,
FlightDate1: FlightDate1,
FlightDate2: FlightDate2,
ServiceType: 2,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetVEIFlightBill`, searchParams);
const _result = errcode !== 0 ? [] : result;
setVEIFlightBill(_result);
setLoading(false);
},
//保存费用
async postFlightCost(values) {
const formData = new FormData();
for (const [key, value] of Object.entries(values)) {
formData.append(key, value); //先用原始数据填充一遍,确保复制了全部数据到新表
}
const postUrl = HT_HOST + "/Service_BaseInfoWeb/edit_or_new_flight_cost";
return postForm(postUrl, formData).then(json => {
if (json.errcode == 0) {
return json;
} else {
throw new Error(json.errmsg + ": " + json.errcode);
}
});
},
//删除费用
async deleteFlightCost(CLC_SN) {
const searchParams = {
CLC_SN: CLC_SN,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/Delete_flight_cost`, searchParams);
const _result = errcode !== 0 ? [] : result;
return _result;
},
//获取变更信息
async getVeiPlanChange(VEI_SN, GRI_SN) {
const { setVeiPlanChangeTxt } = get();
const searchParams = {
VEI_SN: VEI_SN,
GRI_SN: GRI_SN,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetVeiFlightPlanChange`, searchParams);
const _result = errcode !== 0 ? [] : result;
setVeiPlanChangeTxt(_result);
},
//提交变更确认
async postVeiFlightPlanConfirm(VEI_SN, GRI_SN, LMI_SN, ConfirmInfo) {
const formData = new FormData();
formData.append("VEI_SN", VEI_SN);
formData.append("GRI_SN", GRI_SN);
formData.append("LMI_SN", LMI_SN);
formData.append("ServiceType", 2);
formData.append("ConfirmInfo", ConfirmInfo);
const postUrl = HT_HOST + "/Service_BaseInfoWeb/VeiFlightPlanConfirm";
return postForm(postUrl, formData).then(json => {
if (json.errcode == 0) {
return json;
} else {
throw new Error(json.errmsg + ": " + json.errcode);
}
});
},
//提交账单
async postVEIFlightBillSubmit(VEI_SN, values) {
const formData = new FormData();
formData.append("vei_sn", VEI_SN);
formData.append("ServiceType", 2);
formData.append("billdata", JSON.stringify(values));
const postUrl = HT_HOST + "/Service_BaseInfoWeb/VEIFlightBillSubmit";
return postForm(postUrl, formData).then(json => {
if (json.errcode == 0) {
return json;
} else {
throw new Error(json.errmsg + ": " + json.errcode);
}
});
},
//通知顾问查看车票信息
async ticketIssuedNotifications(LMI_SN, CLF_SN, OPI_SN, FlightMemo_messages) {
const searchParams = {
CLF_SN: CLF_SN,
OPI_SN: OPI_SN,
LMI_SN: LMI_SN,
ServiceType: 2,
FlightMemo_messages: FlightMemo_messages,
};
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/TicketIssuedNotifications`, searchParams);
const _result = errcode !== 0 ? [] : result;
return _result;
},
// 定义座位表对象
seatTable: [
{ code: "9", name: "商务座", enName: "Business Class Seat" },
{ code: "P", name: "特等座", enName: "Superior Seat" },
{ code: "M", name: "一等座", enName: "First Class Seat" },
{ code: "O", name: "二等座", enName: "Second Class Seat" },
{ code: "6", name: "高级软卧", enName: "Superior Soft Sleeper" },
{ code: "5", name: "包厢硬卧", enName: "Hard Sleeper in Private Compartment" },
{ code: "4", name: "软卧", enName: "Soft Sleeper" },
{ code: "F", name: "动卧", enName: "High - Speed Train Sleeper" },
{ code: "3", name: "硬卧", enName: "Hard Sleeper" },
{ code: "2", name: "软座", enName: "Soft Seat" },
{ code: "1", name: "硬座", enName: "Hard Seat" },
{ code: "Q", name: "观光座", enName: "Sightseeing Seat" },
{ code: "H", name: "其它", enName: "Other" },
{ code: "WZ", name: "无座", enName: "Standing - room Only" },
{ code: "YDW", name: "一等卧", enName: "First - Class Sleeper" },
{ code: "EDW", name: "二等卧", enName: "Second - Class Sleeper" },
],
}));
export default trainTicketStore;

@ -18,8 +18,9 @@ import { useThemeContext } from '@/stores/ThemeContext'
import { usingStorage } from '@/hooks/usingStorage' import { usingStorage } from '@/hooks/usingStorage'
import { useDefaultLgc } from '@/i18n/LanguageSwitcher' import { useDefaultLgc } from '@/i18n/LanguageSwitcher'
import { appendRequestParams } from '@/utils/request' import { appendRequestParams } from '@/utils/request'
import { uploadPageSpyLog } from '@/pageSpy';
import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/config' import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT,PERM_TRAIN_TICKET } from '@/config'
const { Header, Content, Footer } = Layout const { Header, Content, Footer } = Layout
@ -92,12 +93,12 @@ function App() {
insetInlineEnd: 94, insetInlineEnd: 94,
}} }}
> >
<FloatButton icon={<BugOutlined />} onClick={() => uploadLog()} /> <FloatButton icon={<BugOutlined />} onClick={() => uploadPageSpyLog()} />
<FloatButton.BackTop /> <FloatButton.BackTop />
</FloatButton.Group> </FloatButton.Group>
{contextHolder} {contextHolder}
<ErrorBoundary> <ErrorBoundary>
<Layout className='min-h-screen'> <Layout className='min-h-screen h-dvh'>
<Header className='sticky top-0 z-10 w-full'> <Header className='sticky top-0 z-10 w-full'>
<Row gutter={{ md: 24 }} justify='end' align='middle'> <Row gutter={{ md: 24 }} justify='end' align='middle'>
<Col span={15}> <Col span={15}>
@ -114,6 +115,7 @@ function App() {
isPermitted(PERM_OVERSEA) ? { key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</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_OVERSEA) ? { 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_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, isProductPermitted ? { key: 'products', label: <Link to={productLink}>{t('menu.Products')}</Link> } : null,
{ {
key: 'notice', key: 'notice',
@ -157,7 +159,7 @@ function App() {
</Col> </Col>
</Row> </Row>
</Header> </Header>
<Content className='p-6 m-0 min-h-72 bg-white'> <Content className='p-6 m-0 min-h-72 bg-white overflow-auto'>
{needToLogin ? <>login...</> : <Outlet />} {needToLogin ? <>login...</> : <Outlet />}
</Content> </Content>
<Footer>China Highlights International Travel Service Co., LTD, Version: {BUILD_VERSION}</Footer> <Footer>China Highlights International Travel Service Co., LTD, Version: {BUILD_VERSION}</Footer>

@ -1,15 +1,14 @@
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/logo-gh.png";
import { useThemeContext } from '@/stores/ThemeContext' import { useThemeContext } from "@/stores/ThemeContext";
import { BUILD_VERSION, } from '@/config'; import { BUILD_VERSION } from "@/config";
const { Header, Content, Footer } = Layout const { Header, Content, Footer } = Layout;
function Standlone() { function Standlone() {
const { colorPrimary } = useThemeContext();
const { colorPrimary } = useThemeContext()
return ( return (
<ConfigProvider <ConfigProvider
@ -20,23 +19,19 @@ function Standlone() {
algorithm: theme.defaultAlgorithm, algorithm: theme.defaultAlgorithm,
}}> }}>
<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">
<Row gutter={{ md: 24 }} justify='center'> <img src={AppLogo} className="float-left h-9 my-4 mr-6 ml-0 bg-white/30" alt="App logo" />
<Col span={2}> <p className="text-white text-center">Global Highlights Hub</p>
<img src={AppLogo} className='float-left h-9 my-4 mr-6 ml-0 bg-white/30' alt='App logo' />
</Col>
<Col span={5}><h1 className='text-white text-center'>Global Highlights Hub</h1></Col>
</Row>
</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 />
</Content> </Content>
<Footer>China Highlights International Travel Service Co., LTD, Version: {BUILD_VERSION}</Footer> <Footer>China Highlights International Travel Service Co., LTD, Version: {BUILD_VERSION}</Footer>
</Layout> </Layout>
</AntApp> </AntApp>
</ConfigProvider> </ConfigProvider>
) );
} }
export default Standlone export default Standlone;

@ -68,6 +68,7 @@ function RoleList() {
['oversea', '海外供应商'], ['oversea', '海外供应商'],
['domestic', '国内供应商'], ['domestic', '国内供应商'],
['air-ticket', '机票供应商'], ['air-ticket', '机票供应商'],
['train-ticket', '火车票供应商'],
['products', '产品价格'], ['products', '产品价格'],
['page', '默认页面'], ['page', '默认页面'],
]); ]);

@ -91,7 +91,7 @@ const Airticket = props => {
const showTotal = total => `合计 ${total} `; const showTotal = total => `合计 ${total} `;
useEffect(() => { useEffect(() => {
getPlanList(travelAgencyId, "", dayjs().startOf("M").format(DATE_FORMAT), dayjs().add(3, "M").endOf("M").format(DATE_FORMAT),'-1','-1'); !planList && getPlanList(travelAgencyId, "", dayjs().startOf("M").format(DATE_FORMAT), dayjs().add(3, "M").endOf("M").format(DATE_FORMAT), "-1", "-1");
}, []); }, []);
return ( return (
@ -115,9 +115,14 @@ const Airticket = props => {
/> />
</Col> </Col>
<Col md={4} lg={4} xxl={4}> <Col md={4} lg={4} xxl={4}>
<Button icon={<AuditOutlined />} onClick={() => navigate(`/airticket/invoice`)}> <Space>
报账 <Button icon={<AuditOutlined />} onClick={() => navigate(`/airticket/invoice`)}>
</Button> 报账
</Button>
<Button icon={<AuditOutlined />} onClick={() => navigate(`/airticket/invoicepaid`)}>
汇款记录
</Button>
</Space>
</Col> </Col>
</Row> </Row>

@ -1,5 +1,5 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, Checkbox, Table, Button, App } from "antd"; import { Grid, Divider, Layout, Steps, Statistic, Col, Row, Space, Checkbox, Table, Button, App, Typography } from "antd";
import { PhoneOutlined, CustomerServiceOutlined, FrownTwoTone, LikeTwoTone } from "@ant-design/icons"; import { PhoneOutlined, CustomerServiceOutlined, FrownTwoTone, LikeTwoTone } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime } from "@/utils/commons"; import { isEmpty, formatColonTime } from "@/utils/commons";
@ -24,7 +24,11 @@ const Invoice = props => {
title: "团名", title: "团名",
key: "GRI_No", key: "GRI_No",
dataIndex: "GRI_No", dataIndex: "GRI_No",
render: (text, record) => `${record.GRI_No} ${record.WL}`, render: (text, record) => (
<Typography.Text title={record.Memo}>
{record.GRI_No} {record.WL}
</Typography.Text>
),
}, },
{ {
title: "状态", title: "状态",
@ -42,10 +46,16 @@ const Invoice = props => {
}, },
}, },
{ {
title: "城市", title: "出发",
key: "FromCity", key: "FromCity",
dataIndex: "FromCity", dataIndex: "FromCity",
render: (text, record) => (record.CostType == "出票" ? `${record.FromCity} - ${record.ToCity}` : "-"), render: (text, record) => (record.CostType == "出票" ? `${record.FromCity}` : "-"),
},
{
title: "抵达",
key: "ToCity",
dataIndex: "ToCity",
render: (text, record) => (record.CostType == "出票" ? `${record.ToCity}` : "-"),
}, },
{ {
title: "航班", title: "航班",
@ -97,11 +107,6 @@ const Invoice = props => {
dataIndex: "Discount", dataIndex: "Discount",
render: (text, record) => (record.CostType == "出票" ? text : "-"), render: (text, record) => (record.CostType == "出票" ? text : "-"),
}, },
{
title: "客人信息/备注",
key: "Memo",
dataIndex: "Memo",
},
{ {
title: "审核状态", title: "审核状态",
children: [ children: [
@ -118,10 +123,28 @@ const Invoice = props => {
待提交 待提交
</Checkbox> </Checkbox>
) : ( ) : (
record.CheckStatusName <Steps
size="small"
current={record.CheckStatus - 1}
items={[
{
title: "提交账单",
},
{
title: "顾问审核",
},
{
title: "财务处理",
},
{
title: "账单支付",
},
]}
/>
), ),
}, },
], ],
sorter: (a, b) => { sorter: (a, b) => {
return b.CheckStatus - a.CheckStatus; return b.CheckStatus - a.CheckStatus;
}, },
@ -133,7 +156,7 @@ const Invoice = props => {
}; };
// checkbox // checkbox
const handleCheckboxChange = (event, data) => { const handleCheckboxChange = (event, data) => {
const value = { CLC_SN: data.CLC_SN, WL: data.WL, OPI_SN: data.OPI_SN, OPI_Email: data.OPI_Email, GRI_SN: data.GRI_SN, GRI_Name: data.GRI_Name }; const value = { CLC_SN: data.CLC_SN, WL: data.WL, OPI_SN: data.OPI_SN, OPI_Email: data.OPI_Email, GRI_SN: data.GRI_SN, GRI_Name: data.GRI_Name, Cost: data.Cost, ServiceFee: data.ServiceFee };
if (event.target.checked) { if (event.target.checked) {
setSelectedValues([...selectedValues, value]); // setSelectedValues([...selectedValues, value]); //
} else { } else {
@ -168,7 +191,11 @@ const Invoice = props => {
if (isEmpty(vEIFlightBill)) return; if (isEmpty(vEIFlightBill)) return;
const allChecked = selectedValues.length === vEIFlightBill.filter(item => item.CheckStatus < 2).length; const allChecked = selectedValues.length === vEIFlightBill.filter(item => item.CheckStatus < 2).length;
setSelectedValues( setSelectedValues(
allChecked ? [] : vEIFlightBill.filter(item => item.CheckStatus < 2).map(item => ({ CLC_SN: item.CLC_SN, WL: item.WL, OPI_SN: item.OPI_SN, OPI_Email: item.OPI_Email, GRI_SN: item.GRI_SN, GRI_Name: item.GRI_Name })) allChecked
? []
: vEIFlightBill
.filter(item => item.CheckStatus < 2)
.map(item => ({ CLC_SN: item.CLC_SN, WL: item.WL, OPI_SN: item.OPI_SN, OPI_Email: item.OPI_Email, GRI_SN: item.GRI_SN, GRI_Name: item.GRI_Name, Cost: item.Cost, ServiceFee: item.ServiceFee }))
); // ); //
}; };
@ -183,14 +210,14 @@ const Invoice = props => {
dates: [dayjs().startOf("M"), dayjs().endOf("M")], dates: [dayjs().startOf("M"), dayjs().endOf("M")],
}} }}
fieldsConfig={{ fieldsConfig={{
shows: ["referenceNo", "dates", "invoiceStatus"], shows: ["referenceNo", "dates", "invoiceCheckStatus"],
fieldProps: { fieldProps: {
referenceNo: { label: "搜索计划" }, referenceNo: { label: "搜索计划" },
dates: { label: "出发日期", col: 8 }, dates: { label: "出发日期", col: 8 },
}, },
}} }}
onSubmit={(err, formVal, filedsVal) => { onSubmit={(err, formVal, filedsVal) => {
getVEIFlightBill(travelAgencyId, formVal.referenceNo, formVal.invoiceStatus, formVal.startdate, formVal.endtime); getVEIFlightBill(travelAgencyId, formVal.referenceNo, formVal.invoiceCheckStatus, formVal.startdate, formVal.endtime);
}} }}
/> />
</Col> </Col>
@ -202,14 +229,20 @@ const Invoice = props => {
<Row> <Row>
<Col md={24} lg={24} xxl={24}> <Col md={24} lg={24} xxl={24}>
<Table bordered={true} rowKey="CLC_SN" columns={vEIFlightBillColumns} dataSource={vEIFlightBill} loading={loading} pagination={{ defaultPageSize: 100, showTotal: showTotal }} /> <Table bordered={true} rowKey="CLC_SN" columns={vEIFlightBillColumns} dataSource={vEIFlightBill} loading={loading} pagination={{ defaultPageSize: 100, showTotal: showTotal }} />
<TableExportBtn btnTxt="导出账单" label={`机票账单`} {...{ columns: vEIFlightBillColumns, dataSource: vEIFlightBill }} /> <TableExportBtn btnTxt="导出账单" label={`机票账单`} {...{ columns: vEIFlightBillColumns, dataSource: vEIFlightBill }} />
</Col> </Col>
<Col md={24} lg={24} xxl={24}></Col> <Col md={24} lg={24} xxl={24}></Col>
</Row> </Row>
<Row> <Row>
<Col md={24} lg={22} xxl={22}></Col> <Col md={24} lg={18} xxl={18}></Col>
<Col md={24} lg={2} xxl={2}>
<Statistic title="已选机票价格" value={selectedValues.reduce((acc, curr) => acc + curr.Cost, 0)} />
</Col>
<Col md={24} lg={2} xxl={2}>
<Statistic title="已选服务费" value={selectedValues.reduce((acc, curr) => acc + curr.ServiceFee, 0)} />
</Col>
<Col md={24} lg={2} xxl={2}> <Col md={24} lg={2} xxl={2}>
<Button type="primary" size="large" onClick={postInvoice} disabled={selectedValues.length == 0}> <Button type="primary" size="large" onClick={postInvoice} disabled={selectedValues.length == 0}>
提交账单 提交账单

@ -0,0 +1,137 @@
import { useState, useEffect } from "react";
import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, Checkbox, Table, Button, App } from "antd";
import { PhoneOutlined, CustomerServiceOutlined, FrownTwoTone, LikeTwoTone } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime, formatDate, isNotEmpty } from "@/utils/commons";
import { DATE_FORMAT } from "@/config";
import dayjs from "dayjs";
import SearchForm from "@/components/SearchForm";
import BackBtn from "@/components/BackBtn";
import { TableExportBtn } from "@/components/Data";
import useInvoiceStore from "@/stores/Invoice";
import { fetchInvoicePaidDetail } from "@/stores/Invoice";
import airTicketStore from "@/stores/Airticket";
import { usingStorage } from "@/hooks/usingStorage";
const InvoicePaid = props => {
const navigate = useNavigate();
const { notification } = App.useApp();
const { travelAgencyId } = usingStorage();
const [invoicePaidDetail, setInvoicePaidDetail] = useState([]);
const [invoiceNO, setInvoiceNO] = useState([]); //
const [loading, invoicePaid, fetchInvoicePaid] = useInvoiceStore(state => [state.loading, state.invoicePaid, state.fetchInvoicePaid]);
const showTotal = total => `Total ${total} items`;
const showTotal_detail = total => `Total ${total} items`;
useEffect(() => {
// fetchInvoicePaid(travelAgencyId, "", dayjs().subtract(2, "M").startOf("M").format(DATE_FORMAT), dayjs().endOf("M").format(DATE_FORMAT));
}, []);
const invoicePaidColumns = [
{
title: "编号",
dataIndex: "fl_finaceNo",
key: "fl_finaceNo",
},
{
title: "报账日期",
key: "fl_adddate",
dataIndex: "fl_adddate",
render: (text, record) => (isNotEmpty(text) ? formatDate(new Date(text)) : ""),
},
{
title: "团数",
key: "fcount",
dataIndex: "fcount",
},
{
title: "总额",
key: "pSum",
dataIndex: "pSum",
//render: (text, record) => (isNotEmpty(record.GMD_Currency) ? record.GMD_Currency + " " + text : text),
},
{
title: "查看",
key: "pSum",
dataIndex: "pSum",
render: (text, record) => (
<Button
type="link"
onClick={() => {
fetchInvoicePaidDetail(travelAgencyId, record.key).then(res => setInvoicePaidDetail(res));
setInvoiceNO(record.fl_finaceNo);
}}>
查看明细
</Button>
),
},
];
const invoicePaidDetailColumns = [
{
title: "团号",
dataIndex: "fl2_GroupName",
key: "fl2_GroupName",
},
{
title: "金额",
key: "fl2_price",
dataIndex: "fl2_price",
},
{
title: "报账日期",
key: "fl2_ArriveDate",
dataIndex: "fl2_ArriveDate",
render: (text, record) => (isNotEmpty(text) ? formatDate(new Date(text)) : ""),
},
{
title: "顾问",
dataIndex: "fl2_wl",
key: "fl2_wl",
},
];
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row>
<Col md={20} lg={20} xxl={20}>
<SearchForm
initialValue={{
dates: [dayjs().subtract(2, "M").startOf("M"), dayjs().endOf("M")],
}}
fieldsConfig={{
shows: ["dates"],
fieldProps: {
dates: { col: 10, label: "报账日期" },
},
}}
onSubmit={(err, formVal) => {
fetchInvoicePaid(travelAgencyId, "", formVal.startdate, formVal.enddate);
setInvoicePaidDetail([]);
}}
/>
</Col>
<Col md={4} lg={4} xxl={4}>
<BackBtn to={"/airticket"} />
</Col>
</Row>
<Row>
<Col md={24} lg={16} xxl={16}>
<Divider orientation="left">汇款列表</Divider>
<Table bordered columns={invoicePaidColumns} dataSource={invoicePaid} loading={loading} pagination={{ defaultPageSize: 20, showTotal: showTotal }} />
</Col>
<Col md={24} lg={4} xxl={4}></Col>
</Row>
<Row>
<Col md={24} lg={20} xxl={20}>
<Divider orientation="left">账单明细 {invoiceNO}</Divider>
<Table bordered columns={invoicePaidDetailColumns} dataSource={invoicePaidDetail} pagination={{ defaultPageSize: 100, showTotal: showTotal_detail }} />
<TableExportBtn btnTxt="导出账单明细" label={`机票账单`} {...{ columns: invoicePaidDetailColumns, dataSource: invoicePaidDetail }} />
</Col>
<Col md={24} lg={4} xxl={4}></Col>
</Row>
</Space>
);
};
export default InvoicePaid;

@ -147,9 +147,11 @@ const AirticketPlan = props => {
const Airticket_form = props => { const Airticket_form = props => {
const airInfo = props.airInfo; const airInfo = props.airInfo;
const [airinfo_form] = Form.useForm();
return ( return (
<> <>
<Form <Form
form={airinfo_form}
name={"ticket_form_" + airInfo.id} name={"ticket_form_" + airInfo.id}
labelCol={{ labelCol={{
span: 6, span: 6,
@ -249,10 +251,6 @@ const AirticketPlan = props => {
</Form.Item> </Form.Item>
</Space> </Space>
</Form.Item> </Form.Item>
<Form.Item label="备注" name="FlightMemo">
<Input />
</Form.Item>
</Col> </Col>
<Col md={24} lg={4} xxl={4}> <Col md={24} lg={4} xxl={4}>
<Space direction="vertical"> <Space direction="vertical">
@ -281,12 +279,19 @@ const AirticketPlan = props => {
</Row> </Row>
<Divider orientation="left"></Divider> <Divider orientation="left"></Divider>
<Row gutter={16}> <Row gutter={16}>
<Col md={24} lg={20} xxl={20}></Col> <Col md={24} lg={20} xxl={20}>
<Form.Item label="提醒信息" name="FlightMemo_messages">
<Input placeholder="没有提醒请留空,信息会抄送给上下站地接" />
</Form.Item>
<Form.Item label="已发提醒" name="FlightMemo">
<Input.TextArea rows={4} readOnly disabled />
</Form.Item>
</Col>
<Col md={24} lg={4} xxl={4}> <Col md={24} lg={4} xxl={4}>
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
ticketIssuedNotifications(airInfo.CLF_SN, airInfo.OPI_SN) ticketIssuedNotifications(userId,airInfo.CLF_SN, airInfo.OPI_SN,airinfo_form.getFieldValue('FlightMemo_messages'))
.then(() => { .then(() => {
notification.success({ notification.success({
message: `成功`, message: `成功`,
@ -295,6 +300,7 @@ const AirticketPlan = props => {
duration: 4, duration: 4,
icon: <LikeTwoTone />, icon: <LikeTwoTone />,
}); });
airinfo_form.setFieldValue('FlightMemo_messages','')
}) })
.catch(() => { .catch(() => {
notification.error({ notification.error({

@ -1,7 +1,7 @@
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Row, Col, Space, Button, Typography, Card, Form, Upload, Input, Divider, DatePicker, Select, App, Descriptions, Image } from "antd"; import { Row, Col, Space, Button, Typography, Card, Form, Upload, Input, Divider, DatePicker, Select, App, Descriptions, Image } from "antd";
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined,AuditOutlined } from "@ant-design/icons";
import { isNotEmpty } from "@/utils/commons"; import { isNotEmpty } from "@/utils/commons";
import * as config from "@/config"; import * as config from "@/config";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -224,7 +224,7 @@ function Detail() {
<Descriptions column={1}> <Descriptions column={1}>
<Descriptions.Item label="Amount">{data.GMD_Cost}</Descriptions.Item> <Descriptions.Item label="Amount">{data.GMD_Cost}</Descriptions.Item>
<Descriptions.Item label="Currency">{data.GMD_Currency}</Descriptions.Item> <Descriptions.Item label="Currency">{data.GMD_Currency}</Descriptions.Item>
<Descriptions.Item label="Due Dat">{data.GMD_PayDate}</Descriptions.Item> <Descriptions.Item label="Due Date">{data.GMD_PayDate}</Descriptions.Item>
<Descriptions.Item label="Status">{invoiceStatus(data.FKState)}</Descriptions.Item> <Descriptions.Item label="Status">{invoiceStatus(data.FKState)}</Descriptions.Item>
</Descriptions> </Descriptions>
</Col> </Col>
@ -358,6 +358,9 @@ function Detail() {
</Col> </Col>
<Col span={4}> <Col span={4}>
<BackBtn /> <BackBtn />
<Button icon={<AuditOutlined />} onClick={() => navigate(`/invoice/history/0/338787`)}>
Billing Records
</Button>
</Col> </Col>
</Row> </Row>
<Title level={5}></Title> <Title level={5}></Title>

@ -0,0 +1,130 @@
import { useParams, NavLink, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import { Row, Col, Space, Table, Image,App } from "antd";
import { formatDate, isNotEmpty } from "@/utils/commons";
import SearchForm from "@/components/SearchForm";
import dayjs from "dayjs";
import BackBtn from "@/components/BackBtn";
import { fetchInvoiceDetail } from "@/stores/Invoice";
import useInvoiceStore from "@/stores/Invoice";
import { usingStorage } from "@/hooks/usingStorage";
function History() {
const { travelAgencyId } = usingStorage();
const { GMDSN, GSN } = useParams();
const [dataLoading, setDataLoading] = useState(false);
const [invoiceZDDetail, setInvoiceZDDetail] = useState([]);
const { notification } = App.useApp();
useEffect(() => {
defaultShow();
}, [GMDSN, GSN]);
function defaultShow() {
setDataLoading(true);
fetchInvoiceDetail(travelAgencyId, GMDSN, GSN)
.then((json) => {
//console.log("id:"+travelAgencyId+",gmdsn:"+GMDSN+",GSN:"+GSN+"#13"+json);
setInvoiceZDDetail(json.invoiceZDDetail);
})
.catch((ex) => {
notification.error({
message: `Notification`,
description: ex.message,
placement: "top",
duration: 4,
});
})
.finally(() => {
setDataLoading(false);
});
}
const invoicePaidColumns = [
{
title: "ID",
dataIndex: "GMD_SN",
key: "GMD_SN",
},
{
title: "Due Date",
key: "GMD_PayDate",
dataIndex: "GMD_PayDate",
render: (text, record) =>
isNotEmpty(text) ? formatDate(new Date(text)) : "",
},
{
title: "Amount",
key: "GMD_Cost",
dataIndex: "GMD_Cost",
},
{
title: "Currency",
key: "GMD_Currency",
dataIndex: "GMD_Currency",
},
{
title: "Status",
key: "FKState",
dataIndex: "FKState",
render: (text, record) => (isNotEmpty(record.FKState) ? invoiceStatus(record.FKState) : text),
},
{
title: "Invoice",
key: "GMD_Pic",
dataIndex: "GMD_Pic",
render: showPIc,
},
];
const invoiceStatus = (FKState) => {
switch (FKState - 1) {
case 1:
return "Submitted";
case 2:
return "Travel Advisor";
case 3:
return "Finance Dept";
case 4:
return "Paid";
default:
return "";
}
};
function showPIc(text, record) {
let strPic = record.GMD_Pic;
//console.log(JSON.parse(strPic));
if (isNotEmpty(strPic)) {
return JSON.parse(strPic).map((item, index) => {
return <Image key={index} width={90} src={item.url} />;
});
} else {
return "";
}
}
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row gutter={16}>
<Col span={20}></Col>
<Col span={4}>
<BackBtn />
</Col>
</Row>
<Row>
<Col md={24} lg={24} xxl={24}>
<Table
rowKey={"GMD_SN"}
bordered
columns={invoicePaidColumns}
dataSource={invoiceZDDetail}
/>
</Col>
</Row>
</Space>
);
}
export default History;

@ -72,33 +72,35 @@ function Index() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }}> <Space direction="vertical" style={{ width: "100%" }}>
<Row gutter={16}> <Row gutter={16}>
<Col flex="auto"> <Col md={16} sm={16} xs={24} >
<SearchForm <SearchForm
initialValue={{ initialValue={{
dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')], dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
}} }}
fieldsConfig={{ fieldsConfig={{
shows: ['referenceNo', 'invoiceStatus', 'dates'], shows: ['referenceNo', 'invoiceStatus', 'dates'],
fieldProps: { fieldProps: {
referenceNo: { col: 7 }, referenceNo: { col: 7 },
invoiceStatus: { col: 4}, invoiceStatus: { col: 4},
dates: { col: 10 }, dates: { col: 10 },
}, },
}} }}
onSubmit={(err, formVal, filedsVal) => { onSubmit={(err, formVal, filedsVal) => {
fetchInvoiceList(travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.enddate, formVal.invoiceStatus); fetchInvoiceList(travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.enddate, formVal.invoiceStatus);
}} }}
/> />
</Col> </Col>
<Col md={24} lg={4} xxl={4}> <Col md={8} sm={8} xs={24} >
<Button icon={<AuditOutlined />} onClick={() => navigate(`/invoice/detail/0/338787`)}> <Space>
Misc. Invoice <Button icon={<AuditOutlined />} onClick={() => navigate(`/invoice/detail/0/395074`)}>
</Button> Misc. Invoice
<Button icon={<AuditOutlined />} onClick={() => navigate(`/invoice/paid`)}> </Button>
Bank statement <Button icon={<AuditOutlined />} onClick={() => navigate(`/invoice/paid`)}>
</Button> Bank statement
</Col> </Button>
</Row> </Space>
</Col>
</Row>
<Row> <Row>
<Col md={24} lg={24} xxl={24}> <Col md={24} lg={24} xxl={24}>
<Table bordered pagination={{ defaultPageSize: 20, showTotal: showTotal }} columns={invoiceListColumns} dataSource={(invoiceList)} /> <Table bordered pagination={{ defaultPageSize: 20, showTotal: showTotal }} columns={invoiceListColumns} dataSource={(invoiceList)} />

@ -43,11 +43,11 @@ function Detail() {
backTo={false} backTo={false}
header={<Header title={activeAgency.travel_agency_name} refresh={handleGetAgencyProducts} handleNewProduct={() => setAddProductVisible(true)} />}> header={<Header title={activeAgency.travel_agency_name} refresh={handleGetAgencyProducts} handleNewProduct={() => setAddProductVisible(true)} />}>
<> <>
<Flex gap={10}> <Flex gap={10} className='h-full'>
{/* onNodeSelect={handleNodeSelect} */} {/* onNodeSelect={handleNodeSelect} */}
<ProductsTree className='basis-80 sticky top-16 overflow-y-auto shrink-0' style={{ height: 'calc(100vh - 150px)' }} /> <ProductsTree className='basis-80 sticky top-0 overflow-y-auto shrink-0' style1={{ height: 'calc(100vh - 150px)' }} />
<Divider type={'vertical'} className='mx-1 h-auto' /> <Divider type={'vertical'} className='mx-1 h-auto' />
<div className=' flex-auto '> <div className=' flex-auto overflow-auto '>
<ProductInfo /> <ProductInfo />
</div> </div>
</Flex> </Flex>

@ -1,35 +1,46 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from "react";
import { useParams, Link, useNavigate, useLocation } from 'react-router-dom'; import { useParams, Link, useNavigate, useLocation } from "react-router-dom";
import { App, Button, Divider, Popconfirm, Select } from 'antd'; import { App, Button, Divider, Popconfirm, Select } from "antd";
import { ReloadOutlined } from '@ant-design/icons'; import { ReloadOutlined } from "@ant-design/icons";
import { useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import { useProductsAuditStatesMapVal } from "@/hooks/useProductsSets";
import { useTranslation } from 'react-i18next'; import { useTranslation } from "react-i18next";
import useProductsStore, { postAgencyProductsAuditAction, postAgencyAuditAction, getAgencyAllExtrasAction } from '@/stores/Products/Index'; import useProductsStore, {
import { isEmpty, objectMapper } from '@/utils/commons'; postAgencyProductsAuditAction,
import useAuthStore from '@/stores/Auth'; postAgencyAuditAction,
import RequireAuth from '@/components/RequireAuth'; getAgencyAllExtrasAction,
} from "@/stores/Products/Index";
import { isEmpty, objectMapper } from "@/utils/commons";
import useAuthStore from "@/stores/Auth";
import RequireAuth from "@/components/RequireAuth";
// import PrintContractPDF from './PrintContractPDF'; // import PrintContractPDF from './PrintContractPDF';
import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from "@/config";
import dayjs from 'dayjs'; import dayjs from "dayjs";
import VendorSelector from '@/components/VendorSelector'; import VendorSelector from "@/components/VendorSelector";
import AuditStateSelector from '@/components/AuditStateSelector'; import AuditStateSelector from "@/components/AuditStateSelector";
import { usingStorage } from '@/hooks/usingStorage'; import { usingStorage } from "@/hooks/usingStorage";
import AgencyContract from '../Print/AgencyContract_v0903'; import AgencyContract from "../Print/AgencyContract";
// import AgencyContract from "../Print/AgencyContract_v0903";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
import { Packer } from "docx"; import { Packer } from "docx";
const Header = ({ refresh, ...props }) => { const Header = ({ refresh, ...props }) => {
const location = useLocation(); const location = useLocation();
const isEditPage = location.pathname.includes('edit'); const isEditPage = location.pathname.includes("edit");
const showEditA = !location.pathname.includes('edit'); const showEditA = !location.pathname.includes("edit");
const showAuditA = !location.pathname.includes('audit'); const showAuditA = !location.pathname.includes("audit");
const { travel_agency_id, use_year, audit_state } = useParams(); const { travel_agency_id, use_year, audit_state } = useParams();
const { travelAgencyId } = usingStorage(); const { travelAgencyId } = usingStorage();
const { t } = useTranslation(); const { t } = useTranslation();
const isPermitted = useAuthStore((state) => state.isPermitted); const isPermitted = useAuthStore((state) => state.isPermitted);
const [activeAgency, setActiveAgency] = useProductsStore((state) => [state.activeAgency, state.setActiveAgency]); const [activeAgency, setActiveAgency] = useProductsStore((state) => [
const [switchParams, setSwitchParams] = useProductsStore((state) => [state.switchParams, state.setSwitchParams]); state.activeAgency,
state.setActiveAgency,
]);
const [switchParams, setSwitchParams] = useProductsStore((state) => [
state.switchParams,
state.setSwitchParams,
]);
// const [activeAgencyState] = useProductsStore((state) => [state.activeAgencyState]); // const [activeAgencyState] = useProductsStore((state) => [state.activeAgencyState]);
const [agencyProducts] = useProductsStore((state) => [state.agencyProducts]); const [agencyProducts] = useProductsStore((state) => [state.agencyProducts]);
const stateMapVal = useProductsAuditStatesMapVal(); const stateMapVal = useProductsAuditStatesMapVal();
@ -38,26 +49,49 @@ const Header = ({ refresh, ...props }) => {
const yearOptions = []; const yearOptions = [];
const currentYear = switchParams.use_year || dayjs().year(); const currentYear = switchParams.use_year || dayjs().year();
const baseYear = use_year ? Number(use_year === 'all' ? currentYear : use_year) : currentYear; const baseYear = use_year
? Number(use_year === "all" ? currentYear : use_year)
: currentYear;
for (let i = currentYear - 5; i <= baseYear + 5; i++) { for (let i = currentYear - 5; i <= baseYear + 5; i++) {
yearOptions.push({ label: i, value: i }); yearOptions.push({ label: i, value: i });
} }
const [param, setParam] = useState({ pick_year: baseYear, pick_agency: travel_agency_id }); const { getRemarkList } = useProductsStore((selector) => ({
getRemarkList: selector.getRemarkList,
}));
const [param, setParam] = useState({
pick_year: baseYear,
pick_agency: travel_agency_id,
});
const [pickYear, setPickYear] = useState(baseYear); const [pickYear, setPickYear] = useState(baseYear);
const [pickAgency, setPickAgency] = useState({ value: activeAgency.travel_agency_id, label: activeAgency.travel_agency_name }); const [pickAgency, setPickAgency] = useState({
value: activeAgency.travel_agency_id,
label: activeAgency.travel_agency_name,
});
const [pickAuditState, setPickAuditState] = useState(); const [pickAuditState, setPickAuditState] = useState();
useEffect(() => { useEffect(() => {
const _param = objectMapper(param, { pick_year: 'use_year', pick_agency: 'travel_agency_id', pick_state: 'audit_state' }); const _param = objectMapper(param, {
setSwitchParams({ ..._param, travel_agency_id: _param?.travel_agency_id || travelAgencyId }); pick_year: "use_year",
pick_agency: "travel_agency_id",
pick_state: "audit_state",
});
setSwitchParams({
..._param,
travel_agency_id: _param?.travel_agency_id || travelAgencyId,
});
refresh(param); refresh(param);
return () => {}; return () => {};
}, [param]); }, [param]);
const emptyPickState = { value: '', label: t('products:State') }; const emptyPickState = { value: "", label: t("products:State") };
useEffect(() => { useEffect(() => {
const baseState = audit_state ? (audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`]) : emptyPickState; const baseState = audit_state
? audit_state === "all"
? emptyPickState
: stateMapVal[`${audit_state}`]
: emptyPickState;
if (isEmpty(pickAuditState)) { if (isEmpty(pickAuditState)) {
setPickAuditState(baseState); setPickAuditState(baseState);
} }
@ -84,20 +118,23 @@ const Header = ({ refresh, ...props }) => {
// const s = Object.keys(agencyProducts).map((typeKey) => { // const s = Object.keys(agencyProducts).map((typeKey) => {
// }); // });
postAgencyProductsAuditAction(state, { travel_agency_id: activeAgency.travel_agency_id, use_year: switchParams.use_year }) postAgencyProductsAuditAction(state, {
travel_agency_id: activeAgency.travel_agency_id,
use_year: switchParams.use_year,
})
.then((json) => { .then((json) => {
if (json.errcode === 0) { if (json.errcode === 0) {
message.success(json.errmsg); message.success(json.errmsg);
if (typeof refresh === 'function') { if (typeof refresh === "function") {
refresh(param); refresh(param);
} }
} }
}) })
.catch((ex) => { .catch((ex) => {
notification.error({ notification.error({
message: 'Notification', message: "Notification",
description: ex.message, description: ex.message,
placement: 'top', placement: "top",
duration: 4, duration: 4,
}); });
}); });
@ -107,105 +144,166 @@ const Header = ({ refresh, ...props }) => {
postAgencyAuditAction(activeAgency.travel_agency_id, switchParams.use_year) postAgencyAuditAction(activeAgency.travel_agency_id, switchParams.use_year)
.then((json) => { .then((json) => {
if (json.errcode === 0) { if (json.errcode === 0) {
message.success(t('Success')); message.success(t("Success"));
if (typeof refresh === 'function') { if (typeof refresh === "function") {
refresh(param); refresh(param);
const auditPagePath = isPermitted(PERM_PRODUCTS_OFFER_AUDIT) const auditPagePath = isPermitted(PERM_PRODUCTS_OFFER_AUDIT)
? `/products/${activeAgency.travel_agency_id}/${switchParams.use_year}/all/audit` ? `/products/${activeAgency.travel_agency_id}/${switchParams.use_year}/all/audit`
: isPermitted(PERM_PRODUCTS_OFFER_PUT) : isPermitted(PERM_PRODUCTS_OFFER_PUT)
? `/products/audit` ? `/products/audit`
: ''; : "";
navigate(auditPagePath); navigate(auditPagePath);
} }
} }
}) })
.catch((ex) => { .catch((ex) => {
notification.error({ notification.error({
message: 'Notification', message: "Notification",
description: ex.message, description: ex.message,
placement: 'top', placement: "top",
duration: 4, duration: 4,
}); });
}); });
}; };
const handleDownload = async () => { const handleDownload = async () => {
// await refresh(); // await refresh();
const agencyExtras = await getAgencyAllExtrasAction(switchParams); const agencyExtras = await getAgencyAllExtrasAction(switchParams);
const remarks = await getRemarkList()
const documentCreator = new AgencyContract(); const documentCreator = new AgencyContract();
const doc = documentCreator.create([switchParams, activeAgency, agencyProducts, agencyExtras]); const doc = documentCreator.create([
switchParams,
activeAgency,
agencyProducts,
agencyExtras,
remarks
]);
const _d = dayjs().format('YYYYMMDD_HH.mm.ss.SSS'); // Date.now().toString(32) const _d = dayjs().format("YYYYMMDD_HH.mm.ss.SSS"); // Date.now().toString(32)
Packer.toBlob(doc).then(blob => { Packer.toBlob(doc).then((blob) => {
saveAs(blob, `${activeAgency.travel_agency_name}${pickYear}年地接合同-${_d}.docx`); saveAs(
blob,
`${activeAgency.travel_agency_name}${pickYear}年地接合同-${_d}.docx`
);
}); });
}; };
return ( return (
<div className='flex justify-end items-center gap-4 h-full'> <div className="flex justify-end items-center gap-4 h-full">
<div className='grow'> <div className="grow">
<h2 className='m-0 leading-tight'> <h2 className="m-0 leading-tight">
{isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? ( {isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? (
<VendorSelector <VendorSelector
value={{ label: activeAgency.travel_agency_name, value: activeAgency.travel_agency_id }} value={{
label: activeAgency.travel_agency_name,
value: activeAgency.travel_agency_id,
}}
onChange={handleAgencyChange} onChange={handleAgencyChange}
allowClear={false} allowClear={false}
mode={null} mode={null}
className='w-72' className="w-72"
size='large' size="large"
variant={'borderless'} variant={"borderless"}
/> />
) : ( ) : (
activeAgency.travel_agency_name activeAgency.travel_agency_name
)} )}
<Divider type={'vertical'} /> <Divider type={"vertical"} />
<Select options={yearOptions} variant={'borderless'} className='w-24' size='large' value={pickYear} onChange={handleYearChange} /> <Select
<Divider type={'vertical'} /> options={yearOptions}
<AuditStateSelector variant={'borderless'} className='w-32' size='large' value={pickAuditState} onChange={handleAuditStateChange} /> variant={"borderless"}
className="w-24"
size="large"
value={pickYear}
onChange={handleYearChange}
/>
<Divider type={"vertical"} />
<AuditStateSelector
variant={"borderless"}
className="w-32"
size="large"
value={pickAuditState}
onChange={handleAuditStateChange}
/>
{/* <Divider type={'vertical'} /> {/* <Divider type={'vertical'} />
{(use_year || '').replace('all', '')} */} {(use_year || '').replace('all', '')} */}
<Button onClick={() => refresh(param)} type='text' className='text-primary round-none' icon={<ReloadOutlined />} /> <Button
onClick={() => refresh(param)}
type="text"
className="text-primary round-none"
icon={<ReloadOutlined />}
/>
</h2> </h2>
</div> </div>
{/* todo: export, 审核完成之后才能导出 */} {/* todo: export, 审核完成之后才能导出 */}
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}> <RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Button size='small' onClick={handleDownload}>{t('Export')} .docx</Button> <Button size="small" onClick={handleDownload}>
{t("Export")} .docx
</Button>
{/* <PrintContractPDF /> */} {/* <PrintContractPDF /> */}
</RequireAuth> </RequireAuth>
{/* {activeAgencyState === 0 && ( */} {/* {activeAgencyState === 0 && ( */}
<> <>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}> <RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Popconfirm title={t('products:sureSubmitAudit')} onConfirm={handleSubmitForAudit} okText={t('Yes')} placement={'bottomLeft'}> <Popconfirm
<Button size='small' type={'primary'} > title={t("products:sureSubmitAudit")}
{t('Submit')} onConfirm={handleSubmitForAudit}
{t('Audit')} okText={t("Yes")}
</Button> placement={"bottomLeft"}
</Popconfirm> >
</RequireAuth> <Button size="small" type={"primary"}>
</> {t("Submit")}
{t("Audit")}
</Button>
</Popconfirm>
</RequireAuth>
</>
{/* )} */} {/* )} */}
{showEditA && ( {showEditA && (
<Link className='px-2' to={isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? `/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/edit` : `/products/edit`}> <Link
{t('Edit')} className="px-2"
to={
isPermitted(PERM_PRODUCTS_OFFER_AUDIT)
? `/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/edit`
: `/products/edit`
}
>
{t("Edit")}
</Link> </Link>
)} )}
{showAuditA && ( {showAuditA && (
<Link className='px-2' to={isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? `/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/audit` : `/products/audit`}> <Link
{t('products:AuditRes')} className="px-2"
to={
isPermitted(PERM_PRODUCTS_OFFER_AUDIT)
? `/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/audit`
: `/products/audit`
}
>
{t("products:AuditRes")}
</Link> </Link>
)} )}
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}> <RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Button size='small' type={'primary'} onClick={() => handleAuditAgency('1')}> <Button
{t('products:auditStateAction.Published')} size="small"
type={"primary"}
onClick={() => handleAuditAgency("1")}
>
{t("products:auditStateAction.Published")}
</Button> </Button>
</RequireAuth> </RequireAuth>
{/* <Button size='small' type={'primary'} ghost onClick={() => handleAuditAgency('2')}> {/* <Button size='small' type={'primary'} ghost onClick={() => handleAuditAgency('2')}>
{t('products:auditStateAction.Approved')} {t('products:auditStateAction.Approved')}
</Button> */} </Button> */}
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}> <RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Button size='small' type={'primary'} danger ghost onClick={() => handleAuditAgency('3')}> <Button
{t('products:auditStateAction.Rejected')} size="small"
type={"primary"}
danger
ghost
onClick={() => handleAuditAgency("3")}
>
{t("products:auditStateAction.Rejected")}
</Button> </Button>
</RequireAuth> </RequireAuth>
</div> </div>

@ -41,6 +41,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditabl
const lgc_details_mapped = (editingProduct?.lgc_details || []).reduce((r, c) => ({ ...r, [c.lgc]: c }), {}); const lgc_details_mapped = (editingProduct?.lgc_details || []).reduce((r, c) => ({ ...r, [c.lgc]: c }), {});
form.setFieldValue('lgc_details_mapped', lgc_details_mapped); form.setFieldValue('lgc_details_mapped', lgc_details_mapped);
form.setFieldValue('quotation', editingProduct?.quotation); form.setFieldValue('quotation', editingProduct?.quotation);
form.setFieldValue('display_to_c', editingProduct.info?.display_to_c || '0');
setPickEditedInfo({ ...pickEditedInfo, product_title: editingProduct?.info?.product_title }); setPickEditedInfo({ ...pickEditedInfo, product_title: editingProduct?.info?.product_title });
setFormEditable(infoEditable || priceEditable); setFormEditable(infoEditable || priceEditable);
@ -296,7 +297,7 @@ function getFields(props) {
labelInValue={false} labelInValue={false}
options={[ options={[
{ value: '153001', label: '在计划显示,不在报价信显示' }, { value: '153001', label: '在计划显示,不在报价信显示' },
{ value: 0, label: '计划和报价信都要显示' }, { value: '0', label: '计划和报价信都要显示' },
{ value: '153001, 153002', label: '计划和报价信都不用显示' }, { value: '153001, 153002', label: '计划和报价信都不用显示' },
]} ]}
{...styleProps} {...styleProps}
@ -399,7 +400,7 @@ const formValuesMapper = (values) => {
key: 'lgc_details', key: 'lgc_details',
transform: (value) => { transform: (value) => {
const valueArr = Object.values(value) const valueArr = Object.values(value)
.filter((_v) => !isEmpty(_v)) .filter((_v) => !isEmpty(_v.lgc))
.map((e) => ({ title: '', ...e, descriptions: e.descriptions || '' })); .map((e) => ({ title: '', ...e, descriptions: e.descriptions || '' }));
return valueArr; return valueArr;
}, },
@ -408,7 +409,7 @@ const formValuesMapper = (values) => {
key: 'lgc_details_mapped', key: 'lgc_details_mapped',
transform: (value) => { transform: (value) => {
const valueArr = Object.values(value) const valueArr = Object.values(value)
.filter((_v) => !isEmpty(_v)) .filter((_v) => !isEmpty(_v.lgc))
.map((e) => ({ title: '', ...e, descriptions: e.descriptions || '' })); .map((e) => ({ title: '', ...e, descriptions: e.descriptions || '' }));
return valueArr.reduce((r, c) => ({ ...r, [c.lgc]: c }), {}); return valueArr.reduce((r, c) => ({ ...r, [c.lgc]: c }), {});
}, },

@ -46,7 +46,7 @@ const ProductInfoLgc = ({ editable, formInstance, pickEditedInfo, ...props }) =>
<Form.Item name={['lgc_details_mapped', `${ele.lgc}`, 'descriptions']} label={t('products:Description')} initialValue={ele.descriptions} tooltip={t('FormTooltip.Description')}> <Form.Item name={['lgc_details_mapped', `${ele.lgc}`, 'descriptions']} label={t('products:Description')} initialValue={ele.descriptions} tooltip={t('FormTooltip.Description')}>
<Input.TextArea <Input.TextArea
className={'!text-slate-600'} className={'!text-slate-600'}
rows={3} rows={3} maxLength={2000} showCount
allowClear allowClear
// onChange={(e) => handleChange('description', e.target.value)} // onChange={(e) => handleChange('description', e.target.value)}
// disabled={ignoreEditable ? false : (!isEmpty(ele.descriptions) || !editable)} // disabled={ignoreEditable ? false : (!isEmpty(ele.descriptions) || !editable)}
@ -98,7 +98,7 @@ const ProductInfoLgc = ({ editable, formInstance, pickEditedInfo, ...props }) =>
<Input allowClear placeholder={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)} /> <Input allowClear placeholder={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)} />
</Form.Item> </Form.Item>
<Form.Item name={['lgc_details_mapped', `${lgcItem.value}`, 'descriptions']} preserve={false} label={t('products:Description')} tooltip={t('FormTooltip.Description')}> <Form.Item name={['lgc_details_mapped', `${lgcItem.value}`, 'descriptions']} preserve={false} label={t('products:Description')} tooltip={t('FormTooltip.Description')}>
<Input.TextArea rows={3} allowClear /> <Input.TextArea rows={3} maxLength={2000} showCount allowClear />
</Form.Item> </Form.Item>
<Form.Item hidden name={['lgc_details_mapped', `${lgcItem.value}`, 'lgc']} preserve={false} initialValue={lgcItem.value}> <Form.Item hidden name={['lgc_details_mapped', `${lgcItem.value}`, 'lgc']} preserve={false} initialValue={lgcItem.value}>
<Input /> <Input />

File diff suppressed because it is too large Load Diff

@ -43,7 +43,7 @@ function Detail() {
]; ];
function detailTextRender(_, confirm) { function detailTextRender(_, confirm) {
const formattedText = confirm.PCI_ConfirmText;//.replace(/\;/g, '\n\n'); const formattedText = confirm.PCI_ConfirmText;
return ( return (
<div className='whitespace-pre-line'> <div className='whitespace-pre-line'>
{formattedText} {formattedText}
@ -76,6 +76,8 @@ function Detail() {
const [confirmText, setConfirmText] = useState(''); const [confirmText, setConfirmText] = useState('');
const [newConfirmText, setNewConfirmText] = useState(''); const [newConfirmText, setNewConfirmText] = useState('');
const [dataLoading, setDataLoading] = useState(false); const [dataLoading, setDataLoading] = useState(false);
const [reservationPreviewUrl, setReservationPreviewUrl] = useState('');
const [nameCardPreviewUrl, setNameCardPreviewUrl] = useState('');
const { notification } = App.useApp(); const { notification } = App.useApp();
const { reservationId } = useParams(); const { reservationId } = useParams();
@ -93,9 +95,6 @@ function Detail() {
const nameCardUrl = 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=${travelAgencyId}&token=${loginToken}&FileType=2&v=${randomString}`
const reservationPreviewUrl = officeWebViewerUrl + encodeURIComponent(reservationUrl);
const nameCardPreviewUrl = officeWebViewerUrl + encodeURIComponent(nameCardUrl);
const showConfirmModal = (confirm) => { const showConfirmModal = (confirm) => {
setIsModalOpen(true); setIsModalOpen(true);
const formattedText = confirm.PCI_ConfirmText;//.replace(/\;/g, '\n\n'); const formattedText = confirm.PCI_ConfirmText;//.replace(/\;/g, '\n\n');
@ -118,6 +117,10 @@ function Detail() {
useEffect(() => { useEffect(() => {
setDataLoading(true); setDataLoading(true);
setReservationPreviewUrl(officeWebViewerUrl + encodeURIComponent(reservationUrl))
setNameCardPreviewUrl(officeWebViewerUrl + encodeURIComponent(nameCardUrl))
getReservationDetail(reservationId) getReservationDetail(reservationId)
.catch(ex => { .catch(ex => {
notification.error({ notification.error({

@ -0,0 +1,140 @@
import { useState, useEffect } from "react";
import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, List, Table, Button, Typography } from "antd";
import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined, AuditOutlined } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime } from "@/utils/commons";
import dayjs from "dayjs";
import SearchForm from "@/components/SearchForm";
import { DATE_FORMAT } from "@/config";
import { TableExportBtn } from "@/components/Data";
import trainTicketStore from "@/stores/Trainticket";
import { usingStorage } from "@/hooks/usingStorage";
const planListColumns = [
{
title: "团名",
key: "GRI_No",
dataIndex: "GRI_No",
// sorter: (a, b) => b.GRI_No - a.GRI_No,
render: (text, record) => (
<Typography.Text title={record.Memo}>
{record.GRI_No} {record.WL}
</Typography.Text>
),
},
{
title: "人数",
dataIndex: "PersonNum",
key: "PersonNum",
},
{
title: "出发",
key: "FromAirport",
dataIndex: "FromAirport",
},
{
title: "抵达",
key: "ToAirport",
dataIndex: "ToAirport",
},
{
title: "车次",
key: "FlightNo",
dataIndex: "FlightNo",
},
{
title: "出发日期",
key: "StartDate",
dataIndex: "StartDate",
sorter: (a, b) => {
const dateA = new Date(a.StartDate);
const dateB = new Date(b.StartDate);
return dateB.getTime() - dateA.getTime();
},
},
{
title: "开始时间",
key: "FlightStart",
dataIndex: "FlightStart",
render: text => formatColonTime(text),
},
{
title: "抵达时间",
key: "FlightEnd",
dataIndex: "FlightEnd",
render: text => formatColonTime(text),
},
{
title: "出票处理",
key: "TicketIssued",
dataIndex: "TicketIssued",
render: (text, record) => record.TicketIssuedName,
},
{
title: "计划状态",
key: "FlightStatus",
dataIndex: "FlightStatus",
render: (text, record) => record.FlightStatusName,
},
{
title: "操作",
key: "FlightInfo",
dataIndex: "FlightInfo",
render: (text, record) => <NavLink to={`/trainticket/plan/${record.COLI_SN}/${record.GRI_SN}`}>{"编辑"}</NavLink>,
},
];
const Trainticket = props => {
const navigate = useNavigate();
const { travelAgencyId } = usingStorage();
const [getPlanList, planList, loading] = trainTicketStore(state => [state.getPlanList, state.planList, state.loading]);
const showTotal = total => `合计 ${total} `;
useEffect(() => {
!planList && getPlanList(travelAgencyId, "", dayjs().startOf("M").format(DATE_FORMAT), dayjs().endOf("M").format(DATE_FORMAT), "-1", "-1");
}, []);
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row>
<Col md={20} lg={20} xxl={20}>
<SearchForm
initialValue={{
dates: [dayjs().startOf("M"), dayjs().endOf("M")],
}}
fieldsConfig={{
shows: ["referenceNo", "dates", "airticket_state", "plan_state"],
fieldProps: {
referenceNo: { label: "搜索计划" },
dates: { label: "出发日期", col: 8 },
},
}}
onSubmit={(err, formVal, filedsVal) => {
getPlanList(travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.endtime, formVal.plan_state, formVal.airticket_state);
}}
/>
</Col>
<Col md={4} lg={4} xxl={4}>
<Space>
<Button icon={<AuditOutlined />} onClick={() => navigate(`/trainticket/invoice`)}>
报账
</Button>
<Button icon={<AuditOutlined />} onClick={() => navigate(`/trainticket/invoicepaid`)}>
汇款记录
</Button>
</Space>
</Col>
</Row>
<Row gutter={16}>
<Col md={24} lg={24} xxl={24}>
<Table bordered={true} rowKey="id" columns={planListColumns} dataSource={planList} loading={loading} pagination={{ defaultPageSize: 20, showTotal: showTotal }} />
<TableExportBtn btnTxt="导出计划" label={`出票计划`} {...{ columns: planListColumns, dataSource: planList }} />
</Col>
<Col md={24} lg={24} xxl={24}></Col>
</Row>
</Space>
);
};
export default Trainticket;

@ -0,0 +1,245 @@
import { useState, useEffect } from "react";
import { Grid, Divider, Layout, Steps, Statistic, Col, Row, Space, Checkbox, Table, Button, App, Typography } from "antd";
import { PhoneOutlined, CustomerServiceOutlined, FrownTwoTone, LikeTwoTone } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime } from "@/utils/commons";
import dayjs from "dayjs";
import SearchForm from "@/components/SearchForm";
import BackBtn from "@/components/BackBtn";
import { TableExportBtn } from "@/components/Data";
import trainTicketStore from "@/stores/Trainticket";
import { usingStorage } from "@/hooks/usingStorage";
const Invoice = props => {
const navigate = useNavigate();
const { notification } = App.useApp();
const { travelAgencyId } = usingStorage();
const [getVEIFlightBill, vEIFlightBill, loading, postVEIFlightBillSubmit] = trainTicketStore(state => [state.getVEIFlightBill, state.vEIFlightBill, state.loading, state.postVEIFlightBillSubmit]);
const showTotal = total => `合计 ${total} `;
const [selectedValues, setSelectedValues] = useState([]);
const vEIFlightBillColumns = [
{
title: "团名",
key: "GRI_No",
dataIndex: "GRI_No",
render: (text, record) => (
<Typography.Text title={record.Memo}>
{record.GRI_No} {record.WL}
</Typography.Text>
),
},
{
title: "状态",
key: "CostType",
dataIndex: "CostType",
},
{
title: "出发日期",
key: "StartDate",
dataIndex: "StartDate",
sorter: (a, b) => {
const dateA = new Date(a.StartDate);
const dateB = new Date(b.StartDate);
return dateB.getTime() - dateA.getTime();
},
},
{
title: "出发",
key: "CLF_FromAirport",
dataIndex: "CLF_FromAirport",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "抵达",
key: "ToAirport",
dataIndex: "ToAirport",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "车次",
key: "FlightNo",
dataIndex: "FlightNo",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "取票号",
key: "TicketNo",
dataIndex: "TicketNo",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "车票类型",
key: "SeatClass",
dataIndex: "SeatClass",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "车票价格",
children: [
{
title: vEIFlightBill && vEIFlightBill.reduce((acc, curr) => acc + curr.Cost, 0),
dataIndex: "Cost",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
],
key: "Cost",
},
{
title: "服务费",
children: [
{
title: vEIFlightBill && vEIFlightBill.reduce((acc, curr) => acc + curr.ServiceFee, 0),
dataIndex: "ServiceFee",
},
],
},
{
title: "审核状态",
children: [
{
title: (
<Button type="link" onClick={() => checkALL()}>
全选
</Button>
),
dataIndex: "CheckStatus", //2
render: (text, record) =>
record.CheckStatus < 2 ? (
<Checkbox onChange={event => handleCheckboxChange(event, record)} checked={checkboxStates(record.CLC_SN)}>
待提交
</Checkbox>
) : (
<Steps
size="small"
current={record.CheckStatus - 1}
items={[
{
title: "提交账单",
},
{
title: "顾问审核",
},
{
title: "财务处理",
},
{
title: "账单支付",
},
]}
/>
),
},
],
sorter: (a, b) => {
return b.CheckStatus - a.CheckStatus;
},
},
];
//
const checkboxStates = CLC_SN => {
return selectedValues.some(v => v.CLC_SN === CLC_SN);
};
// checkbox
const handleCheckboxChange = (event, data) => {
const value = { CLC_SN: data.CLC_SN, WL: data.WL, OPI_SN: data.OPI_SN, OPI_Email: data.OPI_Email, GRI_SN: data.GRI_SN, GRI_Name: data.GRI_Name, Cost: data.Cost, ServiceFee: data.ServiceFee };
if (event.target.checked) {
setSelectedValues([...selectedValues, value]); //
} else {
setSelectedValues(selectedValues.filter(v => v.CLC_SN !== value.CLC_SN)); //
}
};
//
const postInvoice = () => {
postVEIFlightBillSubmit(travelAgencyId, selectedValues)
.then(() => {
notification.success({
message: `成功`,
description: "账单提交成功!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
setSelectedValues([]); //
})
.catch(() => {
notification.error({
message: `错误`,
description: "保存失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
};
//
const checkALL = () => {
if (isEmpty(vEIFlightBill)) return;
const allChecked = selectedValues.length === vEIFlightBill.filter(item => item.CheckStatus < 2).length;
setSelectedValues(
allChecked
? []
: vEIFlightBill
.filter(item => item.CheckStatus < 2)
.map(item => ({ CLC_SN: item.CLC_SN, WL: item.WL, OPI_SN: item.OPI_SN, OPI_Email: item.OPI_Email, GRI_SN: item.GRI_SN, GRI_Name: item.GRI_Name, Cost: item.Cost, ServiceFee: item.ServiceFee }))
); //
};
useEffect(() => {}, []);
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row>
<Col md={20} lg={20} xxl={20}>
<SearchForm
initialValue={{
dates: [dayjs().startOf("M"), dayjs().endOf("M")],
}}
fieldsConfig={{
shows: ["referenceNo", "dates", "invoiceCheckStatus"],
fieldProps: {
referenceNo: { label: "搜索计划" },
dates: { label: "出发日期", col: 8 },
},
}}
onSubmit={(err, formVal, filedsVal) => {
getVEIFlightBill(travelAgencyId, formVal.referenceNo, formVal.invoiceCheckStatus, formVal.startdate, formVal.endtime);
console.log(vEIFlightBill);
}}
/>
</Col>
<Col md={4} lg={4} xxl={4}>
<BackBtn to={"/trainticket"} />
</Col>
</Row>
<Row>
<Col md={24} lg={24} xxl={24}>
<Table bordered={true} rowKey="CLC_SN" columns={vEIFlightBillColumns} dataSource={vEIFlightBill} loading={loading} pagination={{ defaultPageSize: 100, showTotal: showTotal }} />
<TableExportBtn btnTxt="导出账单" label={`车票账单`} {...{ columns: vEIFlightBillColumns, dataSource: vEIFlightBill }} />
</Col>
<Col md={24} lg={24} xxl={24}></Col>
</Row>
<Row>
<Col md={24} lg={18} xxl={18}></Col>
<Col md={24} lg={2} xxl={2}>
<Statistic title="已选车票价格" value={selectedValues.reduce((acc, curr) => acc + curr.Cost, 0)} />
</Col>
<Col md={24} lg={2} xxl={2}>
<Statistic title="已选服务费" value={selectedValues.reduce((acc, curr) => acc + curr.ServiceFee, 0)} />
</Col>
<Col md={24} lg={2} xxl={2}>
<Button type="primary" size="large" onClick={postInvoice} disabled={selectedValues.length == 0}>
提交账单
</Button>
</Col>
</Row>
</Space>
);
};
export default Invoice;

@ -0,0 +1,136 @@
import { useState, useEffect } from "react";
import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, Checkbox, Table, Button, App } from "antd";
import { PhoneOutlined, CustomerServiceOutlined, FrownTwoTone, LikeTwoTone } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime, formatDate, isNotEmpty } from "@/utils/commons";
import { DATE_FORMAT } from "@/config";
import dayjs from "dayjs";
import SearchForm from "@/components/SearchForm";
import BackBtn from "@/components/BackBtn";
import { TableExportBtn } from "@/components/Data";
import useInvoiceStore from "@/stores/Invoice";
import { fetchInvoicePaidDetail } from "@/stores/Invoice";
import { usingStorage } from "@/hooks/usingStorage";
const InvoicePaid = props => {
const navigate = useNavigate();
const { notification } = App.useApp();
const { travelAgencyId } = usingStorage();
const [invoicePaidDetail, setInvoicePaidDetail] = useState([]);
const [invoiceNO, setInvoiceNO] = useState([]); //
const [loading, invoicePaid, fetchInvoicePaid] = useInvoiceStore(state => [state.loading, state.invoicePaid, state.fetchInvoicePaid]);
const showTotal = total => `Total ${total} items`;
const showTotal_detail = total => `Total ${total} items`;
useEffect(() => {
// fetchInvoicePaid(travelAgencyId, "", dayjs().subtract(2, "M").startOf("M").format(DATE_FORMAT), dayjs().endOf("M").format(DATE_FORMAT));
}, []);
const invoicePaidColumns = [
{
title: "编号",
dataIndex: "fl_finaceNo",
key: "fl_finaceNo",
},
{
title: "报账日期",
key: "fl_adddate",
dataIndex: "fl_adddate",
render: (text, record) => (isNotEmpty(text) ? formatDate(new Date(text)) : ""),
},
{
title: "团数",
key: "fcount",
dataIndex: "fcount",
},
{
title: "总额",
key: "pSum",
dataIndex: "pSum",
//render: (text, record) => (isNotEmpty(record.GMD_Currency) ? record.GMD_Currency + " " + text : text),
},
{
title: "查看",
key: "pSum",
dataIndex: "pSum",
render: (text, record) => (
<Button
type="link"
onClick={() => {
fetchInvoicePaidDetail(travelAgencyId, record.key).then(res => setInvoicePaidDetail(res));
setInvoiceNO(record.fl_finaceNo);
}}>
查看明细
</Button>
),
},
];
const invoicePaidDetailColumns = [
{
title: "团号",
dataIndex: "fl2_GroupName",
key: "fl2_GroupName",
},
{
title: "金额",
key: "fl2_price",
dataIndex: "fl2_price",
},
{
title: "报账日期",
key: "fl2_ArriveDate",
dataIndex: "fl2_ArriveDate",
render: (text, record) => (isNotEmpty(text) ? formatDate(new Date(text)) : ""),
},
{
title: "顾问",
dataIndex: "fl2_wl",
key: "fl2_wl",
},
];
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row>
<Col md={20} lg={20} xxl={20}>
<SearchForm
initialValue={{
dates: [dayjs().subtract(2, "M").startOf("M"), dayjs().endOf("M")],
}}
fieldsConfig={{
shows: ["dates"],
fieldProps: {
dates: { col: 10, label: "报账日期" },
},
}}
onSubmit={(err, formVal) => {
fetchInvoicePaid(travelAgencyId, "", formVal.startdate, formVal.enddate);
setInvoicePaidDetail([]);
}}
/>
</Col>
<Col md={4} lg={4} xxl={4}>
<BackBtn to={"/trainticket"} />
</Col>
</Row>
<Row>
<Col md={24} lg={16} xxl={16}>
<Divider orientation="left">汇款列表</Divider>
<Table bordered columns={invoicePaidColumns} dataSource={invoicePaid} loading={loading} pagination={{ defaultPageSize: 20, showTotal: showTotal }} />
</Col>
<Col md={24} lg={4} xxl={4}></Col>
</Row>
<Row>
<Col md={24} lg={20} xxl={20}>
<Divider orientation="left">账单明细 {invoiceNO}</Divider>
<Table bordered columns={invoicePaidDetailColumns} dataSource={invoicePaidDetail} pagination={{ defaultPageSize: 100, showTotal: showTotal_detail }} />
<TableExportBtn btnTxt="导出账单明细" label={`车票账单`} {...{ columns: invoicePaidDetailColumns, dataSource: invoicePaidDetail }} />
</Col>
<Col md={24} lg={4} xxl={4}></Col>
</Row>
</Space>
);
};
export default InvoicePaid;

@ -0,0 +1,706 @@
import { useState, useEffect } from "react";
import { Checkbox, Divider, DatePicker, Modal, Form, Input, Col, Row, Space, Collapse, Table, Button, Select, App, Popconfirm, Switch, Radio, List } from "antd";
import { PhoneOutlined, FrownTwoTone, LikeTwoTone, ArrowUpOutlined, ArrowDownOutlined, PlusOutlined } from "@ant-design/icons";
import { useParams, useHref, useNavigate, NavLink } from "react-router-dom";
import { isEmpty, formatColonTime } from "@/utils/commons";
import { OFFICEWEBVIEWERURL } from "@/config";
import dayjs from "dayjs";
import trainTicketStore from "@/stores/Trainticket";
import { usingStorage } from "@/hooks/usingStorage";
import BackBtn from "@/components/BackBtn";
import { station_names } from "@/views/trainticket/station_name";
const TrainticketPlan = props => {
const { coli_sn, gri_sn } = useParams();
const { travelAgencyId, loginToken, userId } = usingStorage();
const [
getPlanDetail,
planDetail,
getGuestList,
guestList,
loading,
postFlightDetail,
postFlightCost,
deleteFlightCost,
getVeiPlanChange,
veiPlanChangeTxt,
postVeiFlightPlanConfirm,
ticketIssuedNotifications,
delete_flight_info,
seatTable,
] = trainTicketStore(state => [
state.getPlanDetail,
state.planDetail,
state.getGuestList,
state.guestList,
state.loading,
state.postFlightDetail,
state.postFlightCost,
state.deleteFlightCost,
state.getVeiPlanChange,
state.veiPlanChangeTxt,
state.postVeiFlightPlanConfirm,
state.ticketIssuedNotifications,
state.delete_flight_info,
state.seatTable,
]);
const reservationUrl = `https://p9axztuwd7x8a7.mycht.cn/Service_BaseInfoWeb/FlightPlanDocx?GRI_SN=${gri_sn}&VEI_SN=${travelAgencyId}&token=${loginToken}`;
const reservationPreviewUrl = OFFICEWEBVIEWERURL + encodeURIComponent(reservationUrl);
const [form] = Form.useForm();
const { notification } = App.useApp();
//
const guestList_select = () => {
return (
guestList &&
guestList.map(item => {
return { label: `${item.MEI_Name} , ${item.MEI_PassportNo}`, value: `${item.MEI_Name} , ${item.MEI_PassportNo} , ${item.MEI_Country} , ${item.MEI_Gender} , ${item.MEI_age} , ${item.MEI_Birthday}` };
})
);
};
const station_name_select = rawStr => {
const records = rawStr.split("@").filter(item => item); // 1. @
return records.map(record => {
// |8
const [
abbreviation, // bjb
name, //
code, // VAP
pinyin, // beijingbei
shortPinyin, // bjb
sequence, // 0
areaCode, // 0357
city, //
] = record.split("|");
return {
value: name + "-" + pinyin,
label: name,
};
});
};
const guestList_OnChange = e => {
ticket_form.setFieldsValue({ Memo: `${e.target.value}` });
};
//
const seatTableList_select = () => {
return (
seatTable &&
seatTable.map(item => {
return { label: `${item.name}`, value: `${item.name}` };
})
);
};
//
const costListColumns = [
{
title: "客人信息/备注",
key: "Memo",
dataIndex: "Memo",
},
{
title: "费用类型",
key: "CostType",
dataIndex: "CostType",
},
{
title: "车厢",
key: "Cabin",
dataIndex: "Cabin",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "座位号",
key: "SeatNo",
dataIndex: "SeatNo",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "座位类型",
key: "SeatClass",
dataIndex: "SeatClass",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "车票价格",
key: "Cost",
dataIndex: "Cost",
render: (text, record) => (record.CostType == "出票" ? text : "-"),
},
{
title: "服务费",
key: "ServiceFee",
dataIndex: "ServiceFee",
},
{
title: "编辑",
key: "CLC_SN",
dataIndex: "CLC_SN",
render: (text, record) =>
record.CheckStatus <= 2 ? (
<Space>
<a onClick={() => showModal(record)}>编辑</a>
<Popconfirm title="删除" description="请确认是否删除?" onConfirm={() => handleDelete(record.CLC_SN)} okText="是" cancelText="否">
<Button danger type="link">
删除
</Button>
</Popconfirm>
</Space>
) : (
record.CheckStatusName
),
},
];
const Trainticket_form = props => {
const trainInfo = props.airInfo;
const [traininfo_form] = Form.useForm();
return (
<>
<Form
form={traininfo_form}
name={"ticket_form_" + trainInfo.id}
labelCol={{
span: 6,
}}
wrapperCol={{
span: 16,
}}
initialValues={{ ...trainInfo, StartDate: dayjs(trainInfo.StartDate) }}
onFinish={values => {
postFlightDetail(trainInfo.CLF_SN, trainInfo.GRI_SN, trainInfo.VEI_SN, trainInfo, values)
.then(() => {
notification.success({
message: `成功`,
description: "车票信息保存成功!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
})
.catch(() => {
notification.error({
message: `错误`,
description: "保存失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
}}
autoComplete="off">
<Divider orientation="left">火车信息</Divider>
<Row gutter={16}>
<Col md={24} lg={20} xxl={20}>
<Form.Item label="出发日期、车次、取票号" required>
<Space>
<Form.Item name="StartDate" noStyle rules={[{ required: true, message: "请输入出发日期!" }]}>
<DatePicker
placeholder="出发日期"
style={{
minWidth: 160,
}}
/>
</Form.Item>
<Form.Item name="FlightNo" noStyle rules={[{ required: true, message: "请输入车次!" }]}>
<Input placeholder="车次" />
</Form.Item>
<Form.Item name="TicketNo" noStyle rules={[{ required: true, message: "请输入取票号!" }]}>
<Input placeholder="取票号" maxLength={9} />
</Form.Item>
</Space>
</Form.Item>
<Form.Item label="出发站、抵达站" required>
<Space>
<Form.Item name="FromAirport" noStyle rules={[{ required: true, message: "请输入出发站!" }]}>
{/* <Input placeholder="出发" /> */}
<Select
placeholder="出发"
showSearch
style={{
minWidth: 160,
}}
options={station_name_select(station_names)}
/>
</Form.Item>
<Form.Item name="FlightStart" noStyle rules={[{ required: true, message: "请输入出发时间!" }]}>
<Input placeholder="出发时间" />
</Form.Item>
-
<Form.Item name="ToAirport" noStyle rules={[{ required: true, message: "请输入抵达站!" }]}>
{/* <Input placeholder="抵达" /> */}
<Select
placeholder="抵达"
showSearch
style={{
minWidth: 160,
}}
options={station_name_select(station_names)}
/>
</Form.Item>
<Form.Item name="FlightEnd" noStyle rules={[{ required: true, message: "请输入抵达时间!" }]}>
<Input placeholder="抵达时间" />
</Form.Item>
</Space>
</Form.Item>
<Form.Item name="ServiceType" hidden initialValue="2"></Form.Item>
</Col>
<Col md={24} lg={4} xxl={4}>
<Space direction="vertical">
<Form.Item name="TicketIssued">
<Switch checkedChildren="已处理" unCheckedChildren="未处理" />
</Form.Item>
<Button type="primary" htmlType="submit">
1. 保存车票信息
</Button>
</Space>
</Col>
</Row>
<Divider orientation="left">出票信息</Divider>
<Row gutter={16}>
<Col md={24} lg={20} xxl={20}>
<Table bordered={true} rowKey="CLC_SN" columns={costListColumns} dataSource={trainInfo.Flightcost_AsJOSN} loading={loading} pagination={false} />
</Col>
<Col md={24} lg={4} xxl={4}>
<Space direction="vertical">
<Button type="primary" onClick={() => showModal(trainInfo)}>
2. 添加出票信息
</Button>
</Space>
</Col>
</Row>
<Divider orientation="left"></Divider>
<Row gutter={16}>
<Col md={24} lg={20} xxl={20}>
<Form.Item label="上下站提醒信息" name="FlightMemo_messages">
<Input placeholder="没有提醒请留空,信息会抄送给上下站地接" />
</Form.Item>
<Form.Item label="已发提醒" name="FlightMemo">
<Input.TextArea rows={4} readOnly disabled />
</Form.Item>
</Col>
<Col md={24} lg={4} xxl={4}>
<Button
type="primary"
onClick={() => {
ticketIssuedNotifications(userId, trainInfo.CLF_SN, trainInfo.OPI_SN, traininfo_form.getFieldValue("FlightMemo_messages"))
.then(() => {
notification.success({
message: `成功`,
description: "提醒信息已发出!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
traininfo_form.setFieldValue("FlightMemo_messages", "");
})
.catch(() => {
notification.error({
message: `错误`,
description: "提醒失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
}}>
3. 通知顾问
</Button>
</Col>
</Row>
</Form>
</>
);
};
const detail_items = () => {
return planDetail
? planDetail.map(item => {
return {
key: item.id,
label: `${item.StartDate} ${item.FlightNo}(${item.FromAirport}${item.FlightStart}-${item.ToAirport}${item.FlightEnd})(${item.FlightCabin})`,
extra: (
<Popconfirm
title="请确认要删除车票记录"
description=""
onConfirm={() => {
delete_flight_info(item.CLF_SN); //
getPlanDetail(travelAgencyId, gri_sn); //
}}
okText="是"
cancelText="否">
<Button type="dashed" size="small" disabled={item.Flightcost_AsJOSN.length == 0 ? false : true}>
删除
</Button>
</Popconfirm>
),
children: <Trainticket_form airInfo={item} />,
};
})
: [];
};
// begin
const [isModalOpen, setIsModalOpen] = useState(false);
const [isModalOpen_confirmInfo, setisModalOpen_confirmInfo] = useState(false);
const [isTicketType, setisTicketType] = useState(true);
const [isAddNew, setisAddNew] = useState(true); //
const [ticket_form] = Form.useForm();
const [confirmInfo_form] = Form.useForm();
const showModal = ticket => {
setIsModalOpen(true);
ticket_form.resetFields();
if (isEmpty(ticket.CostType)) ticket.CostType = "出票";
ticket.CostType == "出票" ? setisTicketType(true) : setisTicketType(false); //
const isNew = isEmpty(ticket.CLC_SN); //
setisAddNew(isNew);
if (isNew) {
ticket.ServiceFee = "60"; // // 60
}
ticket_form.setFieldsValue(ticket);
if (isEmpty(ticket.Memo)) ticket_form.setFieldsValue({ Memo: "" });
};
const handleOk = (close_modal = true) => {
ticket_form
.validateFields()
.then(values => {
//
postFlightCost(values)
.then(() => {
notification.success({
message: `成功`,
description: "保存成功!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
getPlanDetail(travelAgencyId, gri_sn);
})
.catch(() => {
notification.error({
message: `错误`,
description: "保存失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
if (close_modal) setIsModalOpen(false);
})
.catch(info => {
console.log("Validate Failed:", info);
});
};
const handleCancel = () => {
ticket_form.resetFields();
setIsModalOpen(false);
};
const handleDelete = CLC_SN => {
deleteFlightCost(CLC_SN)
.then(() => {
notification.success({
message: `成功`,
description: "删除成功!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
getPlanDetail(travelAgencyId, gri_sn);
})
.catch(() => {
notification.error({
message: `错误`,
description: "删除失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
};
const onChangeType = value => {
if (value == "出票") {
setisTicketType(true);
} else {
setisTicketType(false);
}
};
//
const showModal_confirmInfo = ConfirmInfo => {
setisModalOpen_confirmInfo(true);
confirmInfo_form.setFieldsValue({ ConfirmInfo: ConfirmInfo });
};
const handleCancel_confirmInfo = () => {
setisModalOpen_confirmInfo(false);
confirmInfo_form.resetFields();
};
const handleOk_confirmInfo = () => {
confirmInfo_form
.validateFields()
.then(values => {
console.log("Received values of form: ", values.ConfirmInfo);
postVeiFlightPlanConfirm(travelAgencyId, gri_sn, userId, values.ConfirmInfo)
.then(() => {
notification.success({
message: `成功`,
description: "保存成功!",
placement: "top",
duration: 4,
icon: <LikeTwoTone />,
});
getVeiPlanChange(travelAgencyId, gri_sn);
})
.catch(() => {
notification.error({
message: `错误`,
description: "保存失败",
placement: "top",
duration: 4,
icon: <FrownTwoTone />,
});
});
confirmInfo_form.resetFields();
setisModalOpen_confirmInfo(false);
})
.catch(info => {
console.log("Validate Failed:", info);
});
};
// end
useEffect(() => {
getPlanDetail(travelAgencyId, gri_sn); //
getGuestList(coli_sn); //
getVeiPlanChange(travelAgencyId, gri_sn); //
}, []);
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Row>
<Col md={20} lg={20} xxl={20}></Col>
<Col md={4} lg={4} xxl={4}>
<BackBtn to={"/trainticket"} />
</Col>
</Row>
<Row>
<Col md={24} lg={24} xxl={24} style={{ height: "100%" }}>
<iframe id="msdoc-iframe-reservation" title="msdoc-iframe-reservation" src={reservationPreviewUrl + "&v=" + Math.random()} style={{ width: "100%", height: "600px" }}></iframe>
<Button type="link" target="_blank" href={reservationUrl}>
下载
</Button>
</Col>
</Row>
<Row>
<Divider orientation="center">{planDetail ? `${planDetail[0].GRI_No} - ${planDetail[0].WL}` : ""}</Divider>
<Col md={24} lg={24} xxl={24}>
<Collapse items={detail_items()} />
</Col>
<Col md={24} lg={24} xxl={24}>
<br />
<p style={{ textAlign: "right" }}>
<Popconfirm
title="请确认要增加车票记录"
description=""
onConfirm={() => {
postFlightDetail("", gri_sn, travelAgencyId, { FlightNo: "新的记录", FlightStatus: 1, ServiceType: 2 }, []); //
getPlanDetail(travelAgencyId, gri_sn); //
}}
okText="是"
cancelText="否">
<Button type="dashed" icon={<PlusOutlined />}>
新增车票记录
</Button>
</Popconfirm>
</p>
</Col>
</Row>
<Row>
<Divider orientation="left">计划变更</Divider>
<Col md={24} lg={12} xxl={12}>
<Space direction="vertical" style={{ width: "90%" }}>
<Input.TextArea rows={16} readOnly value={veiPlanChangeTxt && veiPlanChangeTxt.ChangeText} />
<Button
type="primary"
onClick={() => {
showModal_confirmInfo(veiPlanChangeTxt && veiPlanChangeTxt.ChangeText);
}}
disabled={isEmpty(veiPlanChangeTxt) || isEmpty(veiPlanChangeTxt.ChangeText)}>
确认变更
</Button>
</Space>
</Col>
<Col md={24} lg={12} xxl={12}>
<Input.TextArea rows={16} readOnly value={veiPlanChangeTxt && veiPlanChangeTxt.ConfirmInfo} />
</Col>
</Row>
<Modal title="变更" open={isModalOpen_confirmInfo} onOk={handleOk_confirmInfo} onCancel={handleCancel_confirmInfo}>
<Form
form={confirmInfo_form}
labelCol={{
span: 5,
}}>
<Form.Item label="确认信息" name="ConfirmInfo" rules={[{ required: true }]}>
<Input.TextArea rows={4} />
</Form.Item>
</Form>
</Modal>
<Modal
title="费用信息"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
okText="保存"
cancelText="关闭"
footer={(_, { OkBtn, CancelBtn }) => (
<>
<CancelBtn />
{isAddNew ? (
<>
<Button type="primary" onClick={() => handleOk(false)}>
添加并继续新增
</Button>{" "}
<Button type="primary" onClick={() => handleOk(true)}>
添加并关闭
</Button>
</>
) : (
<OkBtn />
)}
</>
)}>
<Form
form={ticket_form}
labelCol={{
span: 5,
}}>
<Form.Item label="费用类型" name="CostType">
<Select
style={{
width: 160,
}}
onChange={onChangeType}
options={[
{
value: "出票",
label: "出票",
},
{
value: "改签",
label: "改签",
},
{
value: "取消",
label: "取消",
},
]}
/>
</Form.Item>
{isTicketType && (
<>
<Form.Item label="车厢" name="Cabin" rules={[{ required: true }]}>
<Select
style={{
width: 160,
}}
options={Array.from({ length: 16 }, (_, index) => ({
value: `${String(index + 1).padStart(2, "0")}`,
label: `${String(index + 1).padStart(2, "0")}`,
}))}
/>
</Form.Item>
<Form.Item label="座位号" name="SeatNo" rules={[{ required: true }]}>
<Input
style={{
width: 160,
}}
/>
</Form.Item>
<Form.Item label="座位类型" name="SeatClass" rules={[{ required: true }]}>
<Select
style={{
width: 160,
}}
options={seatTableList_select()}
/>
</Form.Item>
<Form.Item label="车票价格" name="Cost" rules={[{ required: true }]}>
<Input
prefix="¥"
style={{
width: 160,
}}
/>
</Form.Item>
</>
)}
<Form.Item label="服务费" name="ServiceFee" rules={[{ required: true }]}>
<Input
prefix="¥"
style={{
width: 160,
}}
/>
</Form.Item>
{isTicketType && (
<>
<Form.Item label="选择客人" name="MEI_Name66">
<Radio.Group
onChange={e => guestList_OnChange(e)}
style={{
minWidth: 320,
}}>
<List
bordered
dataSource={guestList_select()}
renderItem={item => (
<List.Item>
<Radio value={item.value}>{item.label}</Radio>
</List.Item>
)}></List>
</Radio.Group>
{/* <Select onChange={value => guestList_OnChange(value)} options={guestList_select()} placeholder="如果列表里面没有客人信息,请手动录到备注里" /> */}
</Form.Item>
</>
)}
<Form.Item label="客人信息/备注" name="Memo">
<Input.TextArea rows={4} disabled={isTicketType} />
</Form.Item>
<Form.Item name="CLF_SN" hidden>
<input />
</Form.Item>
<Form.Item name="GRI_SN" hidden>
<input />
</Form.Item>
<Form.Item name="VEI_SN" hidden>
<input />
</Form.Item>
<Form.Item name="CLC_SN" hidden>
<input />
</Form.Item>
</Form>
</Modal>
</Space>
);
};
export default TrainticketPlan;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save