From 08aa01e33c2b8496ee438a04050ecb6a153592ce Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 21 Apr 2025 10:48:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=A2=E6=9C=8D:=20=E4=B8=89?= =?UTF-8?q?=E5=B3=A1=E6=B8=B8=E8=88=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SearchForm.jsx | 106 ++++++++++++++----- src/stores/HotelCruise.js | 147 +++++++++++++++++++++++++++ src/stores/Index.js | 2 + src/views/Cruise.jsx | 63 +++++++++--- 4 files changed, 278 insertions(+), 40 deletions(-) create mode 100644 src/stores/HotelCruise.js diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 6a7b55f..6b0400d 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -3,7 +3,7 @@ import { toJS } from 'mobx'; import { observer } from 'mobx-react'; import { DATE_FORMAT, SMALL_DATETIME_FORMAT, stores_Context } from './../../config'; import { SearchOutlined } from '@ant-design/icons'; -import { Form, Row, Col, Select, Button, Space, DatePicker, Input } from 'antd'; +import { Form, Row, Col, Select, Button, Space, DatePicker, Input, InputNumber } from 'antd'; import moment from 'moment'; // import locale from 'antd/es/date-picker/locale/zh_CN'; import BusinessSelect from './BusinessSelect'; @@ -181,6 +181,11 @@ export default observer((props) => { transform: (value) => value?.value || value?.key || '', default: '', }, + 'cruiseDirection': { + key: 'cruiseDirection', + transform: (value) => value?.key || '', + default: '', + }, }; let dest = {}; const { departureDateType, applyDate, applyDate2, year, yearDiff, dates, months, date, ...omittedValue } = values; @@ -275,14 +280,14 @@ function getFields(props) { }; let baseChildren = []; baseChildren = [ - item( - "keyword", // 关键词搜索 {...fieldComProps.keyword} - 99, - - - , - fieldProps?.keyword?.col || 6 - ), + item( + 'keyword', // 关键词搜索 {...fieldComProps.keyword} + 99, + + + , + fieldProps?.keyword?.col || 6 + ), item( 'agency', 99, @@ -377,11 +382,11 @@ function getFields(props) { item( 'orderStatus', 99, - - {fieldProps?.orderStatus?.show_all && ( - - 所有 + + 成行状态 )} @@ -407,10 +412,7 @@ function getFields(props) { 'departureDateType', 99, - {departureDateTypes.map((ele) => ( {ele.label} @@ -502,28 +504,80 @@ function getFields(props) { ), item( - 'cruiseType', + 'cruiseDirection', 99, - - + {fieldProps?.cruiseDirection?.show_all && ( + )} - - - */} + + , + 3 + ), + item( + 'cruiseBookType', + 99, + + , 3 ), + item( + 'roomsRange', + 99, + + + + + + + + + + + , + fieldProps?.roomsRange?.col || 4 + ), + item( + 'personRange', + 99, + + + + + + + + + + + , + fieldProps?.personRange?.col || 4 + ), item( 'bookType', 99, diff --git a/src/stores/HotelCruise.js b/src/stores/HotelCruise.js new file mode 100644 index 0000000..ea32eb7 --- /dev/null +++ b/src/stores/HotelCruise.js @@ -0,0 +1,147 @@ +import { makeAutoObservable, runInAction, toJS } from 'mobx'; +import { fetchJSON } from '../utils/request'; +import { isEmpty, sortDescBy, objectMapper, groupBy, pick, unique, cloneDeep, omit, fixTo2Decimals } from '../utils/commons'; +import { groupsMappedByCode, dataFieldAlias } from './../libs/ht'; +import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from './../config'; +import moment from 'moment'; + +const fetchHotelData = async (param) => { + const defaultParam = { + DEI_SN:'', + City:'', + OrderState:'', + BookingType:'-1', + RecommendedLevel:'-1', + Star:'-1', + ArriveDateCheck:'0', + ArriveDateStart:'', + ArriveDateEnd:'', + ConfirmDateCheck:'0', + ConfirmDateStart:'', + ConfirmDateEnd:'', + Compare:'0', + CompareDateStart:'', + CompareDateEnd:'', + }; + const json = await fetchJSON('/service-Analyse2/HotelReservation', { ...defaultParam, ...param }); + return json.errcode === 0 ? json.result : []; +}; + +const fetchCruiseData = async (param) => { + const defaultParam = { + DEI_SN: '', + OrderState: '', // 0: 不成行 1: 成行 + ArriveDateStart: '', + ArriveDateEnd: '', + Compare: '', + CompareDateStart: '', + CompareDateEnd: '', + BookingType: '', // 0: 非单订三峡,1: 单订三峡 + ProductName: '', + Direction: '', // 1: 上水 2: 下水 + VEI_SN: '-1', // 只要列出常用游船供应商选择 + RoomNumStart: '0', + RoomNumEnd: '', + PersonNumStart: '0', + PersonNumEnd: '', + Country: '-1', + }; + const json = await fetchJSON('/service-Analyse2/CruiseReservation', { ...defaultParam, ...param }); + return json.errcode === 0 ? json.result : []; +}; + +const keyMapped = { + 'applyDate': { key: '' }, + 'startDate': { key: 'ArriveDateCheck'}, + 'comfirmDate': { key: 'ConfirmDateCheck'}, + 'DepartmentList': { key: 'DEI_SN' }, + 'orderStatus': { key: 'OrderState' }, + 'Date1': { key: 'ArriveDateStart' }, + 'Date2': { key: 'ArriveDateEnd' }, + 'DateDiff1': { key: 'CompareDateStart' }, + 'DateDiff2': { key: 'CompareDateEnd' }, + 'keyword': { key: 'ProductName' }, + 'cruiseDirection': { key: 'Direction' }, +}; + +class HotelCruise { + constructor(appStore) { + this.appStore = appStore; + makeAutoObservable(this); + } + + async getCruiseData(param = {}) { + this.cruise.loading = true; + this.cruise.dataSource = []; + const _queryParam = objectMapper(param, { + 'DepartmentList': { key: 'DEI_SN' }, + 'orderStatus': { key: 'OrderState' }, + 'Date1': { key: 'ArriveDateStart' }, + 'Date2': { key: 'ArriveDateEnd' }, + 'DateDiff1': { key: 'CompareDateStart' }, + 'DateDiff2': { key: 'CompareDateEnd' }, + 'keyword': { key: 'ProductName' }, + 'cruiseDirection': { key: 'Direction' }, + }); + const queryParam = omit({ ...this.searchValuesToSub, ..._queryParam }, ['DepartmentList', 'orderStatus', 'keyword', 'Date1', 'Date2', 'DateDiff1', 'DateDiff2', 'cruiseDirection']); + queryParam.Compare = isEmpty(param.DateDiff1) ? '' : '1'; + const res = await fetchCruiseData(queryParam); + const resCP = + queryParam.Compare === '' + ? res + : (res || []).map((ele) => ({ + ...ele, + TotalNumPercent: ele.CPTotalNum ? fixTo2Decimals(((ele.TotalNum - ele.CPTotalNum) / ele.CPTotalNum) * 100) : '-', + TotalProfitPercent: ele.CPTotalProfit ? fixTo2Decimals(((ele.TotalProfit - ele.CPTotalProfit) / ele.CPTotalProfit) * 100) : '-', + })); + runInAction(() => { + this.cruise.loading = false; + this.cruise.dataSource = resCP; + }); + return this.cruise; + } + + async getHotelData(param = {}) { + this.hotel.loading = true; + this.hotel.dataSource = []; + const res = await fetchHotelData({ ...this.searchValuesToSub, ...param }); + runInAction(() => { + this.hotel.loading = false; + this.hotel.dataSource = [].concat(this.hotel.dataSource, res); + }); + } + + searchValues = { + date: moment(), + DateType: { key: 'applyDate', label: '提交日期' }, + WebCode: { key: '', label: '所有来源' }, + // IncludeTickets: { key: '1', label: '含门票'}, + DepartmentList: [{ key: '', label: '所有小组' }], + operator: '-1', + opisn: '-1', + }; + + searchValuesToSub = {}; + + setSearchValues(obj, values) { + this.searchValues = { ...this.searchValues, ...values }; + this.searchValuesToSub = obj; + } + + cruise = { loading: false, dataSource: [] }; + hotel = { loading: false, dataSource: [] }; + resetData = () => { + this.results.loading = false; + for (const key of Object.keys(this.results)) { + if (key !== 'loading') { + this.results[key] = []; + } + } + for (const key of Object.keys(this.hotel)) { + if (key !== 'loading') { + this.hotel[key] = []; + } + } + }; +} +export default HotelCruise; diff --git a/src/stores/Index.js b/src/stores/Index.js index 1e303e0..2f3f127 100644 --- a/src/stores/Index.js +++ b/src/stores/Index.js @@ -18,6 +18,7 @@ import DataPivot from './DataPivot'; import MeetingData from './MeetingData2024'; import MeetingData2025 from './MeetingData2025'; import SalesCRMData from './SalesCRMData'; +import HotelCruise from './HotelCruise'; class Index { constructor() { this.dashboard_store = new DashboardStore(this); @@ -39,6 +40,7 @@ class Index { this.MeetingDataStore = new MeetingData(this); this.MeetingData2025Store = new MeetingData2025(this); this.SalesCRMDataStore = new SalesCRMData(this); + this.HotelCruiseStore = new HotelCruise(this); makeAutoObservable(this); } diff --git a/src/views/Cruise.jsx b/src/views/Cruise.jsx index ab189fd..fc94e33 100644 --- a/src/views/Cruise.jsx +++ b/src/views/Cruise.jsx @@ -4,23 +4,54 @@ import { stores_Context } from '../config'; import moment from 'moment'; import { Row, Col, Table, Select, Space, Typography, Progress, Spin, Divider, Button, Switch } from 'antd'; import SearchForm from './../components/search/SearchForm'; +import { VSTag, TableExportBtn } from './../components/Data'; + +const {Text} = Typography; export default observer((props) => { const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context); - const { customerServicesStore, date_picker_store } = useContext(stores_Context); + const { customerServicesStore, HotelCruiseStore, date_picker_store } = useContext(stores_Context); + const { loading, dataSource } = HotelCruiseStore.cruise; const { formValues, siderBroken } = searchFormStore; const tableProps = { size: 'small', + bordered: true, pagination: false, columns: [ - { title: '产品', dataIndex: 'op', key: 'op' }, - { title: '房间数', dataIndex: 'action', key: 'action' }, - { title: '人数', dataIndex: 'action', key: 'action' }, - { title: '总利润', dataIndex: 'action', key: 'action' }, - { title: '单订船', dataIndex: 'action', key: 'action' }, - { title: '订单含行程', dataIndex: 'action', key: 'action' }, - { title: '国籍', dataIndex: 'action', key: 'action' }, + { title: '产品', dataIndex: 'ProductName', key: 'ProductName' }, + { + title: '房间数', + dataIndex: 'TotalNum', + key: 'TotalNum', + render: (v, r) => ( + <> + + + {v} + {r.CPTotalNum && VS {r.CPTotalNum}} + + {r.CPTotalNum && } + + + ), + }, + { title: '人数', dataIndex: 'TotalPersonNum', key: 'TotalPersonNum' }, + { title: '总利润', dataIndex: 'TotalProfit', key: 'TotalProfit', + render: (v, r) => ( + <> + + + {v} + {r.CPTotalProfit && VS {r.CPTotalProfit}} + + {r.CPTotalNum && } + + + ), }, + // { title: '单订船', dataIndex: '', key: '' }, + // { title: '订单含行程', dataIndex: '', key: '' }, + // { title: '国籍', dataIndex: '', key: '' }, ], }; @@ -32,27 +63,31 @@ export default observer((props) => { defaultValue={{ initialValue: { ...date_picker_store.formValues, - ...customerServicesStore.searchValues, + ...HotelCruiseStore.searchValues, }, // 'countryArea', - shows: ['DepartmentList', 'orderStatus', 'years', 'keyword', 'agency', 'cruiseType'], - sort: { keyword: 101, cruiseType: 102, agency: 103 }, + shows: [ + 'DepartmentList', 'orderStatus', 'dates', 'keyword', 'cruiseDirection', 'agency', + // 'cruiseBookType', 'country', 'roomsRange', 'personRange' + ], + sort: { keyword: 101, agency: 103, cruiseDirection: 102, country: 104 }, fieldProps: { keyword: { placeholder: '产品名', col: 4 }, DepartmentList: { show_all: true, mode: 'multiple' }, orderStatus: { show_all: true }, + cruiseDirection: { show_all: true }, // years: { hide_vs: false }, }, }} onSubmit={(_err, obj, form) => { - customerServicesStore.setSearchValues(obj, form); - // customerServicesStore.fetchDestinationGroupCount(); + HotelCruiseStore.setSearchValues(obj, form); + HotelCruiseStore.getCruiseData(obj); }} />
- +
record.ProductName} /> );