Commit d73f0e6c authored by 陈龙's avatar 陈龙

feat: 更新轻量化报表功能

parent dbdf13a3
......@@ -11,6 +11,7 @@ import { returnDefaultValueOrConfigs } from '../utils/utils';
const { Option } = Select;
const { Search } = Input;
const USER_ID = window.globalConfig.userInfo.OID;
const TextSearchComponent = ({ onChange, style, onSearch, placeholder }) => {
return (
<Search
......@@ -39,6 +40,7 @@ const SelectSearchComponent = ({
configItems,
filterFields,
filterValues,
filterObject,
}) => {
const [value, setValue] = useState('');
const [options, setOptions] = useState([]);
......@@ -46,19 +48,23 @@ const SelectSearchComponent = ({
const { defaultValue } = defaultConfigs;
const getData = () => {
reportService
.getReportFilterValue({
reportName,
.getReportFilterValues({
fieldAlias,
...filterObject,
})
.then((res) => {
if (res.code === 0) {
let _options = res.data;
let _filterFields = filterFields.split('|');
let _filterValues = filterValues.split('|');
let _index = _filterFields.findIndex((item) => item === fieldAlias);
if (_index > -1) {
let _needToFilterValues = _filterValues[_index]?.split(',') || [];
_options = _options.filter((item) => _needToFilterValues.includes(item.filterValue));
let _options = res.data
.find((item) => item.fieldAlias === fieldAlias)
.filterValues.filter((item) => item.filterValue);
if (filterValues && filterFields) {
let _filterFields = filterFields.split('|');
let _filterValues = filterValues.split('|');
let _index = _filterFields.findIndex((item) => item === fieldAlias);
if (_index > -1) {
let _needToFilterValues = _filterValues[_index]?.split(',') || [];
_options = _options.filter((item) => _needToFilterValues.includes(item.filterValue));
}
}
setOptions(_options);
}
......@@ -107,6 +113,7 @@ const ReturnControlComponent = ({
configItems,
filterValues,
filterFields,
filterObject,
}) => {
let _component = '';
switch (type) {
......@@ -133,6 +140,7 @@ const ReturnControlComponent = ({
configItems={configItems}
filterFields={filterFields}
filterValues={filterValues}
filterObject={filterObject}
/>
);
break;
......
......@@ -4,33 +4,37 @@
** 功能路径:src\pages\product\ReportsManage\ReportEditForm.js
** 菜单参数列表:*变量名*(变量说明,数据类型,是否必填,取值范围)
**/
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useContext } from 'react';
import {
Form,
Input,
DatePicker,
InputNumber,
Space,
Row,
Col,
Button,
message,
Select,
ConfigProvider,
} from 'antd';
import moment from 'moment';
import { submitReportData } from '../api/service/report';
import FileUpload from './Components/fileUpload/fileUpload';
import { reportService } from '../api';
import { isSelect, returnOptions } from './utils/utils';
import { isSelect, returnOptions, returnRows, returnCols } from './utils/utils';
import style from './ReportEditForm.less';
const { Option } = Select;
const { TextArea } = Input;
// 类型
const USER_ID = window.globalConfig.userInfo.OID;
const TEXT_ARRAY = ['文本', '标签'];
const TEXTAREA_ARRAY = ['多行文本'];
const DATE_PICKER_ARRAY = ['日期'];
const DATE_TIME_PICKER_ARRAY = ['日期时刻'];
const DATE_TYPE = ['日期', '日期时刻']; // 用来匹配是否需要转为日期对象;
const NUMBER_ARRAY = ['数值', '金额'];
const NUMBER_ARRAY = ['数值', '数值标签'];
const FILE_ARRAY = ['附件'];
// 形态对应组件
......@@ -47,14 +51,16 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName, modal
// if (!reportData || Object.keys(reportData).length === 0) return <>未传递表单数据</>;
const [fileAlias, setFileAlias] = useState([]); // 附件需要单独处理提交的数据
const [form] = Form.useForm();
const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls();
/* const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};*/
// 数值类的后端会处理再返回,故无序处理精度问题
const handleDate = (reportDetails, data) => {
let _data = { ...data };
......@@ -67,19 +73,22 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName, modal
};
const componentMap = (config) => {
if (DATE_TIME_PICKER_ARRAY.includes(config.type)) {
return <DatePicker showTime />;
return <DatePicker showTime readonly={config.isReadOnly} />;
} else if (DATE_PICKER_ARRAY.includes(config.type)) {
return <DatePicker />;
return <DatePicker disabled={config.isReadOnly} />;
} else if (NUMBER_ARRAY.includes(config.type)) {
return <InputNumber />;
return <InputNumber disabled={config.isReadOnly} />;
} else if (FILE_ARRAY.includes(config.type)) {
return <FileUpload schema={{ renderTo: 'File' }} />;
return <FileUpload schema={{ renderTo: 'File' }} disabled={config.isReadOnly} />;
} else if (TEXTAREA_ARRAY.includes(config.type)) {
let _rows = returnRows(config.configItems);
return <TextArea rows={_rows} disabled={config.isReadOnly} />;
} else {
if (isSelect(config.configItems)) {
let options = returnOptions(config.configItems);
if (options) {
return (
<Select>
<Select disabled={config.isReadOnly}>
{options.map((item) => (
<Option value={item}>{item}</Option>
))}
......@@ -87,7 +96,7 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName, modal
);
}
}
return <Input />;
return <Input disabled={config.isReadOnly} />;
}
};
const submitReportForm = () => {
......@@ -174,29 +183,38 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName, modal
form.setFieldsValue(handleDate(reportDetails, reportData));
}, [reportData]);
return (
<div style={{ position: 'relative' }}>
<div className={style.reportEditForm} style={{ position: 'relative' }}>
<div>
<Form {...formItemLayout} form={form}>
<Form form={form}>
<Row style={{ overflowY: 'scroll', maxHeight: 'calc(100vh - 300px)' }}>
{reportDetails &&
reportDetails.map((config) => {
return (
<Col span={8} key={config.fieldAlias}>
<Form.Item
label={config.fieldAlias}
name={config.fieldAlias}
rules={[
{
required: config.isRequired,
message: `${config.fieldAlias}必填`,
},
]}
reportDetails
.filter((config) => {
if (modalType === '新增' && config.isReadOnly) return false;
return config;
})
.map((config) => {
// return <Col span={returnCols(config.configItems) * 8} key={config.fieldAlias}>
return (
<div
style={{ width: `${returnCols(config.configItems) * 33.3}%` }}
key={config.fieldAlias}
>
{componentMap(config)}
</Form.Item>
</Col>
);
})}
<Form.Item
label={config.fieldAlias}
name={config.fieldAlias}
rules={[
{
required: config.isRequired,
message: `${config.fieldAlias}必填`,
},
]}
>
{componentMap(config)}
</Form.Item>
</div>
);
})}
</Row>
<Row>
<Col span={24} style={{ textAlign: 'right' }}>
......
@import '~antd/es/style/themes/default.less';
.reportEditForm {
:global {
.@{ant-prefix}-form-item-label {
width: 120px !important;
}
input[disabled] {
background-color: rgb(250, 250, 250);
cursor: default;
}
// 多行文本框
.@{ant-prefix}-input[disabled] {
color: rgba(0, 0, 0, 0.85);
background-color: rgb(250, 250, 250);
border-color: #d9d9d9;
box-shadow: none;
cursor: default;
opacity: 1;
}
// 时间选择器的disabled样式
.@{ant-prefix}-picker-disabled {
color: rgba(0, 0, 0, 0.85);
background-color: rgb(250, 250, 250);
cursor: default;
& + span {
cursor: default;
}
}
// radio的disabled样式
.@{ant-prefix}-radio-disabled {
cursor: default;
& + span {
cursor: default;
}
.@{ant-prefix}-radio-inner {
cursor: default;
}
}
// 下拉选已选的disabled样式
.@{ant-prefix}-cascader-picker-disabled {
color: rgba(0, 0, 0, 0.85);
background-color: rgb(250, 250, 250);
cursor: default;
}
// 按钮的disabled样式
.@{ant-prefix}-btn[disabled] {
text-shadow: none;
background: #ffffff;
border-color: #d9d9d9;
box-shadow: none;
cursor: default;
&:hover,
&:focus,
&:active {
color: rgba(0, 0, 0, 0.25);
text-shadow: none;
background: #ffffff;
border-color: #d9d9d9;
box-shadow: none;
}
}
// 多选下的tag的disabled样式
.@{ant-prefix}-select-disabled.@{ant-prefix}-select-multiple {
.@{ant-prefix}-select-selection-item {
color: #bfbfbf;
border-color: #d9d9d9;
cursor: default;
}
}
.@{ant-prefix}-select-disabled.@{ant-prefix}-select:not(.@{ant-prefix}-select-customize-input) {
.@{ant-prefix}-select-selector input {
cursor: default;
}
}
// 时间选择框
.@{ant-prefix}-picker-input > input[disabled] {
color: rgba(0, 0, 0, 0.85);
}
}
}
@import '~antd/es/style/themes/default.less';
.reportsDataSourceSetting {
height: 100%;
overflow: hidden;
.contentWrapper {
display: flex;
flex-direction: column;
height: calc(100% - 16px);
margin: 8px;
@media screen and (min-width: 1680px) {
.content {
grid-template-columns: repeat(5, 1fr);
}
}
@media screen and (max-width: 1680px) {
.content {
grid-template-columns: repeat(4, 1fr);
}
}
@media screen and (max-width: 1320px) {
.content {
grid-template-columns: repeat(3, 1fr);
}
}
@media screen and (max-width: 960px) {
.content {
grid-template-columns: repeat(2, 1fr);
}
}
.controlRow {
display: flex;
flex-direction: column;
margin-bottom: 8px;
padding: 8px;
background: #ffffff;
:global {
.@{ant-prefix}-form-item {
margin-bottom: 0;
}
}
}
.content {
display: grid;
grid-gap: 10px;
padding-bottom: 8px;
overflow: scroll;
.card {
height: 198px;
.cardInfo {
padding-left: 48px;
.cardInfoItem {
margin-bottom: 4px;
color: rgb(100, 100, 100, 0.65);
.item {
color: rgba(58, 58, 58, 0.55);
font-weight: bold;
}
.blue {
color: rgba(23, 130, 252, 0.65);
}
.ellipsis {
display: inline-block;
max-width: 120px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: top;
}
}
}
}
}
}
}
import MySQLIcon from '../assets/mysql.png';
import SQLServerIcon from '../assets/sqlserver.png';
import WebAPIIcon from '../assets/webapi.png';
const tagColors = [
'rgb(129, 134, 143)',
'rgb(41, 114, 244)',
......@@ -20,4 +24,9 @@ const tagBackgroundColors = [
'rgb(251, 219, 255)',
'rgb(255, 219, 234)',
];
export { tagColors, tagBackgroundColors };
const sourceIconMap = {
mysql: MySQLIcon,
sqlserver: SQLServerIcon,
webapi: WebAPIIcon,
};
export { tagColors, tagBackgroundColors, sourceIconMap };
......@@ -8,10 +8,8 @@ import { tagColors, tagBackgroundColors } from './constant';
/**
* @params: config下的数值的configRule结构如下,[{最大值: 10,最小值: 0,颜色:'#AAAAAA'}];
* @business: configRule有值,则按照configRule设置;没有,按照color设置; 有最大值,无最小值;['', 1]
*
* 有最大值,有最小值;[2,
*
* 10]
* 有最大值,有最小值;[2,
* 10]
* 有最小值,无最大值;[11,'']
*/
// 链接 功能 弹窗功能待提出需求
......@@ -71,16 +69,22 @@ const clickModal = (config, showModal, setExtra) => {
showModal(true);
return setExtra(extraComponents[_fn](_data));
};
const returnOpacity = (rgba) => {
if (!rgba) rgba = 'rgba(0,0,0,.65)';
let _str = rgba.replace('rgba(', '').replace(')', '');
let _splitStr = _str.split(',');
return `rgba(${_splitStr[0]},${_splitStr[1]},${_splitStr[2]},.25)`;
};
export const handleNumber = (config, number) => {
let _color = '';
let _number;
if (number) _number = Number(number); // 当设置精度后,会被转成字符串
if (config.numericalConfigs && config.numericalConfigs.length) {
if (config.type === '数值' && config.numericalConfigs && config.numericalConfigs.length) {
config.numericalConfigs.forEach((item) => {
// 接口对于数值类型的返回为null
if (!_color) {
let _max = item.maxValue || '';
let _min = item.minValue || '';
let _max = isNumber(item.maxValue) ? item.maxValue : '';
let _min = isNumber(item.minValue) ? item.minValue : '';
if (_max !== '' && _min === '') {
_color = _number <= Number(_max) ? '' : item.color;
} else if (_min !== '' && _max === '') {
......@@ -90,6 +94,9 @@ export const handleNumber = (config, number) => {
}
}
});
} else if (config.type === '数值标签' && config.labelConfigs && config.labelConfigs.length) {
console.log('数值标签');
_color = 'red';
} else if (config.color) {
_color = config.color;
}
......@@ -109,11 +116,7 @@ export const handleNumber = (config, number) => {
margin: '0 5px',
}}
>
{hasMoney(config?.configItems)
? number
? Number(number)?.toLocaleString()
: number
: number}
{returnHandledNumber(config.configItems, number)}
</span>
<span className={style.prefixOrSuffix} style={{ color: _color }}>
{config.suffix || ''}
......@@ -123,7 +126,45 @@ export const handleNumber = (config, number) => {
'-'
);
};
export const handleNumberTag = (config, number) => {
let _color = '';
let _map = {};
// 标签需要设置分隔符 2022年7月13日 ChenLong
let _configItems = config.configItems.split('|');
let _configMap = {};
_configItems.forEach((item) => {
let _arr = item.split('=');
_configMap[_arr[0]] = _arr[1];
});
// 处理label的颜色
if (config.labelConfigs && config.labelConfigs.length) {
config.labelConfigs.forEach((item) => {
_map[item.labelValue] = item.color;
});
}
_color = config.color || 'rgba(0,0,0,.85)';
// String(text)为了解决可能存在数值类型的数据需要设置成标签的需求
// return String(text).split(_configMap['分隔符']).map(item => <Tag color={_map[item]}>{item}</Tag>);
// let a = 'abc' a.split(undefined); a = ['abc']
return (
<Tag
style={{
background: mapTag(_map, number) || _color,
border: `1px solid ${mapTag(_map, number) || _color}`,
borderRadius: 2,
color: mapTag(_map, number)
? tagColors[tagBackgroundColors.findIndex((c) => c === mapTag(_map, number))]
: `rgba(0, 0, 0, .65)`,
}}
>
{(config.prefix || '') +
returnHandledNumber(config.configItems, number) +
(config.suffix || '')}
</Tag>
);
};
/** @params: 标签形态的configRule,[{标签值:'字符串',颜色: '#AAAAAA'}]; */
/* 支持正则配置 RegExp */
export const handleTag = (config, text) => {
let _color = '';
let _map = {};
......@@ -143,16 +184,17 @@ export const handleTag = (config, text) => {
_color = config.color || 'rgba(0,0,0,.85)';
// String(text)为了解决可能存在数值类型的数据需要设置成标签的需求
// return String(text).split(_configMap['分隔符']).map(item => <Tag color={_map[item]}>{item}</Tag>);
// let a = 'abc' a.split(undefined); a = ['abc']
return String(text)
.split(_configMap['分隔符'])
.map((item) => (
<Tag
style={{
background: _map[item] || _color,
border: `1px solid ${_map[item] || _color}`,
background: mapTag(_map, item) || _color,
border: `1px solid ${mapTag(_map, item) || _color}`,
borderRadius: 2,
color: _map[item]
? tagColors[tagBackgroundColors.findIndex((c) => c === _map[item])]
color: mapTag(_map, item)
? tagColors[tagBackgroundColors.findIndex((c) => c === mapTag(_map, item))]
: `rgba(0, 0, 0, .65)`,
}}
>
......@@ -160,6 +202,21 @@ export const handleTag = (config, text) => {
</Tag>
));
};
const mapTag = (map, key) => {
if (map[key]) {
return map[key];
} else {
let _keys = Object.keys(map);
return _keys.reduce((final, _key) => {
let isRegExp = _key.includes('RegExp=');
let reg = new RegExp(_key.replace('RegExp=', ''));
if (isRegExp && reg.test(key)) {
final = map[_key];
}
return final;
}, '');
}
};
export const handleText = (config, text) => {
return <span style={{ color: config.color || 'rgba(0,0,0,.85)' }}>{text}</span>;
};
......
......@@ -28,13 +28,29 @@ const returnOptions = (configItems) => {
if (!_options) return false;
return _options.replace('options=', '').split(',');
};
const returnRows = (configItems) => {
if (!configItems) return 3;
let _items = configItems.split('|');
let _options = _items.find((item) => item.includes('rows='));
if (!_options) return 3;
let _rows = Number(_options.replace('rows=', ''));
return !isNaN(_rows) && _rows >= 1 ? _rows : 3;
};
const returnCols = (configItems) => {
if (!configItems) return 1;
let _items = configItems.split('|');
let _options = _items.find((item) => item.includes('cols='));
if (!_options) return 1;
let _cols = Number(_options.replace('cols=', ''));
return !isNaN(_cols) && _cols <= 3 && _cols >= 0 ? _cols : 1;
};
/**
* @description: 用来在summary中处理数值的配置
* @params: 参数描述
* @date: 2022/8/10
* @author: ChenLong
*/
const returnHandledNumber = (configItems, num) => {
const returnHandledNumber = (configItems, num, isSummary) => {
// 精度、前缀、后缀、倍率
// $_d|_d%|_d*0.0001|金额|0.00
if (isNaN(num)) return '-';
......@@ -54,7 +70,7 @@ const returnHandledNumber = (configItems, num) => {
} else if (item.match(/^_d\*/)) {
// 倍率
let _rate = item.replace(/_d\*/, '');
rate = _rate ? Number(_rate) : 1;
rate = _rate && isSummary ? Number(_rate) : 1; // 总结栏计算需要计算倍率,其他不用
} else if (item.match(/^0\./)) {
// 精度
precision = item.replace('0.', '').length;
......@@ -63,7 +79,7 @@ const returnHandledNumber = (configItems, num) => {
// 可能存在NaN的问题
let final = _items.includes('金额')
? Number((num * rate).toFixed(precision)).toLocaleString()
: Number((num * rate).toFixed(precision));
: Number((num * rate).toFixed(precision)).toLocaleString();
return template.replace(/_d/, isString(final) ? final : '-');
};
/**
......@@ -136,4 +152,6 @@ export {
filenameVerification,
isSelect,
returnOptions,
returnRows,
returnCols,
};
......@@ -11,6 +11,7 @@ const BASEURL = '/PandaAssets/Assets/ReportManager';
export const API = {
GET_REPORT_INFO: `${BASEURL}/GetReportInfo`, // 获取报表信息
GET_REPORT_FILTER_VALUE: `${BASEURL}/GetReportFilterValue`, // 获取过滤字段的值的枚举
GET_REPORT_FILTER_VALUES: `${BASEURL}/GetReportFilterValues`, // 获取过滤字段的值的枚举
GET_REPORT_CONFIG_LIST: `${BASEURL}/GetReportConfigList`, // 获取配置列表
GET_REPORT_DETAILS_INFO: `${BASEURL}/GetReportDetailsInfo`, // 获取配置详情
GET_TABLES: `${BASEURL}/GetTables`, // 查询表
......@@ -31,6 +32,13 @@ export const API = {
EXPORT_JPG: `${BASEURL}/ExportJPGAccountData`,
EXPORT_REPORT_CONFIG: `${BASEURL}/ExportReportConfig`,
IMPORT_REPORT_CONFIG: `${BASEURL}/ImportReportConfig`,
// 多数据源
GET_DATA_SOURCES: `${BASEURL}/GetDataSources`, // 获取数据源列表
ADD_DB_CONNECTION: `${BASEURL}/AddDbConnection`, // 新增/编辑数据源
DELETE_DB_CONNECTION: `${BASEURL}/DeleteDbConnection`, // 删除数据源
GET_DB_SOURCES: `${BASEURL}/GetDBSources`, // 获取数据库
TEST_CONNECTION: `${BASEURL}/TestConnection`, // 测试链接
DEL_REPORT_CHILD: `${BASEURL}/DelReportChlid`, // 删除挂接关系
};
const reportService = {
getReportInfo: {
......@@ -43,6 +51,11 @@ const reportService = {
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
getReportFilterValues: {
url: API.GET_REPORT_FILTER_VALUES,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
getReportConfigList: {
url: API.GET_REPORT_CONFIG_LIST,
method: constants.REQUEST_METHOD_GET,
......@@ -138,6 +151,36 @@ const reportService = {
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
getDataSources: {
url: API.GET_DATA_SOURCES,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
addDbConnection: {
url: API.ADD_DB_CONNECTION,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
deleteDbConnection: {
url: API.DELETE_DB_CONNECTION,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
getDbSources: {
url: API.GET_DB_SOURCES,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
testConnection: {
url: API.TEST_CONNECTION,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
delReportChild: {
url: API.DEL_REPORT_CHILD,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
};
export const submitReportData = (params, data) =>
request({
......
This diff is collapsed.
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