diff --git a/src/components/Data.jsx b/src/components/Data.jsx
new file mode 100644
index 0000000..f907dd2
--- /dev/null
+++ b/src/components/Data.jsx
@@ -0,0 +1,17 @@
+import { Tag } from 'antd';
+import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
+
+export const VSTag = (diffPercent, diffData, data1, data2) => {
+ const CaretIcon = parseInt(diffPercent) < 0 ? CaretDownOutlined : CaretUpOutlined;
+ const tagColor = parseInt(diffPercent) < 0 ? "gold" : "lime";
+ return parseInt(diffPercent) === 0 ? "-" : (
+
+
+ {data1} vs {data2}
+
+ } color={tagColor}>
+ {diffPercent} {diffData}
+
+
+ );
+};
diff --git a/src/components/DateGroupRadio/date.js b/src/components/DateGroupRadio/date.js
new file mode 100644
index 0000000..904f5d5
--- /dev/null
+++ b/src/components/DateGroupRadio/date.js
@@ -0,0 +1,132 @@
+import moment from 'moment';
+
+export const datePartOptions = [
+ { label: '日', value: 'day' },
+ { label: '周', value: 'week' },
+ { label: '月', value: 'month' },
+ { label: '季', value: 'quarter' },
+ { label: '年', value: 'year' },
+];
+export const datePartMethod = {
+ 'day': (date) => {
+ return { 'dateKey': date, 'groupKey': date, date };
+ },
+ 'week': (date, ...args) => {
+ const dateO = moment(date);
+ const year = dateO.weekYear();
+ const week = dateO.week();
+ // const weekOfMonth = dateO.week() - moment(date).startOf('month').week() + 1;
+ const key = `W${String(week).padStart(2, '0')}`;
+ const dateKey = `${year}-W${String(week).padStart(2, '0')}`;
+ return { dateKey, 'groupKey': dateKey, date };
+ },
+ 'quarter': (date) => {
+ const dateO = moment(date);
+ // const key = dateO.format('YYYY-Q');
+ const key = `${dateO.year()}-${String(dateO.quarter()).padStart(2, 'Q')}`;
+ const dateKey = `${dateO.year()}-${String(dateO.quarter()).padStart(2, 'Q')}`;
+ return { dateKey, 'groupKey': key, date };
+ },
+ 'month': (date) => {
+ const dateO = moment(date);
+ const key = dateO.format('YYYY-MM');
+ const dateKey = dateO.format('YYYY-MM');
+ return { dateKey, 'groupKey': key, date };
+ },
+ 'year': (date) => {
+ const dateO = moment(date);
+ const key = dateO.format('YYYY');
+ const dateKey = dateO.format('YYYY');
+ return { dateKey, 'groupKey': key, date };
+ },
+};
+
+/**
+ *
+ * @param {Array} data 结果数组
+ * @param {*} dateType 切换的日期类型
+ * @param {Object} mapper 映射结果字段
+ * @returns {Object} { data, avg }
+ * * data: 转换后的数组
+ * * avg: 平均值
+ */
+export const parseDateType = (data, dateType = 'day', { dateKey, valueKey, seriesKey, _f }) => {
+ const _calcF = {
+ 'avg': (sum, len) => sum / len,
+ };
+ const seriesDataMapped = data.reduce((r, v) => {
+ const _k = v[seriesKey];
+ (r[_k] || (r[_k] = [])).push(v);
+ return r;
+ }, {});
+ const everySeries = Object.keys(seriesDataMapped).reduce((rs, _series) => {
+ const e = seriesDataMapped[_series].reduce((r, v) => {
+ const datePart = datePartMethod[dateType](v[dateKey]);
+ const mergeKey = `${datePart.groupKey}@${_series}`;
+ (r[mergeKey] || (r[mergeKey] = [])).push({ ...v, 'dateKey': datePart.dateKey, 'groupKey': datePart.groupKey, datePart });
+ return r;
+ }, {});
+ return { ...rs, ...e };
+ }, {});
+ const resultKeys = Object.keys(everySeries);
+ resultKeys.sort();
+ const dateArr = [];
+ const groupSum = resultKeys.reduce((a, key) => {
+ const [_dateKey, _seriesKey] = key.split('@');
+ dateArr.push(_dateKey);
+ const containDate = everySeries[key].map((ele) => ele.datePart.date);
+ const containDateM = [...new Set(containDate)].map((ele) => moment(ele));
+ const min = moment.min(containDateM).format('YYYY-MM-DD');
+ const max = moment.max(containDateM).format('YYYY-MM-DD');
+ const dateRangeStr = min === max ? min : `${min}~${max}`;
+ const dateRange = [min, max];
+ const summaryVal = everySeries[key].reduce((rows, row) => rows + row[valueKey], 0);
+ const retValue = _f === 'sum' ? summaryVal : _calcF(summaryVal, everySeries[key].length);
+ a.push({ groupKey: key, value: retValue, dateKey: dateRangeStr, dateRange, containDate, [seriesKey]: _seriesKey, [dateKey]: _dateKey });
+ return a;
+ }, []);
+ const avgDiv = [...new Set(dateArr)].length;
+ const avgVal = groupSum.length !== 0 ? groupSum.reduce((s, c) => s + c.value, 0) / avgDiv : 0;
+ return { data: groupSum, avgVal };
+};
+
+/**
+ *
+ * @param {Array} dataRaw 结果数组
+ * @param {*} dateGroup 切换的日期类型, radio onchange 的值
+ * @param {Object} dataMapper 映射数据集字段 { data1, data2 }
+ * @param {Object} mapper 映射结果字段 { dateKey, valueKey, _f }
+ * * dateKey: 日期. 'ApplyDate'
+ * * valueKey: 结果. 'orderCount'
+ * * seriesKey: 序列. 'WebCode'
+ * * _f: 计算方法. 'sum' | 'avg
+ * @author LYT
+ */
+export const resultDataCb = (dataRaw, dateGroup, { data1, data2 }, fieldMapper, cb) => {
+ const _data1 = data1 ? dataRaw[data1] : dataRaw;
+ const _data2 = data2 ? dataRaw[data2] : [];
+ const parse1 = parseDateType(_data1, dateGroup, fieldMapper);
+ const parseData1 = parse1.data.map((ele) => ({
+ [fieldMapper.dateKey]: ele[fieldMapper.dateKey],
+ [fieldMapper.valueKey]: ele.value,
+ [fieldMapper.seriesKey]: ele[fieldMapper.seriesKey],
+ groups: _data1[0].groups,
+ dateKey: ele.dateKey,
+ dateRange: ele.dateRange,
+ dateGroup: ele[fieldMapper.dateKey],
+ }));
+ const parse2 = parseDateType(_data2, dateGroup, fieldMapper);
+ const parseData2 = parse2.data.map((ele) => ({
+ [fieldMapper.dateKey]: ele.groupKey,
+ [fieldMapper.valueKey]: ele.value,
+ groups: _data2[0].groups,
+ dateKey: ele.dateKey,
+ dateRange: ele.dateRange,
+ dateGroup: ele[fieldMapper.dateKey],
+ }));
+ const useKeys = parseData1.map((ele) => ele[fieldMapper.dateKey]);
+ const reindecData2 = parseData2.map((ele, index) => ({ ...ele, [fieldMapper.dateKey]: useKeys[index] || `X.${ele[fieldMapper.dateKey]}`, dateKey: ele.dateKey }));
+ const retData = [].concat(parseData1, reindecData2);
+ const avg1 = parse1.avgVal;
+ cb(dateGroup, retData, avg1);
+};
diff --git a/src/components/DateGroupRadio/index.jsx b/src/components/DateGroupRadio/index.jsx
new file mode 100644
index 0000000..e8b1fd2
--- /dev/null
+++ b/src/components/DateGroupRadio/index.jsx
@@ -0,0 +1,12 @@
+import { Radio } from 'antd';
+import { observer } from 'mobx-react';
+import { datePartOptions, resultDataCb } from './date';
+
+export default observer((props) => {
+ const { visible, dataRaw, dataMapper, fieldMapper, onChange, ...extProps } = props;
+ const _dataMapper = dataMapper || { 'data1': null, data2: null };
+ const handleChange = ({ target: { value } }) => {
+ resultDataCb(dataRaw, value, _dataMapper, fieldMapper, onChange);
+ };
+ return <>{visible !== false ? : null}>;
+});