From d1ee551a4e24af6d8f3c7bc25b1b3b91402ecc8e Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 18 Feb 2025 15:19:55 +0800 Subject: [PATCH 1/5] =?UTF-8?q?todo:=20=E4=B8=89=E5=B3=A1,=20=E9=85=92?= =?UTF-8?q?=E5=BA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 8 ++- src/components/search/HotelStarSelect.jsx | 40 ++++++++++++ src/components/search/SearchForm.jsx | 78 ++++++++++++++++++++++- src/libs/ht.js | 16 +++++ src/stores/CustomerServices.js | 1 + src/views/Cruise.jsx | 59 +++++++++++++++++ src/views/Hotel.jsx | 55 ++++++++++++++++ 7 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 src/components/search/HotelStarSelect.jsx create mode 100644 src/views/Cruise.jsx create mode 100644 src/views/Hotel.jsx diff --git a/src/App.jsx b/src/App.jsx index 4db3f01..1c4bec5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -55,6 +55,8 @@ import CooperationIcon from './components/icons/CooperationIcon'; import OPDashboard from './views/OPDashboard'; import OPActivity from './views/OPActivity'; import OPRisk from './views/OPRisk'; +import Cruise from './views/Cruise'; +import Hotel from './views/Hotel'; const App = () => { const { Content, Footer, Sider, } = Layout; @@ -158,6 +160,8 @@ const App = () => { key: 62, label: 目的地接团, }, + { key: 'cruise', label: 三峡游船 }, + { key: 'hotel', label: 酒店 }, ], }, { @@ -259,7 +263,9 @@ const App = () => { } /> } /> } /> - + } /> + } /> + }> } /> } /> diff --git a/src/components/search/HotelStarSelect.jsx b/src/components/search/HotelStarSelect.jsx new file mode 100644 index 0000000..8796dd0 --- /dev/null +++ b/src/components/search/HotelStarSelect.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Select } from 'antd'; +import { observer } from 'mobx-react'; +import { HotelStars as options } from '../../libs/ht'; + +const HotelStars = (props) => { + const { mode, value, onChange, show_all, ...extProps } = props; + const _show_all = ['tags', 'multiple'].includes(mode) ? false : show_all; + return ( +
+ +
+ ); +}; +/** + * 酒店星级 + */ +export default observer(HotelStars); diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index cb93e7f..59b58b5 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 } from 'antd'; +import { Form, Row, Col, Select, Button, Space, DatePicker, Input } from 'antd'; import moment from 'moment'; // import locale from 'antd/es/date-picker/locale/zh_CN'; import BusinessSelect from './BusinessSelect'; @@ -17,6 +17,7 @@ import SearchInput from './Input'; import { objectMapper, at, empty, isEmpty } from './../../utils/commons'; import './search.css'; +import HotelStarSelect from './HotelStarSelect'; const EditableContext = createContext(); const Option = Select.Option; @@ -263,6 +264,14 @@ function getFields(props) { }; let baseChildren = []; baseChildren = [ + item( + "keyword", // 关键词搜索 {...fieldComProps.keyword} + 99, + + + , + fieldProps?.keyword?.col || 6 + ), item( 'agency', 99, @@ -456,6 +465,73 @@ function getFields(props) { ), + item( + 'cruiseType', + 99, + + + , + 3 + ), + item( + 'bookType', + 99, + + + , + 3 + ), + item( + 'recommandRate', + 99, + + + , + 3 + ), + item( + 'hotelStar', + 99, + + + + ), ]; baseChildren = baseChildren .map((x) => { diff --git a/src/libs/ht.js b/src/libs/ht.js index 5f878c5..a1fa130 100644 --- a/src/libs/ht.js +++ b/src/libs/ht.js @@ -178,6 +178,22 @@ export const KPISubjects = [ // { key: 'sum_person_num', value: 'sum_person_num', label: '人数' }, ]; +export const HotelStars = [ + { key: '5', value: '5', label: '5星' }, + { key: '4', value: '4', label: '4星' }, + { key: '3', value: '3', label: '3星' }, + { key: '2', value: '2', label: '2星' }, + { key: 'intl', value: 'intl', label: '国际社' }, + { key: 'internal', value: 'internal', label: '国内社' }, + { key: 'other', value: 'other', label: '其它' }, + { key: 'f5', value: 'f5', label: '准5星' }, + { key: 'f4', value: 'f4', label: '准4星' }, + { key: 'inn', value: 'inn', label: '客栈' }, + { key: 'apartment', value: 'apartment', label: '公寓' }, + { key: 'siheyuan', value: 'siheyuan', label: '四合院酒店' }, + { key: 'luxury5', value: 'luxury5', label: '豪华五星' }, +]; + /** * 计算指标值的分段区间 * @param {number} value diff --git a/src/stores/CustomerServices.js b/src/stores/CustomerServices.js index 141a7f4..e7ed2d4 100644 --- a/src/stores/CustomerServices.js +++ b/src/stores/CustomerServices.js @@ -459,6 +459,7 @@ class CustomerServices { searchValues = { DateType: { key: 'startDate', label: '走团日期'}, + countryArea: { key: 'china', label: '国内' }, }; setSearchValues(obj, values) { diff --git a/src/views/Cruise.jsx b/src/views/Cruise.jsx new file mode 100644 index 0000000..ab189fd --- /dev/null +++ b/src/views/Cruise.jsx @@ -0,0 +1,59 @@ +import React, { Children, useContext, useState } from 'react'; +import { observer } from 'mobx-react'; +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'; + +export default observer((props) => { + const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context); + const { customerServicesStore, date_picker_store } = useContext(stores_Context); + + const { formValues, siderBroken } = searchFormStore; + + const tableProps = { + size: 'small', + 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' }, + ], + }; + + return ( + <> + + + { + customerServicesStore.setSearchValues(obj, form); + // customerServicesStore.fetchDestinationGroupCount(); + }} + /> + + +
+ + + + ); +}); diff --git a/src/views/Hotel.jsx b/src/views/Hotel.jsx new file mode 100644 index 0000000..8ad1902 --- /dev/null +++ b/src/views/Hotel.jsx @@ -0,0 +1,55 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { stores_Context } from '../config'; +import { Row, Col, Table } from 'antd'; +import SearchForm from './../components/search/SearchForm'; + +export default observer((props) => { + const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context); + const { customerServicesStore, date_picker_store } = useContext(stores_Context); + + const { formValues, siderBroken } = searchFormStore; + + const tableProps = { + size: 'small', + columns: [ + { title: '目的地', dataIndex: 'op', key: 'op' }, + { title: '总间夜', dataIndex: 'action', key: 'action' }, + { title: '主推', dataIndex: 'action', key: 'action' }, + { title: '使用比例', dataIndex: 'action', key: 'action' }, + ], + }; + + return ( + <> + + + { + customerServicesStore.setSearchValues(obj, form); + // customerServicesStore.fetchDestinationGroupCount(); + }} + /> + + +
+
+ + + ); +}); From a7b24d206de43f0891f15919dac89ded5910ebd0 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 11 Apr 2025 11:08:31 +0800 Subject: [PATCH 2/5] =?UTF-8?q?perf:=20=E8=BF=87=E7=A8=8B=E6=8C=87?= =?UTF-8?q?=E6=A0=87:=20tips?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/OPProcess.jsx | 101 ++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/src/views/OPProcess.jsx b/src/views/OPProcess.jsx index 7303aca..78d2bee 100644 --- a/src/views/OPProcess.jsx +++ b/src/views/OPProcess.jsx @@ -1,11 +1,10 @@ -import React, { Children, useContext, useState } from 'react'; +import React, { useContext } from 'react'; import { observer } from 'mobx-react'; import { stores_Context } from '../config'; -import moment from 'moment'; -import { SlackOutlined, SketchOutlined, AntCloudOutlined, RedditOutlined, GithubOutlined } from '@ant-design/icons'; -import { Row, Col, Table, Select, Space, Typography, Progress, Spin, Divider, Button, Switch } from 'antd'; +import { InfoCircleOutlined } from '@ant-design/icons'; +import { Row, Col, Table, Tooltip } from 'antd'; import SearchForm from '../components/search/SearchForm'; -import { cloneDeep, fixTo2Decimals, sortBy, isEmpty, pick } from '../utils/commons'; +import { pick } from '../utils/commons'; const COLOR_SETS = [ "#FFFFFF", @@ -58,6 +57,7 @@ export default observer((props) => { rowClassName: (record, rowIndex) => { return record.groupType === 'operator' ? '': 'ant-tag-blue'; }, + showSorterTooltip: false, columns: [ { title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem', filterSearch: true, @@ -68,18 +68,60 @@ export default observer((props) => { title: '顾问动作', key: 'date', children: [ - { title: '首次响应率24H', dataIndex: 'firstTouch24', key: 'firstTouch24', render: percentageRender, + { title: () => ( + <> + 首次响应率24H{' '} + + + + + ), dataIndex: 'firstTouch24', key: 'firstTouch24', render: percentageRender, sorter: (a, b) => tableSorter(a, b, 'firstTouch24'), }, - { title: '48H内报价率', dataIndex: 'firstQuote48', key: 'firstQuote48',render: percentageRender , + { title: () => ( + <> + 48H内报价率{' '} + + + + + ), dataIndex: 'firstQuote48', key: 'firstQuote48',render: percentageRender , sorter: (a, b) => tableSorter(a, b, 'firstQuote48'), }, - { title: '一次报价率', dataIndex: 'quote1', key: 'quote1',render: percentageRender , + { title: () => ( + <> + 一次报价率{' '} + + + + + ), dataIndex: 'quote1', key: 'quote1',render: percentageRender , sorter: (a, b) => tableSorter(a, b, 'quote1'), }, - { title: '二次报价率', dataIndex: 'quote2', key: 'quote2',render: percentageRender , + { title: () => ( + <> + 二次报价率{' '} + + + + + ), dataIndex: 'quote2', key: 'quote2',render: percentageRender , sorter: (a, b) => tableSorter(a, b, 'quote2'), }, - { title: '>50条会话', dataIndex: 'turnsGT50', key: 'turnsGT50', render: percentageRender , + { title: () => ( + <> + >50条会话{' '} + + + + + ), dataIndex: 'turnsGT50', key: 'turnsGT50', render: percentageRender , sorter: (a, b) => tableSorter(a, b, 'turnsGT50'), }, - { title: '违规数', dataIndex: 'violations', key: 'violations', + { title: () => ( + <> + 违规数{' '} + + + + + ), dataIndex: 'violations', key: 'violations', sorter: (a, b) => tableSorter(a, b, 'violations'), }, ], }, @@ -88,28 +130,56 @@ export default observer((props) => { key: 'department', children: [ { - title: '首次回复率24H', dataIndex: 'firstReply24', key: 'firstReply24', + title: () => ( + <> + 首次回复率24H{' '} + + + + + ), dataIndex: 'firstReply24', key: 'firstReply24', render: (_, r) => ({ props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } }, children: percentageRender(_), }), sorter: (a, b) => tableSorter(a, b, 'firstReply24'), }, { - title: '48H内报价回复率', dataIndex: 'replyQuote48', key: 'replyQuote48', + title: () => ( + <> + 48H内报价回复率{' '} + + + + + ), dataIndex: 'replyQuote48', key: 'replyQuote48', render: (_, r) => ({ props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } }, children: percentageRender(_), }), sorter: (a, b) => tableSorter(a, b, 'replyQuote48'), }, { - title: '一次报价回复率', dataIndex: 'replyQuote1', key: 'replyQuote1', + title: () => ( + <> + 一次报价回复率{' '} + + + + + ), dataIndex: 'replyQuote1', key: 'replyQuote1', render: (_, r) => ({ props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } }, children: percentageRender(_), }), sorter: (a, b) => tableSorter(a, b, 'replyQuote1'), }, { - title: '二次报价回复率', dataIndex: 'replyQuote2', key: 'replyQuote2', + title: () => ( + <> + 二次报价回复率{' '} + + + + + ), dataIndex: 'replyQuote2', key: 'replyQuote2', render: (_, r) => ({ props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } }, children: percentageRender(_), @@ -127,6 +197,7 @@ export default observer((props) => { rowClassName: (record, rowIndex) => { return record.groupType === 'operator' ? '': 'ant-tag-blue'; }, + showSorterTooltip: false, columns: [ { title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem', filterSearch: true, From 08aa01e33c2b8496ee438a04050ecb6a153592ce Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 21 Apr 2025 10:48:30 +0800 Subject: [PATCH 3/5] =?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} /> ); From 17cedf14d925aa13ffe9008e21fc62454cd193f8 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 21 Apr 2025 14:48:59 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E5=AE=A2=E6=9C=8D:=20=E9=85=92?= =?UTF-8?q?=E5=BA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/SearchForm.jsx | 56 ++++++++++++------ src/libs/ht.js | 23 ++++---- src/stores/HotelCruise.js | 88 ++++++++++++++++------------ src/views/Cruise.jsx | 3 + src/views/Hotel.jsx | 79 +++++++++++++++++++++---- 5 files changed, 170 insertions(+), 79 deletions(-) diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx index 6b0400d..1954ca1 100644 --- a/src/components/search/SearchForm.jsx +++ b/src/components/search/SearchForm.jsx @@ -186,6 +186,21 @@ export default observer((props) => { transform: (value) => value?.key || '', default: '', }, + 'hotelBookType': { + key: 'hotelBookType', + transform: (value) => value?.key || '', + default: '', + }, + 'hotelRecommandRate': { + key: 'hotelRecommandRate', + transform: (value) => value?.key || '', + default: '', + }, + 'hotelStar': { + key: 'hotelStar', + transform: (value) => value?.key || '', + default: '', + }, }; let dest = {}; const { departureDateType, applyDate, applyDate2, year, yearDiff, dates, months, date, ...omittedValue } = values; @@ -362,11 +377,11 @@ function getFields(props) { item( 'countryArea', 99, - - {fieldProps?.countryArea?.show_all && ( - )}
+
record.CityName} /> ); From 1e46cf6e631b8e55f7c339151b355e1f9dd6e990 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 21 Apr 2025 15:04:42 +0800 Subject: [PATCH 5/5] =?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:=20=E4=BA=BA=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/HotelCruise.js | 2 +- src/views/Cruise.jsx | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/stores/HotelCruise.js b/src/stores/HotelCruise.js index 76b2b05..567f981 100644 --- a/src/stores/HotelCruise.js +++ b/src/stores/HotelCruise.js @@ -90,6 +90,7 @@ class HotelCruise { : (res || []).map((ele) => ({ ...ele, TotalNumPercent: ele.CPTotalNum ? fixTo2Decimals(((ele.TotalNum - ele.CPTotalNum) / ele.CPTotalNum) * 100) : '-', + TotalPersonNumPercent: ele.CPTotalPersonNum ? fixTo2Decimals(((ele.TotalPersonNum - ele.CPTotalPersonNum) / ele.CPTotalPersonNum) * 100) : '-', TotalProfitPercent: ele.CPTotalProfit ? fixTo2Decimals(((ele.TotalProfit - ele.CPTotalProfit) / ele.CPTotalProfit) * 100) : '-', })); runInAction(() => { @@ -120,7 +121,6 @@ class HotelCruise { RecommendRateDelta: fixTo2Decimals((ele.RecommendRate - (ele.CPRecommendRate || 0)) * 100), CPRecommendRate_100: fixTo2Decimals(ele.CPRecommendRate * 100) + '%', })); - console.log(resCP); runInAction(() => { this.hotel.loading = false; this.hotel.dataSource = resCP; diff --git a/src/views/Cruise.jsx b/src/views/Cruise.jsx index 589c79d..679f5d9 100644 --- a/src/views/Cruise.jsx +++ b/src/views/Cruise.jsx @@ -38,7 +38,19 @@ export default observer((props) => { ), }, - { title: '人数', dataIndex: 'TotalPersonNum', key: 'TotalPersonNum' }, + { title: '人数', dataIndex: 'TotalPersonNum', key: 'TotalPersonNum', + sorter: (a, b) => tableSorter(a, b, 'TotalPersonNum'), + render: (v, r) => ( + <> + + + {v} + {r.CPTotalPersonNum && VS {r.CPTotalPersonNum}} + + {r.CPTotalNum && } + + + ),}, { title: '总利润', dataIndex: 'TotalProfit', key: 'TotalProfit', sorter: (a, b) => tableSorter(a, b, 'TotalProfit'), render: (v, r) => (