Commit d84fc0f8 authored by 刘梦焕's avatar 刘梦焕
parents bc1dfb66 0039d401
...@@ -40,4 +40,14 @@ export default { ...@@ -40,4 +40,14 @@ export default {
'/PandaCore': '/PandaCore', '/PandaCore': '/PandaCore',
}, },
}, },
'/PandaWater': {
target: proxyURL,
changeOrigin: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
pathRewrite: {
'/PandaWater': '/PandaWater',
},
},
}; };
...@@ -171,7 +171,7 @@ ...@@ -171,7 +171,7 @@
"js-sha256": "^0.10.1", "js-sha256": "^0.10.1",
"jszip": "^3.5.0", "jszip": "^3.5.0",
"less": "^3.13.1", "less": "^3.13.1",
"mathjs": "^12.2.0", "mathjs": "^11.2.0",
"mqtt-client": "^1.0.12", "mqtt-client": "^1.0.12",
"npm": "^9.6.4", "npm": "^9.6.4",
"optimize-css-assets-webpack-plugin": "^6.0.1", "optimize-css-assets-webpack-plugin": "^6.0.1",
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.13.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/basicreport@1.12.0...@wisdom-components/basicreport@1.13.0) (2023-12-26)
### Features
- 升级报表组件 ([deac1b8](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/commits/deac1b87d476c209e4ff20b17e546b35389eef93))
# [1.12.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/basicreport@1.11.1...@wisdom-components/basicreport@1.12.0) (2023-11-13) # [1.12.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/basicreport@1.11.1...@wisdom-components/basicreport@1.12.0) (2023-11-13)
### Features ### Features
......
{ {
"name": "@wisdom-components/basicreport", "name": "@wisdom-components/basicreport",
"version": "1.12.0", "version": "1.13.0",
"description": "> TODO: description", "description": "> TODO: description",
"author": "chenlong <857265978@163.com>", "author": "chenlong <857265978@163.com>",
"homepage": "", "homepage": "",
......
import React from 'react';
import { Row, Form, Select, InputNumber, Switch, Input, Slider } from 'antd';
import styles from './index.less';
const { Option } = Select;
const TableLayoutFormItems = (props) => {
const { defaultTableWidth, defaultTableHeight } = props;
return <>
<Form.Item
label="宽度"
name="width"
initialValue={defaultTableWidth}
>
<Slider/>
</Form.Item>
<Form.Item
label="高度"
name="height"
initialValue={defaultTableHeight}
>
<Slider/>
</Form.Item>
<Form.Item
label="标题"
name="title"
rules={[
{
required: true,
message: '标题必填',
},
]}
>
<Input/>
</Form.Item>
</>;
};
const OthersFormItems = () => {
return <>
<Form.Item label="标题" name="text">
<Input/>
</Form.Item>
<Form.Item label="字体大小" name="fontSize" initialValue={14}>
<InputNumber/>
</Form.Item>
<Form.Item
label="字体颜色"
name="fontColor"
initialValue={'#000000'}
>
<Input type="color" style={{ width: 70 }}/>
</Form.Item>
<Row className={styles.commonTitle}>y轴配置</Row>
<Form.Item label="轴1名称" name="yAxisName">
<Input/>
</Form.Item>
<Form.Item label="轴2名称" name="yAxisName2">
<Input/>
</Form.Item>
<Row className={styles.commonTitle}>其他配置</Row>
<Form.Item
label="图例位置"
name="legendPosition"
initialValue={'right'}
>
<Select>
<Option value="left"></Option>
<Option value="center"></Option>
<Option value="right"></Option>
</Select>
</Form.Item>
<Form.Item label="图表布局">
<Form.Item label="左" name="left" initialValue={20}>
<InputNumber/>
</Form.Item>
<Form.Item label="右" name="right" initialValue={20}>
<InputNumber/>
</Form.Item>
<Form.Item label="上" name="top" initialValue={20}>
<InputNumber/>
</Form.Item>
<Form.Item label="下" name="bottom" initialValue={20}>
<InputNumber/>
</Form.Item>
</Form.Item>
<Form.Item
label="数据缩放"
name="showDataZoom"
valuePropName="checked"
>
<Switch/>
</Form.Item>
</>;
};
export {
OthersFormItems, TableLayoutFormItems,
};
import { reportService } from '../../../api';
import { LeftOutlined } from '@ant-design/icons';
import {
Button,
Form,
Input,
InputNumber,
message,
notification,
Radio,
Row,
Select,
Slider,
Switch,
Space,
Tooltip,
} from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { BasicChart } from '@wisdom-components/basicchart';
import styles from './index.less';
import {
OthersFormItems,
TableLayoutFormItems,
} from './forms';
import {
defaultLayoutOptions,
defaultChartOptions,
handleCustomConfig,
handleOthers,
handleSeries, handleTitle, defaultConfig,
} from './utils';
import { returnFields, returnOptionsArr } from '../../utils/handleOption';
import ReportsManage from '../../ReportsManage';
import { cloneDeep } from 'lodash';
const layoutOptions = [
{ label: '上图下表', value: '上图下表', imgSrc: '' },
{ label: '上表下图', value: '上表下图', imgSrc: '' },
{ label: '左表右图', value: '左表右图', imgSrc: '' },
{ label: '左图右表', value: '左图右表', imgSrc: '' },
];
const verticalLayout = ['上表下图', '上图下表'];
const horizontalLayout = ['左表右图', '左图右表'];
const markPointOption = [
{ label: '最大值', value: 'max' },
{ label: '最小值', value: 'min' },
{ label: '平均值', value: 'average' },
];
const { Option } = Select;
const defaultTableWidth = 100;
const defaultTableHeight = 50;
const defaultChartListConfig = [
{
key: '图形1',
},
];
const ChartConfig = (props) => {
const { setCurrentPage, currentReport } = props;
const currentReportId = currentReport?.id ?? 0;
const [layoutType, setLayoutType] = useState('上表下图');
const [chartNum, setChartNum] = useState(1);
const [chartList, setChartList] = useState([]);
const [tableWidth, setTableWidth] = useState(defaultTableWidth);
const [tableHeight, setTableHeight] = useState(defaultTableHeight);
const [configData, setConfigData] = useState([]);
const [curChartIndex, setCurChartIndex] = useState(0);
const [curChart, setCurChart] = useState(defaultChartListConfig); // 默认第一个图形
// const [xDataType, setXDataType] = useState('selectedData'); // x轴坐标
const [xDataType, setXDataType] = useState(''); // x轴坐标
const [columnsData, setColumnsData] = useState([]);
const [reportData, setReportData] = useState([]);
const [optionsList, setOptionsList] = useState([]);
// const [chartType, setChartType] = useState('');
const [curId, setCurId] = useState('');
const [isEditing, setIsEditing] = useState(false);
const [tableForm] = Form.useForm(null);
const [chartForm] = Form.useForm(null);
// const seriesData = Form.useWatch('seriesData', chartForm);
const [seriesData, setSeriesData] = useState([]);
const xAxisData = Form.useWatch('xAxisData', chartForm);
const width = Form.useWatch('width', chartForm);
const height = Form.useWatch('height', chartForm);
const chartType = Form.useWatch('chartType', chartForm);
const seriesArray = Form.useWatch('seriesArray', chartForm);
const reportRef = useRef();
const jsonDataRef = useRef({
optionConsturctor: {
layoutOptions: {
layout: layoutType,
chartCount: chartNum,
tableConfigs: [],
chartConfigs: [],
},
chartOptions: [],
},
});
const trigger = (str) => {
let _data = reportRef?.current?.getData();
setReportData(_data);
};
const calculateMaxValue = (key, arr, current) => {
let _maxValue = 100;
let _value = arr.filter(item => item.key !== current.key).map(item => {
return Number(item.options.custom_style[key].replace('%', ''));
}).reduce((final, cur) => (final + cur), 0);
return _maxValue - _value;
};
const returnChartOptions = async (chartList) => {
let columns = returnFields(JSON.stringify(chartList));
let _data = [];
// 如果没有columns表明是初始化时,无需请求数据,此时图表渲染出来的话,会是空白
if (columns.length) {
let _request = await reportService.getChartConfigDataByColumn({ reportName: currentReport.reportName, columns });
if (_request.code === 0) {
_data = _request.data.data;
}
}
let _keys = chartList.map(item => item.key);
let _options = chartList.map(item => item.options);
let _opts = returnOptionsArr(_options, _data, reportData.selectedRows);
setOptionsList(_opts.map((item, index) => ({ key: _keys[index], options: item })));
};
useEffect(() => {
let _list = chartList.filter(item => item.options && item.options.series);
if (_list.length) {
returnChartOptions(_list);
}
}, [chartList]);
// 图容器
const renderChartWrap = useMemo(() => {
return (
<div className={styles.chartWrapper}>
{optionsList.map((item, index) => (
<div
className={classNames(
styles.chartItem,
curChartIndex === index ? styles.activeChart : '',
)}
key={item.key}
onClick={() => clickChart(chartList[index], index)}
style={{
width: item?.options?.custom_style?.width ?? 'auto',
height: item?.options?.custom_style?.height ?? 'auto',
}}
>
{
item.options ? <div style={{ width: '100%', height: '100%', pointerEvents: 'none' }}>
<BasicChart option={item.options}/>
</div> : item.key
}
</div>
))}
</div>
);
}, [curChartIndex, chartList, optionsList, layoutType, chartNum]);
// 表格容器
const renderTableWrap = useMemo(() => {
return (
<div
className={styles.tableWrapper}
style={{
width: tableWidth,
height: tableHeight,
}}
>
{/*图表1*/}
<ReportsManage params={{ reportName: currentReport.reportName, customState: [] }}
style={{ width: '100%', height: '100%' }} ref={reportRef} trigger={trigger}/>
</div>
);
}, [tableWidth, tableHeight, currentReport]);
// 渲染左侧布局
const renderLayout = () => {
switch (layoutType) {
case '上图下表':
return (
<div
className={classNames(styles.layoutWrapper, styles.columnLayout)}
>
{renderChartWrap}
{renderTableWrap}
</div>
);
case '上表下图':
return (
<div
className={classNames(styles.layoutWrapper, styles.columnLayout)}
>
{renderTableWrap}
{renderChartWrap}
</div>
);
case '左表右图':
return (
<div className={classNames(styles.layoutWrapper, styles.rowLayout)}>
{renderTableWrap}
{renderChartWrap}
</div>
);
case '左图右表':
return (
<div className={classNames(styles.layoutWrapper, styles.rowLayout)}>
{renderChartWrap}
{renderTableWrap}
</div>
);
}
};
// 点击单个chart
const clickChart = (item, index) => {
setCurChartIndex(index);
setCurChart(item);
//更新右侧面板form表单数据
updateFormData(item.options);
};
// 更新表单数据
const updateFormData = (data) => {
// 如果data不存在,可能是未配置的图形,此时需要重置表单
if (!data) {
chartForm.resetFields();
// setChartType('');
setXDataType('selectedData');
}
const _xDataType = data.custom_config.renderBy;
const _xAxisData =
_xDataType === 'selectedData'
? data.xAxis.data
: extraText(data.xAxis.data);
const _seriesData = data.series.map(item => {
let _str = extraText(item.data);
return _str;
});
const _seriesArray = data.series.map(item => {
return {
chartType: item.type,
// chartName: item.name,
unit: item.custom_config.unit,
markPoint: item.markPoint.data,
markLine: item.markLine.data,
};
});
setXDataType(_xDataType);
setSeriesData(_seriesData);
chartForm.setFieldsValue({
width: data.custom_style.width.match(/\d+/)?.[0],
height: data.custom_style.height.match(/\d+/)?.[0],
xAxisType: data.xAxis.type,
xDataType: _xDataType,
xAxisData: _xAxisData?.split(',') || [],
xAxisName: data.xAxis.name,
// barWidth:
// seriesData: _seriesData,
seriesArray: _seriesArray,
// chartColor: data.color,
showTitle: data.title.show,
text: data.title.text,
fontSize: data.title.textStyle.fontSize,
fontColor: data.title.textStyle.color,
yAxisName: data.yAxis[0].name,
left: data.grid.left,
right: data.grid.right,
top: data.grid.top,
bottom: data.grid.bottom,
showDataZoom: data.dataZoom.show,
legendPosition: data.legend.left,
});
};
// 处理字符串 提取纯文字
const extraText = (str) => {
if (!str) return void 0;
const text = str.slice(2, str.length - 1);
return text;
};
// 图形数量
const onChangeNum = (e) => {
const num = e.target.value;
// 判断num是否大于chartList的长度,大于则赋值add,小于则赋值delete
const type = num > chartList.length ? 'add' : 'delete';
let newArr = [];
if (type === 'add') {
newArr = chartList.concat();
for (let i = 0; i < num - chartList.length; i++) {
let _defaultOptions = cloneDeep(defaultChartOptions);
newArr.push({
key: `图形${chartList.length + i + 1}`,
options: _defaultOptions,
});
}
}
if (type === 'delete') {
newArr = chartList.slice(0, num);
}
newArr = newArr.map((item, index) => {
if (num === 1) {
item.options.custom_style = {
width: '100%',
height: '100%',
};
}
if (num === 2) {
item.options.custom_style = {
width: verticalLayout.includes(layoutType) ? '50%' : '100%',
height: horizontalLayout.includes(layoutType) ? '50%' : '100%',
};
}
if (num === 3) {
if (index === 0) {
item.options.custom_style = {
width: verticalLayout.includes(layoutType) ? '34%' : '100%',
height: horizontalLayout.includes(layoutType) ? '34%' : '100%',
};
} else {
item.options.custom_style = {
width: verticalLayout.includes(layoutType) ? '33%' : '100%',
height: horizontalLayout.includes(layoutType) ? '33%' : '100%',
};
}
}
return item;
});
setChartList(newArr);
setChartNum(num);
};
// 表格表单字段变化
const onTableFormChange = (changedValues, allValues) => {
const field = Object.keys(changedValues)[0];
const fieldValue = changedValues[field];
switch (field) {
case 'width':
setTableWidth(fieldValue + '%');
break;
case 'height':
setTableHeight(fieldValue + '%');
break;
default:
break;
}
};
// 图形表单字段变化
const onChartFormChange = (changedValues, allValues, index) => {
const field = Object.keys(changedValues)[0];
const fieldValue = changedValues[field];
if (field === 'xDataType') {
setXDataType(fieldValue);
}
if (field === 'chartType') {
// setChartType(fieldValue);
}
if (field === 'showDataZoom' && fieldValue) {
chartForm.setFieldsValue({ bottom: 40 });
}
};
// 提交
const handleOptions = (tableFormData) => {
const _chartOptions = chartList
.filter((item) => !!item.options)
.map((v) => v.options);
// 表格配置数据
const tableConfigs = [
{
title: tableFormData.title,
width: tableFormData.width + '%',
height: tableFormData.height + '%',
},
];
jsonDataRef.current.optionConsturctor.layoutOptions = {
layout: layoutType,
chartCount: chartNum,
tableConfigs: tableConfigs,
};
jsonDataRef.current.optionConsturctor.chartOptions = _chartOptions;
return JSON.stringify(jsonDataRef.current.optionConsturctor);
};
const onSubmit = async () => {
// 表格相关配置
const tableFormData = await tableForm.validateFields();
let _data = handleOptions(tableFormData);
saveConfig(_data);
};
// 调用保存接口
const saveConfig = async (jsonData) => {
const data = {
id: curId || undefined,
reportID: currentReportId,
configInfo: jsonData,
};
const res = await reportService.saveChartConfig(data);
if (res.code === 0) {
message.success('配置成功!');
getChartConfig();
} else {
notification.error({
message: `请求错误`,
description: res?.msg ?? '',
});
}
};
useEffect(() => {
if (chartList.length) {
saveFormData('modify');
}
}, [width, height]);
// 保存当前图形form表单数据
const saveFormData = async (type) => {
let form = {};
if (type === 'save') form = await chartForm.validateFields();
if (type === 'modify') form = await chartForm.getFieldsValue();
// x轴数据 格式如下:
// selectedData: [data1,data2...]
// allData: ${data1}
const _title = handleTitle(form);
const _series = handleSeries(form, seriesData, markPointOption);
const _others = handleOthers(form);
const _custom = handleCustomConfig(form);
const xData =
form.xDataType === 'selectedData'
? form.xAxisData
: (form.xAxisData ? '${' + form.xAxisData + '}' : '');
const _options = {
..._custom,
..._title,
..._series,
..._others,
xAxis: {
name: form?.xAxisName ?? '',
type: form.xAxisType,
data: xData,
},
yAxis:
form?.yAxisName2 ?? ''
? [
{
name: form?.yAxisName ?? '',
gridIndex: 0,
},
{
name: form?.yAxisName2 ?? '',
gridIndex: 1,
},
]
: [
{
name: form?.yAxisName ?? '',
gridIndex: 0,
},
],
};
const newData = [...chartList];
newData[curChartIndex].options = _options;
setChartList(newData);
};
const getChartConfig = async () => {
const res = await reportService.getChartConfig({
reportName: currentReport.reportName,
});
if (res.code === 0) {
const data = res?.data?.configInfo
? JSON.parse(res.data.configInfo)
: cloneDeep(defaultConfig);
let { chartOptions, layoutOptions } = data;
// 无设置,即初始化时,给定默认配置
layoutOptions.tableConfigs[0].title = `${currentReport.reportName}`;
// 有设置
const _chartList = chartOptions.map((item, index) => ({
options: item,
key: '图形' + (index + 1),
}));
updateFormData(_chartList?.[0]?.options);
setCurChartIndex(0);
setCurId(res?.data?.id ?? '');
setCurChart(_chartList[0]);
setChartList(_chartList);
dealTableConfig(layoutOptions);
setChartNum(layoutOptions.chartCount);
}
};
// 回填表格配置
const dealTableConfig = (data) => {
const { layout, tableConfigs } = data;
const tableWidth = tableConfigs[0].width;
const tableHeight = tableConfigs[0].height;
setTableWidth(tableWidth);
setTableHeight(tableHeight);
setLayoutType(layout);
tableForm.setFieldsValue({
width: tableWidth.match(/\d+/)?.[0],
height: tableHeight.match(/\d+/)?.[0],
title: tableConfigs[0].title,
});
};
// 获取配置数据
const getConfig = async () => {
const params = {
reportName: currentReport.reportName,
};
const res = await reportService.getReportDetails(params);
if (res.code === 0) {
const data = (res?.data?.data || []).map((v) => ({
label: v.fieldAlias,
value: v.fieldAlias,
}));
setConfigData(data);
}
};
const setTableSize = (e) => {
const _map = {
horizontal: {
width: '100%',
height: '70%',
},
vertical: {
width: '70%',
height: '100%',
},
};
let _obj = {};
if (horizontalLayout.includes(e)) {
_obj = _map.horizontal;
} else {
_obj = _map.vertical;
}
// 设置报表宽度
let { width, height } = _obj;
setTableHeight(height);
setTableWidth(width);
tableForm.setFieldsValue({
width: width.match(/\d+/)?.[0],
height: height.match(/\d+/)?.[0],
});
// 设置图表宽度
};
const widthMax = useMemo(() => {
return verticalLayout.includes(layoutType) ? calculateMaxValue('width', chartList, curChart) : 100;
}, [layoutType, chartList, curChart]);
const heightMax = useMemo(() => {
return verticalLayout.includes(layoutType) ? 100 : calculateMaxValue('height', chartList, curChart);
}, []);
const clickPanel = (e) => {
if (!isEditing) {
message.info('请点击编辑按钮后,进行编辑');
}
};
// form其他props
const formFrops = {
labelAlign: 'right',
labelCol: { span: 4, offset: 2 },
wrapperCol: { span: 14 },
};
const allDataForm = () => (
<>
<Form.Item
label="图形数据"
rules={[
{
required: true,
message: '数据来源必填',
},
]}
extra={returnSeriesExtra}
>
<Select value={seriesData} mode="multiple"
onChange={(e) => {
setSeriesData(e);
}}
allowClear options={configData}
placeholder={'请选择图形数据来源字段'}/>
</Form.Item>
<Form.Item
label="类型"
name={'chartType'}
rules={[
{
required: true,
message: '类型必选',
},
]}
>
<Select>
<Option value="line">折线图</Option>
<Option value="bar">柱状图</Option>
<Option value="pie">饼图</Option>
</Select>
</Form.Item>
{chartType === 'bar' && (
<>
<Form.Item label="柱条宽度" name={'barWidth'}>
<Slider/>
</Form.Item>
</>
)}
{/* <Form.Item label="图形名称"
name={'chartName'}
rules={[
{
// required: true,
message: '图形名称必填',
},
]}
>
<Input/>
</Form.Item>*/}
<Form.Item label="单位" name={'unit'}>
<Input/>
</Form.Item>
<Form.Item label="标注点" name={'markPoint'}>
<Select mode="multiple" allowClear options={markPointOption}/>
</Form.Item>
<Form.Item label="标注线" name={'markLine'}>
<Select mode="multiple" allowClear options={markPointOption}/>
</Form.Item>
</>
);
const returnSeriesExtra = useMemo(() => {
if (seriesData?.length) {
if (xDataType === 'selectedData') return seriesData?.length > 1 ? `图形的系列名称的格式为${seriesData.join('-')}` : `图形系列的名称为${seriesData.join('')}列的数据`;
if (xDataType === 'allData') return seriesData?.length > 1 ? `图形将展示${seriesData.join(',')}列的数据,请分别配置属性` : `图形将展示${seriesData.join('')}列的数据`;
}
return '';
}, [seriesData, xDataType]);
const returnXAxisExtra = useMemo(() => {
if (xAxisData?.length) {
if (xDataType === 'selectedData') return `x轴坐标分别为${xAxisData.join(',')}`;
if (xDataType === 'allData') return xAxisData.length === 1 ? `x轴坐标为${xAxisData}列的值` : `x轴坐标为拼接选中${xAxisData.length}列的数据,形式为${xAxisData.join('-')}`;
}
return '';
}, [xAxisData]);
useEffect(() => {
if (currentReportId) {
getChartConfig();
getConfig();
}
}, []);
return (
<div className={styles.chartConfig}>
<Row className={styles.controlRow}>
<LeftOutlined
className={styles.leftBtn}
onClick={() => {
setCurrentPage('报表列表');
}}
/>
<Form layout={'inline'}>
<Form.Item label={'图表布局'}>
<Select
options={layoutOptions}
onChange={(e) => {
setTableSize(e);
setLayoutType(e);
}}
value={layoutType}
/>
</Form.Item>
<Form.Item label={'图形数量'}>
<Radio.Group onChange={onChangeNum} value={chartNum}>
<Radio value={1}>1</Radio>
<Radio value={2}>2</Radio>
<Radio value={3}>3</Radio>
</Radio.Group>
</Form.Item>
</Form>
</Row>
<div className={styles.contentWrapper}>
<div className={styles.leftLayout}>{renderLayout()}</div>
<div className={styles.rightPanel}>
<div className={styles.configForm}>
<Row className={styles.titleWrap}>表格配置</Row>
<Form
form={tableForm}
{...formFrops}
onValuesChange={onTableFormChange}
className={styles.tableFormWrap}
>
<TableLayoutFormItems defaultTableWidth={defaultTableWidth} defaultTableHeight={defaultTableHeight}/>
</Form>
<Row className={styles.titleWrap}>{curChart.key}配置</Row>
<Row className={styles.saveBtnWrap}>
<Tooltip title={'点击按钮进入、退出编辑状态。请注意及时保存配置!'}><QuestionCircleOutlined/></Tooltip>
<Button onClick={() => setIsEditing(!isEditing)}>{isEditing ? '退出编辑' : '编辑'}</Button>
</Row>
<Form
form={chartForm}
onValuesChange={(changedValues, allValues) =>
onChartFormChange(changedValues, allValues)
}
{...formFrops}
className={classNames(styles.chartFormWrap, 'wkt-scroll-light')}
>
<div className={classNames(isEditing ? '' : styles.pointerEvents)}>
<Row className={styles.commonTitle}>宽高配置</Row>
<Form.Item label="宽度" name="width">
<InputNumber min={0} max={widthMax} disabled={horizontalLayout.includes(layoutType)}/>
</Form.Item>
<Form.Item label="高度" name="height">
<InputNumber min={0} max={heightMax} disabled={verticalLayout.includes(layoutType)}/>
</Form.Item>
<Row className={styles.commonTitle}>x轴配置</Row>
<Form.Item
label="x轴类型"
name="xAxisType"
initialValue={'category'}
>
<Radio.Group>
<Radio value="category">分类、周期、个体等</Radio>
<Radio value="time">时间</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="x轴坐标"
name="xDataType"
initialValue={'selectedData'}
rules={[
{
required: true,
message: '必选',
},
]}
>
<Radio.Group>
<Radio value="allData">表内数据</Radio>
<Radio value="selectedData">表头数据</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="x轴数据源"
name="xAxisData"
rules={[
{
required: true,
message: 'x轴数据源必填',
},
]}
extra={returnXAxisExtra}
>
<Select placeholder={'请勾选x轴需要的字段'} mode="multiple" allowClear options={configData}/>
</Form.Item>
<Form.Item label="轴名称" name="xAxisName">
<Input placeholder={'指定轴的名称,非必填'}/>
</Form.Item>
<Row className={styles.commonTitle}>图形配置</Row>
{
xDataType === 'allData' ? <Form.List name={'seriesArray'}>
{
(fields, { add, remove }) => (
<>
<Form.Item
label={<span><span style={{ color: 'red', marginRight: 4 }}>*</span>图形数据</span>}
rules={[
{
required: true,
message: '数据来源必填',
},
]}
extra={returnSeriesExtra}
>
<Select mode="multiple"
value={seriesData}
onSelect={() => add()}
onDeselect={(e) => {
let _index = seriesData.findIndex(list => list === e);
remove(_index);
}}
onChange={(e) => {
setSeriesData(e);
}}
allowClear options={configData}
placeholder={'请选择图形数据来源字段'}/>
</Form.Item>
{
fields.map((item, index) => (
<div key={`${item.key}_${index}`}>
<Form.Item colon={false}
label={<span style={{ fontWeight: 'bold' }}>{seriesData?.[index]}</span>}/>
<Form.Item
label="类型"
name={[item.name, 'chartType']}
rules={[
{
required: true,
message: '类型必选',
},
]}
>
<Select>
<Option value="line">折线图</Option>
<Option value="bar">柱状图</Option>
<Option value="pie">饼图</Option>
</Select>
</Form.Item>
<Form.Item hidden={seriesArray?.[index]?.chartType !== 'bar'} label="柱条宽度"
name={[item.name, 'barWidth']}>
<Slider/>
</Form.Item>
{/* <Form.Item
style={{ pointerEvents: 'none' }}
label="图形名称"
name={[item.name, 'chartName']}
rules={[
{
// required: true,
message: '图形名称必填',
},
]}
>
<Input/>
</Form.Item>*/}
<Form.Item label="单位" name={[item.name, 'unit']}>
<Input/>
</Form.Item>
<Form.Item label="标注点" name={[item.name, 'markPoint']}>
<Select mode="multiple" allowClear options={markPointOption}/>
</Form.Item>
<Form.Item label="标注线" name={[item.name, 'markLine']}>
<Select mode="multiple" allowClear options={markPointOption}/>
</Form.Item>
</div>
))
}
</>
)
}
</Form.List> : ''
}
{
xDataType === 'selectedData' ? <>
{allDataForm()}
</> : ''
}
<Row className={styles.commonTitle}>标题配置</Row>
<Form.Item
label="图表标题"
name="showTitle"
valuePropName="checked"
>
<Switch/>
</Form.Item>
{/*包含标题、y轴等非必要的设置*/}
<OthersFormItems/>
</div>
</Form>
</div>
<div className={styles.submitWrap}>
<Button
style={{ marginLeft: 10 }}
className={styles.submitBtn}
type="primary"
onClick={onSubmit}
>
提交
</Button>
<Button type="primary" onClick={() => saveFormData('save')}>
预览
</Button>
</div>
</div>
</div>
</div>
);
};
export default ChartConfig;
.chartConfig {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
.controlRow {
display: flex;
align-items: center;
margin-bottom: 4px;
height: 44px;
padding: 6px;
background: #ffffff;
.leftBtn {
font-size: 18px;
font-weight: bold;
cursor: pointer;
margin-right: 10px;
&:hover {
color: #1685FF;
opacity: 0.8;
}
}
}
.contentWrapper {
flex: 1;
width: 100%;
display: flex;
column-gap: 8px;
overflow: hidden;
:global {
.@{ant-prefix}-form-item {
margin-bottom: 8px;
}
.@{ant-prefix}-input-number {
width: 100%;
}
}
.leftLayout {
flex: 1;
height: 100%;
padding: 8px;
background: #fff;
overflow: auto;
.layoutWrapper {
width: 100%;
height: 100%;
display: flex;
.chartWrapper {
display: flex;
column-gap: 8px;
flex: 1;
overflow: hidden;
.chartItem {
border: 1px dashed #3d3d3d;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&.activeChart {
//background: #1685FF;
border-color: #1685FF;
color: #fff;
}
}
}
.tableWrapper {
border: 1px dashed #3d3d3d;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
// 上下布局
&.columnLayout {
flex-direction: column;
row-gap: 8px;
.tableWrapper {
width: 100%;
height: 50%;
}
.chartWrapper {
width: 100%;
flex: 1;
.chartItem {
//flex: 1;
width: 100%;
border: 1px dashed #333;
&.activeChart {
border-color: #1685FF;
color: #fff;
}
}
}
}
// 左右布局
&.rowLayout {
column-gap: 8px;
.chartWrapper {
display: flex;
flex-direction: column;
row-gap: 8px;
.chartItem {
//flex: 1;
width: 100%;
border: 1px dashed #333;
&.activeChart {
border-color: #1685FF;
color: #fff;
}
}
}
}
}
}
.rightPanel {
flex: none;
width: 500px;
height: 100%;
background: #fff;
overflow: hidden;
display: flex;
flex-direction: column;
.configForm {
width: 100%;
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
.titleWrap {
display: flex;
height: 40px;
flex: none;
justify-content: center;
align-items: center;
background: #1685FF;
color: #ffffff;
}
.tableFormWrap {
flex: none;
}
.commonTitle {
position: relative;
padding-left: 20px;
&::before {
position: absolute;
top: 50%;
left: 8px;
transform: translateY(-50%);
content: '';
width: 3px;
height: 14px;
background: #1685FF;
}
}
.saveBtnWrap {
flex-direction: row-reverse;
column-gap: 4px;
padding: 0 12px;
margin-top: 8px;
align-items: center;
}
.chartFormWrap {
flex: 1;
overflow: auto;
.pointerEvents {
pointer-events: none;
}
}
}
.submitWrap {
width: 100%;
height: 40px;
flex: none;
display: flex;
align-items: center;
flex-direction: row-reverse;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}
}
}
}
const handleMarkLineOrMarkPoint = (options, arr) => {
return options
.filter((item) => arr?.includes(item.value))
?.map((v) => ({
type: v.value,
name: v.label,
}));
};
const handleSeries = (form, seriesData, option) => {
let series = form?.seriesArray?.map((item, index) => {
let _temp = {
type: item.chartType,
// name: item?.chartName ?? '',
colorBy: 'series',
data:
form.xDataType === 'selectedData'
? `$[${form.seriesData}]`
: '${' + seriesData[index] + '}',
custom_config: {
unit: item?.unit ?? '',
},
};
_temp.markPoint = {
data: handleMarkLineOrMarkPoint(option, item.markPoint),
};
_temp.markLine = {
data: handleMarkLineOrMarkPoint(option, item.markLine),
};
return _temp;
});
return {
series,
};
};
const handleOthers = (form) => {
let legend = {
left: form.legendPosition ?? '',
};
let grid = {
left: form.left,
right: form.right,
top: form.top,
bottom: form.bottom,
};
let dataZoom = {
show: form?.showDataZoom ?? false,
};
return { legend, grid, dataZoom };
};
const handleCustomConfig = (form) => {
return {
custom_style: {
width: form.width + '%',
height: form.height + '%',
},
custom_config: {
renderBy: form.xDataType,
},
};
};
const handleTitle = (form) => {
return {
title: {
show: form?.showTitle ?? false,
text: form?.text ?? '',
textStyle: {
color: form?.fontColor ?? '',
fontSize: form.fontSize,
},
},
};
};
const defaultChartOptions = {
'custom_style': {
'width': '',
'height': '',
},
'custom_config': {
'renderBy': 'allData',
},
'title': {
'show': false,
'text': '',
'textStyle': {
'color': '#000000',
'fontSize': 14,
},
},
'series': [],
'legend': {
'left': 'right',
},
'grid': {
'left': 20,
'right': 20,
'top': 20,
'bottom': 20,
},
'dataZoom': {
'show': false,
},
'xAxis': {
'name': '',
'type': 'category',
'data': '',
},
'yAxis': [
{
'name': '',
'gridIndex': 0,
},
],
};
const defaultLayoutOptions = {
'layout': '上表下图',
'chartCount': 1,
'tableConfigs': [
{
'title': '',
'width': '100%',
'height': '70%',
},
],
};
const defaultConfig = {
'layoutOptions': {
'layout': '上表下图',
'chartCount': 1,
'tableConfigs': [
{
'title': '',
'width': '100%',
'height': '60%',
},
],
},
'chartOptions': [
{
'custom_style': {
'width': '100%',
'height': '100%',
},
'custom_config': {
'renderBy': 'allData',
},
'title': {
'text': '',
'textStyle': {
'color': '#000000',
'fontSize': 14,
},
},
'xAxis': {
'name': '',
'type': 'category',
'data': '',
},
'yAxis': [
{
'name': '',
'gridIndex': 0,
},
],
'series': [],
'legend': {
'left': 'right',
},
'grid': {
'left': 20,
'right': 20,
'top': 20,
'bottom': 20,
},
'dataZoom': {
show: false,
},
},
],
};
const calculateWidth = () => {
};
export {
handleSeries, handleOthers, handleCustomConfig, handleTitle, defaultChartOptions, defaultLayoutOptions,defaultConfig
};
import React, { useEffect, useState } from 'react';
import { AutoComplete, InputNumber } from 'antd';
import { useRef } from 'react/index';
/**
* @params {string} type 单元格 统计栏
* */
const NumberConfig = ({ value, onChange, defaultValues, type }) => {
const [values, setValues] = useState({
prefix: '',
suffix: '',
precision: '',
ratio: 1,
});
const wrapper = {
display: 'flex',
gap: 6,
};
const width = { width: 80 };
const optRef = useRef({
prefix: [
{ value: '$' }, { value: '¥' },
],
suffix: [
{ value: '千' }, { value: '万' }, { value: '元' }, { value: '万元' },
],
precision: [
{ value: '0.0' }, { value: '0.00' }, { value: '0.000' }, { value: '0.0000' },
],
});
const valueChange = (key, value) => {
let _values = { ...values };
_values[key] = value;
setValues(_values);
onChange(_values);
};
useEffect(() => {
let _values = { ...values };
_values.prefix = value;
}, [value]);
return <div style={wrapper}>
{/* 精度 倍率 前缀 后缀 类型*/}
<AutoComplete onChange={(e) => valueChange('prefix', e)}
value={values.prefix} placeholder={'前缀'}
options={optRef.current.prefix} style={{ ...width }}/>
<AutoComplete onChange={(e) => valueChange('suffix', e)}
value={values.suffix} placeholder={'单位/后缀'}
options={optRef.current.suffix}
style={{ ...width, width: 100 }}/>
<AutoComplete onChange={(e) => valueChange('precision', e)}
value={values.precision} placeholder={'精度'}
options={optRef.current.precision} style={{ ...width }}/>
<span>倍率:<InputNumber onChange={(e) => valueChange('ratio', e)}
value={values.ratio} step={1} style={{ ...width }}
min={1}/></span>
</div>;
};
export default NumberConfig;
import React from 'react';
import ReactDOM from 'react-dom';
import Simple_table from './modal/simple_table';
const Print = (data, printTemp) => {
//创建iframe标签
const iframe = document.createElement('iframe');
iframe.setAttribute(
'style',
'position:absolute;width:0px;height:0px;left:500px;top:500px;size:auto;margin:0mm;',
);
//将iframe添加到body
document.body.appendChild(iframe);
//创建打印内容document对象
let doc = iframe.contentWindow.document;
let tem = '';
switch (printTemp) {
case 'simple_table':
tem = <Simple_table data={data}/>;
break;
default:
tem = <Simple_table data={data}/>;
}
ReactDOM.render(tem, doc);
setTimeout(() => {
doc.close();
//打印
iframe.contentWindow.focus();
iframe.contentWindow.print();
//清理iframe
if (navigator.userAgent.indexOf('MSIE') > 0) {
document.body.removeChild(iframe);
}
}, 1000);
};
export default Print;
import moment from 'moment';
import React, { useMemo } from 'react';
const Template_record = (props) => {
const { data } = props;
const { tableData, reportName, columns } = data;
const handleHead = (columns) => {
let _rows = [];
let countLevel = (arr) => {
let _childrenArr = [];
let _currentRowArr = [];
arr.forEach((item, index) => {
let _item = { ...item };
if (_item?.children?.length) {
_item.hasChild = true;
_childrenArr = _childrenArr.concat(_item.children);
}
_currentRowArr.push(_item);
});
_rows.push(_currentRowArr);
if (_childrenArr.length) countLevel(_childrenArr);
};
countLevel(columns);
return _rows;
};
const handleCols = (columns) => {
let _cols = [];
let handleColumns = (arr) => {
let _childrenArr = [];
arr.forEach((item, index) => {
let _item = { ...item };
if (_item?.children?.length) {
_childrenArr = _childrenArr.concat(_item.children);
} else {
_cols.push(_item)
}
});
if (_childrenArr.length) handleColumns(_childrenArr);
};
handleColumns(columns);
return _cols;
}
const headArr = useMemo(() => {
return handleHead(columns);
}, [columns]);
const columnsArr = useMemo(()=>{
return handleCols(columns)
},[columns])
return (
<div style={{ padding: '0px 0px' }}>
<style>
{`@page{margin: 20px 80px 20px 80px;}table{border-collapse:collapse;font-size:10px;}
table,th,td{border:1px solid #333333;text-align:center;padding:3px 1px;}`}
</style>
<div
style={{
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{reportName}
</div>
<div style={{ marginTop: '10px' }}>
<table width="100%" border="0">
<thead>
{
headArr.map((row, index) => {
return <tr>
{
row.map(col => {
return <th rowSpan={col?.children?.length ? 1 : headArr.length - index} width={`${col.width??200}px`}
colSpan={col?.children?.length ?? 1}>{col.title}</th>;
})
}
</tr>;
})
}
</thead>
{tableData.map((item, index) => {
return (
<tr key={`${item.r}_ ${index}`}>
{
/**
* 1. 序号没有onCell,单独输出
* 2. 其他的有onCell的,按照rowSpan输出来分配位置
* 3. 没有onCell的,或者rowSpan等于0的,打印的时候需要被移除
* */
columnsArr.map(col => {
if (col.title === '序号') return <td>{item[col.dataIndex]}</td>;
if (col.onCell && col.onCell(item, index).rowSpan !== 0) return <td
rowSpan={col.onCell(item, index).rowSpan}>{item[col.dataIndex]}</td>;
return null;
}).filter(item => item)
}
</tr>
);
})}
</table>
</div>
</div>
);
};
export default Template_record;
...@@ -34,6 +34,7 @@ import { UploadOutlined, DownloadOutlined } from '@ant-design/icons'; ...@@ -34,6 +34,7 @@ import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
import './fileUpload.less'; import './fileUpload.less';
import { downloadFunc, filenameVerification } from '../../utils/utils'; import { downloadFunc, filenameVerification } from '../../utils/utils';
import { uploadFileUrl, downloadFileUrl } from '../../../api/service/workflow'; import { uploadFileUrl, downloadFileUrl } from '../../../api/service/workflow';
import {isArray} from "lodash";
const videoTypeArray = ['.mp4']; const videoTypeArray = ['.mp4'];
const audioTypeArray = ['.mp4']; const audioTypeArray = ['.mp4'];
const fileTypeArray = []; const fileTypeArray = [];
...@@ -47,22 +48,21 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -47,22 +48,21 @@ const FileUpload = ({ value, onChange, schema }) => {
const [previewTitle, setPreviewTitle] = useState(''); const [previewTitle, setPreviewTitle] = useState('');
const [previewVisible, setPreviewVisible] = useState(false); const [previewVisible, setPreviewVisible] = useState(false);
const [previewUrl, setPreviewUrl] = useState(''); const [previewUrl, setPreviewUrl] = useState('');
const [showList, setShowList] = useState(''); const [showList, setShowList] = useState([]);
const file = value || schema.default;
const option = { const option = {
name: 'file', name: 'file',
action: `${window.location.origin}${uploadFileUrl}`, action: `${window.location.origin}${uploadFileUrl}`,
listType: _isRecordingOrVideo ? 'picture-card' : 'picture', listType: _isRecordingOrVideo ? 'picture-card' : 'picture',
withCredentials: true, withCredentials: true,
beforeUpload(file, fileList) { beforeUpload(file, fileList) {
/** @tips: 解决提交文件中存在特殊字符的问题 */ /** @Tips: 解决提交文件中存在特殊字符的问题 */
let _continueUpload = true; let _continueUpload = true;
let _msg = { let _msg = {
type: 'success', type: 'success',
content: '上传成功!', content: '上传成功!',
}; };
fileList.forEach((item) => { fileList.forEach(item => {
let _msgObject = filenameVerification(item); let _msgObject = filenameVerification(item.name);
if (_msgObject.type === 'error') { if (_msgObject.type === 'error') {
_continueUpload = false; _continueUpload = false;
_msg = { _msg = {
...@@ -74,9 +74,10 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -74,9 +74,10 @@ const FileUpload = ({ value, onChange, schema }) => {
_msg.type === 'error' ? message[_msg.type](_msg.content) : ''; _msg.type === 'error' ? message[_msg.type](_msg.content) : '';
return _continueUpload; return _continueUpload;
}, },
onRemove:()=>{return true},
onChange: ({ file, fileList, event }) => { onChange: ({ file, fileList, event }) => {
// 检验名字,名字不通过不允许显示 // 检验名字,名字不通过不允许显示
if (filenameVerification(file).type === 'error') return false; if (filenameVerification(file.name).type === 'error') return false;
// 返回的链接在file.response内;不设置url,预览图表不可点击 // 返回的链接在file.response内;不设置url,预览图表不可点击
if (file.status === 'done' && file.response.code === 0) { if (file.status === 'done' && file.response.code === 0) {
file.url = `${downloadFileUrl}?filePath=${file.response.data}`; file.url = `${downloadFileUrl}?filePath=${file.response.data}`;
...@@ -86,8 +87,8 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -86,8 +87,8 @@ const FileUpload = ({ value, onChange, schema }) => {
file.status = 'error'; file.status = 'error';
message.error('上传失败!'); message.error('上传失败!');
} }
onChange((fileList && fileList.length && JSON.stringify(fileList)) || ''); onChange(fileList ?? []);
setShowList(JSON.stringify(fileList)); // setShowList(fileList ?? []);
}, },
onPreview(file) { onPreview(file) {
if (_isRecordingOrVideo) { if (_isRecordingOrVideo) {
...@@ -95,7 +96,6 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -95,7 +96,6 @@ const FileUpload = ({ value, onChange, schema }) => {
setPreviewUrl(file.url); setPreviewUrl(file.url);
} }
}, },
previewFile(file) {},
onDownload(file) { onDownload(file) {
downloadFunc(file.url, file.name, '_self'); downloadFunc(file.url, file.name, '_self');
}, },
...@@ -105,11 +105,11 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -105,11 +105,11 @@ const FileUpload = ({ value, onChange, schema }) => {
setPreviewTitle(''); setPreviewTitle('');
}; };
/** /**
* @description: 返回文件类型限定值 * @Description: 返回文件类型限定值
* @params: {Array} typeArray: Video | Recording | File * @Params: {Array} typeArray: Video | Recording | File
* @date: 2021/12/2 * @Date: 2021/12/2
* @author: ChenLong * @Author: ChenLong
*/ * */
const returnFileTypeString = (type) => { const returnFileTypeString = (type) => {
let _obj = { let _obj = {
Video: videoTypeArray, Video: videoTypeArray,
...@@ -120,101 +120,53 @@ const FileUpload = ({ value, onChange, schema }) => { ...@@ -120,101 +120,53 @@ const FileUpload = ({ value, onChange, schema }) => {
return _obj[type].join(','); return _obj[type].join(',');
}; };
useEffect(() => { useEffect(() => {
let fileList = []; if (isArray(value)) {
(file || '').split(',').forEach((item, index) => { setShowList(value);
if (item && filenameVerification({ name: item }, true).type !== 'error') {
// @Tips: 直接过滤掉名字中有异常字符的文件
let _obj = {
uid: index + '_' + Math.random(),
value: item,
name: item.split('\\').reverse()[0],
type: schema.renderTo === 'Image' ? 'image' : 'file',
status: 'done',
url: `${downloadFileUrl}?filePath=${item}`,
sourcePath: item,
};
if (schema.renderTo === 'Image') _obj.thumbUrl = `${downloadFileUrl}?filePath=${item}`;
fileList.push(_obj);
}
});
// onChange(fileList.length && JSON.stringify(fileList) || '');
setShowList(JSON.stringify(fileList));
}, []);
useEffect(() => {
if (value) {
let fileList = [];
(file || '').split(',').forEach((item, index) => {
if (item && filenameVerification({ name: item }, true).type !== 'error') {
// @Tips: 直接过滤掉名字中有异常字符的文件
let _obj = {
uid: index + '_' + Math.random(),
value: item,
name: item.split('\\').reverse()[0],
type: schema.renderTo === 'Image' ? 'image' : 'file',
status: 'done',
url: `${downloadFileUrl}?filePath=${item}`,
sourcePath: item,
};
if (schema.renderTo === 'Image') _obj.thumbUrl = `${downloadFileUrl}?filePath=${item}`;
fileList.push(_obj);
}
});
// onChange(fileList.length && JSON.stringify(fileList) || '');
setShowList(JSON.stringify(fileList));
} }
}, [value]); }, [value]);
return ( return (
<> <>
{/** @tips: 裁剪功能无法正常工作,暂不提供 */} <Upload
{/* <ImgCrop beforeCrop={(file) => { disabled={schema.disabled}
let _returnObj = filenameVerification(file); {...option}
if (_returnObj.type === 'error') { fileList={showList ?? []}
message.error(_returnObj.content); multiple
return false; style={{ width: '100%' }}
} className={_isRecordingOrVideo ? 'formUploadVideoOrRecording' : 'formUpload'}
return schema.renderTo === 'Image'; showUploadList={{
}} rotate locale={'zh-cn'} modalTitle={'编辑图片'} modalOk={'确定'} showPreviewIcon: _isRecordingOrVideo,
modalCancel={'取消'}>*/} showDownloadIcon: true,
<Upload downloadIcon: <DownloadOutlined/>,
disabled={schema.disabled} }}
{...option} accept={returnFileTypeString(schema.renderTo)}
fileList={showList ? JSON.parse(showList) : []} >
multiple {
style={{ width: '100%' }} !_isRecordingOrVideo ?
className={_isRecordingOrVideo ? 'formUploadVideoOrRecording' : 'formUpload'} <Button disabled={schema.disabled} icon={<UploadOutlined/>}>Upload</Button> : '+ Upload'
showUploadList={{ }
showPreviewIcon: _isRecordingOrVideo,
showDownloadIcon: true, </Upload>
downloadIcon: <DownloadOutlined />, {/* </ImgCrop>*/}
}} <Modal
accept={returnFileTypeString(schema.renderTo)} style={{ width: '30%' }}
> bodyStyle={{ textAlign: 'center' }}
{!_isRecordingOrVideo ? ( visible={previewVisible}
<Button disabled={schema.disabled} icon={<UploadOutlined />}> title={previewTitle}
Upload footer={null}
</Button> onCancel={handleCancel}
) : ( >
'+ Upload' {
)} _isVideo ?
</Upload> <video width={'100%'} height={'100%'} controls autoPlay src={previewUrl}/> : ''
{/* </ImgCrop>*/} }
<Modal {
style={{ width: '30%' }} _isAudio ? <audio controls autoPlay src={previewUrl}/> : ''
bodyStyle={{ textAlign: 'center' }} }
visible={previewVisible} {
title={previewTitle} _isImage ? <img width={'100%'} height={'100%'} src={previewUrl} alt="缩略图"/> : ''
footer={null} }
onCancel={handleCancel} </Modal>
> </>
{_isVideo ? (
<video width={'100%'} height={'100%'} controls autoPlay src={previewUrl} />
) : (
''
)}
{_isAudio ? <audio controls autoPlay src={previewUrl} /> : ''}
{_isImage ? <img width={'100%'} height={'100%'} src={previewUrl} alt="缩略图" /> : ''}
</Modal>
</>
); );
}; };
......
import React, { useEffect, useMemo, useState,useRef } from 'react';
import { BasicChart } from '@wisdom-components/basicchart';
import { reportService } from '../../../api';
import styles from './index.less';
import { returnFields, returnOptionsArr } from '../../utils/handleOption';
const ChartComponent = (props) => {
const [columnsData, setColumnsData] = useState([]);
const [options, setOptions] = useState([]);
const { reportName, chartOptions, reportData } = props;
const chartRef = useRef();
const getData = () => {
let columns = returnFields(JSON.stringify(chartOptions));
if (!columns.length) return ''; //
reportService.getChartConfigDataByColumn({
reportName,
columns,
}).then(res => {
let _data = [];
if (res.code === 0) _data = res.data.data;
setColumnsData(_data);
});
};
useEffect(() => {
getData();
}, []);
useEffect(() => {
let _options = returnOptionsArr(chartOptions, columnsData, reportData.selectedRows);
setOptions(_options);
}, [columnsData, reportData.selectedRows.length]);
const resizeChart = () => {
chartRef.current.resize();
};
useEffect(() => {
window.addEventListener('resize', resizeChart);
return () => window.removeEventListener('resize', resizeChart);
}, []);
return <div className={styles.chartWrapper}>
{
options.map((option, index) => <div className={styles.chart} style={{ ...option.custom_style }}>
<BasicChart ref={chartRef} style={{ width: '100%', height: '100%' }} key={`${index}_${option.series.length}`}
option={option}/>
</div>)
}
</div>;
};
export default ChartComponent;
.chartWrapper {
width: 100%;
height: 100%;
display: flex;
padding: 0 8px 8px;
gap: 8px;
overflow: hidden;
.chart {
background: #ffffff;
}
}
import React, { useRef } from 'react';
import styles from './index.less';
const LayOutComponent = ({ layoutOptions, children }) => {
const {
layout,
chartCount,
tableConfigs,
chartConfigs,
} = layoutOptions;
const { width, height } = tableConfigs[0];
const layoutRef = useRef({
layoutMap: {
'上表下图': {
flexDirection: 'column',
},
'上图下表': {
flexDirection: 'column-reverse',
},
'左表右图': {
flexDirection: 'row',
},
'左图右表': {
flexDirection: 'row-reverse',
},
},
chartLayoutMap: {
'上表下图': {
flexDirection: 'row',
},
'上图下表': {
flexDirection: 'row',
},
'左表右图': {
flexDirection: 'column',
},
'左图右表': {
flexDirection: 'column',
},
},
});
return <div className={styles.wrapper} style={{ ...layoutRef.current.layoutMap[layout] }}>
<div className={styles.tableWrapper} style={{ width, height }}>
{children.find(item => item.key === 'table')}
</div>
<div className={styles.chartWrapper}
style={{ ...layoutRef.current.chartLayoutMap[layout] }}>
{children.find(item => item.key === 'chart')}
</div>
</div>;
};
export default LayOutComponent;
.wrapper {
display: flex;
width: 100%;
height: 100%;
gap: 6px;
.chartWrapper {
flex:1;
overflow: hidden;
}
.tableWrapper {
}
}
/**
* 原版报表功能,支持报表展示;
* 当前功能,配置了图表,则暂时报表+图表;否则只展示报表
* @Steps
* 1. 获取图表配置
* */
import React, { useEffect, useRef, useState } from 'react';
import ChartComponent from '../ReportWithChart/ChartComponent';
import LayOutComponent from '../ReportWithChart/ChartComponent';
import ReportsManage from '../../ReportsManage/ReportsManage';
import { reportService } from '../../api';
import { isString } from 'lodash';
const ReportWithChart = (props) => {
const { reportName, config } = props.params;
const [layoutOptions, setLayoutOptions] = useState(null);
const [chartOptions, setChartOptions] = useState(null);
const [reportData, setReportData] = useState(null);
const reportRef = useRef(null);
const getChartConfig = () => {
return reportService.getChartConfig({
reportName,
});
};
const trigger = (str) => {
let _data = reportRef?.current?.getData();
setReportData(_data);
};
useEffect(() => {
async function _getData() {
let _layoutOptions = null;
let _chartOptions = null;
if (config && isString(config)) {
let _str = JSON.parse(config);
_layoutOptions = _str.layoutOptions;
_chartOptions = _str.chartOptions;
} else {
let _config = await getChartConfig();
let _str = JSON.parse(_config.data.configInfo);
_layoutOptions = _str.layoutOptions;
_chartOptions = _str.chartOptions;
}
setLayoutOptions(_layoutOptions);
setChartOptions(_chartOptions);
}
_getData();
}, [config]);
return <>{layoutOptions && chartOptions ? <LayOutComponent layoutOptions={layoutOptions}>
<ReportsManage key={'table'} {...props} ref={reportRef} trigger={trigger}/>
<ChartComponent key={'chart'} reportName={reportName} chartOptions={chartOptions} reportData={reportData}/>
</LayOutComponent> : <ReportsManage {...props} ref={reportRef} trigger={trigger}/>
}</>;
};
export default ReportWithChart;
...@@ -102,7 +102,6 @@ const ReportsDataSourceSetting = () => { ...@@ -102,7 +102,6 @@ const ReportsDataSourceSetting = () => {
setSubmitLoading(false); setSubmitLoading(false);
}) })
.catch((err) => { .catch((err) => {
console.log(err);
setSubmitLoading(false); setSubmitLoading(false);
}); });
}, },
......
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
* edit 除删除外的权限 * edit 除删除外的权限
* scan 查看权限 * scan 查看权限
* */ * */
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, {forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import { import {
DeleteOutlined, DeleteOutlined,
DownOutlined, DownOutlined,
...@@ -159,11 +159,14 @@ import { ...@@ -159,11 +159,14 @@ import {
isArray, isArray,
isString, isString,
returnHandledNumber, returnHandledNumber,
returnSummaryNumber,
} from './utils/utils'; } from './utils/utils';
import { reportService } from '../api/index'; import { reportService } from '../api/index';
import { exportAccountData, reportFilesDownload } from '../api/service/report'; import { exportAccountData, reportFilesDownload } from '../api/service/report';
import { DownloadAccountFiles } from '../api/service/workflow'; import { DownloadAccountFiles } from '../api/service/workflow';
import DatePickerGroup from '../components/DatePickerGroup'; import DatePickerGroup from '../components/DatePickerGroup';
import Print from './Components/Print';
const ControlsType = ['下拉', '多选', '日期', '复选框']; const ControlsType = ['下拉', '多选', '日期', '复选框'];
const fieldSplitStr = '-'; // fieldGroup用来分割 const fieldSplitStr = '-'; // fieldGroup用来分割
const { Option } = Select; const { Option } = Select;
...@@ -178,6 +181,7 @@ const PERMISSION = { ...@@ -178,6 +181,7 @@ const PERMISSION = {
// 操作条按钮 // 操作条按钮
'sortBtn', 'sortBtn',
'exportBtn', 'exportBtn',
'printBtn',
// 操作列 // 操作列
'editBtn', 'editBtn',
'deleteBtn', 'deleteBtn',
...@@ -188,6 +192,7 @@ const PERMISSION = { ...@@ -188,6 +192,7 @@ const PERMISSION = {
'pagination', 'pagination',
'sortBtn', 'sortBtn',
'exportBtn', 'exportBtn',
'printBtn',
// 操作列 // 操作列
'editBtn', 'editBtn',
], ],
...@@ -195,7 +200,8 @@ const PERMISSION = { ...@@ -195,7 +200,8 @@ const PERMISSION = {
}; };
const USER_ID = window?.globalConfig?.userInfo?.OID || 0; const USER_ID = window?.globalConfig?.userInfo?.OID || 0;
const MODEL = ['all', 'year', 'quarter', 'month', 'week', 'day']; const MODEL = ['all', 'year', 'quarter', 'month', 'week', 'day'];
const ReportsManage = (props) => { const ReportsManage = forwardRef((props, ref) => {
const { trigger } = props;
const { const {
reportName, reportName,
pageSize, pageSize,
...@@ -207,8 +213,8 @@ const ReportsManage = (props) => { ...@@ -207,8 +213,8 @@ const ReportsManage = (props) => {
permissionType, permissionType,
permissionField, permissionField,
waterMark, waterMark,
rowSelect,
} = props.params; } = props.params;
const handleCustomerState = (customState) => { const handleCustomerState = (customState) => {
if (isArray(customState)) { if (isArray(customState)) {
return customState; return customState;
...@@ -216,19 +222,18 @@ const ReportsManage = (props) => { ...@@ -216,19 +222,18 @@ const ReportsManage = (props) => {
return customState.split(','); return customState.split(',');
} }
}; };
const permission = customState const permission = customState
? handleCustomerState(customState) ? handleCustomerState(customState)
: PERMISSION[state || 'delete']; : PERMISSION[state || 'delete'];
const waterMarkCustom = props.params.waterMark === undefined ? false : true; const waterMarkCustom = props.params.waterMark === undefined ? false : true;
const tableWrapperRef = useRef(); const tableWrapperRef = useRef();
const controlRef = useRef(); const controlRef = useRef();
if (!reportName) if (!reportName)
return ( return (
<div className={style.lackParams}> <div className={style.lackParams}>
未配置reportName,请完善配置并重新登陆后查看页面! 未配置reportName,请完善配置并重新登陆后查看页面!
</div> </div>
); );
const [isInit, setIsInit] = useState(true); const [isInit, setIsInit] = useState(true);
const [firstToGetData, setFirstToGetData] = useState(false); const [firstToGetData, setFirstToGetData] = useState(false);
...@@ -243,8 +248,8 @@ const ReportsManage = (props) => { ...@@ -243,8 +248,8 @@ const ReportsManage = (props) => {
pageSizeOptions: [ pageSizeOptions: [
...new Set([20, 50, 100].concat(handlePageSize(pageSize))), ...new Set([20, 50, 100].concat(handlePageSize(pageSize))),
] ]
.filter((item) => Number(item)) .filter((item) => Number(item))
.sort((a, b) => Number(a) - Number(b)), .sort((a, b) => Number(a) - Number(b)),
showQuickJumper: true, showQuickJumper: true,
showSizeChanger: true, showSizeChanger: true,
}); });
...@@ -275,13 +280,13 @@ const ReportsManage = (props) => { ...@@ -275,13 +280,13 @@ const ReportsManage = (props) => {
const [selectedRows, setSelectedRows] = useState([]); // 选中的表格行 const [selectedRows, setSelectedRows] = useState([]); // 选中的表格行
// const // const
const [timeFrom, setTimeFrom] = useState( const [timeFrom, setTimeFrom] = useState(
moment().startOf(initDateModel).format(dateFormat), moment().startOf(initDateModel).format(dateFormat),
); );
const [timeTo, setTimeTo] = useState( const [timeTo, setTimeTo] = useState(
moment().endOf(initDateModel).format(dateFormat), moment().endOf(initDateModel).format(dateFormat),
); );
const [extra, setExtra] = useState(<></>); const [extra, setExtra] = useState(<></>);
const [sortModalVisible, setSortModalVisible] = useState(false); const [showSortBtn, setShowSortBtn] = useState(false);
const [currentReportId, setCurrentReportId] = useState(null); const [currentReportId, setCurrentReportId] = useState(null);
const [hasDatePicker, setHasDatePicker] = useState(''); const [hasDatePicker, setHasDatePicker] = useState('');
const [defaultDateConfig, setDefaultDateConfig] = useState({ const [defaultDateConfig, setDefaultDateConfig] = useState({
...@@ -305,73 +310,79 @@ const ReportsManage = (props) => { ...@@ -305,73 +310,79 @@ const ReportsManage = (props) => {
const [isLocalDataSource, setIsLocalDataSource] = useState(0); // 如果是本地数据源,那么值为0;反之,则是外部数据源 const [isLocalDataSource, setIsLocalDataSource] = useState(0); // 如果是本地数据源,那么值为0;反之,则是外部数据源
const [showWaterMarkModal, setShowWaterMarkModal] = useState(false); const [showWaterMarkModal, setShowWaterMarkModal] = useState(false);
const [waterMarkContent, setWaterMarkContent] = useState(waterMark); // 水印内容 const [waterMarkContent, setWaterMarkContent] = useState(waterMark); // 水印内容
useImperativeHandle(ref, () => {
let _a = selectedRows;
return {
getData: () => ({ selectedRows }),
};
}, [selectedRows.length]);
const menu = () => { const menu = () => {
const _item = [ const _item = [
{ {
label: ( label: (
<Button <Button
size="middle" size="middle"
loading={exportLoading} loading={exportLoading}
type="text" type="text"
onClick={() => exportModule('pdf', 'pdf')} onClick={() => exportModule('pdf', 'pdf')}
icon={<ExportOutlined/>} icon={<ExportOutlined/>}
> >
导出pdf 导出pdf
</Button> </Button>
), ),
key: 'exportPdf', key: 'exportPdf',
}, },
{ {
label: ( label: (
<Button <Button
size="middle" size="middle"
loading={exportLoading} loading={exportLoading}
type="text" type="text"
onClick={() => exportModule('excel', 'xls')} onClick={() => exportModule('excel', 'xls')}
icon={<ExportOutlined/>} icon={<ExportOutlined/>}
> >
导出Excel 导出Excel
</Button> </Button>
), ),
key: 'exportExcel', key: 'exportExcel',
}, },
{ {
label: ( label: (
<Button <Button
size="middle" size="middle"
loading={exportLoading} loading={exportLoading}
type="text" type="text"
onClick={() => exportImage()} onClick={() => exportImage()}
icon={<ExportOutlined/>} icon={<ExportOutlined/>}
> >
导出JPG 导出JPG
</Button> </Button>
), ),
key: 'excelPdf', key: 'excelPdf',
}, },
]; ];
let exportFilesItem = { let exportFilesItem = {
label: ( label: (
<Button <Button
size="middle" size="middle"
loading={exportLoading} loading={exportLoading}
type="text" type="text"
onClick={() => onClick={() =>
selectedRows.length === 0 selectedRows.length === 0
? message.warning('请选择导出数据!') ? message.warning('请选择导出数据!')
: // 配置有waterMark字段,但是没有设置默认值时,弹出弹窗,输入水印内容;如果有默认值,则使用默认水印 : // 配置有waterMark字段,但是没有设置默认值时,弹出弹窗,输入水印内容;如果有默认值,则使用默认水印
!!waterMarkCustom && !waterMark !!waterMarkCustom && !waterMark
? setShowWaterMarkModal(true) ? setShowWaterMarkModal(true)
: exportFiles() : exportFiles()
} }
icon={<ExportOutlined/>} icon={<ExportOutlined/>}
> >
批量导出 批量导出
</Button> </Button>
), ),
key: 'exportGroup', key: 'exportGroup',
}; };
if (permission.includes('rowSelect')) _item.push(exportFilesItem); if (rowSelect === 'true') _item.push(exportFilesItem);
return <Menu items={_item}/>; return <Menu items={_item}/>;
}; };
const exportModule = (type, extension) => { const exportModule = (type, extension) => {
...@@ -383,41 +394,41 @@ const ReportsManage = (props) => { ...@@ -383,41 +394,41 @@ const ReportsManage = (props) => {
userId: USER_ID, userId: USER_ID,
}); });
exportAccountData({ responseType: 'blob' }, { exportType: type }, _data) exportAccountData({ responseType: 'blob' }, { exportType: type }, _data)
.then((res) => { .then((res) => {
const file = new FileReader(); const file = new FileReader();
file.readAsText(res, 'utf-8'); file.readAsText(res, 'utf-8');
file.onload = function() { file.onload = function() {
// 接口返回的是blob类型,需转换为json判断code值 // 接口返回的是blob类型,需转换为json判断code值
const messageInfo = isSuccess(file.result); const messageInfo = isSuccess(file.result);
if (messageInfo.code !== 0) { if (messageInfo.code !== 0) {
message.warning( message.warning(
messageInfo.code === -1 && messageInfo.msg messageInfo.code === -1 && messageInfo.msg
? messageInfo.msg ? messageInfo.msg
: '下载失败!', : '下载失败!',
); );
setExportLoading(false); setExportLoading(false);
} else { } else {
const url = window.URL.createObjectURL( const url = window.URL.createObjectURL(
new Blob([res], { new Blob([res], {
type: 'application/octet-stream;charset=UTF-8', type: 'application/octet-stream;charset=UTF-8',
}), }),
); );
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.target = '_blank'; a.target = '_blank';
a.download = a.download =
`${reportName}${moment() `${reportName}${moment()
.format('YYYY-MM-DD-HH-mm-ss') .format('YYYY-MM-DD-HH-mm-ss')
.replaceAll('-', '')}.` + extension; .replaceAll('-', '')}.` + extension;
a.click(); a.click();
a.remove(); a.remove();
setExportLoading(false); setExportLoading(false);
} }
}; };
}) })
.catch((err) => { .catch((err) => {
setExportLoading(false); setExportLoading(false);
}); });
}; };
const exportImage = () => { const exportImage = () => {
let _data = addFilterAndSearchParams({ let _data = addFilterAndSearchParams({
...@@ -434,21 +445,21 @@ const ReportsManage = (props) => { ...@@ -434,21 +445,21 @@ const ReportsManage = (props) => {
const messageInfo = isSuccess(file.result); const messageInfo = isSuccess(file.result);
if (messageInfo.code !== 0) { if (messageInfo.code !== 0) {
message.warning( message.warning(
messageInfo.code === -1 && messageInfo.msg messageInfo.code === -1 && messageInfo.msg
? messageInfo.msg ? messageInfo.msg
: '下载失败!', : '下载失败!',
); );
setExportLoading(false); setExportLoading(false);
} else { } else {
const url = window.URL.createObjectURL( const url = window.URL.createObjectURL(
new Blob([res], { type: 'application/zip;charset=UTF-8' }), new Blob([res], { type: 'application/zip;charset=UTF-8' }),
); );
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.target = '_blank'; a.target = '_blank';
a.download = `${reportName}${moment() a.download = `${reportName}${moment()
.format('YYYY-MM-DD-HH-mm-ss') .format('YYYY-MM-DD-HH-mm-ss')
.replaceAll('-', '')}.zip`; .replaceAll('-', '')}.zip`;
a.click(); a.click();
a.remove(); a.remove();
setExportLoading(false); setExportLoading(false);
...@@ -471,9 +482,9 @@ const ReportsManage = (props) => { ...@@ -471,9 +482,9 @@ const ReportsManage = (props) => {
watermark: waterMarkContent, // 文件水印内容 watermark: waterMarkContent, // 文件水印内容
}); });
reportFilesDownload( reportFilesDownload(
{ responseType: 'blob' }, { responseType: 'blob' },
{ exportType: 'application/zip;charset=UTF-8' }, { exportType: 'application/zip;charset=UTF-8' },
_data, _data,
).then((res) => { ).then((res) => {
const file = new FileReader(); const file = new FileReader();
file.readAsText(res, 'utf-8'); file.readAsText(res, 'utf-8');
...@@ -481,21 +492,21 @@ const ReportsManage = (props) => { ...@@ -481,21 +492,21 @@ const ReportsManage = (props) => {
const messageInfo = isSuccess(file.result); const messageInfo = isSuccess(file.result);
if (messageInfo.code !== 0) { if (messageInfo.code !== 0) {
message.warning( message.warning(
messageInfo.code === -1 && messageInfo.msg messageInfo.code === -1 && messageInfo.msg
? messageInfo.msg ? messageInfo.msg
: '下载失败!', : '下载失败!',
); );
setExportLoading(false); setExportLoading(false);
} else { } else {
const url = window.URL.createObjectURL( const url = window.URL.createObjectURL(
new Blob([res], { type: 'application/octet-stream;charset=UTF-8' }), new Blob([res], { type: 'application/octet-stream;charset=UTF-8' }),
); );
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.target = '_blank'; a.target = '_blank';
a.download = `${reportName}${moment() a.download = `${reportName}${moment()
.format('YYYY-MM-DD-HH-mm-ss') .format('YYYY-MM-DD-HH-mm-ss')
.replaceAll('-', '')}.zip`; .replaceAll('-', '')}.zip`;
a.click(); a.click();
a.remove(); a.remove();
setExportLoading(false); setExportLoading(false);
...@@ -509,7 +520,7 @@ const ReportsManage = (props) => { ...@@ -509,7 +520,7 @@ const ReportsManage = (props) => {
// 添加附件下载功能 --edit by zhangzhiwei 2023/06/27 // 添加附件下载功能 --edit by zhangzhiwei 2023/06/27
const downloadAccountFiles = (path) => { const downloadAccountFiles = (path) => {
let isZip = path.includes(','); let isZip = path.includes(',');
let fileName = path.split('\\')[1]; let fileName = path.includes('\\') ? path.split('\\')[1] : path.split('/')[1];
let fileType = path.split('.')[1]; let fileType = path.split('.')[1];
let _data = { let _data = {
files: path, files: path,
...@@ -524,14 +535,14 @@ const ReportsManage = (props) => { ...@@ -524,14 +535,14 @@ const ReportsManage = (props) => {
const messageInfo = isSuccess(file.result); const messageInfo = isSuccess(file.result);
if (messageInfo.code !== 0) { if (messageInfo.code !== 0) {
message.warning( message.warning(
messageInfo.code === -1 && messageInfo.msg messageInfo.code === -1 && messageInfo.msg
? messageInfo.msg ? messageInfo.msg
: '下载失败!', : '下载失败!',
); );
setExportLoading(false); setExportLoading(false);
} else { } else {
const url = window.URL.createObjectURL( const url = window.URL.createObjectURL(
new Blob([res], { type: 'application/zip;charset=UTF-8' }), new Blob([res], { type: 'application/zip;charset=UTF-8' }),
); );
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
...@@ -580,26 +591,26 @@ const ReportsManage = (props) => { ...@@ -580,26 +591,26 @@ const ReportsManage = (props) => {
if (searchContent) _data.content = searchContent; if (searchContent) _data.content = searchContent;
// filterObject是存起来的控制栏的过滤条件 // filterObject是存起来的控制栏的过滤条件
let _filters = Object.keys(filterObject) let _filters = Object.keys(filterObject)
.filter((key) => { .filter((key) => {
let _value = filterObject[key]; let _value = filterObject[key];
return ( return (
(isString(_value) && _value) || (isArray(_value) && _value.length) (isString(_value) && _value) || (isArray(_value) && _value.length)
); );
}) })
.map((key) => { .map((key) => {
let _value = filterObject[key]; let _value = filterObject[key];
if (isString(_value) && _value) if (isString(_value) && _value)
return { return {
fieldAlias: key, fieldAlias: key,
fieldValue: _value, fieldValue: _value,
}; };
if (isArray(_value) && _value.length) if (isArray(_value) && _value.length)
return { return {
fieldAlias: key, fieldAlias: key,
fieldValue: _value.join(','), fieldValue: _value.join(','),
}; };
return false; return false;
}); });
// 加上时间过滤参数 // 加上时间过滤参数
if (dateModel !== 'all' && hasDatePicker && timeFrom && timeTo) { if (dateModel !== 'all' && hasDatePicker && timeFrom && timeTo) {
_filters.push({ _filters.push({
...@@ -609,8 +620,8 @@ const ReportsManage = (props) => { ...@@ -609,8 +620,8 @@ const ReportsManage = (props) => {
} }
// 合并手动传入的filters;当配置为筛选框时,筛选框选择值后,会优先取筛选框值 // 合并手动传入的filters;当配置为筛选框时,筛选框选择值后,会优先取筛选框值
if ( if (
filterFields && filterFields &&
!_filters.find((item) => item.fieldAlias === filterFields) !_filters.find((item) => item.fieldAlias === filterFields)
) { ) {
let _customerFilterArray = filterFields?.split('|') || []; let _customerFilterArray = filterFields?.split('|') || [];
let _customerValueArray = filterValues?.split('|') || []; let _customerValueArray = filterValues?.split('|') || [];
...@@ -624,7 +635,7 @@ const ReportsManage = (props) => { ...@@ -624,7 +635,7 @@ const ReportsManage = (props) => {
// 表格上的自定义排序的按钮 // 表格上的自定义排序的按钮
if (sorterObject && sorterObject.order) { if (sorterObject && sorterObject.order) {
_data.sortFields = `${sorterObject.columnKey} ${ _data.sortFields = `${sorterObject.columnKey} ${
sorterObject.order === 'ascend' ? 'asc' : 'desc' sorterObject.order === 'ascend' ? 'asc' : 'desc'
}`; }`;
} }
// 增加权限过滤的参数 // 增加权限过滤的参数
...@@ -638,21 +649,12 @@ const ReportsManage = (props) => { ...@@ -638,21 +649,12 @@ const ReportsManage = (props) => {
}; };
// 排序字符串处理成数组 // 排序字符串处理成数组
const handleSortString = (sortString) => { const handleSortString = (sortString) => {
let _sortStringArray = sortString.split(',').map((item, index) => { let _sortStringArray = sortString.map((item, index) => {
let _item = item.split(' '); return {
if (index === 0) { label: index === 0 ? '主要排序' : '次要排序',
return { value: item.fieldAlias,
label: '主要排序', sort: item.sortType,
value: _item[0].replace(/\[|\]/g, ''), };
sort: _item[1],
};
} else {
return {
label: '次要排序',
value: _item[0].replace(/\[|\]/g, ''),
sort: _item[1],
};
}
}); });
setSelectedSortFields(_sortStringArray); setSelectedSortFields(_sortStringArray);
}; };
...@@ -699,74 +701,61 @@ const ReportsManage = (props) => { ...@@ -699,74 +701,61 @@ const ReportsManage = (props) => {
userId: USER_ID, userId: USER_ID,
}); });
reportService reportService
.getReportInfo(_data) .getReportInfo(_data)
.then((res) => { .then((res) => {
if (res.code === 0) { if (res.code === 0) {
let _reportDetails = res.data?.reportDetails; let _statisticalValues = res.data?.statisticalValues;
let _statisticalValues = res.data?.statisticalValues; // webAPIData 存在值时使用webAPIData,否则使用原始数据
// webAPIData 存在值时使用webAPIData,否则使用原始数据 let _tableData = res?.data?.data?.list || [];
let _tableData = res.data.webAPIData?.webAPIData if (isInit) {
? JSON.parse(res.data.webAPIData.webAPIData) setIsInit(false);
: res.data?.data?.list.map((item) => { }
if ( getTableSummaryConfig(reportConfigs, _statisticalValues);
Object.prototype.toString.call(item) === '[object String]' getTableHeaderConfig(reportConfigs, _tableData);
) { let _pagination = { ...pagination };
return JSON.parse(item); _pagination.total = res.data.data?.totalCount || 0;
setPagination(_pagination);
setTableData(_tableData);
// 处理排序字段
if (_tableData) {
setShowSortBtn(true);
} else {
setShowSortBtn(false);
} }
return item;
}) || [];
let _sortString = res.data.sortString;
if (isInit) {
setIsInit(false);
}
setIsLocalDataSource(res.data.sourcesId);
getTableSummaryConfig(_reportDetails, _statisticalValues);
getTableHeaderConfig(_reportDetails, _tableData);
let _pagination = { ...pagination };
_pagination.total = res.data.webAPIData?.webAPIData
? res.data.webAPIData.totalCount
: res.data.data?.totalCount;
setPagination(_pagination);
setTableData(_tableData);
// 处理排序字段
handleSortString(_sortString);
if (_tableData) {
setSortModalVisible(true);
setCurrentReportId(_reportDetails[0]?.reportId);
} else { } else {
setSortModalVisible(false); setShowSortBtn(false);
setCurrentReportId(null); message.error('未能查询到报表数据!');
let _pagination = { ...pagination };
_pagination.total = 0;
_pagination.current = 1;
setPagination(_pagination);
setTableData([]);
} }
} else { if (isInit) setIsInit(false);
setSortModalVisible(false); setTableLoading(false);
setCurrentReportId(null); })
message.error('未能查询到报表数据!'); .catch((err) => {
let _pagination = { ...pagination }; setTableLoading(false);
_pagination.total = 0; });
_pagination.current = 1;
setPagination(_pagination);
setTableData([]);
}
if (isInit) setIsInit(false);
setTableLoading(false);
})
.catch((err) => {
console.log(err);
setTableLoading(false);
});
}; };
const getConfigs = () => { const getConfigs = async () => {
reportService.getReportDetails({ reportName }).then((res) => { let _configs = await reportService.getReportDetails({ reportName });
if (res.code === 0) { if (_configs.code === 0) {
setReportConfigs(res.data); let _data = _configs.data.data;
setConfig(res.data); setReportConfigs(_data);
setColumns(returnColumn(res.data)); setIsLocalDataSource(_configs.data.sourceID);
setTableStruct(res.data); setConfig(_data);
setFirstToGetData(true); setColumns(returnColumn(_data));
} else { setTableStruct(_data);
message.error(res.msg); setCurrentReportId(_configs.data.reportId);
setFirstToGetData(true);
let _sortFields = await reportService.getReportSortFields({ reportId: _configs.data.reportId });
if (_sortFields.code === 0) {
handleSortString(_sortFields.data);
} }
}); } else {
message.error('获取配置失败!');
}
}; };
/** /**
* @Description: 在配置项中,isFilter: true 用来渲染控制框;filterRule: 下拉/文本/多选 * @Description: 在配置项中,isFilter: true 用来渲染控制框;filterRule: 下拉/文本/多选
...@@ -775,19 +764,20 @@ const ReportsManage = (props) => { ...@@ -775,19 +764,20 @@ const ReportsManage = (props) => {
let _data = config.filter((item) => item.isFilter); let _data = config.filter((item) => item.isFilter);
let _searchPlaceholder = []; let _searchPlaceholder = [];
_data _data
.filter((item) => item.filterRule === '文本') .filter((item) => item.filterRule === '文本')
.forEach((item) => _searchPlaceholder.push(item.fieldAlias)); .forEach((item) => _searchPlaceholder.push(item.fieldAlias));
setSearchPlaceholder(_searchPlaceholder); setSearchPlaceholder(_searchPlaceholder);
let _controls = _data.filter((item) => let _controls = _data.filter((item) =>
ControlsType.includes(item.filterRule), ControlsType.includes(item.filterRule),
); );
setControls(_controls); setControls(_controls);
handleControls(_controls); // 处理控制条,设定默认值 handleControls(_controls); // 处理控制条,设定默认值
handleDate(_controls); // 处理日期, 设定默认值 handleDate(_controls); // 处理日期, 设定默认值
}; };
const getTableHeaderConfig = (config, data) => { const getTableHeaderConfig = (config, data) => {
// setTableStruct(config); // 上一版本,下两句代码是被注释了,注释后影响了向下合并功能;如果发现其他问题,请排查此处代码
// setColumns(returnColumn(config, data)); setTableStruct(config);
setColumns(returnColumn(config, data));
setAllSortFields(returnSortFields(config)); setAllSortFields(returnSortFields(config));
}; };
const getTableSummaryConfig = (config, summary) => { const getTableSummaryConfig = (config, summary) => {
...@@ -801,32 +791,32 @@ const ReportsManage = (props) => { ...@@ -801,32 +791,32 @@ const ReportsManage = (props) => {
let _colSpan = -1; let _colSpan = -1;
let _index = 0; let _index = 0;
config config
.filter((item) => item.isShow) .filter((item) => item.isShow)
.forEach((item, index) => { .forEach((item, index) => {
if (item.isStatistics) { if (item.isStatistics) {
_index += 1; _index += 1;
_colSpan = -1; _colSpan = -1;
_summaryConfig[item.fieldAlias] = { _summaryConfig[item.fieldAlias] = {
fieldName: item.fieldAlias, fieldName: item.fieldAlias,
index, index,
alignType: item.alignType, alignType: item.alignType,
type: item.statisticsRule, type: item.statisticsRule,
isMoney: item.type === '数值' && !!hasMoney(item.configItems), isMoney: item.type === '数值' && !!hasMoney(item.configItems),
configItems: item.configItems, configItems: item.configItems,
}; };
} else {
let _name = `空值行${_index}`;
if (_colSpan === -1) {
_colSpan = 1;
} else { } else {
_colSpan += 1; let _name = `空值行${_index}`;
if (_colSpan === -1) {
_colSpan = 1;
} else {
_colSpan += 1;
}
_summaryConfig[_name] = {
fieldName: _name,
colSpan: _colSpan,
};
} }
_summaryConfig[_name] = { });
fieldName: _name,
colSpan: _colSpan,
};
}
});
summary.forEach((item) => { summary.forEach((item) => {
switch (item.totalType) { switch (item.totalType) {
case '全部': case '全部':
...@@ -898,8 +888,8 @@ const ReportsManage = (props) => { ...@@ -898,8 +888,8 @@ const ReportsManage = (props) => {
let _tempObj = {}; let _tempObj = {};
let _fieldAliasArray = data let _fieldAliasArray = data
? handleDataToGetRowSpanArray(_config, data) ? handleDataToGetRowSpanArray(_config, data)
: false; // 需要向下合并的字段 : false; // 需要向下合并的字段
_config.forEach((item) => { _config.forEach((item) => {
let _item = { let _item = {
title: item.fieldAlias, title: item.fieldAlias,
...@@ -907,7 +897,6 @@ const ReportsManage = (props) => { ...@@ -907,7 +897,6 @@ const ReportsManage = (props) => {
key: item.fieldAlias, key: item.fieldAlias,
ellipsis: true, ellipsis: true,
onCell: (record, rowIndex) => { onCell: (record, rowIndex) => {
// console.log('Record: ', record); // record是这条记录,index是rowIndex
// 1. 如果该字段是需要向下合并的,则进入判断 // 1. 如果该字段是需要向下合并的,则进入判断
let _obj = {}; let _obj = {};
if (_fieldAliasArray && _fieldAliasArray[item.fieldAlias]) { if (_fieldAliasArray && _fieldAliasArray[item.fieldAlias]) {
...@@ -935,36 +924,41 @@ const ReportsManage = (props) => { ...@@ -935,36 +924,41 @@ const ReportsManage = (props) => {
rest = [setModalVisible, setExtra]; rest = [setModalVisible, setExtra];
} else if (item.type === '附件') { } else if (item.type === '附件') {
// 附件支持点击下载 --- edit by zhangzhiwei on 2023/06/27 // 附件支持点击下载 --- edit by zhangzhiwei on 2023/06/27
let _value = value ? value.split('\\')[1] : ''; let _value = '';
if (value && value.includes('\\')) {
_value = value.split('\\')[1];
} else if (value && value.includes('/')) {
_value = value.split('/')[1];
}
return ( return (
_value && ( _value && (
<div <div
className={style.link} className={style.link}
onClick={() => { onClick={() => {
downloadAccountFiles(value); downloadAccountFiles(value);
}} }}
> >
{_value} {_value}
</div> </div>
) )
); );
} }
return mapHandleType(item.type)( return mapHandleType(item.type)(
item, item,
[undefined, null].includes(value) ? '' : value, [undefined, null].includes(value) ? '' : value,
record, record,
...rest, ...rest,
); );
}, },
}; };
_item.width = _item.width =
(!isNaN(Number(item.columnWidth)) && item.columnWidth) || 200; // 列宽,不设置时默认给200; (!isNaN(Number(item.columnWidth)) && item.columnWidth) || 200; // 列宽,不设置时默认给200;
if (item.fixedColumn) _item.fixed = item.fixedColumn; // 固定位置 if (item.fixedColumn) _item.fixed = item.fixedColumn; // 固定位置
_item.align = item.alignType || 'left'; // 单元格内对齐方式 _item.align = item.alignType || 'left'; // 单元格内对齐方式
let _keyArray = ( let _keyArray = (
item.fieldGroup || item.fieldGroup ||
item.fieldAlias || item.fieldAlias ||
item.fieldName item.fieldName
).split(fieldSplitStr); ).split(fieldSplitStr);
// 自定义排序 // 自定义排序
let _sortFields = handleSortFields(sortFields); let _sortFields = handleSortFields(sortFields);
...@@ -1010,71 +1004,70 @@ const ReportsManage = (props) => { ...@@ -1010,71 +1004,70 @@ const ReportsManage = (props) => {
fixed: 'right', fixed: 'right',
render: (text, record) => { render: (text, record) => {
return ( return (
<Space className={style.handleColumnWrapper}> <Space className={style.handleColumnWrapper}>
{record.IsAllow ? ( {record.IsAllow ? (
<Popconfirm <Popconfirm
placement="topRight" placement="topRight"
title={'确认取消关注吗'} title={'确认取消关注吗'}
icon={<QuestionCircleOutlined/>} icon={<QuestionCircleOutlined/>}
onConfirm={() => focusProject(record)} onConfirm={() => focusProject(record)}
> >
<Tooltip title={'点击取消关注'}> <Tooltip title={'点击取消关注'}>
<HeartTwoTone twoToneColor="#eb2f96"/> <HeartTwoTone twoToneColor="#eb2f96"/>
</Tooltip> </Tooltip>
</Popconfirm> </Popconfirm>
) : ( ) : (
<Popconfirm <Popconfirm
placement="topRight" placement="topRight"
title={'确认关注项目吗'} title={'确认关注项目吗'}
icon={<QuestionCircleOutlined/>} icon={<QuestionCircleOutlined/>}
onConfirm={() => focusProject(record)} onConfirm={() => focusProject(record)}
> >
<Tooltip title={'点击添加关注'}> <Tooltip title={'点击添加关注'}>
<HeartOutlined/> <HeartOutlined/>
</Tooltip> </Tooltip>
</Popconfirm> </Popconfirm>
)} )}
{permission.includes('editBtn') && !isLocalDataSource ? ( {permission.includes('editBtn') && !isLocalDataSource ? (
<FormOutlined <FormOutlined
className={style.editButton} className={style.editButton}
onClick={() => { onClick={() => {
// setEditComponentVisible(true); // setEditComponentVisible(true);
setModalType('编辑'); setModalType('编辑');
setCurrentData(record); setCurrentData(record);
}} }}
/> />
) : ( ) : (
'' ''
)} )}
{permission.includes('deleteBtn') && !isLocalDataSource ? ( {permission.includes('deleteBtn') && !isLocalDataSource ? (
<DeleteOutlined <DeleteOutlined
disabled disabled
className={style.deleteButton} className={style.deleteButton}
onClick={() => { onClick={() => {
Modal.confirm({ Modal.confirm({
content: '你确定要删除该数据吗?', content: '你确定要删除该数据吗?',
onOk: () => { onOk: () => {
reportService reportService
.delReportData({ .delReportData({
reportName: reportName, reportName: reportName,
userId: USER_ID, userId: USER_ID,
key: record.key, key: record.key,
}) })
.then((res) => { .then((res) => {
if (res.code === 0) { if (res.code === 0) {
message.success('删除成功!'); message.success('删除成功!');
setGetNewData(!getNewData); setGetNewData(!getNewData);
// getData(pagination); // 在onOk的回调函数内,打包后的代码会形成闭包 }
} });
},
}); });
}, }}
}); />
}} ) : (
/> ''
) : ( )}
'' </Space>
)}
</Space>
); );
}, },
}); });
...@@ -1086,163 +1079,161 @@ const ReportsManage = (props) => { ...@@ -1086,163 +1079,161 @@ const ReportsManage = (props) => {
setTableX(_x); setTableX(_x);
return _tempArray; return _tempArray;
}; };
// 返回表格数据 // 返回表格数据
const returnTable = useMemo(() => { const returnTable = useMemo(() => {
return ( return (
<BasicTable <BasicTable
rowKey={'Key'} rowKey={'key'}
bordered bordered
loading={tableLoading} loading={tableLoading}
dataSource={tableData} dataSource={tableData}
columns={columns} columns={columns}
onChange={(p, filters, sorter, extra) => { onChange={(p, filters, sorter, extra) => {
// 2023年4月7日,报表当前未实现filter功能,该参数暂未使用 // 2023年4月7日,报表当前未实现filter功能,该参数暂未使用
const { action } = extra; const { action } = extra;
if (action === 'paginate') { if (action === 'paginate') {
let _pagination = { ...pagination }; let _pagination = { ...pagination };
_pagination.current = p.current; _pagination.current = p.current;
_pagination.pageSize = p.pageSize; _pagination.pageSize = p.pageSize;
setPagination(_pagination); setPagination(_pagination);
} }
if (action === 'sort') { if (action === 'sort') {
setSorterObject(sorter); setSorterObject(sorter);
} }
}} }}
// 添加表格行勾选功能 --edit by zhangzhiwei on 2023/06/26 // 添加表格行勾选功能 --edit by zhangzhiwei on 2023/06/26
rowSelection={ rowSelection={
permission.includes('rowSelect') rowSelect === 'true'
? { ? {
type: 'checkbox', type: 'checkbox',
selectedRowKeys: selectedRows.map((item) => item.key), selectedRowKeys: selectedRows.map((item) => item.key),
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows); setSelectedRows(selectedRows);
}, },
}
: false
} }
: false pagination={permission.includes('pagination') ? pagination : false}
} // 237是内置图片高度
pagination={permission.includes('pagination') ? pagination : false} scroll={{
// 237是内置图片高度 y: tableData && tableData.length ? `calc(100% - 44px)` : 237,
scroll={{ x: tableX,
y: tableData && tableData.length ? `calc(100% - 44px)` : 237, }}
x: tableX, summary={(pageData) => {
}} if (summaryArray.length && tableData && tableData.length)
summary={(pageData) => { return (
if (summaryArray.length && tableData && tableData.length) <Table.Summary fixed>
return ( <Table.Summary.Row>
<Table.Summary fixed> {hasSinglePage
<Table.Summary.Row> ? summaryArray.map((item, index) => {
{hasSinglePage if (item.fieldName === '空值行0') {
? summaryArray.map((item, index) => { return (
if (item.fieldName === '空值行0') { <Table.Summary.Cell
return ( key={`summary_${index}`}
<Table.Summary.Cell index={0}
key={`summary_${index}`} colSpan={item.colSpan + (rowSelect === 'true' ? 2 : 1)}
index={0} >
colSpan={item.colSpan + 1}
>
<span <span
style={{ style={{
display: 'inline-block', display: 'inline-block',
width: '100%', width: '100%',
textAlign: 'center', textAlign: 'center',
}} }}
> >
小计 小计
</span> </span>
</Table.Summary.Cell> </Table.Summary.Cell>
); );
} else if (item.fieldName.includes('空值行')) { } else if (item.fieldName.includes('空值行')) {
return ( return (
<Table.Summary.Cell <Table.Summary.Cell
key={`summary_${index}`} key={`summary_${index}`}
index={0} index={0}
colSpan={item.colSpan} colSpan={item.colSpan}
/> />
); );
} else { } else {
return ( return (
<Table.Summary.Cell <Table.Summary.Cell
key={`summary_${index}`} key={`summary_${index}`}
index={0} index={0}
> >
<span <span
style={{ style={{
display: 'inline-block', display: 'inline-block',
width: '100%', width: '100%',
textAlign: item.alignType, textAlign: item.alignType,
}} }}
> >
{item.type.replace('', '')}:{' '} {item.type.replace('', '')}:{' '}
{returnHandledNumber( {returnSummaryNumber(
item.configItems, item.configItems,
item['单页'], item['单页'],
true, true,
)} )}
</span> </span>
</Table.Summary.Cell> </Table.Summary.Cell>
); );
} }
}) })
: ''} : ''}
</Table.Summary.Row> </Table.Summary.Row>
<Table.Summary.Row> <Table.Summary.Row>
{hasTotal {hasTotal
? summaryArray.map((item) => { ? summaryArray.map((item) => {
if (item.fieldName === '空值行0') { if (item.fieldName === '空值行0') {
return ( return (
<Table.Summary.Cell <Table.Summary.Cell
index={0} index={0}
colSpan={item.colSpan + 1} colSpan={item.colSpan + (rowSelect === 'true' ? 2 : 1)}
> >
<span <span
style={{ style={{
display: 'inline-block', display: 'inline-block',
width: '100%', width: '100%',
textAlign: 'center', textAlign: 'center',
}} }}
> >
总计 总计
</span> </span>
</Table.Summary.Cell> </Table.Summary.Cell>
); );
} else if (item.fieldName.includes('空值行')) { } else if (item.fieldName.includes('空值行')) {
return ( return (
<Table.Summary.Cell <Table.Summary.Cell
index={0} index={0}
colSpan={item.colSpan} colSpan={item.colSpan}
/> />
); );
} else { } else {
return ( return (
<Table.Summary.Cell index={0}> <Table.Summary.Cell index={0}>
<span <span
style={{ style={{
display: 'inline-block', display: 'inline-block',
width: '100%', width: '100%',
textAlign: item.alignType, textAlign: item.alignType,
}} }}
> >
{item.type.replace('', '')}:{' '} {item.type.replace('', '')}:{' '}
{returnHandledNumber( {returnSummaryNumber(
item.configItems, item.configItems,
item['全部'], item['全部'],
true, true,
)} )}
</span> </span>
</Table.Summary.Cell> </Table.Summary.Cell>
); );
} }
}) })
: ''} : ''}
</Table.Summary.Row> </Table.Summary.Row>
</Table.Summary> </Table.Summary>
); );
}} }}
/> />
); );
}, [columns, tableLoading, tableData, selectedRows]); }, [columns, tableLoading, tableData, selectedRows]);
const changeSortField = (value, index, key) => { const changeSortField = (value, index, key) => {
let _selectedSortFields = [...selectedSortFields]; let _selectedSortFields = [...selectedSortFields];
_selectedSortFields[index][key] = value; _selectedSortFields[index][key] = value;
...@@ -1270,46 +1261,46 @@ const ReportsManage = (props) => { ...@@ -1270,46 +1261,46 @@ const ReportsManage = (props) => {
const paginationHeight = 75; // 分页部分的高度 const paginationHeight = 75; // 分页部分的高度
const tableHeaderHeight = tableHeaderLevel * 40; // 表头高度 const tableHeaderHeight = tableHeaderLevel * 40; // 表头高度
const summaryHeight = summaryArray.length const summaryHeight = summaryArray.length
? 40 * (Number(hasTotal) + Number(hasSinglePage)) ? 40 * (Number(hasTotal) + Number(hasSinglePage))
: 0; // 总结栏的高度 : 0; // 总结栏的高度
const _minus = const _minus =
clientHeight - clientHeight -
_height - _height -
16 - 16 -
4 - 4 -
tableHeaderHeight - tableHeaderHeight -
paginationHeight - paginationHeight -
summaryHeight - summaryHeight -
10; 10;
setListHeight(clientHeight - _height - paginationHeight - 4 - 6 - 16 - 2); setListHeight(clientHeight - _height - paginationHeight - 4 - 6 - 16 - 2);
setTableY(_minus); setTableY(_minus);
}; };
const getTableLevel = (config) => { const getTableLevel = (config) => {
let _level = let _level =
config.reduce((final, curr) => { config.reduce((final, curr) => {
return (final = curr.level > final ? curr.level : final); return (final = curr.level > final ? curr.level : final);
}, 1) || 1; }, 1) || 1;
setTableHeaderLevel(_level); setTableHeaderLevel(_level);
}; };
const saveReportListSortFields = (callback) => { const saveReportListSortFields = (callback) => {
reportService reportService
.saveReportListSortFields({ .saveReportListSortFields({
reportId: currentReportId, reportId: currentReportId,
sortFields: selectedSortFields sortFields: selectedSortFields
.filter((item) => item.value) .filter((item) => item.value)
.map((item) => ({ .map((item) => ({
fieldAlias: item.value, fieldAlias: item.value,
sortType: item.sort, sortType: item.sort,
})), })),
}) })
.then((res) => { .then((res) => {
if (res.code === 0) { if (res.code === 0) {
message.success('排序保存成功!'); message.success('排序保存成功!');
callback(); callback();
} else { } else {
message.error(res.msg); message.error(res.msg);
} }
}); });
}; };
/** /**
* @Description: 判断是否存在【时间】类型的选择,并返回组件;并记录默认值 * @Description: 判断是否存在【时间】类型的选择,并返回组件;并记录默认值
...@@ -1322,8 +1313,8 @@ const ReportsManage = (props) => { ...@@ -1322,8 +1313,8 @@ const ReportsManage = (props) => {
_configItems.forEach((str) => { _configItems.forEach((str) => {
if (str.includes('defaultValue=')) { if (str.includes('defaultValue=')) {
controlSelectChange( controlSelectChange(
item.fieldAlias, item.fieldAlias,
str.replace('defaultValue=', ''), str.replace('defaultValue=', ''),
); );
} }
}); });
...@@ -1334,17 +1325,17 @@ const ReportsManage = (props) => { ...@@ -1334,17 +1325,17 @@ const ReportsManage = (props) => {
setHasDatePicker(_typeObj ? _typeObj.fieldAlias : ''); setHasDatePicker(_typeObj ? _typeObj.fieldAlias : '');
const _configItems = _typeObj?.configItems.split('|') || ['']; const _configItems = _typeObj?.configItems.split('|') || [''];
let _showModels = _configItems let _showModels = _configItems
.find((item) => item.includes('showModels=')) .find((item) => item.includes('showModels='))
?.replace('showModels=', '') ?.replace('showModels=', '')
?.split(','); ?.split(',');
let _defaultDate = _configItems let _defaultDate = _configItems
.find((item) => item.includes('defaultDate=')) .find((item) => item.includes('defaultDate='))
?.replace('defaultDate=', '') ?.replace('defaultDate=', '')
?.split(','); ?.split(',');
let _defaultModel = let _defaultModel =
_configItems _configItems
.find((item) => item.includes('defaultModel=')) .find((item) => item.includes('defaultModel='))
?.replace('defaultModel=', '') || 'year'; ?.replace('defaultModel=', '') || 'year';
_defaultDate = MODEL.includes(_defaultModel) ? _defaultDate : 'year'; // 确保值符合要求 _defaultDate = MODEL.includes(_defaultModel) ? _defaultDate : 'year'; // 确保值符合要求
if (_defaultDate && _defaultDate.length > 1) { if (_defaultDate && _defaultDate.length > 1) {
_defaultDate = { _defaultDate = {
...@@ -1361,17 +1352,17 @@ const ReportsManage = (props) => { ...@@ -1361,17 +1352,17 @@ const ReportsManage = (props) => {
} }
// 给定默认值,初始化时可以加载 // 给定默认值,初始化时可以加载
changeDate( changeDate(
{ {
dateFrom: _defaultDate?.dateFrom dateFrom: _defaultDate?.dateFrom
.clone() .clone()
.startOf(_defaultModel) .startOf(_defaultModel)
.format(dateFormat), .format(dateFormat),
dateTo: _defaultDate?.dateTo dateTo: _defaultDate?.dateTo
.clone() .clone()
.endOf(_defaultModel) .endOf(_defaultModel)
.format(dateFormat), .format(dateFormat),
}, },
_defaultModel, _defaultModel,
); );
setDefaultDateConfig({ setDefaultDateConfig({
defaultDate: _defaultDate?.dateFrom, defaultDate: _defaultDate?.dateFrom,
...@@ -1381,19 +1372,23 @@ const ReportsManage = (props) => { ...@@ -1381,19 +1372,23 @@ const ReportsManage = (props) => {
}; };
const focusProject = (record) => { const focusProject = (record) => {
reportService reportService
.setReportAllow({ .setReportAllow({
userId: USER_ID, userId: USER_ID,
reportName: reportName, reportName: reportName,
reportKey: record.key, reportKey: record.key,
}) })
.then((res) => { .then((res) => {
if (res.code === 0) { if (res.code === 0) {
message.success(`${record.IsAllow ? '取消' : '关注'}成功!`); message.success(`${record.IsAllow ? '取消' : '关注'}成功!`);
getData(pagination); getData(pagination);
} else { } else {
message.error(`${record.IsAllow ? '取消' : '关注'}失败!`); message.error(`${record.IsAllow ? '取消' : '关注'}失败!`);
} }
}); });
};
const printTable = () => {
let _columns = columns.filter(item => item.title !== '操作');
Print({ tableData, columns: _columns, name: reportName }, 'simple_table');
}; };
function getRefHeight() { function getRefHeight() {
...@@ -1404,9 +1399,6 @@ const ReportsManage = (props) => { ...@@ -1404,9 +1399,6 @@ const ReportsManage = (props) => {
}, 100); }, 100);
} }
useEffect(() => {
getConfigs();
}, []);
useEffect(() => { useEffect(() => {
if (firstToGetData) { if (firstToGetData) {
getData(pagination); getData(pagination);
...@@ -1417,7 +1409,6 @@ const ReportsManage = (props) => { ...@@ -1417,7 +1409,6 @@ const ReportsManage = (props) => {
if (tableHeaderLevel) setTableHeight(); if (tableHeaderLevel) setTableHeight();
}, [tableHeaderLevel]); }, [tableHeaderLevel]);
useEffect(() => { useEffect(() => {
// if (!isInit) getData(pagination);
if (!isInit) { if (!isInit) {
let _pagination = { ...pagination }; let _pagination = { ...pagination };
if (_pagination.current === 1) { if (_pagination.current === 1) {
...@@ -1434,312 +1425,327 @@ const ReportsManage = (props) => { ...@@ -1434,312 +1425,327 @@ const ReportsManage = (props) => {
} }
}, [pagination.current, pagination.pageSize]); }, [pagination.current, pagination.pageSize]);
useEffect(() => { useEffect(() => {
getConfigs();
window.addEventListener('resize', getRefHeight); window.addEventListener('resize', getRefHeight);
return () => window.removeEventListener('resize', getRefHeight); return () => window.removeEventListener('resize', getRefHeight);
}, []); }, []);
useEffect(() => {
trigger?.('selectedRows');
}, [selectedRows]);
return ( return (
<div className={style.reportManage} ref={tableWrapperRef}> <div className={style.reportManage} style={{ ...(props.style ?? {}) }} ref={tableWrapperRef}>
{/* 预留容器,提供给点击后的功能显示 */} {/* 预留容器,提供给点击后的功能显示 */}
{detailsComponentVisible ? ( {detailsComponentVisible ? (
<div
className={style.contentWrapper}
style={{
position: 'absolute',
zIndex: 100,
width: 'calc(100% - 16px)',
height: 'calc(100% - 16px)',
}}
>
<DetailsComponent
url={detailConfig.url}
params={detailConfig.params}
onCancel={() => setDetailsComponentVisible(false)}
/>
</div>
) : (
''
)}
{/* 为方便阅读,分开两部分代码 */}
<div <div
className={style.contentWrapper} className={style.contentWrapper}
style={{ style={{ zIndex: detailsComponentVisible ? -1 : 'auto' }}
position: 'absolute',
zIndex: 100,
width: 'calc(100% - 16px)',
height: 'calc(100% - 16px)',
}}
> >
<DetailsComponent <Row className={style.controlRow} ref={controlRef}>
url={detailConfig.url} <Space style={{ flex: 1 }} size={8} wrap={true}>
params={detailConfig.params} {/*时间搜索控件,确保时间搜索控件在第一个,单独匹配*/}
onCancel={() => setDetailsComponentVisible(false)} {hasDatePicker &&
/> defaultDateConfig.defaultDate !== null &&
</div> permission.includes('filters') ? (
) : ( <DatePickerGroup
'' showModels={defaultDateConfig.showModels}
)} onChange={changeDate}
{/* 为方便阅读,分开两部分代码 */} format={dateFormat}
<div defaultModel={defaultDateConfig.defaultModel}
className={style.contentWrapper} defaultDate={defaultDateConfig.defaultDate}
style={{ zIndex: detailsComponentVisible ? -1 : 'auto' }} />
> ) : (
<Row className={style.controlRow} ref={controlRef}> ''
<Space style={{ flex: 1 }} size={8} wrap={true}> )}
{/*时间搜索控件,确保时间搜索控件在第一个,单独匹配*/} {controls && controls.length && permission.includes('filters')
{hasDatePicker && ? controls
defaultDateConfig.defaultDate !== null && .filter((control) =>
permission.includes('filters') ? ( ['下拉', '多选', '复选框'].includes(control.filterRule),
<DatePickerGroup )
showModels={defaultDateConfig.showModels} .map((control) => {
onChange={changeDate} return (
format={dateFormat} <Form.Item
defaultModel={defaultDateConfig.defaultModel} label={control.fieldAlias}
defaultDate={defaultDateConfig.defaultDate} key={control.fieldAlias}
/> >
<ReturnControlComponent
style={{ width: 240 }}
type={control.filterRule}
reportName={reportName}
fieldAlias={control.fieldAlias}
configItems={control.configItems}
onChange={(e) =>
controlSelectChange(control.fieldAlias, e)
}
filterFields={filterFields}
filterValues={filterValues}
filterObject={addFilterAndSearchParams({
reportName: reportName,
userId: USER_ID,
})}
/>
</Form.Item>
);
})
: ''}
{permission.includes('filters') ? (
<Form.Item
label={
searchPlaceholder && searchPlaceholder.length
? '快速索引'
: ''
}
key={'快速搜索控件'}
>
{searchPlaceholder && searchPlaceholder.length ? (
<ReturnControlComponent
placeholder={`请输入${
searchPlaceholder.length
? searchPlaceholder.join('')
: '关键字'
}搜索`}
style={{ width: 240 }}
type={'文本'}
onChange={(e) => {
searchInputChange(e);
}}
onSearch={searchData}
/>
) : (
<Button type={'primary'} onClick={searchData}>
搜索
</Button>
)}
</Form.Item>
) : (
''
)}
</Space>
{permission.includes('sortBtn') ||
permission.includes('exportBtn') ||
permission.includes('addBtn') ? (
<div style={{ width: 'auto', textAlign: 'end' }}>
<Space size={8} nowrap>
{permission.includes('addBtn') && !isLocalDataSource ? (
<Form.Item>
<Button
type={'primary'}
title={'自定义排序'}
icon={<PlusOutlined/>}
onClick={() => {
setModalType('新增');
setCurrentData({});
}}
>
添加
</Button>
</Form.Item>
) : (
''
)}
{showSortBtn && permission.includes('sortBtn') ? (
<Form.Item>
<Button
type={'primary'}
title={'自定义排序'}
icon={<SortAscendingOutlined/>}
onClick={() => setModalVisible(true)}
>
排序
</Button>
</Form.Item>
) : (
''
)}
{permission.includes('printBtn') ? (
<Form.Item>
<Button
type={'primary'}
title={'打印当前页'}
onClick={printTable}
>
打印
</Button>
</Form.Item>
) : (
''
)}
{permission.includes('exportBtn') ? (
<Form.Item>
<Dropdown style={{ float: 'right' }} overlay={menu}>
<Button>
<Space>
导出
<DownOutlined/>
</Space>
</Button>
</Dropdown>
</Form.Item>
) : (
''
)}
</Space>
</div>
) : ( ) : (
'' ''
)} )}
{controls && controls.length && permission.includes('filters') </Row>
? controls <div
.filter((control) => className={style.tableContent}
['下拉', '多选', '复选框'].includes(control.filterRule), style={{ height: `calc(100% - ${controlsHeight || 0}px)` }}
) >
.map((control) => { {columns && columns.length ? (
return ( returnTable
<Form.Item
label={control.fieldAlias}
key={control.fieldAlias}
>
<ReturnControlComponent
style={{ width: 240 }}
type={control.filterRule}
reportName={reportName}
fieldAlias={control.fieldAlias}
configItems={control.configItems}
onChange={(e) =>
controlSelectChange(control.fieldAlias, e)
}
filterFields={filterFields}
filterValues={filterValues}
filterObject={addFilterAndSearchParams({
reportName: reportName,
userId: USER_ID,
})}
/>
</Form.Item>
);
})
: ''}
{permission.includes('filters') ? (
<Form.Item
label={
searchPlaceholder && searchPlaceholder.length
? '快速索引'
: ''
}
key={'快速搜索控件'}
>
{searchPlaceholder && searchPlaceholder.length ? (
<ReturnControlComponent
placeholder={`请输入${
searchPlaceholder.length
? searchPlaceholder.join('')
: '关键字'
}搜索`}
style={{ width: 240 }}
type={'文本'}
onChange={(e) => {
searchInputChange(e);
}}
onSearch={searchData}
/>
) : (
<Button type={'primary'} onClick={searchData}>
搜索
</Button>
)}
</Form.Item>
) : ( ) : (
'' <div className={style.spinWrapper}>
<Spin/>
</div>
)} )}
</Space> </div>
{permission.includes('sortBtn') || </div>
permission.includes('exportBtn') || <Modal
permission.includes('addBtn') ? ( title={'自定义排序字段'}
<div style={{ width: 270, textAlign: 'end' }}> visible={modalVisible}
<Space size={8} nowrap> onCancel={() => setModalVisible(false)}
{permission.includes('addBtn') && !isLocalDataSource ? ( footer={
<Form.Item> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button <div>
<Button type={'link'} onClick={() => addOtherSortFields()}>
增加次要排序
</Button>
</div>
<div>
<Button onClick={() => setModalVisible(false)}>取消</Button>
<Button
type={'primary'} type={'primary'}
title={'自定义排序'}
icon={<PlusOutlined/>}
onClick={() => { onClick={() => {
setModalType('新增'); saveReportListSortFields(() => getData(pagination));
setCurrentData({}); setModalVisible(false);
}} }}
>
确认
</Button>
</div>
</div>
}
>
{selectedSortFields.map((item, index) => (
<Row key={'label'} className={style.controlRow}>
<Space size={8} wrap={true}>
<Form.Item label={item.label}>
<Select
style={{ width: 240 }}
defaultValue={item.value}
value={item.value}
onChange={(e) => changeSortField(e, index, 'value')}
> >
添加 <Option value="">未选择</Option>
</Button> {allSortFields.map((item) => (
<Option value={item} key={item}>{item}</Option>
))}
</Select>
</Form.Item> </Form.Item>
) : (
''
)}
{sortModalVisible && permission.includes('sortBtn') ? (
<Form.Item> <Form.Item>
<Button <Select
type={'primary'} style={{ width: 120 }}
title={'自定义排序'} defaultValue={item.sort}
icon={<SortAscendingOutlined/>} value={item.sort}
onClick={() => setModalVisible(true)} onChange={(e) => changeSortField(e, index, 'sort')}
> >
排序 <Option value={'asc'}>升序</Option>
</Button> <Option value={'desc'}>降序</Option>
</Select>
</Form.Item> </Form.Item>
) : ( {index !== 0 ? (
'' <Form.Item>
)} <MinusCircleOutlined
{permission.includes('exportBtn') ? ( style={{ color: 'rgba(0,0,0,.65)' }}
<Form.Item> onClick={() => deleteSortField(index)}
<Dropdown style={{ float: 'right' }} overlay={menu}> />
<Button> </Form.Item>
<Space> ) : (
导出 ''
<DownOutlined/> )}
</Space> </Space>
</Button> </Row>
</Dropdown> ))}
</Form.Item> </Modal>
) : ( <Modal
'' visible={extraModal}
)} onCancel={() => setExtraModal(false)}
</Space> destroyOnClose
</div> width={800}
) : (
''
)}
</Row>
<div
className={style.tableContent}
style={{ height: `calc(100% - ${controlsHeight || 0}px)` }}
> >
{columns && columns.length ? ( {extra}
returnTable </Modal>
) : ( {/* 编辑表单 */}
<div className={style.spinWrapper}> <Modal
<Spin/> title={`${modalType}报表信息`}
</div> visible={!!modalType}
)} width={'80%'}
</div> footer={null}
</div> // visible={true}
<Modal destroyOnClose
title={'自定义排序字段'} onCancel={() => setModalType('')}
visible={modalVisible}
onCancel={() => setModalVisible(false)}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<Button type={'link'} onClick={() => addOtherSortFields()}>
增加次要排序
</Button>
</div>
<div>
<Button onClick={() => setModalVisible(false)}>取消</Button>
<Button
type={'primary'}
onClick={() => {
saveReportListSortFields(() => getData(pagination));
setModalVisible(false);
}}
>
确认
</Button>
</div>
</div>
}
>
{selectedSortFields.map((item, index) => (
<Row key={'label'} className={style.controlRow}>
<Space size={8} wrap={true}>
<Form.Item label={item.label}>
<Select
style={{ width: 240 }}
defaultValue={item.value}
value={item.value}
onChange={(e) => changeSortField(e, index, 'value')}
>
<Option value="">未选择</Option>
{allSortFields.map((item) => (
<Option value={item} key={item}>{item}</Option>
))}
</Select>
</Form.Item>
<Form.Item>
<Select
style={{ width: 120 }}
defaultValue={item.sort}
value={item.sort}
onChange={(e) => changeSortField(e, index, 'sort')}
>
<Option value={'asc'}>升序</Option>
<Option value={'desc'}>降序</Option>
</Select>
</Form.Item>
{index !== 0 ? (
<Form.Item>
<MinusCircleOutlined
style={{ color: 'rgba(0,0,0,.65)' }}
onClick={() => deleteSortField(index)}
/>
</Form.Item>
) : (
''
)}
</Space>
</Row>
))}
</Modal>
<Modal
visible={extraModal}
onCancel={() => setExtraModal(false)}
destroyOnClose
width={800}
>
{extra}
</Modal>
{/* 编辑表单 */}
<Modal
title={`${modalType}报表信息`}
visible={!!modalType}
width={'80%'}
footer={null}
// visible={true}
destroyOnClose
onCancel={() => setModalType('')}
>
<ReportEditForm
modalType={modalType}
reportDetails={tableStruct}
reportData={currentData}
onCancel={() => {
setModalType('');
getData(pagination);
}}
reportName={reportName}
/>
</Modal>
{/* 编辑水印 */}
<Modal
title={`添加水印`}
visible={showWaterMarkModal}
destroyOnClose
onCancel={() => setShowWaterMarkModal(false)}
onOk={exportFiles}
>
<Form.Item
label="水印内容"
name="水印内容"
rules={[
{
required: true,
message: '请输入水印内容...!',
},
]}
> >
<Input <ReportEditForm
placeholder="请输入水印内容..." modalType={modalType}
onChange={(e) => { reportDetails={tableStruct}
setWaterMarkContent(e.target.value); reportData={currentData}
}} onCancel={() => {
setModalType('');
getData(pagination);
}}
reportName={reportName}
/> />
</Form.Item> </Modal>
</Modal> {/* 编辑水印 */}
</div> <Modal
title={`添加水印`}
visible={showWaterMarkModal}
destroyOnClose
onCancel={() => setShowWaterMarkModal(false)}
onOk={exportFiles}
>
<Form.Item
label="水印内容"
name="水印内容"
rules={[
{
required: true,
message: '请输入水印内容...!',
},
]}
>
<Input
placeholder="请输入水印内容..."
onChange={(e) => {
setWaterMarkContent(e.target.value);
}}
/>
</Form.Item>
</Modal>
</div>
); );
}; });
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const allWidgets = state.getIn(['global', 'globalConfig', 'allWidgets']); const allWidgets = state.getIn(['global', 'globalConfig', 'allWidgets']);
let _flatWidgets = []; let _flatWidgets = [];
...@@ -1757,4 +1763,4 @@ const mapStateToProps = (state) => { ...@@ -1757,4 +1763,4 @@ const mapStateToProps = (state) => {
allWidgets: _flatWidgets, allWidgets: _flatWidgets,
}; };
}; };
export default connect(mapStateToProps, null)(ReportsManage); export default ReportsManage;
This source diff could not be displayed because it is too large. You can view the blob instead.
import { cloneDeep } from 'lodash';
const returnFields = (str) => {
let _arr = [];
let list = str.matchAll(/\$\{[^}\s]+}/g); // ${水箱个数}
// _arr = [...list].map(item => item[0].replace(/[${}]/g, ''));
[...list].forEach(item => {
let _item = item[0].replace(/[${}]/g, '').split(',');
_arr = _arr.concat(_item);
});
return _arr;
};
const handleMaxValue = (value) => {
if (value <= 1) return value.toFixed(2);
if (value >= 100000) return `${(value / 1000).toFixed(2)}k`;
return value.toFixed(2);
};
const reduceYAxis = (arr, opt) => {
let _offsetValue = [];
// 1. 合并相同单位的坐标轴
let _arr = arr.reduce((final, cur) => {
let _key = cur.name === null ? 'null' : cur.name;
if (!final[_key]) {
final[_key] = cur;
}
return final;
}, {});
// 2. 合并相同单位的数据,找出最大值
let _maxValueArr = Object.values(
opt.series.reduce((final, cur, index) => {
let _key = `${opt.title.text || 'NoKey'}_${index}`;
let _maxValue = cur?.data?.reduce((final, num) => {
// eslint-disable-next-line no-param-reassign
if (Number(num) > final) final = Number(num);
return final;
}, 0);
if (final[_key] === undefined) {
final[_key] = _maxValue;
} else {
final[_key] = Math.max([final[_key], _maxValue]);
}
return final;
}, {}),
);
// 3. 合并,生成Y轴配置
return Object.values(_arr).map((item, index) => {
let _key = item.name === null ? 'null' : item.name;
let _lastAxisNumber = _maxValueArr[index - 2];
let _baseOffset = _offsetValue[index - 2] ?? 0;
let _finalOffset =
(_lastAxisNumber !== undefined // 没有相邻的轴
? (_lastAxisNumber === 0 ? 20 : _lastAxisNumber.toFixed(2).replaceAll('.', '').length) * 12
: 0) + _baseOffset;
_offsetValue.push(_finalOffset);
return {
...item,
offset: _finalOffset,
position: index % 2 === 0 ? 'left' : 'right',
nameTextStyle: {
align: index % 2 === 0 ? 'right' : 'left',
},
};
});
};
const handleYAxis = (opt) => {
const yAxisMap = new Map();
// 1. 找出最大值; 2. 计算出y轴最大宽度动态计算偏移距离;
opt?.series?.forEach((item, index) => {
const { custom_config } = item;
const key = custom_config.unit;
if (!yAxisMap.has(key)) {
const axis = {
type: 'value',
name: custom_config.unit,
axisLabel: {
formatter: (value) => {
return handleMaxValue(value);
},
},
axisLine: {
show: true,
},
minorTick: {
lineStyle: {
color: '#e2e2e2',
},
},
minorSplitLine: {
lineStyle: {
color: '#e2e2e2',
type: 'dashed',
},
},
};
yAxisMap.set(key, axis);
}
});
return yAxisMap.size > 0 ? reduceYAxis([...yAxisMap.values()], opt) : { type: 'value' };
};
/**
* 1. 解析图表的配置,并且处理数据
* @param {Array} chartOptions 配置的json字符串
* @param {Array:[{[string]:[any]}]} columnsData 配置的数据数组 [{分公司:'',业绩:100}]
* @param rowData
* */
const returnOptionsArr = (chartOptions, columnsData, rowData) => {
let _keysArr = [];
//1. 将数据转换成 {
// 分公司:[江西,湖北,湖南]
// 业绩: [1,2,3]
// }
let _dataMap = columnsData.reduce((final, cur, index) => {
if (index === 0) _keysArr = Object.keys(cur);
_keysArr.forEach(key => {
if (!final[key]) final[key] = [];
final[key].push(cur[key]);
});
return final;
}, {});
//2. 解析${分公司}这种字符串,替换掉数据
let _optionsArr = cloneDeep(chartOptions).map(item => {
let _item = cloneDeep(item);
if (!_item) return null;
if (_item?.custom_config?.renderBy === 'allData') {
// ${分公司} -> 分公司
let _key = _item.xAxis.data.replace(/[${}]/g, '');
// 给x轴赋值
_item.xAxis.data = _dataMap[_key];
// 处理系列内的配置
_item.series = _item.series.map(obj => {
let _obj = { ...obj };
let _objKey = _obj.data.replace(/[${}]/g, '');
// 赋值数据
_obj.data = _dataMap[_objKey];
return _obj;
});
}
if (_item?.custom_config?.renderBy === 'selectedData') {
// 勾选的交互方式,只允许配置一种类型的曲线
let _config = _item?.series?.[0] ?? {};
let _keys = _config?.data?.replaceAll(/[$\[\]]/g, '')?.split(',') ?? [];
let _xAxisArr = _item.xAxis.data;
_item.series = rowData.map(row => {
return {
..._config,
data: _xAxisArr.map(field => row[field]),
name: _keys.map(key => row[key]).join('-'),
};
});
}
return _item;
});
// 3. 配置通用属性,未在配置功能里展示
_optionsArr = cloneDeep(_optionsArr).map(item => {
if (!item) return null;
item.grid = {
...item.grid,
containLabel: true,
};
item.legend = { ...item.legend, padding: [2, 8] };
if (item?.dataZoom?.show) {
item.dataZoom = [
{ show: true, start: 0, end: 100, type: 'slider', height: 20 },
{ type: 'inside', start: 0, end: 100 },
];
}
// 多轴问题
let yAxis = handleYAxis(item);
item.yAxis = yAxis;
item.series.forEach(_series => {
_series.yAxisIndex = yAxis.find(axis => axis.unit === _series.custom_config.unit);
});
return item;
});
return _optionsArr;
};
export {
returnFields, returnOptionsArr,
};
...@@ -96,7 +96,6 @@ export const handleNumber = (config, number) => { ...@@ -96,7 +96,6 @@ export const handleNumber = (config, number) => {
} }
}); });
} else if (config.type === '数值标签' && config.labelConfigs && config.labelConfigs.length) { } else if (config.type === '数值标签' && config.labelConfigs && config.labelConfigs.length) {
console.log('数值标签');
_color = 'red'; _color = 'red';
} else if (config.color) { } else if (config.color) {
_color = config.color; _color = config.color;
......
// 方案1 页面配置
// 以下以2023年分公司年度业绩为例子,配置的结果
/*const optionStructure = {
layoutOptions: {
layout: '上图下表',
chartCount: 3,
tableConfigs: [
{
title: '业绩报表',
width: '100%',
height: '500px',
},
],
chartConfigs: [
{
title: '业绩图',
width: '100%',
height: '100%',
},
{
title: '业绩图',
width: '100%',
height: '100%',
},
{
title: '业绩图',
width: '100%',
height: '100%',
},
],
},
chartOptions: [
{
title: {
show: true,
text: '2023年业绩报表',
textStyle: {
color: '#123445',
fontSize: 18,
},
},
xAxis: {
name: '分公司',
type: 'category', // 我们的场景,使用两种,category/time
// minInterval:'', // category为time时,才显示
// 1. category 表头名称时,data的结构为 ['01月','02月','03月','04月','05月','06月','07月','08月']
// 2. category 表内数据时 data为带 $ + 字段名的组合
// 3. time 表内数据时 data 为 $ + 字段名的组合
data: '${分公司}',
// time时,直接使用以下配置
// axisLabel:{
// formatter: {
// year: '{yyyy}',
// month: '{MMM}',
// day: '{d}',
// hour: '{HH}:{mm}',
// minute: '{HH}:{mm}',
// second: '{HH}:{mm}:{ss}',
// millisecond: '{hh}:{mm}:{ss} {SSS}',
// none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss} {SSS}'
// }
// }
},
yAxis: [
{
name: '万元',
gridIndex: 0,
// 特定情况下才需要,比如PH值
// minInterval:1
// precision
},
],
series: [
{
type: 'line',
name: '销售业绩',
colorBy: 'series',
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
markLine: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
stack: '',
customerConfig: {
unit: 'Mpa',
},
data:'${销售业绩}'
},
],
legend: {
left: 'right', // left right center
},
// left/right/top/bottom 这几个允许配置
grid: {
left: 10,
right: 20,
top: 40,
bottom: 40,
labelContainer: true,
},
// 是否显示
dataZoom: {
show: true,
},
},
{
title: {
show: true,
text: '2023年业绩报表',
textStyle: {
color: '#123445',
fontSize: 18,
},
},
xAxis: {
name: '分公司',
type: 'category', // 我们的场景,使用两种,category/time
// minInterval:'', // category为time时,才显示
// 1. category 表头名称时,data的结构为 ['01月','02月','03月','04月','05月','06月','07月','08月']
// 2. category 表内数据时 data为带 $ + 字段名的组合
// 3. time 表内数据时 data 为 $ + 字段名的组合
data: '$分公司',
// time时,直接使用以下配置
// axisLabel:{
// formatter: {
// year: '{yyyy}',
// month: '{MMM}',
// day: '{d}',
// hour: '{HH}:{mm}',
// minute: '{HH}:{mm}',
// second: '{HH}:{mm}:{ss}',
// millisecond: '{hh}:{mm}:{ss} {SSS}',
// none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss} {SSS}'
// }
// }
},
yAxis: [
{
name: '万元',
gridIndex: 0,
// 特定情况下才需要,比如PH值
// minInterval:1
// precision
},
],
series: [
{
type: 'line',
name: '分公司',
colorBy: 'series',
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
markLine: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
stack: '',
customerConfig: {
unit: 'Mpa',
},
},
],
legend: {
left: 'right', // left right center
},
// left/right/top/bottom 这几个允许配置
grid: {
left: 10,
right: 20,
top: 40,
bottom: 40,
labelContainer: true,
},
// 是否显示
dataZoom: {
show: true,
},
},
{
title: {
show: true,
text: '2023年业绩报表',
textStyle: {
color: '#123445',
fontSize: 18,
},
},
xAxis: {
name: '分公司',
type: 'category', // 我们的场景,使用两种,category/time
// minInterval:'', // category为time时,才显示
// 1. category 表头名称时,data的结构为 ['01月','02月','03月','04月','05月','06月','07月','08月']
// 2. category 表内数据时 data为带 $ + 字段名的组合
// 3. time 表内数据时 data 为 $ + 字段名的组合
data: '$分公司',
// time时,直接使用以下配置
// axisLabel:{
// formatter: {
// year: '{yyyy}',
// month: '{MMM}',
// day: '{d}',
// hour: '{HH}:{mm}',
// minute: '{HH}:{mm}',
// second: '{HH}:{mm}:{ss}',
// millisecond: '{hh}:{mm}:{ss} {SSS}',
// none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss} {SSS}'
// }
// }
},
yAxis: [
{
name: '万元',
gridIndex: 0,
// 特定情况下才需要,比如PH值
// minInterval:1
// precision
},
],
series: [
{
type: 'line',
name: '分公司',
colorBy: 'series',
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
markLine: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
stack: '',
customerConfig: {
unit: 'Mpa',
},
},
],
legend: {
left: 'right', // left right center
},
// left/right/top/bottom 这几个允许配置
grid: {
left: 10,
right: 20,
top: 40,
bottom: 40,
labelContainer: true,
},
// 是否显示
dataZoom: {
show: true,
},
},
],
};*/
const optionStructure = {
layoutOptions: {
layout: '上图下表',
chartCount: 1,
tableConfigs: [
{
title: '水箱个数',
width: '100%',
height: '500px',
},
],
chartConfigs: [
{
title: '水箱个数',
width: '100%',
height: '100%',
},
],
},
chartOptions: [
{
title: {
show: true,
text: '泵房水箱个数',
textStyle: {
color: '#123445',
fontSize: 18,
},
},
xAxis: {
name: '泵房名称',
type: 'category',
data: '${设备名称}',
customConfig:{
dataType:'columnData',// columnData
combineSame:true
},
},
yAxis: [
{
name: '个',
gridIndex: 0,
},
],
series: [
{
type: 'bar',
name: '水箱个数',
colorBy: 'series',
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
markLine: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' },
{ type: 'average', name: '平均值' },
],
},
stack: '',
customerConfig: {
unit: '个',
},
data:'${水箱个数}'
},
],
legend: {
left: 'right',
},
grid: {
left: 10,
right: 20,
top: 40,
bottom: 40,
labelContainer: true,
},
dataZoom: {
show: true,
},
},
],
};
export default optionStructure;
// 方案2 配置完,某些样式、设置没有提供,此时可以生成配置,然后手动修改、添加配置。
import style from '../ReportsManage.less';
import { downloadFileUrl } from '../../api/service/workflow';
const isObject = (obj) => { const isObject = (obj) => {
return Object.prototype.toString.call(obj) === '[object Object]'; return Object.prototype.toString.call(obj) === '[object Object]';
}; };
...@@ -50,10 +53,10 @@ const returnCols = (configItems) => { ...@@ -50,10 +53,10 @@ const returnCols = (configItems) => {
* @Date: 2022/8/10 * @Date: 2022/8/10
* @Author: ChenLong * @Author: ChenLong
* */ * */
const returnHandledNumber = (configItems, num, isSummary) => { const returnHandledNumber = (configItems, num) => {
// 精度、前缀、后缀、倍率 // 精度、前缀、后缀、倍率
// $_d|_d%|_d*0.0001|金额|0.00|_d_sum*0.01*百万 // $_d|_d%|_d*0.0001|金额|0.00|$_d_sum*0.01*百万
if (isNaN(num)) return '-'; if (isNaN(Number(num))) return '-';
if (!configItems) return num; if (!configItems) return num;
num = Number(num); num = Number(num);
let _items = configItems.split('|'); let _items = configItems.split('|');
...@@ -63,7 +66,6 @@ const returnHandledNumber = (configItems, num, isSummary) => { ...@@ -63,7 +66,6 @@ const returnHandledNumber = (configItems, num, isSummary) => {
let precision = 0; let precision = 0;
let rate = 1; let rate = 1;
_items.forEach(item => { _items.forEach(item => {
let _arr = [];
if (item.match(/_d[^(\*|_sum)]/)) { if (item.match(/_d[^(\*|_sum)]/)) {
// 后缀 // 后缀
template = item; template = item;
...@@ -75,20 +77,49 @@ const returnHandledNumber = (configItems, num, isSummary) => { ...@@ -75,20 +77,49 @@ const returnHandledNumber = (configItems, num, isSummary) => {
} else if (item.match(/^0\./)) { } else if (item.match(/^0\./)) {
// 精度 // 精度
precision = item.replace('0.', '').length; precision = item.replace('0.', '').length;
} else if (item.match(/_d_sum\*/)) { }
});
// 可能存在NaN的问题
return _items.includes('金额') ? Number((num * rate).toFixed(precision)).toLocaleString() : (num * rate).toFixed(precision); // 最后用_d%这个带了后缀的模板,将_d用计算后的数字替换掉,就得出带了后缀、单位的数值字符串
};
const returnSummaryNumber = (configItems, num, isSummary) => {
// 总结栏配置规则
// $_d_sum*0.01*百万
if (isNaN(Number(num))) return '-';
if (!configItems) return num;
num = Number(num);
let _items = configItems.split('|');
/* let prefix = '';
let suffix = '';*/
let template = '_d';
let precision = 0;
let rate = 1;
let combine = '_combine_'; // 连接符
let _prefix = '';
_items.forEach(item => {
if (item.match(/_d_sum\*/)) {
// 总结栏的倍率 // 总结栏的倍率
let hasPrefix = item.match(/(\s+)?_d_sum/);
_prefix = hasPrefix ? item.split('_d_sum')?.[0] : '';
let _rateStr = item.replace(/_d_sum\*/, ''); let _rateStr = item.replace(/_d_sum\*/, '');
let _temp = _rateStr.split('*'); // ['0.01','百万'] let _temp = _rateStr.split('*'); // ['0.01','百万']
let _rate = _temp?.[0]; let _rate = _temp?.[0];
rate = (_rate && !isNaN(Number(_rate)) && isSummary) ? (Number(_rate)) : 1; rate = (_rate && !isNaN(Number(_rate)) && isSummary) ? Number(_rate) : 1;
if (isSummary && _temp?.[1]) { if (isSummary && _temp?.[1]) {
template = `_d ${_temp[1]}`; template = `_d${combine}${_temp[1]}`;
} }
} }
}); });
// 可能存在NaN的问题 // 可能存在NaN的问题
let final = _items.includes('金额') ? Number((num * rate).toFixed(precision)).toLocaleString() : Number((num * rate).toFixed(precision)).toLocaleString(); let final = _items.includes('金额') ? Number((num * rate).toFixed(precision)).toLocaleString() : (num * rate).toFixed(precision);
return template.replace(/_d/, isString(final) ? final : '-'); // 最后用_d%这个带了后缀的模板,将_d用计算后的数字替换掉,就得出带了后缀、单位的数值字符串 let _template = template.split(combine);
return <>
<span className={style.prefixOrSuffix}>{_prefix || ''}</span>
{template.replace(/_d(.+)?/, isString(final) ? final : '-')}
<span className={style.prefixOrSuffix}>{_template[1] || ''}</span>
</>;
}; };
/** /**
* @Description: 返回configItems内配置的默认值、默认模式等等 * @Description: 返回configItems内配置的默认值、默认模式等等
...@@ -144,6 +175,26 @@ function filenameVerification(file, special) { ...@@ -144,6 +175,26 @@ function filenameVerification(file, special) {
}; };
} }
function handleFiles(file) {
let _filesArr = !file ? [] : file.split(',').filter(item => item);
let fileList = [];
_filesArr.forEach((item, index) => {
if (item && filenameVerification({ name: item }, true).type !== 'error') { // @Tips: 直接过滤掉名字中有异常字符的文件
let _obj = {
uid: index + '_' + Math.random(),
value: item,
name: item.includes('\\') ? item.split('\\').reverse()[0] : item.split('/').reverse()[0],
type: 'file',
status: 'done',
url: `${downloadFileUrl}?filePath=${item}`,
sourcePath: item,
};
fileList.push(_obj);
}
});
return fileList
}
export { export {
isObject, isObject,
isString, isString,
...@@ -151,6 +202,7 @@ export { ...@@ -151,6 +202,7 @@ export {
hasMoney, hasMoney,
isArray, isArray,
returnHandledNumber, returnHandledNumber,
returnSummaryNumber,
returnDefaultValueOrConfigs, returnDefaultValueOrConfigs,
downloadFunc, downloadFunc,
filenameVerification, filenameVerification,
...@@ -158,4 +210,5 @@ export { ...@@ -158,4 +210,5 @@ export {
returnOptions, returnOptions,
returnRows, returnRows,
returnCols, returnCols,
handleFiles,
}; };
...@@ -9,7 +9,8 @@ import * as constants from '../constants'; ...@@ -9,7 +9,8 @@ import * as constants from '../constants';
const BASEURL = '/PandaWater/CityWater/ReportManager'; const BASEURL = '/PandaWater/CityWater/ReportManager';
export const API = { export const API = {
GET_REPORT_INFO: `${BASEURL}/GetReportInfo`, // 获取报表信息 // GET_REPORT_INFO: `${BASEURL}/GetReportInfo`, // 获取报表信息
GET_REPORT_INFO: `${BASEURL}/GetReportData`, // 获取报表信息
GET_REPORT_FILTER_VALUE: `${BASEURL}/GetReportFilterValue`, // 获取过滤字段的值的枚举 GET_REPORT_FILTER_VALUE: `${BASEURL}/GetReportFilterValue`, // 获取过滤字段的值的枚举
GET_REPORT_FILTER_VALUES: `${BASEURL}/GetReportFilterValues`, // 获取过滤字段的值的枚举 GET_REPORT_FILTER_VALUES: `${BASEURL}/GetReportFilterValues`, // 获取过滤字段的值的枚举
GET_REPORT_CONFIG_LIST: `${BASEURL}/GetReportConfigList`, // 获取配置列表 GET_REPORT_CONFIG_LIST: `${BASEURL}/GetReportConfigList`, // 获取配置列表
...@@ -21,16 +22,20 @@ export const API = { ...@@ -21,16 +22,20 @@ export const API = {
ADD_REPORT_DETAIL_INFO: `${BASEURL}/AddReportDetailInfo`, // 附加子表字段到主表 ADD_REPORT_DETAIL_INFO: `${BASEURL}/AddReportDetailInfo`, // 附加子表字段到主表
DELETE_REPORT_INFO: `${BASEURL}/DeleteReportInfo`, // 删除报表 DELETE_REPORT_INFO: `${BASEURL}/DeleteReportInfo`, // 删除报表
DELETE_REPORT_DETAIL_INFO: `${BASEURL}/DeleteReportDetailInfo`, // 删除字段 DELETE_REPORT_DETAIL_INFO: `${BASEURL}/DeleteReportDetailInfo`, // 删除字段
EXPORT_ACCOUNT_DATA: `${BASEURL}/ExportAccountData`, // 导出数据 // EXPORT_ACCOUNT_DATA: `${BASEURL}/ExportAccountData`, // 导出数据
REPORT_FILES_DOWNLOAD: `${BASEURL}/ReportFilesDownload`, // 导出选中数据 EXPORT_ACCOUNT_DATA: `${BASEURL}/ExportReportData`, // 导出数据
SAVE_REPORT_LIST_SORT_FIELDS: `${BASEURL}/SaveReportListSortFields`, // 保存排序 // REPORT_FILES_DOWNLOAD: `${BASEURL}/ReportFilesDownload`, // 导出选中数据
REPORT_FILES_DOWNLOAD: `${BASEURL}/DownloadReportFiles`, // 导出选中数据
SAVE_REPORT_LIST_SORT_FIELDS: `${BASEURL}/SaveReportSortFields`, // 保存排序
GET_REPORT_SORT_FIELDS:`${BASEURL}/GetReportSortFields`,
ADD_REPORT_DETAIL_INFO_INDEX: `${BASEURL}/AddReportDetailInfoIndex`, // 变更接口顺序 ADD_REPORT_DETAIL_INFO_INDEX: `${BASEURL}/AddReportDetailInfoIndex`, // 变更接口顺序
UPDATE_REPORT_DATA: `${BASEURL}/UpdateReportData`, // 更新报表数据 UPDATE_REPORT_DATA: `${BASEURL}/UpdateReportData`, // 更新报表数据
GET_REPORT_DETAILS: `${BASEURL}/GetReportDetails`, // 获取报表配置 GET_REPORT_DETAILS: `${BASEURL}/GetReportDetails`, // 获取报表配置
DEL_REPORT_DATA: `${BASEURL}/DelReportData`, // 删除报表数据 DEL_REPORT_DATA: `${BASEURL}/DelReportData`, // 删除报表数据
SET_REPORT_ALLOW: `${BASEURL}/SetReportAllow`, // 设置关注 SET_REPORT_ALLOW: `${BASEURL}/SetReportAllow`, // 设置关注
ADD_REPORT_DATA: `${BASEURL}/AddReportData`, // 添加报表数据 ADD_REPORT_DATA: `${BASEURL}/AddReportData`, // 添加报表数据
EXPORT_JPG: `${BASEURL}/ExportJPGAccountData`, // EXPORT_JPG: `${BASEURL}/ExportJPGAccountData`,
EXPORT_JPG: `${BASEURL}/ExportReportDataWithJPG`,
EXPORT_REPORT_CONFIG: `${BASEURL}/ExportReportConfig`, EXPORT_REPORT_CONFIG: `${BASEURL}/ExportReportConfig`,
IMPORT_REPORT_CONFIG: `${BASEURL}/ImportReportConfig`, IMPORT_REPORT_CONFIG: `${BASEURL}/ImportReportConfig`,
// 多数据源 // 多数据源
...@@ -40,6 +45,11 @@ export const API = { ...@@ -40,6 +45,11 @@ export const API = {
GET_DB_SOURCES: `${BASEURL}/GetDBSources`, // 获取数据库 GET_DB_SOURCES: `${BASEURL}/GetDBSources`, // 获取数据库
TEST_CONNECTION: `${BASEURL}/TestConnection`, // 测试链接 TEST_CONNECTION: `${BASEURL}/TestConnection`, // 测试链接
DEL_REPORT_CHILD: `${BASEURL}/DelReportChlid`, // 删除挂接关系 DEL_REPORT_CHILD: `${BASEURL}/DelReportChlid`, // 删除挂接关系
// 图表配置开发
SAVE_CHART_CONFIG: `${BASEURL}/SaveChartConfig`, // 保存图表配置
GET_CHART_CONFIG: `${BASEURL}/GetChartConfig`, // 获取图表配置
// 图表部分接口
GET_CHART_CONFIG_DATA_BY_COLUMN: `${BASEURL}/GetChartConfigDataByColumn`, //获取图表数据
}; };
const reportService = { const reportService = {
getReportInfo: { getReportInfo: {
...@@ -102,6 +112,11 @@ const reportService = { ...@@ -102,6 +112,11 @@ const reportService = {
method: constants.REQUEST_METHOD_GET, method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET, type: constants.REQUEST_METHOD_GET,
}, },
getReportSortFields:{
url: API.GET_REPORT_SORT_FIELDS,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
saveReportListSortFields: { saveReportListSortFields: {
url: API.SAVE_REPORT_LIST_SORT_FIELDS, url: API.SAVE_REPORT_LIST_SORT_FIELDS,
method: constants.REQUEST_METHOD_POST, method: constants.REQUEST_METHOD_POST,
...@@ -182,30 +197,44 @@ const reportService = { ...@@ -182,30 +197,44 @@ const reportService = {
method: constants.REQUEST_METHOD_GET, method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET, type: constants.REQUEST_METHOD_GET,
}, },
saveChartConfig: {
url: API.SAVE_CHART_CONFIG,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
getChartConfigDataByColumn: {
url: API.GET_CHART_CONFIG_DATA_BY_COLUMN,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
getChartConfig: {
url: API.GET_CHART_CONFIG,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
}; };
export const submitReportData = (params, data) => export const submitReportData = (params, data) => request({
request({ url: API.UPDATE_REPORT_DATA,
url: API.UPDATE_REPORT_DATA, method: 'post',
method: 'post', params,
params, data,
data, });
});
export const exportAccountData = (options, params, data) => export const exportAccountData = (options, params, data) =>
request({ request({
url: API.EXPORT_ACCOUNT_DATA, url: API.EXPORT_ACCOUNT_DATA,
method: 'post', method: 'post',
...options, ...options,
params, params,
data, data,
}); });
export const reportFilesDownload = (options, params, data) => export const reportFilesDownload = (options, params, data) =>
request({ request({
url: API.REPORT_FILES_DOWNLOAD, url: API.REPORT_FILES_DOWNLOAD,
method: 'post', method: 'post',
...options, ...options,
params, params,
data, data,
}); });
export const exportJPG = (options, data) => { export const exportJPG = (options, data) => {
return request({ return request({
url: API.EXPORT_JPG, url: API.EXPORT_JPG,
...@@ -214,25 +243,24 @@ export const exportJPG = (options, data) => { ...@@ -214,25 +243,24 @@ export const exportJPG = (options, data) => {
data, data,
}); });
}; };
export const addReportDetailInfoIndex = (data) => export const addReportDetailInfoIndex = (data) => request({
request({ url: API.ADD_REPORT_DETAIL_INFO_INDEX,
url: API.ADD_REPORT_DETAIL_INFO_INDEX, method: 'post',
method: 'post', data,
data, });
});
export const importReportConfig = (options, params, data) => export const importReportConfig = (options, params, data) =>
request({ request({
url: API.IMPORT_REPORT_CONFIG, url: API.IMPORT_REPORT_CONFIG,
method: 'post', method: 'post',
...options, ...options,
params, params,
data, data,
}); });
export const exportReportConfig = (options, params) => export const exportReportConfig = (options, params) =>
request({ request({
url: API.EXPORT_REPORT_CONFIG, url: API.EXPORT_REPORT_CONFIG,
method: 'get', method: 'get',
...options, ...options,
params, params,
}); });
export default reportService; export default reportService;
...@@ -8,7 +8,7 @@ const Demo = () => { ...@@ -8,7 +8,7 @@ const Demo = () => {
<div style={{ height: '600px' }}> <div style={{ height: '600px' }}>
<BasicReport <BasicReport
params={{ params={{
reportName: '订单合同', reportName: '分公司签单',
}} }}
/> />
</div> </div>
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.2.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_alarmcurve@1.1.0...@wisdom-components/ec_alarmcurve@1.2.0) (2023-12-28)
### Features
- 组态分组增加消息通信 ([cf66b90](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/commits/cf66b908d34b46046f88e793177581af1be38339))
# 1.1.0 (2023-12-19) # 1.1.0 (2023-12-19)
### Bug Fixes ### Bug Fixes
......
{ {
"name": "@wisdom-components/ec_alarmcurve", "name": "@wisdom-components/ec_alarmcurve",
"version": "1.1.0", "version": "1.2.0",
"description": "> TODO: description", "description": "> TODO: description",
"author": "lijiwen <961370825@qq.com>", "author": "lijiwen <961370825@qq.com>",
"homepage": "", "homepage": "",
......
...@@ -4,6 +4,15 @@ import { Button } from 'antd'; ...@@ -4,6 +4,15 @@ import { Button } from 'antd';
const Demos = () => { const Demos = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [sensors, setSensors] = useState('出水瞬时流量');
const onOk = () => {
setOpen(false);
};
const onCancel = () => {
setOpen(false);
};
return ( return (
<> <>
...@@ -14,7 +23,21 @@ const Demos = () => { ...@@ -14,7 +23,21 @@ const Demos = () => {
> >
打开限制曲线 打开限制曲线
</Button> </Button>
<LimitCurve open={open} /> <Button
onClick={() => {
setSensors('噪声');
}}
>
改变sensors
</Button>
<LimitCurve
open={open}
onOk={onOk}
onCancel={onCancel}
deviceCode={'EGBF00000120'}
deviceType={'二供泵房'}
sensors={sensors}
/>
</> </>
); );
}; };
......
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { useContext, useEffect, useRef, useState } from 'react';
import { ConfigProvider, Modal, Radio, Slider, InputNumber } from 'antd'; import { ConfigProvider, Modal, Radio, Slider, InputNumber, Input, Button } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import moment from 'moment'; import moment from 'moment';
import { BasicChart } from '@wisdom-components/basicchart'; import { BasicChart } from '@wisdom-components/basicchart';
import { getHistoryInfo } from '../apis'; import { getHistoryInfo } from '../apis';
import { std } from 'mathjs'; import { std } from 'mathjs';
import skmeans from 'skmeans'; import skmeans from 'skmeans';
import LoadBox from '@wisdom-components/loadbox';
import { outlierArr, timeArr, chartArr, average } from './utils';
import './index.less'; import './index.less';
const LimitCurve = (props) => { const LimitCurve = (props) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('limit-curve'); const prefixCls = getPrefixCls('limit-curve');
const { width, deviceCode, sensors, deviceType, getContainer, title } = props; const { width, deviceCode, sensors, deviceType, getContainer, title } = props;
const [spinning,setSpinning] = useState(true);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [cluster, setCluster] = useState('K-means'); // 聚集算法 const [timeType, setTimeType] = useState('近7天'); // 时间
const [outlier, setOutlier] = useState(3); // 过滤异常 const [outlier, setOutlier] = useState(3); // 过滤异常
const [curve, setCurve] = useState('特征曲线'); // 曲线类型 const [curve, setCurve] = useState('特征曲线'); // 曲线类型
const [sensitive, setSensitive] = useState(10); // 敏感度 const [sensitive, setSensitive] = useState(10); // 敏感度
...@@ -32,22 +35,23 @@ const LimitCurve = (props) => { ...@@ -32,22 +35,23 @@ const LimitCurve = (props) => {
// 取消 // 取消
const onCancel = () => { const onCancel = () => {
setOpen(false); setOpen(false);
props.onCancel && props.onCancel();
}; };
// 获取历史数据 // 获取历史数据
const getSensorsData = async () => { const getSensorsData = async () => {
setSpinning(true);
const params = { const params = {
isDilute: true, isDilute: true,
zoom: '', zoom: '',
unit: '', unit: '',
dateFrom: moment().subtract(8, 'day').format('YYYY-MM-DD 00:00:00'), dateFrom: moment().subtract(8, 'day').format('YYYY-MM-DD 00:00:00'),
dateTo: moment().subtract(1, 'day').format('YYYY-MM-DD 23:59:59'), dateTo: moment().subtract(1, 'day').format('YYYY-MM-DD 23:59:59'),
acrossTables: [ acrossTables: [{ deviceCode: deviceCode, sensors: sensors, deviceType: deviceType }],
{ deviceCode: 'EGBF00000120', sensors: '出水瞬时流量', deviceType: '二供泵房' },
],
isBoxPlots: true, isBoxPlots: true,
}; };
const results = await getHistoryInfo(params); const results = await getHistoryInfo(params);
setSpinning(false);
const historyData = results?.data?.[0] || {}; const historyData = results?.data?.[0] || {};
setSensorData(() => { setSensorData(() => {
...@@ -80,6 +84,7 @@ const LimitCurve = (props) => { ...@@ -80,6 +84,7 @@ const LimitCurve = (props) => {
const _centroids = centroids.sort((a, b) => { const _centroids = centroids.sort((a, b) => {
return a[0] - b[0]; return a[0] - b[0];
}); });
console.log(_centroids);
const option = { const option = {
xAxis: { xAxis: {
type: 'time', type: 'time',
...@@ -108,17 +113,23 @@ const LimitCurve = (props) => { ...@@ -108,17 +113,23 @@ const LimitCurve = (props) => {
}, },
series: [ series: [
{ {
type: 'scatter',
name: sensors,
sampling: 'average',
large: true,
symbolSize: 5, symbolSize: 5,
data: _chartData.map((item) => { data: _chartData.map((item) => {
return [new Date(item.time).getTime(), item.pv]; return [new Date(item.time).getTime(), item.pv];
}), }),
type: 'scatter',
}, },
{ {
data: centroids.map((item) => { type: 'line',
name: sensors,
sampling: 'average',
large: true,
data: _centroids.map((item) => {
return [Math.floor(item[0]), item[1]]; return [Math.floor(item[0]), item[1]];
}), }),
type: 'line',
}, },
], ],
}; };
...@@ -126,8 +137,9 @@ const LimitCurve = (props) => { ...@@ -126,8 +137,9 @@ const LimitCurve = (props) => {
}; };
useEffect(() => { useEffect(() => {
getSensorsData(); open && getSensorsData();
}, []); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
useEffect(() => { useEffect(() => {
const { dataModel = [] } = sensorData; const { dataModel = [] } = sensorData;
...@@ -155,7 +167,6 @@ const LimitCurve = (props) => { ...@@ -155,7 +167,6 @@ const LimitCurve = (props) => {
const dataArr = []; const dataArr = [];
const pvArr = data.map((item) => { const pvArr = data.map((item) => {
return item.pv; return item.pv;
// clusteredArr.push([new Date(item.time).getTime(),item.pv])
}); });
const stdVal = pvArr.length ? std(pvArr) : 0; const stdVal = pvArr.length ? std(pvArr) : 0;
const medianVal = pvArr.length ? average(pvArr) : 0; const medianVal = pvArr.length ? average(pvArr) : 0;
...@@ -173,68 +184,12 @@ const LimitCurve = (props) => { ...@@ -173,68 +184,12 @@ const LimitCurve = (props) => {
_clustered.push(...centroids); _clustered.push(...centroids);
} }
renderChart(_chartData, _clustered); renderChart(_chartData, _clustered);
}, [sensorData, cluster, outlier]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [sensorData, timeType, outlier]);
useEffect(() => { useEffect(() => {
console.log(chartData);
const _chartData = chartDataHandle(chartData);
const clusteredData = _chartData.map((item) => {
return [new Date(item.time).getTime(), item.pv];
});
const clustered = clusteredData.length ? skmeans(clusteredData, 24) : {};
console.log(clustered);
const { centroids = [] } = clustered;
const _centroids = centroids.sort((a, b) => {
return a[0] - b[0];
});
const option = {
xAxis: {
type: 'time',
// axisTick: {
// alignWithLabel: true,
// },
// boundaryGap: false,
// splitLine: {
// show: true,
// lineStyle: {
// type: 'dashed',
// },
// },
},
yAxis: {
type: 'value',
name: 'm',
position: 'left',
alignTicks: true,
axisLine: {
show: true,
},
axisLabel: {
formatter: '{value}',
},
},
series: [
{
symbolSize: 5,
data: _chartData.map((item) => {
return [new Date(item.time).getTime(), item.pv];
}),
type: 'scatter',
},
{
data: _centroids.map((item) => {
return [new Date(Math.floor(item[0])).getTime(), item[1]];
}),
type: 'line',
},
],
};
setOptions(option);
}, [chartData]);
useEffect(()=>{
setOpen(props.open); setOpen(props.open);
},[props.open]) }, [props.open]);
return ( return (
<> <>
...@@ -249,18 +204,30 @@ const LimitCurve = (props) => { ...@@ -249,18 +204,30 @@ const LimitCurve = (props) => {
onCancel={onCancel} onCancel={onCancel}
wrapClassName={classNames(`${prefixCls}`)} wrapClassName={classNames(`${prefixCls}`)}
getContainer={getContainer || document.body} getContainer={getContainer || document.body}
destroyOnClose={true}
> >
<div className={classNames(`${prefixCls}-box`)}> <div className={classNames(`${prefixCls}-box`)}>
<div className={classNames(`${prefixCls}-header`)}> <div className={classNames(`${prefixCls}-header`)}>
<div className={classNames(`${prefixCls}-header-list`)}> <div className={classNames(`${prefixCls}-header-list`)}>
<span className={classNames(`${prefixCls}-header-item`)}> <span className={classNames(`${prefixCls}-header-item`)}>
聚类算法: 曲线选择:
<Radio.Group
options={chartArr}
optionType={'button'}
value={curve}
onChange={(e) => {
setCurve(e.target.value);
}}
/>
</span>
<span className={classNames(`${prefixCls}-header-item`)}>
取值方式:
<Radio.Group <Radio.Group
options={clusterArr} options={timeArr}
optionType={'button'} optionType={'button'}
value={cluster} value={timeType}
onChange={(e) => { onChange={(e) => {
setCluster(e.target.value); setTimeType(e.target.value);
}} }}
/> />
</span> </span>
...@@ -276,7 +243,7 @@ const LimitCurve = (props) => { ...@@ -276,7 +243,7 @@ const LimitCurve = (props) => {
/> />
</span> </span>
<span className={classNames(`${prefixCls}-header-item`)}> <span className={classNames(`${prefixCls}-header-item`)}>
敏感度 浮动范围
<Slider <Slider
min={0} min={0}
max={100} max={100}
...@@ -303,15 +270,46 @@ const LimitCurve = (props) => { ...@@ -303,15 +270,46 @@ const LimitCurve = (props) => {
</div> </div>
<div className={classNames(`${prefixCls}-header-list`)}> <div className={classNames(`${prefixCls}-header-list`)}>
<span className={classNames(`${prefixCls}-header-item`)}> <span className={classNames(`${prefixCls}-header-item`)}>
曲线选择: 取值限制:
<Radio.Group <div className={classNames(`${prefixCls}-header-value`)}>
options={chartArr} <Input
optionType={'button'} style={{
value={curve} width: '150px',
onChange={(e) => { }}
setCurve(e.target.value); addonBefore="低低限"
}} disabled
/> />
</div>
<div className={classNames(`${prefixCls}-header-value`)}>
<Input
style={{
width: '150px',
}}
addonBefore="低限"
disabled
/>
</div>
<div className={classNames(`${prefixCls}-header-value`)}>
<Input
style={{
width: '150px',
}}
addonBefore="高限"
disabled
/>
</div>
<div className={classNames(`${prefixCls}-header-value`)}>
<Input
style={{
width: '150px',
}}
addonBefore="高高限"
disabled
/>
</div>
</span>
<span className={classNames(`${prefixCls}-header-item`)}>
<Button type="primary">确定</Button>
</span> </span>
</div> </div>
</div> </div>
...@@ -323,48 +321,11 @@ const LimitCurve = (props) => { ...@@ -323,48 +321,11 @@ const LimitCurve = (props) => {
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
/> />
</div> </div>
{spinning && <div className={classNames(`${prefixCls}-load`)}><LoadBox spinning={spinning}/></div>}
</div> </div>
</Modal> </Modal>
</> </>
); );
}; };
const outlierArr = [
{
label: '低',
value: 3,
},
{
label: '中',
value: 2,
},
{
label: '高',
value: 1,
},
];
const clusterArr = [
{
label: 'K-means',
value: 'K-means',
},
];
const chartArr = [
{
label: '特征曲线',
value: '特征曲线',
},
{
label: '原始曲线',
value: '原始曲线',
},
];
// 平均值方法
const average = (arr) => {
return arr.reduce((acc, cur) => acc + cur, 0) / arr.length;
};
export default LimitCurve; export default LimitCurve;
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative;
} }
&-header { &-header {
...@@ -33,10 +34,31 @@ ...@@ -33,10 +34,31 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
&-value {
margin-right: 10px;
.@{ant-prefix}-input[disabled] {
background-color: #ffffff;
}
}
} }
&-content { &-content {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
} }
&-load {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.9);
z-index: 100;
}
} }
\ No newline at end of file
export const variance = (numbers) => { export const outlierArr = [
let mean = 0; {
let sum = 0; label: '低',
for (const i = 0; i < numbers.length; i++) { value: 3,
sum += numbers[i]; },
} {
mean = sum / numbers.length; label: '中',
sum = 0; value: 2,
for (const i = 0; i < numbers.length; i++) { },
sum += Math.pow(numbers[i] - mean, 2); {
} label: '高',
return sum / numbers.length; value: 1,
}; },
];
export const timeArr = [
{
label: '近7天',
value: '近7天',
},
{
label: '7工作日',
value: '7工作日',
},
{
label: '7节假日',
value: '7节假日',
},
];
export const chartArr = [
{
label: '特征曲线',
value: '特征曲线',
},
{
label: '原始曲线',
value: '原始曲线',
},
];
// 平均值方法
export const average = (arr) => {
return arr.reduce((acc, cur) => acc + cur, 0) / arr.length;
};
\ No newline at end of file
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.6.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_configurationview@1.5.31...@wisdom-components/ec_configurationview@1.6.0) (2023-12-28)
### Features
- 组态分组增加消息通信 ([cf66b90](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/commits/cf66b908d34b46046f88e793177581af1be38339))
## [1.5.31](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_configurationview@1.5.30...@wisdom-components/ec_configurationview@1.5.31) (2023-12-13) ## [1.5.31](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_configurationview@1.5.30...@wisdom-components/ec_configurationview@1.5.31) (2023-12-13)
### Bug Fixes ### Bug Fixes
......
{ {
"name": "@wisdom-components/ec_configurationview", "name": "@wisdom-components/ec_configurationview",
"version": "1.5.31", "version": "1.6.0",
"description": "> TODO: description", "description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>", "author": "tuqian <webtuqian@163.com>",
"homepage": "", "homepage": "",
......
...@@ -35,6 +35,7 @@ group: ...@@ -35,6 +35,7 @@ group:
| config | 全局 globalConfig,没有时需要传递 | object | {} | | config | 全局 globalConfig,没有时需要传递 | object | {} |
| isZoom | 是否可缩放(手持上建议设置 true) | boolean | false | | isZoom | 是否可缩放(手持上建议设置 true) | boolean | false |
| flowShow | 是否水流效果(无数据表现) | boolean | true | | flowShow | 是否水流效果(无数据表现) | boolean | true |
| messaged | 传递信息给组态 | object | {} |
| customBack | 自定义交互的回调,返回点击的模型 | function(node){} | - | | customBack | 自定义交互的回调,返回点击的模型 | function(node){} | - |
### 工艺回放 ### 工艺回放
......
...@@ -61,7 +61,7 @@ const ConfigurationView = (props) => { ...@@ -61,7 +61,7 @@ const ConfigurationView = (props) => {
const [description, setDescription] = useState(''); // 画板无数据描述 const [description, setDescription] = useState(''); // 画板无数据描述
twoID = `TDG${Date.now().toString(36)}`; twoID = `TDG${Date.now().toString(36)}`;
const ConfigurationRef = useRef(); const ConfigurationRef = useRef();
const customBack = props.customBack ? props.customBack : () => {}; const customBack = props.customBack ? props.customBack : () => {};
const { const {
...@@ -313,6 +313,12 @@ const ConfigurationView = (props) => { ...@@ -313,6 +313,12 @@ const ConfigurationView = (props) => {
myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true);
} }
break; break;
case 'groupCase': // 分组模型
shRule = ruleOperation(node, realVal);
if (node.shType === '显隐展示') {
myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true);
}
break;
default: default:
break; break;
} }
...@@ -1140,12 +1146,13 @@ const ConfigurationView = (props) => { ...@@ -1140,12 +1146,13 @@ const ConfigurationView = (props) => {
goJS( goJS(
go.Group, go.Group,
'Auto', 'Auto',
{ ungroupable: true, zOrder: 1 }, { ungroupable: true, zOrder: 1, visible: true },
{ {
// 设置其可选择 // 设置其可选择
selectable: false, selectable: false,
layerName: 'Background', layerName: 'Background',
}, },
new go.Binding('visible', 'visible').makeTwoWay(),
goJS( goJS(
go.Shape, go.Shape,
'RoundedRectangle', // surrounds everything 'RoundedRectangle', // surrounds everything
......
...@@ -80,6 +80,7 @@ const ConfigurationView = (props) => { ...@@ -80,6 +80,7 @@ const ConfigurationView = (props) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('ec-configuration-view'); const prefixCls = getPrefixCls('ec-configuration-view');
const componentPrefix = getPrefixCls(''); const componentPrefix = getPrefixCls('');
const [isDiagram, setIsDiagram] = useState(null);
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [isAuModalVisible, setIsAuModalVisible] = useState(false); // 登录模态框 const [isAuModalVisible, setIsAuModalVisible] = useState(false); // 登录模态框
const [isHIModalVisible, setIsHIModalVisible] = useState(false); // 历史曲线模态框 const [isHIModalVisible, setIsHIModalVisible] = useState(false); // 历史曲线模态框
...@@ -382,6 +383,12 @@ const ConfigurationView = (props) => { ...@@ -382,6 +383,12 @@ const ConfigurationView = (props) => {
myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true);
} }
break; break;
case 'groupCase': // 分组模型
shRule = ruleOperation(node, realVal);
if (node.shType === '显隐展示') {
myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true);
}
break;
default: default:
break; break;
} }
...@@ -883,6 +890,7 @@ const ConfigurationView = (props) => { ...@@ -883,6 +890,7 @@ const ConfigurationView = (props) => {
if (myDiagram) { if (myDiagram) {
myDiagram.div = null; myDiagram.div = null;
myDiagram = null; myDiagram = null;
setIsDiagram(null);
} }
}; };
}, [props.name, numerals]); }, [props.name, numerals]);
...@@ -912,6 +920,65 @@ const ConfigurationView = (props) => { ...@@ -912,6 +920,65 @@ const ConfigurationView = (props) => {
}; };
}, []); }, []);
useEffect(() => {
try {
const { messaged = {} } = props;
if (isDiagram && Object.keys(messaged).length > 0) {
const json = JSON.parse(isDiagram.model.toJson());
const jsonCopy = JSON.parse(JSON.stringify(json));
jsonCopy.nodeDataArray.forEach((item) => {
if (item.category === 'groupCase' && !item.shName) {
const node = isDiagram.model.findNodeDataForKey(item.key);
messagedMethod(node, messaged);
}
});
}
} catch (err) {
// console.log(err);
}
}, [props.messaged, isDiagram]);
// 信息通信
const messagedMethod = (node, messaged) => {
let shRule = [];
try {
switch (node.category) {
case 'groupCase': // 分组模型
shRule = ruleMessaged(node, messaged);
if (node.shType === '显隐展示') {
isDiagram.model.setDataProperty(
node,
'visible',
shRule ? shRule.visible : node.dtVisible,
);
}
break;
default:
break;
}
} catch (e) {
// console.log(e);
}
};
// 信息通信语句解析
const ruleMessaged = (node, messaged) => {
const patt = /[><=]/gi;
const shRule = JSON.parse(node.shRule).find((rule) => {
if (rule.val.toString().match(patt)) {
const ruleStr = 'if(' + rule.val + '){ return true } else { return false }';
try {
return new Function('x', 'X', ruleStr)(messaged, messaged);
} catch (err) {
return false;
}
} else {
return false;
}
});
return shRule;
};
/** ************************************获取画板JSON******************************* */ /** ************************************获取画板JSON******************************* */
const getDiagramJson = async (list, siteInfo) => { const getDiagramJson = async (list, siteInfo) => {
const response = await getSketchPadContent({ const response = await getSketchPadContent({
...@@ -1648,12 +1715,13 @@ const ConfigurationView = (props) => { ...@@ -1648,12 +1715,13 @@ const ConfigurationView = (props) => {
goJS( goJS(
go.Group, go.Group,
'Auto', 'Auto',
{ ungroupable: true, zOrder: 1 }, { ungroupable: true, zOrder: 1, visible: true },
{ {
// 设置其可选择 // 设置其可选择
selectable: false, selectable: false,
layerName: 'Background', layerName: 'Background',
}, },
new go.Binding('visible', 'visible').makeTwoWay(),
goJS( goJS(
go.Shape, go.Shape,
'RoundedRectangle', // surrounds everything 'RoundedRectangle', // surrounds everything
...@@ -3096,6 +3164,9 @@ const ConfigurationView = (props) => { ...@@ -3096,6 +3164,9 @@ const ConfigurationView = (props) => {
if (item.category === 'bgCase') { if (item.category === 'bgCase') {
myDiagram.defaultScale = item.scaling || item.scaling === 0 ? item.scaling * 1 : 1; myDiagram.defaultScale = item.scaling || item.scaling === 0 ? item.scaling * 1 : 1;
} }
if (item.category === 'groupCase') {
item.dtVisible = item?.visible || false;
}
// 兼容V1之前版本(部分展示可支持) // 兼容V1之前版本(部分展示可支持)
if (chartInfo.version === 'V1') return false; if (chartInfo.version === 'V1') return false;
...@@ -3106,6 +3177,7 @@ const ConfigurationView = (props) => { ...@@ -3106,6 +3177,7 @@ const ConfigurationView = (props) => {
}); });
myDiagram.model = go.Model.fromJson(json); myDiagram.model = go.Model.fromJson(json);
getDataModel(); getDataModel();
setIsDiagram(myDiagram);
}; };
return ( return (
...@@ -3271,6 +3343,7 @@ ConfigurationView.defaultProps = { ...@@ -3271,6 +3343,7 @@ ConfigurationView.defaultProps = {
config: {}, config: {},
isZoom: false, isZoom: false,
flowShow: true, flowShow: true,
messaged: {},
customBack: () => {}, customBack: () => {},
}; };
...@@ -3281,6 +3354,7 @@ ConfigurationView.propTypes = { ...@@ -3281,6 +3354,7 @@ ConfigurationView.propTypes = {
config: PropTypes.object, config: PropTypes.object,
isZoom: PropTypes.bool, isZoom: PropTypes.bool,
flowShow: PropTypes.bool, flowShow: PropTypes.bool,
messaged: PropTypes.object,
customBack: PropTypes.func, customBack: PropTypes.func,
}; };
......
...@@ -3,10 +3,11 @@ import { Button } from 'antd'; ...@@ -3,10 +3,11 @@ import { Button } from 'antd';
import PandaConfiguration from '../index'; import PandaConfiguration from '../index';
// import PandaConfigurationView from '../../es/index'; // import PandaConfigurationView from '../../es/index';
const Demo = () => { const Demo = () => {
const [name, setName] = useState('宿场加压站2'); const [name, setName] = useState('cs0320');
const [devices, setDevices] = useState( const [devices, setDevices] = useState('EQZT00000008');
'JYJZ00000001,JYBZ00000001', const [messaged, setMessaged] = useState({
); age: '运行监控1',
});
return ( return (
<> <>
<div style={{ width: '100%', height: '600px', background: '#242835' }}> <div style={{ width: '100%', height: '600px', background: '#242835' }}>
...@@ -21,16 +22,20 @@ const Demo = () => { ...@@ -21,16 +22,20 @@ const Demo = () => {
// devices={'CPAA00000001, CPAD00000001, LJSC00000002'.split(',')} // devices={'CPAA00000001, CPAD00000001, LJSC00000002'.split(',')}
config={globalConfig} config={globalConfig}
deviceName={['工艺流程1', '工艺流程2', '工艺流程3']} deviceName={['工艺流程1', '工艺流程2', '工艺流程3']}
messaged={messaged}
// isZoom={true} // isZoom={true}
// flowShow={false} // flowShow={false}
/> />
</div> </div>
<Button <Button
onClick={() => { onClick={() => {
setName('崇左丽江水厂原水泵房'); // setName('崇左丽江水厂原水泵房');
setDevices( // setDevices(
'EQZT00000007,CPBA00000001,CPAA00000001,EQZT00000008,CPDA00000001,CPAD00000001,LJSC00000002,EQZT00000005,EQZT00000004,EQZT00000002,EQZT00000003', // 'EQZT00000007,CPBA00000001,CPAA00000001,EQZT00000008,CPDA00000001,CPAD00000001,LJSC00000002,EQZT00000005,EQZT00000004,EQZT00000002,EQZT00000003',
); // );
setMessaged({
age: '运行监控',
});
}} }}
style={{ margin: '10px' }} style={{ margin: '10px' }}
> >
...@@ -44,18 +49,18 @@ export default Demo; ...@@ -44,18 +49,18 @@ export default Demo;
const globalConfig = { const globalConfig = {
token: 'a1372ef0ce7b4e4884d31cfd99fe92f6', token: 'a1372ef0ce7b4e4884d31cfd99fe92f6',
mqtt_iotIP: '36.140.140.170:8200', mqtt_iotIP: 'emqttd10.panda-water.cn:443',
mqtt_path: '/ws/', mqtt_path: '/mqtt',
mqtt_IsSSL: false, mqtt_IsSSL: true,
mqtt_site_code: 'site_ds5418ua', mqtt_site_code: 'site_dc8302ni',
mqtt_mess: { mqtt_mess: {
MessageLevel: '1.0', MessageLevel: '1.0',
TcpIP: '36.140.140.170', TcpIP: 'emqttd10.panda-water.cn',
TcpPort: 8200, TcpPort: 443,
site_code: 'site_ds5418ua', site_code: 'site_dc8302ni',
}, },
userInfo: { userInfo: {
LocalSite: 'site_ds5418ua', LocalSite: 'site_dc8302ni',
site: '', site: '',
}, },
}; };
...@@ -2,6 +2,18 @@ ...@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.53.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_historyview@1.52.0...@wisdom-components/ec_historyview@1.53.0) (2023-12-25)
### Features
- 表格去除左侧固定 ([60479c1](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/commits/60479c1a2578958e7ac782e878bca0bb34116858))
# [1.52.0](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_historyview@1.51.3...@wisdom-components/ec_historyview@1.52.0) (2023-12-25)
### Features
- 优化历史曲线名称显示 ([f2d7770](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/commits/f2d77709898c4200f6a03b4526a632f081ade892))
## [1.51.3](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_historyview@1.51.2...@wisdom-components/ec_historyview@1.51.3) (2023-12-13) ## [1.51.3](https://g.civnet.cn:8443/ReactWeb5/wisdom-components/compare/@wisdom-components/ec_historyview@1.51.2...@wisdom-components/ec_historyview@1.51.3) (2023-12-13)
### Bug Fixes ### Bug Fixes
......
{ {
"name": "@wisdom-components/ec_historyview", "name": "@wisdom-components/ec_historyview",
"version": "1.51.3", "version": "1.53.0",
"description": "> TODO: description", "description": "> TODO: description",
"author": "cuijiahao <15927252954@163.com>", "author": "cuijiahao <15927252954@163.com>",
"homepage": "", "homepage": "",
......
...@@ -22,7 +22,7 @@ path: / ...@@ -22,7 +22,7 @@ path: /
## 单图表 ## 单图表
[//]: # (<code src="./demos/index.js"></code>) <code src="./demos/index.js"></code>
## 单图表-状态 ## 单图表-状态
...@@ -30,7 +30,7 @@ path: / ...@@ -30,7 +30,7 @@ path: /
## 单图表-频率 ## 单图表-频率
<code src="./demos/indexForFrequency.js"></code> [//]: # (<code src="./demos/indexForFrequency.js"></code>)
[//]: # '## 移动端' [//]: # '## 移动端'
[//]: # '<code src="./demos/mobile.js"></code>' [//]: # '<code src="./demos/mobile.js"></code>'
......
...@@ -166,10 +166,16 @@ const deviceParams = [ ...@@ -166,10 +166,16 @@ const deviceParams = [
"sensors": "IC", "sensors": "IC",
"deviceType": "水源井" "deviceType": "水源井"
}*/ }*/
{ /* {
"deviceCode": "JYJZ00000002", "deviceCode": "JYJZ00000002",
"sensors": "频率,泵1状态", "sensors": "频率,泵1状态",
"deviceType": "加压机组" "deviceType": "加压机组"
}*/
/*铁山*/
{
"deviceCode": "SZ00000001",
"deviceType": "水质",
"sensors": "PH,浑浊度,氨氮,总氮,总磷,溶解氧,水温,电导率,叶绿素a,藻密度,高锰酸盐指数"
} }
]; ];
......
...@@ -268,7 +268,6 @@ const timeColumn = { ...@@ -268,7 +268,6 @@ const timeColumn = {
dataIndex: 'time', dataIndex: 'time',
key: 'time', key: 'time',
width: 170, width: 170,
fixed: 'left',
ellipsis: true, ellipsis: true,
align: 'center', align: 'center',
sorter: true, sorter: true,
......
...@@ -65,11 +65,13 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION; ...@@ -65,11 +65,13 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
* @param {any} data * @param {any} data
* @param {boolean} contrast 是否为同期对比 * @param {boolean} contrast 是否为同期对比
* @param {any} contrastOption 同期对比周期配置, day|month * @param {any} contrastOption 同期对比周期配置, day|month
* @param nameWithSensor
* @param {boolean} isSingle 是否是单设备,单设备的名称不带设备名
* @returns * @returns
*/ */
const nameFormatter = (data, contrast, contrastOption, nameWithSensor) => { const nameFormatter = (data, contrast, contrastOption, nameWithSensor, isSingle) => {
const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data; const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data;
let name = nameWithSensor ? `${equipmentName}-${sensorName}` : equipmentName; let name = nameWithSensor ? (isSingle?`${sensorName}`:`${equipmentName}-${sensorName}`) : equipmentName;
if (contrast) { if (contrast) {
const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, ''); const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, '');
name = `${name}-${time}`; name = `${name}-${time}`;
...@@ -515,6 +517,11 @@ const returnXAxis = ({ ...@@ -515,6 +517,11 @@ const returnXAxis = ({
_filterArr.push(special.special1.name); _filterArr.push(special.special1.name);
} }
// 生成series // 生成series
// 单设备情况下,不显示设备名称
let deviceNumber = dataSource?.reduce((final, cur) => {
if (!final.includes(cur.stationCode)) final.push(cur.stationCode);
return final
}, [])?.length;
let series = dataSource let series = dataSource
.filter((item) => { .filter((item) => {
if (item.sensorName === '是否在线') { if (item.sensorName === '是否在线') {
...@@ -524,7 +531,7 @@ const returnXAxis = ({ ...@@ -524,7 +531,7 @@ const returnXAxis = ({
}) })
.map((item, index) => { .map((item, index) => {
const {sensorName, unit} = item; const {sensorName, unit} = item;
const name = nameFormatter(item, contrast, contrastOption, nameWithSensor); const name = nameFormatter(item, contrast, contrastOption, nameWithSensor, deviceNumber === 1);
const data = dataAccessor(item, contrast, contrastOption); const data = dataAccessor(item, contrast, contrastOption);
const type = 'line'; const type = 'line';
const areaStyle = areaStyleFormatter(item); const areaStyle = areaStyleFormatter(item);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment