diff --git a/src/charts/Customer_care_regular.jsx b/src/charts/Customer_care_regular.jsx index 44fbdeb..0a462cb 100644 --- a/src/charts/Customer_care_regular.jsx +++ b/src/charts/Customer_care_regular.jsx @@ -6,13 +6,24 @@ import { stores_Context } from '../config'; import { observer } from 'mobx-react'; import SearchForm from './../components/search/SearchForm'; import LineWithAvg from '../components/LineWithAvg'; -import { flow } from 'mobx'; -import { TableExportBtn } from '../components/Data'; +import { toJS } from 'mobx'; +import { TableExportBtn, VSDataTag, RenderVSDataCell } from '../components/Data'; + +import useCustomerRelationsStore from '../zustand/CustomerRelations'; +import { useShallow } from 'zustand/shallow'; +import { fixTo2Decimals } from '@haina/utils-commons'; const Customer_care_regular = () => { - const { orders_store, date_picker_store, customer_store } = useContext(stores_Context); + const { date_picker_store, customer_store } = useContext(stores_Context); const regular_data = customer_store.regular_data; + const [loading, loading2, searchValues, ] = useCustomerRelationsStore(useShallow((state) => [state.loading, state.loading2, state.searchValues,])); + const [setSearchValues] = useCustomerRelationsStore(useShallow((state) => [state.setSearchValues])); + + const [regular] = useCustomerRelationsStore(useShallow((state) => [state.regular])); + + const getRegularCustomer = useCustomerRelationsStore((state) => state.getRegularCustomer); + const columns = [ { title: '订单号', @@ -205,13 +216,14 @@ const Customer_care_regular = () => { return (
- + { }} onSubmit={async (_err, obj, form, str) => { customer_store.setSearchValues(obj, form, 'regular_data'); - regular_data.data_compare=[]; - if (obj.DateDiff1 && obj.DateDiff2){ - regular_data.isCompareLine=true; - regular_data.showCompareSum=true; - await customer_store.regular_customer_order(); - customer_store.regular_customer_order(false,true); - // customer_store.regular_customer_order(true,false,true); - // customer_store.regular_customer_order(true,true,true); - } - else{ - regular_data.isCompareLine=false; - regular_data.showCompareSum=false; - customer_store.regular_customer_order(); - // customer_store.regular_customer_order(true); - } + setSearchValues(obj, form); + getRegularCustomer({ ...obj, IsDetail: 0 }); + getRegularCustomer({ ...obj, IsDetail: 1 }); + + // regular_data.data_compare=[]; + // if (obj.DateDiff1 && obj.DateDiff2){ + // regular_data.isCompareLine=true; + // regular_data.showCompareSum=true; + // await customer_store.regular_customer_order(); + // customer_store.regular_customer_order(false,true); + // // customer_store.regular_customer_order(true,false,true); + // customer_store.regular_customer_order(true,true,true); + // } + // else{ + // regular_data.isCompareLine=false; + // regular_data.showCompareSum=false; + // customer_store.regular_customer_order(); + // customer_store.regular_customer_order(true); + // } }} /> @@ -247,8 +263,8 @@ const Customer_care_regular = () => { { title: () => ( <> 订单数{' '} - + @@ -268,10 +284,14 @@ const Customer_care_regular = () => { key: 'OrderNum', render: (text, record, index) => ( <> - {text}   - { - {index === 0 && regular_data.total_data_tips!=='' && } - } + + {/* {text} */} +    + { + + {index === 0 && regular.total_data_tips !== '' && } + + } ), }, @@ -279,46 +299,49 @@ const Customer_care_regular = () => { title: '订单数占比', dataIndex: 'OrderRate', key: 'OrderRate', - render: (text) => typeof text === 'number'?{parseFloat((text * 100).toFixed(2))}%:text, + render: (text, record) => }, { title: '订单数占比(市场)', dataIndex: 'OrderRate2', key: 'OrderRate2', - render: (text) => typeof text === 'number'?{parseFloat((text * 100).toFixed(2))}%:text, + render: (text, record) => }, { title: '成行数', dataIndex: 'SUCOrderNum', key: 'SUCOrderNum', + render: (text, record) => }, { title: '成行率', dataIndex: 'SUCRate', key: 'SUCRate', - render: (text) => typeof text === 'number'?{Math.round(text * 100)}%:text, + render: (text, record) => }, { title: '毛利', dataIndex: 'ML', key: 'ML', + render: (text, record) => }, { title: '毛利占比', dataIndex: 'OrderMLRate', key: 'OrderMLRate', - render: (text) => typeof text === 'number'?{parseFloat((text * 100).toFixed(2))}%:text, + render: (text, record) => }, { title: '毛利占比(市场)', dataIndex: 'OrderMLRate2', key: 'OrderMLRate2', - render: (text) => typeof text === 'number'?{parseFloat((text * 100).toFixed(2))}%:text, + render: (text, record) => }, { title: '人数(含成人+儿童)', dataIndex: 'PersonNum', key: 'PersonNum', + render: (text, record) => }, ]} size="small" @@ -327,65 +350,38 @@ const Customer_care_regular = () => { /> - -
- <> - -
- 维护中, 暂不可用, 敬请期待 -
-
- - +
+
+ + - - - { - const wb = utils.table_to_book(document.getElementById('table_to_xlsx').getElementsByTagName('table')[0]); - writeFileXLSX(wb, '老客户.xlsx'); - }} - > - 导出下表 - - - -
record.COLI_ID} - /> - - + + + { + const wb = utils.table_to_book(document.getElementById('table_to_xlsx').getElementsByTagName('table')[0]); + writeFileXLSX(wb, '老客户.xlsx'); + }} + > + 导出下表 + + + +
record.COLI_ID} /> + + diff --git a/src/components/Data.jsx b/src/components/Data.jsx index 1cd4732..00c446c 100644 --- a/src/components/Data.jsx +++ b/src/components/Data.jsx @@ -58,6 +58,23 @@ export const VSDataTag = ({ diffPercent=0, diffData=0, data1=0, data2=0, dataSuf ); }; +/** + * 表格中显示数据对比 + * + * @property {boolean | undefined} [showDiffData=false] + * @property {number} [diffPercent=0] + * @property {number} diffData + * @property {number} data1 + * @property {number} data2 + * @property {string} dataSuffix + */ +export const RenderVSDataCell = ({ showDiffData=false, data1, data2, dataSuffix = '', ...props }) => { + if (showDiffData) { + return ; + } + return
{data1}{dataSuffix}
; +}; + /** * 导出表格数据存为xlsx * @property label 文件名字 diff --git a/src/zustand/CustomerRelations.js b/src/zustand/CustomerRelations.js new file mode 100644 index 0000000..e832d61 --- /dev/null +++ b/src/zustand/CustomerRelations.js @@ -0,0 +1,171 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { immer } from 'zustand/middleware/immer'; +import { fetchJSON } from '@haina/utils-request'; +import { HT_HOST } from '../config'; +import { formatDate, isEmpty, sortBy } from '@haina/utils-commons'; +import { groupsMappedByKey, sitesMappedByCode, pivotBy } from './../libs/ht'; + +const defaultParams = {}; + +export const fetchRegularCustomer = async (params) => { + const _params = { + Website: params.WebCode || '', // CHT,AH,JH,GH,ZWQD,GH_ZWQD_HW,GHKYZG,GHKYHW,HTravel + DEI_SNList: params.DepartmentList || '', // 1,2,28,7 + ApplydateCheck: params.DateType === 'applyDate' ? 1 : 0, // + EntrancedateCheck: params.DateType === 'startDate' ? 1 : 0, // + ConfirmDateCheck: params.DateType === 'confirmDate' ? 1 : 0, // + ApplydateStart: params.Date1 || '', + ApplydateEnd: params.Date2 || '', + EntrancedateStart: params.Date1 || '', + EntrancedateEnd: params.Date2 || '', + ConfirmdateStart: params.Date1 || '', + ConfirmdateEnd: params.Date2 || '', + IsDetail: '', // + IncludeTickets: '', // + ...params, + }; + const { WebCode, DepartmentList, DateType, Date1, Date2, ...readyParams } = _params; + const [result1, result2] = await Promise.all([ + fetchJSON(HT_HOST + '/service-tourdesign/RegularCusOrder', { ...defaultParams, ...readyParams }), + ...(params.DateDiff1 && params.IsDetail === 0 + ? [ + fetchJSON(HT_HOST + '/service-tourdesign/RegularCusOrder', { + ...defaultParams, + ...readyParams, + ApplydateStart: params.DateDiff1 || '', + ApplydateEnd: params.DateDiff2 || '', + EntrancedateStart: params.DateDiff1 || '', + EntrancedateEnd: params.DateDiff2 || '', + ConfirmdateStart: params.DateDiff1 || '', + ConfirmdateEnd: params.DateDiff2 || '', + }), + ] + : []), + ]); + if (params.IsDetail === 1) { + return { result1, result2 }; + } + const ret = {}; + const result1Mapped = result1.reduce((r, v) => ({ ...r, [v.ItemName]: v }), {}); + const allKeys = [...new Set([...result1.map((e) => e.ItemName), ...result2.map((e) => e.ItemName)])]; + const result2Mapped = result2.reduce((r, v) => ({ ...r, [v.ItemName]: v }), {}); + const x = {}; + allKeys.forEach((key) => { + x[key] = { ...(result1Mapped?.[key] || { ItemName: key }), diff: result2Mapped[key] || {} }; + }); + ret.result1 = Object.values(x); + console.log(ret); + return { result1: ret.result1 }; // { result1, result2 }; +}; + +// 老客户: 日期对应的数据字段 +const dateTypeDataHelper = { + applyDate: 'SumOrder', + startDate: 'ConfirmOrder', + confirmDate: 'ConfirmOrder', +}; + +/** + * 构建 老客户系列数据 + * * 用明细数据计算 + * @param {[]} details + * @param {string} pivotByOrder + * @param {string} pivotByDate + * @returns + */ +const buildSeriesDataFromDetails = (details, pivotByOrder, pivotByDate) => { + const dataDetail = (details || []).map((ele) => ({ + ...ele, + key: ele.COLI_ID, + orderState: ele.OrderState, + applyDate: formatDate(new Date(ele.COLI_ApplyDate)), + startDate: ele.COLI_OrderStartDate, + confirmDate: formatDate(new Date(ele.COLI_ConfirmDate)), + })); + const { data: IsOldData } = pivotBy( + dataDetail.filter((ele) => ele.COLI_IsOld === '是'), + [['COLI_IsOld'], [], pivotByDate] + ); + const { data: isCusCommendData } = pivotBy( + dataDetail.filter((ele) => ele.COLI_IsCusCommend === '是'), + [['COLI_IsCusCommend'], [], pivotByDate] + ); + // console.log('IsOldData====', IsOldData, '\nisCusCommend', isCusCommendData); + // 合并成两个系列 + const seriesData = [] + .concat( + IsOldData.map((ele) => ({ ...ele, _ylabel: '老客户' })), + isCusCommendData.map((ele) => ({ ...ele, _ylabel: '老客户推荐' })) + ) + .sort(sortBy(pivotByDate)); + return seriesData; +}; + +/** + * -------------------------------------------------------------------------------------------------------- + */ +const initialState = { + loading: false, + loading2: false, + searchValues: { + DepartmentList: ['1', '2', '28', '7'].map((kk) => groupsMappedByKey[kk]), + WebCode: ['CHT', 'AH', 'JH', 'GH', 'ZWQD', 'GH_ZWQD_HW', 'GHKYZG', 'GHKYHW', 'HTravel'].map((kk) => sitesMappedByCode[kk]), + DateType: { key: 'applyDate', label: '提交日期' }, + IncludeTickets: { key: '0', label: '不含门票' }, + }, + searchValuesToSub: {}, + + regular: { data: [], details: [], total_data_tips: '', pivotData: [], pivotY: 'SumOrder', pivotX: '' }, +}; + +const useCustomerRelationsStore = create( + devtools( + immer((set, get) => ({ + ...initialState, + reset: () => set(initialState), + + setLoading: (loading) => set({ loading }), + setLoading2: (loading2) => set({ loading2 }), + setSearchValues: (obj, values) => set((state) => ({ searchValues: values, searchValuesToSub: obj })), + setSearchValuesToSub: (values) => set((state) => ({ searchValuesToSub: values })), + + // 获取数据 --------------------------------------------------------------------------------------------------- + + // 老客户 + getRegularCustomer: async (params) => { + const { setLoading, setLoading2 } = get(); + const { IsDetail } = params; + setLoading(true); + setLoading2(IsDetail === 1); + const pivotByOrder = dateTypeDataHelper[params.DateType]; + const pivotByDate = params.DateType; + try { + const {result1, result2} = await fetchRegularCustomer(params); + set((state) => { + if (IsDetail === 1) { + state.regular.details = result1; + + const dump_l = (result1 || []).filter((ele) => ele.COLI_IsOld !== '' && ele.COLI_IsCusCommend !== '').length; + state.regular.total_data_tips = dump_l > 0 ? `包含 ${dump_l} 条同时勾选的数据` : ''; + /** 使用明细数据画图 */ + const seriesData = buildSeriesDataFromDetails(result1, pivotByOrder, pivotByDate); + state.regular.pivotData = seriesData; + state.regular.pivotX = pivotByDate; + state.regular.pivotY = pivotByOrder; + } else { + state.regular.data = result1; + } + }); + } catch (error) { + } finally { + setLoading(false); + IsDetail === 1 && setLoading2(false); + } + }, + })), + { name: 'CustomerRelations' } + ) +); + +export default useCustomerRelationsStore;