Compare commits

...

18 Commits

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "haina-dashboard", "name": "haina-dashboard",
"version": "2.14.2", "version": "2.14.7",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "haina-dashboard", "name": "haina-dashboard",
"version": "2.14.2", "version": "2.14.7",
"dependencies": { "dependencies": {
"@ant-design/charts": "^1.4.2", "@ant-design/charts": "^1.4.2",
"@ant-design/pro-components": "^2.6.16", "@ant-design/pro-components": "^2.6.16",

@ -1,6 +1,6 @@
{ {
"name": "haina-dashboard", "name": "haina-dashboard",
"version": "2.14.2", "version": "2.14.7",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@ant-design/charts": "^1.4.2", "@ant-design/charts": "^1.4.2",

@ -60,6 +60,7 @@ import TrainsUpsell from './views/biz/reports/TrainsUpsell';
import HostCaseReport from './views/HostCaseReport'; import HostCaseReport from './views/HostCaseReport';
import ToBOrder from './views/toB/ToBOrder'; import ToBOrder from './views/toB/ToBOrder';
import ToBOrderSub from './views/toB/ToBOrderSub'; import ToBOrderSub from './views/toB/ToBOrderSub';
import MeetingSales from './views/reports/MeetingSales';
const App = () => { const App = () => {
const { Content, Footer, Sider, } = Layout; const { Content, Footer, Sider, } = Layout;
@ -83,6 +84,10 @@ const App = () => {
key: 'meeting-2024-GH', key: 'meeting-2024-GH',
label: <NavLink to="/orders/meeting-2024-GH">GH区域数据</NavLink>, // GH-2024 label: <NavLink to="/orders/meeting-2024-GH">GH区域数据</NavLink>, // GH-2024
}, },
{
key: 'sales-insight',
label: <NavLink to="/reports/sales-insight">顾问业绩</NavLink>,
},
], ],
}, },
{ {
@ -304,6 +309,8 @@ const App = () => {
<Route path="/sales-crm/process" element={<OPProcess />} /> <Route path="/sales-crm/process" element={<OPProcess />} />
<Route path="/sales-crm/risk" element={<OPRisk />} /> <Route path="/sales-crm/risk" element={<OPRisk />} />
<Route path="/sales-crm/risk/sales/:opisn" element={<OPRisk />} /> <Route path="/sales-crm/risk/sales/:opisn" element={<OPRisk />} />
<Route path="/reports/sales-insight" element={<MeetingSales />} />
</Route> </Route>
</Routes> </Routes>
</Content> </Content>

@ -38,9 +38,9 @@ const Customer_care_regular = () => {
{ {
title: '订单状态', title: '订单状态',
width: '4rem', width: '4rem',
dataIndex: 'OrderState1', dataIndex: 'orderstate_name',
key: 'OrderState1', key: 'orderstate_name',
render: (text, record) => record.OrderState === 1 ? '成行' : '未成行', // render: (text, record) => record.OrderState === 1 ? '' : '',
sorter: (a, b) => b.OrderState - a.OrderState, sorter: (a, b) => b.OrderState - a.OrderState,
}, },
{ {
@ -70,10 +70,15 @@ const Customer_care_regular = () => {
}, },
{ {
title: '走团国家', title: '走团国家',
dataIndex: 'recommend_country', dataIndex: 'PassCountry_This',
key: 'recommend_country', key: 'PassCountry_This',
width: '4em', width: '4em',
}, },
{
title: '经过城市',
dataIndex: 'PassCity_This',
key: 'PassCity_This',
},
{ {
title: '小组', title: '小组',
dataIndex: 'Department', dataIndex: 'Department',
@ -109,6 +114,11 @@ const Customer_care_regular = () => {
dataIndex: 'COLI_LineClass', dataIndex: 'COLI_LineClass',
key: 'COLI_LineClass', key: 'COLI_LineClass',
}, },
{
title: '产品类型',
dataIndex: 'TourType_Name',
key: 'TourType_Name',
},
{ {
title: '券额', title: '券额',
dataIndex: 'Voucher_amount', dataIndex: 'Voucher_amount',
@ -158,6 +168,14 @@ const Customer_care_regular = () => {
style: { backgroundColor: '#5B8FF9' + '1A' }, style: { backgroundColor: '#5B8FF9' + '1A' },
}), }),
}, },
{
title: '上次经过城市',
dataIndex: 'PassCity_Last',
key: 'PassCity_Last',
onCell: (r) => ({
style: { backgroundColor: '#5B8FF9' + '1A' },
}),
},
{ {
title: '复购周期', title: '复购周期',
dataIndex: 'Repurchase_cycle', dataIndex: 'Repurchase_cycle',
@ -309,19 +327,19 @@ const Customer_care_regular = () => {
/> />
), ),
}, },
{ // {
title: '订单数占比(市场)', // title: '()',
dataIndex: 'OrderRate2', // dataIndex: 'OrderRate2',
key: 'OrderRate2', // key: 'OrderRate2',
render: (text, record) => ( // render: (text, record) => (
<RenderVSDataCell // <RenderVSDataCell
showDiffData={!isEmpty(searchValuesToSub.DateDiff1)} // showDiffData={!isEmpty(searchValuesToSub.DateDiff1)}
data1={fixTo2Decimals(record.OrderRate2 * 100)} // data1={fixTo2Decimals(record.OrderRate2 * 100)}
data2={fixTo2Decimals(record.diff?.OrderRate2 * 100)} // data2={fixTo2Decimals(record.diff?.OrderRate2 * 100)}
dataSuffix="%" // dataSuffix="%"
/> // />
), // ),
}, // },
{ {
title: '成行数', title: '成行数',
dataIndex: 'SUCOrderNum', dataIndex: 'SUCOrderNum',
@ -360,19 +378,19 @@ const Customer_care_regular = () => {
/> />
), ),
}, },
{ // {
title: '毛利占比(市场)', // title: '()',
dataIndex: 'OrderMLRate2', // dataIndex: 'OrderMLRate2',
key: 'OrderMLRate2', // key: 'OrderMLRate2',
render: (text, record) => ( // render: (text, record) => (
<RenderVSDataCell // <RenderVSDataCell
showDiffData={!isEmpty(searchValuesToSub.DateDiff1)} // showDiffData={!isEmpty(searchValuesToSub.DateDiff1)}
data1={fixTo2Decimals(record.OrderMLRate2 * 100)} // data1={fixTo2Decimals(record.OrderMLRate2 * 100)}
data2={fixTo2Decimals(record.diff?.OrderMLRate2 * 100)} // data2={fixTo2Decimals(record.diff?.OrderMLRate2 * 100)}
dataSuffix="%" // dataSuffix="%"
/> // />
), // ),
}, // },
{ {
title: '人数(含成人+儿童)', title: '人数(含成人+儿童)',
dataIndex: 'PersonNum', dataIndex: 'PersonNum',

@ -31,6 +31,19 @@ const pivotOptions = [
</> </>
), ),
}, },
{
key: 'destinations',
value: 'destinations',
labelX: '目的地城市',
label: (
<>
目的地城市&nbsp;&nbsp;
<Tooltip key="total_data_tips" title={'途径的城市将会重复计算'}>
<InfoCircleOutlined className="ant-tag-gold" />
</Tooltip>
</>
),
},
]; ];
const pivotColOptions = [ const pivotColOptions = [
{ key: 'hasOld', value: 'hasOld', label: '老客户+推荐' }, { key: 'hasOld', value: 'hasOld', label: '老客户+推荐' },
@ -40,7 +53,7 @@ const pivotColOptions = [
const CustomerCareRegularPivot = (props) => { const CustomerCareRegularPivot = (props) => {
const { date_picker_store: searchFormStore, customer_store } = useContext(stores_Context); const { date_picker_store: searchFormStore, customer_store } = useContext(stores_Context);
const { formValues, formValuesToSub, siderBroken } = searchFormStore; const { formValues, formValuesToSub, siderBroken } = searchFormStore;
const { loading, pivotResult, filterColValues, rawData, countrySummary } = customer_store.sales_regular_data; const { loading, pivotResult, filterColValues, rawData, countrySummary, citySummary } = customer_store.sales_regular_data;
// console.log('', countrySummary); // console.log('', countrySummary);
const [pivotRow, setPivotRow] = useState('operatorName'); const [pivotRow, setPivotRow] = useState('operatorName');
const [pivotCol, setPivotCol] = useState('hasOld'); const [pivotCol, setPivotCol] = useState('hasOld');
@ -173,6 +186,7 @@ const CustomerCareRegularPivot = (props) => {
sticky={{ offsetHeader: 88 }} sticky={{ offsetHeader: 88 }}
dataSource={dataSource} dataSource={dataSource}
loading={loading} loading={loading}
components={{ body: { cell: TdCell } }}
columns={[ columns={[
{ {
key: ele.key, key: ele.key,
@ -187,7 +201,7 @@ const CustomerCareRegularPivot = (props) => {
]} ]}
summary={() => { summary={() => {
return ( return (
pivotRow === 'country' && pivotRow === 'country' ?
countrySummary.map((srow) => ( countrySummary.map((srow) => (
<Table.Summary.Row key={srow.key}> <Table.Summary.Row key={srow.key}>
<Table.Summary.Cell index1={0}><b>{srow.country}</b></Table.Summary.Cell> <Table.Summary.Cell index1={0}><b>{srow.country}</b></Table.Summary.Cell>
@ -198,6 +212,17 @@ const CustomerCareRegularPivot = (props) => {
))} ))}
</Table.Summary.Row> </Table.Summary.Row>
)) ))
: pivotRow === 'destinations' ? citySummary.map((srow) => (
<Table.Summary.Row key={srow.key}>
<Table.Summary.Cell index1={0}><b>{srow.destinations}</b></Table.Summary.Cell>
{columns.map((td) => (
<Table.Summary.Cell key={td.key}>
<RenderVSDataCell data1={srow[td.dataIndex]} data2={srow.vsData?.[td.dataIndex]} showDiffData={toJS(formValuesToSub.DateDiff1)} dataSuffix={td.suffix} />
</Table.Summary.Cell>
))}
</Table.Summary.Row>
)) : null
); );
}} }}
pagination={false} pagination={false}

@ -0,0 +1,23 @@
import React from 'react';
import { Select } from 'antd';
import { observer } from 'mobx-react';
import { lineClass } from './../../libs/ht';
export const LineClassSelector = ({ value, onChange, ...props }) => {
return (
<div>
<Select
style={{ width: '100%' }}
placeholder="选择来源类型"
value={value}
onChange={onChange}
allowClear
labelInValue
{...props}
options={lineClass}
/>
</div>
);
};
export default observer(LineClassSelector);

@ -14,6 +14,7 @@ import DateTypeSelect from './DataTypeSelect';
import DatePickerCharts from './DatePickerCharts'; import DatePickerCharts from './DatePickerCharts';
import YearPickerCharts from './YearPickerCharts'; import YearPickerCharts from './YearPickerCharts';
import GuideLanguageSelect from './GuideLanguageSelect'; import GuideLanguageSelect from './GuideLanguageSelect';
import LineClassSeletor from './LineClassSeletor';
import SearchInput from './Input'; import SearchInput from './Input';
import { objectMapper, at, empty, isEmpty } from '@haina/utils-commons'; import { objectMapper, at, empty, isEmpty } from '@haina/utils-commons';
import { departureDateTypes } from './../../libs/ht'; import { departureDateTypes } from './../../libs/ht';
@ -81,6 +82,13 @@ export default observer((props) => {
}, },
default: '', default: '',
}, },
'lineClass': {
key: 'lineClass',
transform: (value) => {
return isEmpty(value) ? '': Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.key : '';
},
default: '',
},
'WebCode': { 'WebCode': {
key: 'WebCode', key: 'WebCode',
transform: (value) => { transform: (value) => {
@ -101,7 +109,7 @@ export default observer((props) => {
'operator': { 'operator': {
key: 'operator', key: 'operator',
// transform: (value) => value?.key || '', // transform: (value) => value?.key || '',
transform: (value) => Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? (!isNaN(parseInt(value.key), 10) ? value.key : '') : '', transform: (value) => isEmpty(value) ? '': Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? (!isNaN(parseInt(value.key), 10) ? value.key : '') : '',
default: '', default: '',
}, },
'date': { 'date': {
@ -506,7 +514,7 @@ function getFields(props) {
item( item(
'operator', 'operator',
99, 99,
<Form.Item name={'operator'} dependencies={['DepartmentList']}> <Form.Item name={'operator'} dependencies={['DepartmentList']} initialValue={at(props, 'initialValue.operator')[0]}>
<SearchInput <SearchInput
{...fieldProps.operator} {...fieldProps.operator}
autoGet autoGet
@ -673,6 +681,13 @@ function getFields(props) {
<HotelStarSelect {...fieldProps.hotelStar} labelInValue={true} /> <HotelStarSelect {...fieldProps.hotelStar} labelInValue={true} />
</Form.Item> </Form.Item>
), ),
item(
'lineClass',
99,
<Form.Item name={`lineClass`} initialValue={at(props, 'initialValue.lineClass')[0] || undefined}>
<LineClassSeletor {...fieldProps.lineClass} labelInValue={true} />
</Form.Item>
),
]; ];
baseChildren = baseChildren baseChildren = baseChildren
.map((x) => { .map((x) => {

@ -6,4 +6,4 @@ export const stores_Context = React.createContext();
export const DATE_FORMAT = "YYYY-MM-DD"; export const DATE_FORMAT = "YYYY-MM-DD";
export const SMALL_DATETIME_FORMAT = 'YYYY-MM-DD 23:59:00'; export const SMALL_DATETIME_FORMAT = 'YYYY-MM-DD 23:59:00';
export const DATETIME_FORMAT = 'YYYY-MM-DD 23:59:59'; export const DATETIME_FORMAT = 'YYYY-MM-DD 23:59:59';
export const HT_HOST = process.env.NODE_ENV === "production" ? "https://p9axztuwd7x8a7.mycht.cn" : "http://202.103.68.144:890"; export const HT_HOST = process.env.NODE_ENV === "production" ? "https://p9axztuwd7x8a7.mycht.cn" : "http://202.103.68.144:889";

@ -75,6 +75,7 @@ export const sites = [
{ value: '187', key: '187', label: 'HTravel', code: 'HTravel' }, { value: '187', key: '187', label: 'HTravel', code: 'HTravel' },
{ value: '186', key: '186', label: 'JH', code: 'JH' }, { value: '186', key: '186', label: 'JH', code: 'JH' },
{ value: '163', key: '163', label: 'GH', code: 'GH' }, { value: '163', key: '163', label: 'GH', code: 'GH' },
{ value: '188', key: '188', label: 'Thailand', code: 'Thailand' },
{ value: '184', key: '184', label: 'GH站外渠道 (中国)', code: 'ZWQD' }, { value: '184', key: '184', label: 'GH站外渠道 (中国)', code: 'ZWQD' },
{ value: '185', key: '185', label: 'GH站外渠道 (海外)', code: 'GH_ZWQD_HW' }, { value: '185', key: '185', label: 'GH站外渠道 (海外)', code: 'GH_ZWQD_HW' },
{ value: '28', key: '28', label: '客运中国', code: 'GHKYZG' }, { value: '28', key: '28', label: '客运中国', code: 'GHKYZG' },
@ -116,6 +117,43 @@ export const departureDateTypes = [
{ key: 'departureDate', value: 'departureDate', label: '抵达日期' }, { key: 'departureDate', value: 'departureDate', label: '抵达日期' },
]; ];
/**
* 附加来源类型
*/
export const lineClass = [
{key:78001, value: 78001, label:"Google PPC"},
{key:78002, value: 78002, label:"页面推荐订单"},
{key:78003, value: 78003, label:"Bing PPC"},
// {key:78004, value: 78004, label:null},
{key:78005, value: 78005, label:"Newsletter"},
{key:78006, value: 78006, label:"Facebook订单"},
{key:78007, value: 78007, label:"travelchinacheaper"},
{key:78008, value: 78008, label:"farwestchina"},
{key:78009, value: 78009, label:"petel.bg"},
{key:78010, value: 78010, label:"Instagram订单"},
{key:78011, value: 78011, label:"Pinterest"},
// {key:78012, value: 78012, label:null},
{key:78013, value: 78013, label:"网前自然订单"},
{key:78014, value: 78014, label:"Youtube"},
{key:78015, value: 78015, label:"国际站内广告"},
{key:78016, value: 78016, label:"WhatsApp"},
{key:78017, value: 78017, label:"Reddit"},
{key:78018, value: 78018, label:"邮件订单"},
{key:78019, value: 78019, label:"老客户网前订单"},
{key:78020, value: 78020, label:"1v1"},
{key:78021, value: 78021, label:"Facebook广告"},
{key:78022, value: 78022, label:"来自GH的订单"},
{key:78023, value: 78023, label:"小红书"},
{key:78024, value: 78024, label:"Yandex PPC"},
// {key:78025, value: 78025, label:null},
{key:78026, value: 78026, label:"TikTok"},
{key:78027, value: 78027, label:"Instagram广告"},
{key:78028, value: 78028, label:"Facebook再营销广告"},
{key:78029, value: 78029, label:"AI推荐"},
{key:78030, value: 78030, label:"合作平台"},
{key:78031, value: 78031, label:"客运前端获取"},
{key:78032, value: 78032, label:"CT站群营销"}];
/** /**
* 结果字段 * 结果字段
*/ */

@ -2,7 +2,7 @@ import {makeAutoObservable, runInAction, toJS } from "mobx";
import { fetchJSON } from '@haina/utils-request'; import { fetchJSON } from '@haina/utils-request';
import * as config from "../config"; import * as config from "../config";
import { groupsMappedByKey, sitesMappedByCode, pivotBy } from './../libs/ht'; import { groupsMappedByKey, sitesMappedByCode, pivotBy } from './../libs/ht';
import { sortBy, formatPercent, groupBy, isEmpty, uniqWith, formatPercentToFloat, fixTo2Decimals } from "@haina/utils-commons"; import { sortBy, formatPercent, groupBy, isEmpty, uniqWith, formatPercentToFloat, fixTo2Decimals, unique } from "@haina/utils-commons";
import { show_vs_tag, } from "./../utils/commons"; import { show_vs_tag, } from "./../utils/commons";
import moment from 'moment'; import moment from 'moment';
@ -14,6 +14,29 @@ const getDetailData = async (param) => {
return json.errcode === 0 ? json.result : []; return json.errcode === 0 ? json.result : [];
}; };
const initialSummaryRow = {
SumOrder: 0,
ResumeOrder: 0,
ResumeConfirmOrder: 0,
SumPersonNum: 0,
ConfirmPersonNum: 0,
ConfirmOrder: 0,
transactions: 0,
SumML: 0,
SumML_txt: '',
quotePrice: 0,
tourdays: 0,
applyDays: 0,
confirmDays: 0,
SingleML: 0,
OrderValue: 0,
PPPrice: 0,
AvgPPPrice: 0,
confirmTourdays: 0,
PPPriceRange: '',
unitPPPriceRange: '',
};
const calcSummaryRow = (data1, data2) => { const calcSummaryRow = (data1, data2) => {
const summaryFields = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'ConfirmPersonNum', 'ConfirmOrderKPIvalue', 'OrderKPIvalue', 'MLKPIvalue']; const summaryFields = ['ConfirmOrder', 'SumOrder', 'SumML', 'transactions', 'SumPersonNum', 'ConfirmPersonNum', 'ConfirmOrderKPIvalue', 'OrderKPIvalue', 'MLKPIvalue'];
@ -33,6 +56,65 @@ const calcSummaryRow = (data1, data2) => {
xSummary.vsData = xSummary2; xSummary.vsData = xSummary2;
return xSummary; return xSummary;
}; };
const calcSummaryUniqueRow = (_data1, _data2, rawData = [[], []]) => {
// console.log('calcSummaryRow', _data1, _data2, rawData);
const uniqueKeysData1 = unique(_data1.reduce((a, r) => a.concat(r.key.split('_').reduce((a1, r1) => a1.concat(r1.split('@')[0]), [])), [])).map(x => Number(x));
const uniqueKeysData2 = unique(_data2.reduce((a, r) => a.concat(r.key.split('_').reduce((a1, r1) => a1.concat(r1.split('@')[0]), [])), [])).map(x => Number(x));
// console.log(uniqueKeysData1, uniqueKeysData2);
const data1 = rawData[0].filterHasOld.filter(row => uniqueKeysData1.includes((row.key)));
const data2 = rawData[1].filterHasOld.filter(row => uniqueKeysData2.includes((row.key)));
// console.log(data1, data2);
const xSummary = data1.reduce((r, v) => {
r.SumOrder += 1;
r.SumPersonNum += v.personNum;
r.ConfirmPersonNum += Number(v.orderState) === 1 ? v.personNum : 0;
r.ConfirmOrder += Number(v.orderState) === 1 ? 1 : 0;
r.ResumeOrder += v.hasOld === 1 ? 1 : 0;
r.ResumeConfirmOrder += Number(v.orderState) === 1 && v.hasOld === 1 ? 1 : 0;
r.transactions += v.transactions;
r.SumML += Number(v.orderState) === 1 ? v.ML : 0;
r.quotePrice += Number(v.orderState) === 1 ? v.quotePrice : 0;
r.tourdays += v.tourdays;
r.applyDays += v.applyDays;
r.confirmDays += v.confirmDays;
// r.PPPrice += Number(v.orderState) === 1 ? v.PPPrice : 0;
r.confirmTourdays += Number(v.orderState) === 1 ? v.tourdays : 0;
return r;
}, structuredClone(initialSummaryRow));
const xSummary2 = data2.reduce((r, v) => {
r.SumOrder += 1;
r.SumPersonNum += v.personNum;
r.ConfirmPersonNum += Number(v.orderState) === 1 ? v.personNum : 0;
r.ConfirmOrder += Number(v.orderState) === 1 ? 1 : 0;
r.ResumeOrder += v.hasOld === 1 ? 1 : 0;
r.ResumeConfirmOrder += Number(v.orderState) === 1 && v.hasOld === 1 ? 1 : 0;
r.transactions += v.transactions;
r.SumML += Number(v.orderState) === 1 ? v.ML : 0;
r.quotePrice += Number(v.orderState) === 1 ? v.quotePrice : 0;
r.tourdays += v.tourdays;
r.applyDays += v.applyDays;
r.confirmDays += v.confirmDays;
// r.PPPrice += Number(v.orderState) === 1 ? v.PPPrice : 0;
r.confirmTourdays += Number(v.orderState) === 1 ? v.tourdays : 0;
return r;
}, structuredClone(initialSummaryRow));
xSummary.ConfirmRates = xSummary.SumOrder ? fixTo2Decimals((xSummary.ConfirmOrder / xSummary.SumOrder) * 100) : 0;
xSummary.ConfirmRates_txt = xSummary.ConfirmRates; // + '%';
xSummary.confirmTourdays = '-';
xSummary.SingleML = '-';
xSummary._data = data1;
xSummary2.ConfirmRates = xSummary2.SumOrder ? fixTo2Decimals((xSummary2.ConfirmOrder / xSummary2.SumOrder) * 100) : 0;
xSummary2.ConfirmRates_txt = xSummary2.ConfirmRates; // + '%';
xSummary2.confirmTourdays = '-';
xSummary2.SingleML = '-';
xSummary2._data = data2;
xSummary.vsData = xSummary2;
return xSummary;
};
class CustomerStore { class CustomerStore {
@ -497,6 +579,7 @@ class CustomerStore {
// }, // },
pivotResult: [], pivotResult: [],
countrySummary: [], countrySummary: [],
citySummary: [],
filterColValues: [], filterColValues: [],
rawDataArr: [], rawDataArr: [],
}; };
@ -576,7 +659,7 @@ class CustomerStore {
// console.log('regular_data_pivot ---- ', pivotRow, pivotCol); // console.log('regular_data_pivot ---- ', pivotRow, pivotCol);
const [result1, result2] = this.sales_regular_data.rawDataArr; const [result1, result2] = this.sales_regular_data.rawDataArr;
// const allRows = Array.from(new Set([...result1.filterHasOld.map(row=>row[pivotRow]), ...result2.filterHasOld.map(row=>row[pivotRow])])); // const allRows = Array.from(new Set([...result1.filterHasOld.map(row=>row[pivotRow]), ...result2.filterHasOld.map(row=>row[pivotRow])]));
// console.log(' ------ allRows', allRows); // console.log(' ------ allRows', result1, result2);
const [{ pivotResult: pivot1, rowValues: rowValues1 }, { pivotResult: pivot2, rowValues: rowValues2 }] = [result1, result2].map((_result) => { const [{ pivotResult: pivot1, rowValues: rowValues1 }, { pivotResult: pivot2, rowValues: rowValues2 }] = [result1, result2].map((_result) => {
const dataColField = pivotCol.replace('_txt', ''); const dataColField = pivotCol.replace('_txt', '');
const rawData = pivotCol === 'hasOld' ? _result.filterHasOld : _result.filterHasOld.filter((ele) => ele[dataColField] === '1'); const rawData = pivotCol === 'hasOld' ? _result.filterHasOld : _result.filterHasOld.filter((ele) => ele[dataColField] === '1');
@ -633,7 +716,97 @@ class CustomerStore {
eurusdSummary.key = '欧美4国'; eurusdSummary.key = '欧美4国';
eurusdSummary.country = '欧美4国'; eurusdSummary.country = '欧美4国';
this.sales_regular_data.countrySummary = [aseanSummary, eurusdSummary]; // 西班牙语国家/地区
const esLgc = ['阿根廷','玻利维亚','智利','哥伦比亚','哥斯达黎加','古巴','多米尼加共和国','厄瓜多尔','萨尔瓦多','赤道几内亚','危地马拉','洪都拉斯','墨西哥','尼加拉瓜','巴拿马','巴拉圭','秘鲁','波多黎各','西班牙','乌拉圭','委内瑞拉'];
const [esLgc1, esLgc2] = [
pivot1.filter((ele) => esLgc.includes(ele.country)),
pivot2.filter((ele) => esLgc.includes(ele.country)),
];
const esLgcSummary = calcSummaryRow(esLgc1, esLgc2);
esLgcSummary.key = '西班牙语国家/地区';
esLgcSummary.country = '西班牙语国家/地区';
// 意大利语国家/地区
const itLgc = ['意大利','圣马力诺','梵蒂冈','瑞士',];
const [itLgc1, itLgc2] = [
pivot1.filter((ele) => itLgc.includes(ele.country)),
pivot2.filter((ele) => itLgc.includes(ele.country)),
];
const itLgcSummary = calcSummaryRow(itLgc1, itLgc2);
itLgcSummary.key = '意大利语国家/地区';
itLgcSummary.country = '意大利语国家/地区';
// 德语国家/地区
const deLgc = ['德国','奥地利','瑞士','列支敦士登','卢森堡','比利时',];
const [deLgc1, deLgc2] = [
pivot1.filter((ele) => deLgc.includes(ele.country)),
pivot2.filter((ele) => deLgc.includes(ele.country)),
];
const deLgcSummary = calcSummaryRow(deLgc1, deLgc2);
deLgcSummary.key = '德语国家/地区';
deLgcSummary.country = '德语国家/地区';
// 葡萄牙语国家
const ptLgc = ['葡萄牙','巴西','安哥拉','莫桑比克','几内亚比绍','佛得角','圣多美和普林西比','东帝汶',];
const [ptLgc1, ptLgc2] = [
pivot1.filter((ele) => ptLgc.includes(ele.country)),
pivot2.filter((ele) => ptLgc.includes(ele.country)),
];
const ptLgcSummary = calcSummaryRow(ptLgc1, ptLgc2);
ptLgcSummary.key = '葡萄牙语国家/地区';
ptLgcSummary.country = '葡萄牙语国家/地区';
this.sales_regular_data.countrySummary = [
aseanSummary, eurusdSummary,
esLgcSummary, itLgcSummary, deLgcSummary, ptLgcSummary,
];
}
if (pivotRow === 'destinations') {
const city1 = ['昆明','大理','丽江','中甸','德钦','西双版纳','普洱','泸沽湖','腾冲'];
const [city11, city12] = [
pivot1.filter((ele) => city1.includes(ele.destinations)),
pivot2.filter((ele) => city1.includes(ele.destinations)),
];
const city1Summary = calcSummaryUniqueRow(city11, city12, toJS(this.sales_regular_data.rawDataArr));
city1Summary.key = '云南';
city1Summary.destinations = '云南';
const city2 = ['上海','苏州','杭州','黄山','婺源','上饶','景德镇'];
const [city21, city22] = [
pivot1.filter((ele) => city2.includes(ele.destinations)),
pivot2.filter((ele) => city2.includes(ele.destinations)),
];
const city2Summary = calcSummaryUniqueRow(city21, city22, toJS(this.sales_regular_data.rawDataArr));
city2Summary.key = '华东';
city2Summary.destinations = '华东';
const city3 = ['乌鲁木齐','喀什','伊宁','昭苏','霍城','那拉提','喀纳斯','禾木','布尔津','吐鲁番','库尔勒','赛里木湖','和田','库车'];
const [city31, city32] = [
pivot1.filter((ele) => city3.includes(ele.destinations)),
pivot2.filter((ele) => city3.includes(ele.destinations)),
];
const city3Summary = calcSummaryUniqueRow(city31, city32, toJS(this.sales_regular_data.rawDataArr));
city3Summary.key = '新疆';
city3Summary.destinations = '新疆';
const city4 = ['拉萨','江孜','林芝','鲁朗','巴松措','然乌','波密','日喀则','定日','泽当','塔钦'];
const [city41, city42] = [
pivot1.filter((ele) => city4.includes(ele.destinations)),
pivot2.filter((ele) => city4.includes(ele.destinations)),
];
const city4Summary = calcSummaryUniqueRow(city41, city42, toJS(this.sales_regular_data.rawDataArr));
city4Summary.key = '西藏';
city4Summary.destinations = '西藏';
const city5 = ['贵阳','安顺','黄果树','榕江','从江','毕节','织金','铜仁','梵净山','荔波','平塘','兴义'];
const [city51, city52] = [
pivot1.filter((ele) => city5.includes(ele.destinations)),
pivot2.filter((ele) => city5.includes(ele.destinations)),
];
const city5Summary = calcSummaryUniqueRow(city51, city52, toJS(this.sales_regular_data.rawDataArr));
city5Summary.key = '贵州';
city5Summary.destinations = '贵州';
this.sales_regular_data.citySummary = [city1Summary, city2Summary, city3Summary, city4Summary, city5Summary];
} }
this.sales_regular_data.pivotResult = pivotResultWithCompare; this.sales_regular_data.pivotResult = pivotResultWithCompare;

@ -42,18 +42,19 @@ const filterFields = [
{ key: 'isCusCommend_txt', value: 'isCusCommend_txt', label: '是否老客户推荐' }, { key: 'isCusCommend_txt', value: 'isCusCommend_txt', label: '是否老客户推荐' },
{ key: 'hasOld_txt', value: 'hasOld_txt', label: '老客户(含推荐)' }, { key: 'hasOld_txt', value: 'hasOld_txt', label: '老客户(含推荐)' },
{ key: 'RTXF_WB_range', value: 'RTXF_WB_range', label: '人天消费(外币)' }, { key: 'RTXF_WB_range', value: 'RTXF_WB_range', label: '人天消费(外币)' },
{ key: 'customer_types', value: 'customer_types', label: '分销客户' },
], ],
}, },
{ {
label: '业绩', label: '业绩',
options: [ options: [
{ key: 'operatorName', value: 'operatorName', label: '顾问' },
{ key: 'SumML_ctxt', value: 'SumML_ctxt', label: '毛利' },
{ key: 'startMonth', value: 'startMonth', label: '出行日期-月份' }, { key: 'startMonth', value: 'startMonth', label: '出行日期-月份' },
{ key: 'startYearMonth', value: 'startYearMonth', label: '出行日期-年月' }, { key: 'startYearMonth', value: 'startYearMonth', label: '出行日期-年月' },
{ key: 'applyMonth', value: 'applyMonth', label: '预订日期-月份' }, { key: 'applyMonth', value: 'applyMonth', label: '预订日期-月份' },
{ key: 'applyYearMonth', value: 'applyYearMonth', label: '预订日期-年月' }, { key: 'applyYearMonth', value: 'applyYearMonth', label: '预订日期-年月' },
{ key: 'operatorName', value: 'operatorName', label: '顾问' },
{ key: 'PPPriceRange', value: 'PPPriceRange', label: '人均天/单(外币)' }, { key: 'PPPriceRange', value: 'PPPriceRange', label: '人均天/单(外币)' },
{ key: 'SumML_ctxt', value: 'SumML_ctxt', label: '毛利' },
{ key: 'dealDays_ctxt', value: 'dealDays_ctxt', label: '成团周期(天)' }, { key: 'dealDays_ctxt', value: 'dealDays_ctxt', label: '成团周期(天)' },
{ key: 'applyDays_ctxt', value: 'applyDays_ctxt', label: '预订周期(天)' }, { key: 'applyDays_ctxt', value: 'applyDays_ctxt', label: '预订周期(天)' },
], ],

@ -71,9 +71,11 @@ class Orders extends Component {
</div> : null} </div> : null}
</span> </span>
), ),
titleX: showDiff ? `${date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)} vs ${date_picker_store.start_date_cp.format( titleX: showDiff ? `${
config.DATE_FORMAT date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)
)}~${date_picker_store.end_date_cp.format(config.DATE_FORMAT)}` : `${date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)}`, } vs ${
date_picker_store.start_date_cp.format(config.DATE_FORMAT)}~${date_picker_store.end_date_cp.format(config.DATE_FORMAT)
}` : `${date_picker_store.start_date.format(config.DATE_FORMAT)}~${date_picker_store.end_date.format(config.DATE_FORMAT)}`,
dataIndex: 'OrderType', dataIndex: 'OrderType',
fixed: 'left', fixed: 'left',
render: (text, record) => <NavLink to={`/orders_sub/${orders_store.active_tab_key}/${record.OrderTypeSN}/${encodeURIComponent(record.OrderType)}`}>{text}</NavLink>, render: (text, record) => <NavLink to={`/orders_sub/${orders_store.active_tab_key}/${record.OrderTypeSN}/${encodeURIComponent(record.OrderType)}`}>{text}</NavLink>,
@ -93,7 +95,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.OrderCount_diff} diffData={ordercountTotal1?.OrderCount_diff}
/> />
), ),
titleX: [ordercountTotal1.OrderCount, ordercountTotal2.OrderCount].join(' vs '), titleX: [ordercountTotal1.OrderCount, ordercountTotal2.OrderCount].filter(s => s).filter(s => s).join(' vs '),
dataIndex: 'OrderCount', dataIndex: 'OrderCount',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.OrderCount} diffPercent={r.OrderCount_vs} diffData={r.OrderCount_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.OrderCount} diffPercent={r.OrderCount_vs} diffData={r.OrderCount_diff} />,
}, },
@ -112,7 +114,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.CJCount_diff} diffData={ordercountTotal1?.CJCount_diff}
/> />
), ),
titleX: [ordercountTotal1.CJCount, ordercountTotal2.CJCount].join(' vs '), titleX: [ordercountTotal1.CJCount, ordercountTotal2.CJCount].filter(s => s).join(' vs '),
dataIndex: 'CJCount', dataIndex: 'CJCount',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJCount} diffPercent={r.CJCount_vs} diffData={r.CJCount_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJCount} diffPercent={r.CJCount_vs} diffData={r.CJCount_diff} />,
}, },
@ -131,7 +133,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.CJPersonNum_diff} diffData={ordercountTotal1?.CJPersonNum_diff}
/> />
), ),
titleX: [ordercountTotal1.CJPersonNum, ordercountTotal2.CJPersonNum].join(' vs '), titleX: [ordercountTotal1.CJPersonNum, ordercountTotal2.CJPersonNum].filter(s => s).join(' vs '),
dataIndex: 'CJPersonNum', dataIndex: 'CJPersonNum',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJPersonNum} diffPercent={r.CJPersonNum_vs} diffData={r.CJPersonNum_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJPersonNum} diffPercent={r.CJPersonNum_vs} diffData={r.CJPersonNum_diff} />,
}, },
@ -150,7 +152,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.CJrate_diff} diffData={ordercountTotal1?.CJrate_diff}
/> />
), ),
titleX: [ordercountTotal1.CJrate, ordercountTotal2.CJrate].join(' vs '), titleX: [ordercountTotal1.CJrate, ordercountTotal2.CJrate].filter(s => s).join(' vs '),
dataIndex: 'CJrate', dataIndex: 'CJrate',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJrate} diffPercent={r.CJrate_vs} diffData={r.CJrate_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.CJrate} diffPercent={r.CJrate_vs} diffData={r.CJrate_diff} />,
}, },
@ -169,7 +171,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.YJLY_diff} diffData={ordercountTotal1?.YJLY_diff}
/> />
), ),
titleX: [ordercountTotal1.YJLY, ordercountTotal2.YJLY].join(' vs '), titleX: [ordercountTotal1.YJLY, ordercountTotal2.YJLY].filter(s => s).join(' vs '),
dataIndex: 'YJLY', dataIndex: 'YJLY',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.YJLY} diffPercent={r.YJLY_vs} diffData={r.YJLY_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.YJLY} diffPercent={r.YJLY_vs} diffData={r.YJLY_diff} />,
}, },
@ -189,7 +191,7 @@ class Orders extends Component {
diffData={ordercountTotal1?.Ordervalue_diff} diffData={ordercountTotal1?.Ordervalue_diff}
/> />
), ),
titleX: [ordercountTotal1.Ordervalue, ordercountTotal2.Ordervalue].join(' vs '), titleX: [ordercountTotal1.Ordervalue, ordercountTotal2.Ordervalue].filter(s => s).join(' vs '),
dataIndex: 'Ordervalue', dataIndex: 'Ordervalue',
render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.Ordervalue} diffPercent={r.Ordervalue_vs} diffData={r.Ordervalue_diff} />, render: (text, r) => <RenderVSDataCell showDiffData={showDiff} data1={text} data2={r.diff?.Ordervalue} diffPercent={r.Ordervalue_vs} diffData={r.Ordervalue_diff} />,
}, },

@ -0,0 +1,273 @@
import { useContext } from 'react';
import { Row, Col, Table, Button } from 'antd';
import { SwapOutlined } from '@ant-design/icons';
import * as comm from '@haina/utils-commons';
import SearchForm from '../../components/search/SearchForm';
import { RenderVSDataCell } from '../../components/Data';
import { observer } from 'mobx-react';
import { toJS } from 'mobx';
import { stores_Context } from '../../config';
import { useShallow } from 'zustand/shallow';
import useSalesInsightStore from '../../zustand/SalesInsight';
// TdCellDataTable
const TdCell = (tdprops) => {
// onMouseEnter, onMouseLeave
const { onMouseEnter, onMouseLeave, ...restProps } = tdprops;
return <td {...restProps} />;
};
const MeetingSales = observer(() => {
const { date_picker_store: searchFormStore } = useContext(stores_Context);
const [searchValues, setSearchValues] = useSalesInsightStore(useShallow((state) => [state.searchValues, state.setSearchValues]));
const [loading, typeLoading, ] = useSalesInsightStore(useShallow((state) => [state.loading, state.typeLoading, ]));
const [tableMajorKey, tableData, salesDataTotal] = useSalesInsightStore(useShallow((state) => [state.matrixtableMajorKey, state.matrixTableData, state.salesDataTotal]));
const getMeetingDataSales = useSalesInsightStore((state) => state.getMeetingDataSales);
const onMatrixChange = useSalesInsightStore((state) => state.onMatrixChange);
// const showDiff = !comm.isEmpty(searchFormStore.start_date_cp);
const columns = [
{
title: (
<>
<Button icon={<SwapOutlined />} onClick={onMatrixChange} />
</>
),
key: 'matrix',
children: [
{
// title: '',
title: tableMajorKey === 'opi' ? '顾问' : '页面类型',
fixed: 'left',
dataIndex: tableMajorKey === 'opi' ? 'OPI_Name' : 'LineClass',
key: 'pk',
onCell: (record) => ({
rowSpan: record.rowSpan,
}),
},
{
title: tableMajorKey === 'opi' ? '页面类型' : '顾问',
dataIndex: tableMajorKey === 'opi' ? 'LineClass' : 'OPI_Name',
key: 'k2',
},
],
},
// { // title: '',</div><div></div><div>
{
title: (
<>
本期<div>订单数按订单提交日期; 成团数按订单确认日期; 入境按团抵达日期</div>
</>
),
key: 'current',
children: [
{
title: '订单数',
dataIndex: 'OrderNum',
key: 'OrderNum',
},
{
title: '成团数',
dataIndex: 'GroupNum',
key: 'GroupNum',
},
{
title: '成团率',
dataIndex: 'SuccessRate',
key: 'SuccessRate',
render: (text, r) => <RenderVSDataCell showDiffData={false} data1={comm.fixTo2Decimals(text * 100)} data2={r.diff?.SuccessRate} dataSuffix="%" />,
},
{
title: '总人数',
dataIndex: 'TotalPersonNum',
key: 'TotalPersonNum',
},
{
title: '总报价',
dataIndex: 'PreTotalPrice',
key: 'PreTotalPrice',
},
{
title: '总利润',
dataIndex: 'PreTotalProfit',
key: 'PreTotalProfit',
},
{
title: '利润率',
dataIndex: 'PreProfitRate',
key: 'PreProfitRate',
render: (text, r) => <RenderVSDataCell showDiffData={false} data1={comm.fixTo2Decimals(text * 100)} data2={r.diff?.PreProfitRate} dataSuffix="%" />,
},
{
title: '入境团数',
dataIndex: 'EntranceGroupNum',
key: 'EntranceGroupNum',
},
{
title: '入境人数',
dataIndex: 'EntrancePersonNum',
key: 'EntrancePersonNum',
},
],
},
{
title: (
<>
到目前<div>按订单提交日期</div>
</>
),
key: 'until',
children: [
{
title: '订单数',
dataIndex: 'LYOrderNum',
key: 'LYOrderNum',
onCell: (r) => ({
style: { backgroundColor: '#5B8FF9' + '1A' },
}),
},
{
title: '订单中的成团数',
dataIndex: 'LYGroupNum',
key: 'LYGroupNum',
onCell: (r) => ({
style: { backgroundColor: '#5B8FF9' + '1A' },
}),
},
{
title: '成团率',
dataIndex: 'LYSuccessRate',
key: 'LYSuccessRate',
render: (text, r) => <RenderVSDataCell showDiffData={false} data1={comm.fixTo2Decimals(text * 100)} data2={r.diff?.LYSuccessRate} dataSuffix="%" />,
onCell: (r) => ({
style: { backgroundColor: '#5B8FF9' + '1A' },
}),
},
],
},
{
title: (
<>
本年<div>按团抵达日期</div>
</>
),
key: 'toyear',
children: [
{
title: '团队数',
dataIndex: 'LYTotalGroupNum',
key: 'LYTotalGroupNum',
onCell: (r) => ({
style: { backgroundColor: '#9FB40F' + '1A' },
}),
},
{
title: '总人数',
dataIndex: 'LYTotalPersonNum',
key: 'LYTotalPersonNum',
onCell: (r) => ({
style: { backgroundColor: '#9FB40F' + '1A' },
}),
},
{
title: '总报价',
dataIndex: 'LYPreTotalPrice',
key: 'LYPreTotalPrice',
onCell: (r) => ({
style: { backgroundColor: '#9FB40F' + '1A' },
}),
},
{
title: '总利润',
dataIndex: 'LYPreTotalProfit',
key: 'LYPreTotalProfit',
onCell: (r) => ({
style: { backgroundColor: '#9FB40F' + '1A' },
}),
},
{
title: '利润率',
dataIndex: 'LYPreProfitRate',
key: 'LYPreProfitRate',
render: (text, r) => <RenderVSDataCell showDiffData={false} data1={comm.fixTo2Decimals(text * 100)} data2={r.diff?.LYPreProfitRate} dataSuffix="%" />,
onCell: (r) => ({
style: { backgroundColor: '#9FB40F' + '1A' },
}),
},
],
},
];
const columnsTotal = (toJS(columns)).slice(1);
const tableProps = {
size: 'small',
pagination: false,
scroll: { x: 100 * 7 },
loading,
};
return (
<>
<div>
<Row gutter={16} className={toJS(searchFormStore.siderBroken) ? '' : 'sticky-top'}>
<Col className="gutter-row" span={24}>
<SearchForm
defaultValue={{
initialValue: {
...toJS(searchFormStore.formValues),
...searchValues,
},
//
shows: ['WebCode', 'IncludeTickets', 'DepartmentList', 'dates', 'operator', 'lineClass'],
fieldProps: {
DepartmentList: { show_all: false, mode: 'multiple' },
WebCode: { show_all: false, mode: 'multiple' },
dates: { hide_vs: true },
operator: { param: {} },
},
}}
onSubmit={(_err, obj, form, str) => {
setSearchValues(obj, form);
getMeetingDataSales({ ...obj, IsDetail: 0 });
getMeetingDataSales({ ...obj, IsDetail: 1 });
// onTabChange(activeTab);
}}
/>
</Col>
</Row>
<Row gutter={[16, { sm: 16, lg: 32 }]}>
<Col span={24}>
<h2 key={'t1'}>业绩</h2>
<Table
sticky={{ offsetHeader: 56 }}
key={`table_to_xlsx_total`}
{...tableProps}
columns={columnsTotal}
loading={typeLoading}
dataSource={salesDataTotal}
bordered
components={{ body: { cell: TdCell } }}
/>
<h2 key={'t2'}>顾问×页面类型业绩</h2>
<Table
sticky={{ offsetHeader: 56 }}
key={`table_to_xlsx_lineclass`}
{...tableProps}
loading={typeLoading}
dataSource={tableData}
columns={columns}
bordered
components={{ body: { cell: TdCell } }}
/>
</Col>
</Row>
</div>
</>
);
});
export default MeetingSales;

@ -0,0 +1,122 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { groupsMappedByCode } from '../libs/ht';
import { fetchJSON } from '@haina/utils-request';
import { HT_HOST } from '../config';
import { groupBy, isEmpty, } from '@haina/utils-commons';
/**
* 顾问业绩 (例会数据)
*/
const defaultParams = { OrderType: 227001, IsDYTJ: '', IncludeTickets: 1, Team: '', WebCodeFX: '', CusType: '', OldCus: 0, lineClass: '', IsDetail: -1 };
export const fetchMeetingDataSales = async (params) => {
const { errcode, errmsg, result } = await fetchJSON(HT_HOST + '/service-web/QueryData/WLCountForMeetingNew', {
...defaultParams,
...params,
WebCode: (params.WebCode || '').replace('all', ''),
OPI_SN: params.operator || '',
});
const ret =
errcode !== 0
? []
: (result || [])
// .filter((ele) =>
// Object.keys(ele)
// .filter((col) => !['OPI_SN', 'OPI_Name', 'COLI_LineClass', 'LineClass', 'vi'].includes(col))
// .some((col) => !isEmpty(ele[col])),
// )
.map((ele) => ({ ...ele, key: `${ele.OPI_SN}_${ele.COLI_LineClass || ''}_${ele.vi || ''}` }));
const byOPI = groupBy(structuredClone(ret), 'OPI_SN');
const OPIValue = Object.keys(byOPI).reduce((r, opisn) => {
byOPI[opisn].forEach((ele, xi) => {
ele.rowSpan = xi === 0 ? byOPI[opisn].length : 0;
});
return [...r, ...byOPI[opisn]];
}, []);
const byLineClass = groupBy(structuredClone(ret), 'LineClass');
const LineClassValue = Object.keys(byLineClass).reduce((r, gkey) => {
byLineClass[gkey].forEach((ele, xi) => {
ele.rowSpan = xi === 0 ? byLineClass[gkey].length : 0;
});
return [...r, ...byLineClass[gkey]];
}, []);
return { result: ret, OPIValue, LineClassValue };
};
/**
* --------------------------------------------------------------------------------------------------------
*/
const initialState = {
loading: false,
typeLoading: false,
searchValues: {
// DateType: { key: 'applyDate', label: '提交日期' },
WebCode: { key: 'all', label: '所有来源' },
IncludeTickets: { key: '1', label: '含门票' },
DepartmentList: groupsMappedByCode.GH, // { key: 'All', label: '所有来源' }, //
},
searchValuesToSub: {
// DateType: 'applyDate',
WebCode: 'all',
IncludeTickets: '1',
DepartmentList: -1, // -1: All
},
salesDataTotal: [],
salesData: [],
matrixData: {},
matrixtableMajorKey: 'opi',
matrixTableData: [],
// 二级页面
};
const useSalesInsightStore = create(
devtools(
immer((set, get) => ({
...initialState,
reset: () => set(initialState),
setLoading: (loading) => set({ loading }),
setTypeLoading: (typeLoading) => set({ typeLoading }),
setSearchValues: (obj, values) => set((state) => ({ searchValues: values, searchValuesToSub: obj })),
setSearchValuesToSub: (values) => set((state) => ({ searchValuesToSub: values })),
setActiveTab: (tab) => set({ activeTab: tab }),
// site effects
getMeetingDataSales: async (params) => {
const { setTypeLoading, } = get();
setTypeLoading(true);
try {
const res = await fetchMeetingDataSales(params);
if (params.IsDetail === 1) {
set({ matrixData: res, matrixTableData: res.OPIValue, matrixtableMajorKey: 'opi', salesData: res.result });
}
else {
set({ salesDataTotal: res.result });
}
} catch (error) {
console.error(error);
} finally {
setTypeLoading(false);
}
},
onMatrixChange: () => {
const { matrixtableMajorKey, matrixData } = get();
const newKey = matrixtableMajorKey === 'opi' ? 'lineclass' : 'opi';
const dataKey = matrixtableMajorKey === 'opi' ? 'LineClassValue' : 'OPIValue' ;
set({ matrixtableMajorKey: newKey, matrixTableData: matrixData?.[dataKey] });
},
// sub
})),
{ name: 'SalesInsight' }
)
);
export default useSalesInsightStore;
Loading…
Cancel
Save