Merge branch 'main' into feature/hotel-cruise
# Conflicts: # src/stores/CustomerServices.jsmain
commit
a32943fffb
@ -0,0 +1,168 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Row, Col, Spin } from 'antd';
|
||||||
|
import { Line } from '@ant-design/plots';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { dataFieldAlias } from '../libs/ht';
|
||||||
|
import DateGroupRadio from '../components/DateGroupRadio';
|
||||||
|
import { cloneDeep, groupBy, sortBy } from '../utils/commons';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { dataSource: rawData, showAVG, showSUM, loading, ...config } = props;
|
||||||
|
const { xField, yField, yFieldAlias, seriesField } = config;
|
||||||
|
|
||||||
|
const [dataBeforeXChange, setDataBeforeXChange] = useState([]);
|
||||||
|
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
const [sumSeries, setSumSeries] = useState([]);
|
||||||
|
|
||||||
|
const line_config = {
|
||||||
|
// data: dataSource,
|
||||||
|
padding: 'auto',
|
||||||
|
xField,
|
||||||
|
yField,
|
||||||
|
seriesField,
|
||||||
|
// seriesField: 'rowLabel',
|
||||||
|
// xAxis: {
|
||||||
|
// type: 'timeCat',
|
||||||
|
// },
|
||||||
|
yAxis: {
|
||||||
|
min: 0,
|
||||||
|
maxTickInterval: 5,
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
...cloneDeep(dataFieldAlias),
|
||||||
|
},
|
||||||
|
// smooth: true,
|
||||||
|
label: {}, // 显示标签
|
||||||
|
legend: {
|
||||||
|
position: 'right-top',
|
||||||
|
// title: {
|
||||||
|
// text: '总合计 ' + dataSource.reduce((a, b) => a + b.SumOrder, 0),
|
||||||
|
// },
|
||||||
|
itemMarginBottom: 12, // 垂直间距
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
customItems: (originalItems) => {
|
||||||
|
return originalItems
|
||||||
|
.map((ele) => ({ ...ele, valueR: ele.data[yField] }))
|
||||||
|
.sort(sortBy('valueR'))
|
||||||
|
.reverse();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const [lineConfig, setLineConfig] = useState(cloneDeep(line_config));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resetX();
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [rawData]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lineChartX === 'day') {
|
||||||
|
setDataBeforeXChange(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [dataSource]);
|
||||||
|
|
||||||
|
// 日月年切换
|
||||||
|
const [lineChartX, setLineChartX] = useState('day');
|
||||||
|
const orderCountDataMapper = { data1: 'data1', data2: undefined };
|
||||||
|
const orderCountDataFieldMapper = { 'dateKey': xField, 'valueKey': yField, 'seriesKey': seriesField, _f: 'sum' };
|
||||||
|
const resetX = () => {
|
||||||
|
setLineChartX('day');
|
||||||
|
setDataSource(rawData);
|
||||||
|
setDataBeforeXChange(rawData);
|
||||||
|
// 初始化`平均`线, `总计`线
|
||||||
|
const byDays = groupBy(rawData, xField);
|
||||||
|
const sumY = rawData.reduce((a, b) => a + b[yField], 0);
|
||||||
|
const avgVal = Math.round(sumY / (Object.keys(byDays).length));
|
||||||
|
const avgLine = [
|
||||||
|
{ type: 'text', position: ['start', avgVal], content: avgVal, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||||
|
{ type: 'line', start: [-10, avgVal], end: ['max', avgVal], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||||
|
];
|
||||||
|
setLineConfig({ ...lineConfig, yField, xField, annotations: avgLine });
|
||||||
|
if (showSUM) {
|
||||||
|
const _sumLine = Object.keys(byDays).reduce((r, _d) => {
|
||||||
|
const summaryVal = byDays[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
r.push({ ...byDays[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
// console.log(_sumLine.map((ele) => ele[yField]));
|
||||||
|
setSumSeries(_sumLine);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeXDateFieldGroup = (value, data, avg1) => {
|
||||||
|
// console.log(value, data, avg1);
|
||||||
|
const _sumLine = [];
|
||||||
|
const { xField, yField, seriesField } = lineConfig;
|
||||||
|
const groupByDate = data.reduce((r, v) => {
|
||||||
|
(r[v[xField]] || (r[v[xField]] = [])).push(v);
|
||||||
|
return r;
|
||||||
|
}, {});
|
||||||
|
// console.log(groupByDate);
|
||||||
|
const _data = Object.keys(groupByDate).reduce((r, _d) => {
|
||||||
|
const summaryVal = groupByDate[_d].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
_sumLine.push({ ...groupByDate[_d][0], [yField]: summaryVal, [seriesField]: '总计' });
|
||||||
|
|
||||||
|
const xAxisGroup = groupByDate[_d].reduce((a, v) => {
|
||||||
|
(a[v[seriesField]] || (a[v[seriesField]] = [])).push(v);
|
||||||
|
return a;
|
||||||
|
}, {});
|
||||||
|
// console.log(xAxisGroup);
|
||||||
|
Object.keys(xAxisGroup).map((_group) => {
|
||||||
|
const summaryVal = xAxisGroup[_group].reduce((rows, row) => rows + row[yField], 0);
|
||||||
|
r.push({ ...xAxisGroup[_group][0], [yField]: summaryVal, });
|
||||||
|
return _group;
|
||||||
|
});
|
||||||
|
return r;
|
||||||
|
}, []);
|
||||||
|
// const _sum = Object.values(groupBy(_data, 'dateGroup')).reduce((ac, b) => ({...b, [yField]: 0}), {});
|
||||||
|
// console.log(xField, avg1);
|
||||||
|
// console.log('date source=====', _data);
|
||||||
|
setLineChartX(value);
|
||||||
|
setDataSource(_data);
|
||||||
|
if (showSUM) {
|
||||||
|
setSumSeries(_sumLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setAvgLine1(avg1);
|
||||||
|
const avg1Int = Math.round(avg1);
|
||||||
|
const mergedConfig = { ...lineConfig,
|
||||||
|
annotations: [
|
||||||
|
{ type: 'text', position: ['start', avg1Int], content: avg1Int, offsetX: -15, style: { fill: '#F4664A', textBaseline: 'bottom' } },
|
||||||
|
{ type: 'line', start: [-10, avg1Int], end: ['max', avg1Int], style: { stroke: '#F4664A', lineDash: [2, 2] } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
// console.log(mergedConfig);
|
||||||
|
setLineConfig(mergedConfig);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<Row gutter={16} justify={'space-between'} className="mb-1">
|
||||||
|
<Col flex={'auto'}>
|
||||||
|
<h3>
|
||||||
|
走势: <span style={{ fontSize: 'smaller' }}>{dataFieldAlias[lineConfig.yField].label}</span>
|
||||||
|
</h3>
|
||||||
|
</Col>
|
||||||
|
<Col style={{ textAlign: 'right' }} align={'end'}>
|
||||||
|
<DateGroupRadio
|
||||||
|
visible={true}
|
||||||
|
dataRaw={{ data1: dataBeforeXChange }}
|
||||||
|
onChange={onChangeXDateFieldGroup}
|
||||||
|
value={lineChartX}
|
||||||
|
dataMapper={orderCountDataMapper}
|
||||||
|
fieldMapper={orderCountDataFieldMapper}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Line {...lineConfig} data={[].concat(dataSource, sumSeries)} />
|
||||||
|
</Spin>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
});
|
@ -1,85 +0,0 @@
|
|||||||
import React, { Children, useContext, useState } 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 SearchForm from './../components/search/SearchForm';
|
|
||||||
|
|
||||||
export default observer((props) => {
|
|
||||||
const { sale_store, date_picker_store: searchFormStore } = useContext(stores_Context);
|
|
||||||
|
|
||||||
const { formValues, siderBroken } = searchFormStore;
|
|
||||||
|
|
||||||
const activityTableProps = {
|
|
||||||
columns: [
|
|
||||||
{ title: '', dataIndex: 'op', key: 'op' }, // 维度: 顾问
|
|
||||||
{
|
|
||||||
title: '顾问动作',
|
|
||||||
key: 'date',
|
|
||||||
children: [
|
|
||||||
{ title: '首次响应率24H', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '48H内报价率', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '一次报价率', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '二次报价率', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '>50条会话', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '违规数', dataIndex: 'action', key: 'action' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '客人回复',
|
|
||||||
key: 'department',
|
|
||||||
children: [
|
|
||||||
{ title: '首次回复率24H', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '48H内报价回复率', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '一次报价回复率', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '二次报价回复率', dataIndex: 'action', key: 'action' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const riskTableProps = {
|
|
||||||
columns: [
|
|
||||||
{ title: '', dataIndex: 'date', key: 'date' }, // 维度
|
|
||||||
{ title: '>24H回复', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '首次报价周期>48h', dataIndex: 'action', key: 'action' },
|
|
||||||
{ title: '报价次数<1次', dataIndex: 'action' },
|
|
||||||
{ title: '报价次数<2次', dataIndex: 'action' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
|
||||||
<Col md={24} lg={24} xxl={24}>
|
|
||||||
<SearchForm
|
|
||||||
defaultValue={{
|
|
||||||
initialValue: {
|
|
||||||
...formValues,
|
|
||||||
...sale_store.searchValues,
|
|
||||||
},
|
|
||||||
shows: ['DepartmentList', 'WebCode', 'dates'],
|
|
||||||
fieldProps: {
|
|
||||||
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
|
|
||||||
WebCode: { show_all: false, mode: 'multiple', col: 5 },
|
|
||||||
dates: { hide_vs: true },
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onSubmit={(_err, obj, form, str) => {
|
|
||||||
sale_store.setSearchValues(obj, form);
|
|
||||||
// pageRefresh(obj);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<section>
|
|
||||||
<Table {...activityTableProps} bordered />
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3>未成行订单</h3>
|
|
||||||
<Table {...riskTableProps} bordered />
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
@ -0,0 +1,257 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { stores_Context } from '../config';
|
||||||
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Row, Col, Table, Tooltip } from 'antd';
|
||||||
|
import SearchForm from '../components/search/SearchForm';
|
||||||
|
import { pick } from '../utils/commons';
|
||||||
|
|
||||||
|
const COLOR_SETS = [
|
||||||
|
"#FFFFFF",
|
||||||
|
// "#5B8FF9",
|
||||||
|
// "#FF6B3B",
|
||||||
|
"#9FB40F",
|
||||||
|
"#76523B",
|
||||||
|
"#DAD5B5",
|
||||||
|
"#E19348",
|
||||||
|
"#F383A2",
|
||||||
|
];
|
||||||
|
|
||||||
|
const transparentHex = '1A';
|
||||||
|
|
||||||
|
export default observer((props) => {
|
||||||
|
const { SalesCRMDataStore, date_picker_store: searchFormStore } = useContext(stores_Context);
|
||||||
|
|
||||||
|
// const { formValues, siderBroken } = searchFormStore;
|
||||||
|
const { formValues, formValuesToSub, siderBroken } = searchFormStore;
|
||||||
|
const _formValuesToSub = pick(formValuesToSub, ['DepartmentList', 'WebCode']);
|
||||||
|
const { resetData, process } = SalesCRMDataStore;
|
||||||
|
const operatorObjects = process.details.map((v) => ({ key: v.groupsKey, value: v.groupsKey, label: v.groupsLabel, text: v.groupsLabel }));
|
||||||
|
|
||||||
|
const pageRefresh = async (obj) => {
|
||||||
|
resetData();
|
||||||
|
Promise.allSettled([
|
||||||
|
SalesCRMDataStore.getProcessData({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
// ...subParam,
|
||||||
|
// groupType: 'overview',
|
||||||
|
groupType: 'dept',
|
||||||
|
groupDateType: '',
|
||||||
|
}),
|
||||||
|
SalesCRMDataStore.getProcessData({
|
||||||
|
...(obj || _formValuesToSub),
|
||||||
|
// ...subParam,
|
||||||
|
groupType: 'operator',
|
||||||
|
groupDateType: '',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableSorter = (a, b, colName) => (a.groupType !== 'operator' ? -1 : b.groupType !== 'operator' ? 0 : a[colName] - b[colName]);
|
||||||
|
const percentageRender = val => val ? `${val}%` : '';
|
||||||
|
|
||||||
|
const activityTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: { pageSize: 10, showSizeChanger: false },
|
||||||
|
size: 'small',
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||||
|
},
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||||
|
filterSearch: true,
|
||||||
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||||
|
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||||
|
}, // 维度: 顾问
|
||||||
|
{
|
||||||
|
title: '顾问动作',
|
||||||
|
key: 'date',
|
||||||
|
children: [
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
首次响应率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstTouch24', key: 'firstTouch24', render: percentageRender,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstTouch24'),
|
||||||
|
},
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价率{' '}
|
||||||
|
<Tooltip title="达到48H内报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstQuote48', key: 'firstQuote48',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstQuote48'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
一次报价率{' '}
|
||||||
|
<Tooltip title="首次报价的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'quote1', key: 'quote1',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'quote1'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
二次报价率{' '}
|
||||||
|
<Tooltip title="二次报价的订单数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'quote2', key: 'quote2',render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'quote2'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
>50条会话{' '}
|
||||||
|
<Tooltip title=">50条会话的订单数/总订单">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'turnsGT50', key: 'turnsGT50', render: percentageRender ,
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'turnsGT50'), },
|
||||||
|
{ title: () => (
|
||||||
|
<>
|
||||||
|
违规数{' '}
|
||||||
|
<Tooltip title="未遵循24H回复的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'violations', key: 'violations',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'violations'), },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '客人回复',
|
||||||
|
key: 'department',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
首次回复率24H{' '}
|
||||||
|
<Tooltip title="达到24H内回复的订单数/总订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'firstReply24', key: 'firstReply24',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'firstReply24'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
48H内报价回复率{' '}
|
||||||
|
<Tooltip title="48H内报价后的回复数/报价的订单数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote48', key: 'replyQuote48',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote48'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
一次报价回复率{' '}
|
||||||
|
<Tooltip title="一次报价后回复率/订单总数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote1', key: 'replyQuote1',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote1'), },
|
||||||
|
{
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
二次报价回复率{' '}
|
||||||
|
<Tooltip title="二次报价后回复数/一次报价后回复数">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
), dataIndex: 'replyQuote2', key: 'replyQuote2',
|
||||||
|
render: (_, r) => ({
|
||||||
|
props: { style: { backgroundColor: COLOR_SETS[1]+transparentHex } },
|
||||||
|
children: percentageRender(_),
|
||||||
|
}),
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'replyQuote2'), },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const riskTableProps = {
|
||||||
|
rowKey: 'groupsKey',
|
||||||
|
pagination: { pageSize: 10, showSizeChanger: false },
|
||||||
|
size: 'small',
|
||||||
|
rowClassName: (record, rowIndex) => {
|
||||||
|
return record.groupType === 'operator' ? '': 'ant-tag-blue';
|
||||||
|
},
|
||||||
|
showSorterTooltip: false,
|
||||||
|
columns: [
|
||||||
|
{ title: '', dataIndex: 'groupsLabel', key: 'groupsLabel', width: '6rem',
|
||||||
|
filterSearch: true,
|
||||||
|
filters: operatorObjects.sort((a, b) => a.text.localeCompare(b.text)),
|
||||||
|
onFilter: (value, record) => record.groupsKey === value || record.groupType !== 'operator',
|
||||||
|
// render: (text, record) => { // todo: 点击查看不成行订单明细
|
||||||
|
// },
|
||||||
|
}, // 维度: 顾问
|
||||||
|
{ title: '>24H回复', dataIndex: 'lostTouch24', key: 'lostTouch24',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostTouch24'),
|
||||||
|
},
|
||||||
|
{ title: '首次报价周期>48h', dataIndex: 'lostQuote48', key: 'lostQuote48',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote48'),
|
||||||
|
},
|
||||||
|
{ title: '报价次数<1次', dataIndex: 'lostQuote1', key: 'lostQuote1',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote1'),
|
||||||
|
},
|
||||||
|
{ title: '报价次数<2次', dataIndex: 'lostQuote2', key: 'lostQuote2',
|
||||||
|
sorter: (a, b) => tableSorter(a, b, 'lostQuote2'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={16} className={siderBroken ? '' : 'sticky-top'}>
|
||||||
|
<Col md={24} lg={24} xxl={24}>
|
||||||
|
<SearchForm
|
||||||
|
defaultValue={{
|
||||||
|
initialValue: {
|
||||||
|
...formValues,
|
||||||
|
...SalesCRMDataStore.searchValues,
|
||||||
|
},
|
||||||
|
shows: ['DepartmentList', 'WebCode', 'DateType', 'dates'],
|
||||||
|
fieldProps: {
|
||||||
|
DepartmentList: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
WebCode: { show_all: false, mode: 'multiple', col: 5 },
|
||||||
|
dates: { hide_vs: true },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onSubmit={(_err, obj, form, str) => {
|
||||||
|
SalesCRMDataStore.setSearchValues(obj, form);
|
||||||
|
pageRefresh(obj);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<section>
|
||||||
|
<Table {...activityTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>未成行订单 数量</h3>
|
||||||
|
<Table {...riskTableProps} bordered dataSource={[...process.dataSource, ...process.details]} loading={process.loading} sticky />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue