Commit 437dd7ca authored by 陈龙's avatar 陈龙

feat: 新增大量功能

parent 34c8f109
/*
** 自定义取色板
** create by ChenLong on 2022/8/23
** 功能路径:src\pages\product\ReportsManage\Components\CustomerColorPicker.js
** 菜单参数列表:*变量名*(变量说明,数据类型,是否必填,取值范围)
**/
import React from 'react';
import style from './CustomerColorPicker.less';
import { tagColors, tagBackgroundColors } from '../../utils/constant';
import classnames from 'classnames';
const CustomerColorPicker = (props) => {
return (
<div className={style.colorPickerWrapper}>
{tagColors.map((color, index) => (
<div
className={classnames(style.colorCard)}
onClick={() => props.clickColorPicker(tagBackgroundColors[index])}
style={{
color: color,
backgroundColor: tagBackgroundColors[index],
}}
>
A
</div>
))}
</div>
);
};
export default CustomerColorPicker;
@import '~antd/es/style/themes/default.less';
.colorPickerWrapper {
display: flex;
gap: 5px;
width: 330px;
//height: 40px;
padding: 10px;
background: #ffffff;
border: 1px solid rgba(200, 200, 200, 0.85);
border-radius: 4px;
.colorCard {
display: flex;
flex: 0 0 30px;
align-items: center;
justify-content: center;
height: 30px;
font-size: 14px;
&:hover {
font-weight: bold;
font-size: 16px;
box-shadow: #1685ff;
cursor: pointer;
}
}
}
/** @tips: 约定:该控件,获取文件路径字符串或上传了文件(即对应编辑时从接口获取的文件链接和新增时获取的对象),都会将符合要求的对象(上传的文件数据或自行拼接的,必须包含status、type、uid等必要字段)转化为字符串 */
/**
* @description: 合并老版的文件、可预览文件、录音、视频
* @params: 接收参数 renderTo: Recording/Video/File
* @return: {Object} 文件类型返回需要另外处理
* @date: 2021/12/1
* @author: ChenLong
* @待解决问题: 为什么list不直接返回url,而要把file对象变成对象字符串?
*/
/** @tips: 上传业务为:1.通过上传接口,上传文件,获取上传的链接;2.将文件链接存储到服务器,获取存储的地址;3.提交地址 */
/**
* @description: 完成 1.文件名校验;2.编辑界面的文件加载及显示的能力;3.不同类型文件的判断
* @date: 2021/12/2
*/
/**
* @description: 合并图片/可预览图片到文件上传中
* @date: 2021/12/6
*/
/**
* @description: 文件类型单独处理。文件类型返回的是文件对象的字符串,使用JSON.parse解析之后,取url值即可,拼成用逗号分割的字符串即可。这部分修改在入口文件内
* @description: 需要判断值是文件对象的字符串,约定:文件类必须会有 status: 'done'
* @date: 2021/12/8
*/
/** @tips: 裁剪功能在前端框架内无法正常工作,暂不提供裁剪功能 */
// 自定义Form Render组件
import React, { useState, useEffect } from 'react';
import { Upload, Button, Modal, message } from 'antd';
import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
import './fileUpload.less';
import { downloadFunc, filenameVerification } from '../../utils/utils';
import { uploadFileUrl, downloadFileUrl } from '../../../../../api/service/workflow';
const videoTypeArray = ['.mp4'];
const audioTypeArray = ['.mp4'];
const fileTypeArray = [];
const imageTypeArray = ['.bmp', '.gif', '.jpeg', 'tiff', '.png', '.svg', '.jpg'];
const FileUpload = ({ value, onChange, schema }) => {
const _isRecordingOrVideo = ['Recording', 'Video', 'Image'].includes(schema.renderTo);
const _isVideo = schema.renderTo === 'Video';
const _isAudio = schema.renderTo === 'Recording';
const _isImage = schema.renderTo === 'Image';
const [previewTitle, setPreviewTitle] = useState('');
const [previewVisible, setPreviewVisible] = useState(false);
const [previewUrl, setPreviewUrl] = useState('');
const [showList, setShowList] = useState('');
const file = value || schema.default;
const option = {
name: 'file',
action: `${window.location.origin}${uploadFileUrl}`,
listType: _isRecordingOrVideo ? 'picture-card' : 'picture',
withCredentials: true,
beforeUpload(file, fileList) {
/** @tips: 解决提交文件中存在特殊字符的问题 */
let _continueUpload = true;
let _msg = {
type: 'success',
content: '上传成功!',
};
fileList.forEach((item) => {
let _msgObject = filenameVerification(item);
if (_msgObject.type === 'error') {
_continueUpload = false;
_msg = {
type: 'error',
content: '上传失败!文件名不符合规则!',
};
}
});
_msg.type === 'error' ? message[_msg.type](_msg.content) : '';
return _continueUpload;
},
onChange: ({ file, fileList, event }) => {
// 检验名字,名字不通过不允许显示
if (filenameVerification(file).type === 'error') return false;
// 返回的链接在file.response内;不设置url,预览图表不可点击
if (file.status === 'done' && file.response.code === 0) {
file.url = `${downloadFileUrl}?filePath=${file.response.data}`;
file.sourcePath = file.response.data;
message.success('上传成功!');
} else if (file.status === 'done' && file.response.code !== 0) {
file.status = 'error';
message.error('上传失败!');
}
onChange((fileList && fileList.length && JSON.stringify(fileList)) || '');
setShowList(JSON.stringify(fileList));
},
onPreview(file) {
if (_isRecordingOrVideo) {
setPreviewVisible(true);
setPreviewUrl(file.url);
}
},
previewFile(file) {},
onDownload(file) {
downloadFunc(file.url, file.name, '_self');
},
};
const handleCancel = () => {
setPreviewVisible(false);
setPreviewTitle('');
};
/**
* @description: 返回文件类型限定值
* @params: {Array} typeArray: Video | Recording | File
* @date: 2021/12/2
* @author: ChenLong
*/
const returnFileTypeString = (type) => {
let _obj = {
Video: videoTypeArray,
Recording: audioTypeArray,
File: fileTypeArray,
Image: imageTypeArray,
};
return _obj[type].join(',');
};
useEffect(() => {
debugger;
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));
}, []);
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]);
return (
<>
{/** @tips: 裁剪功能无法正常工作,暂不提供 */}
{/* <ImgCrop beforeCrop={(file) => {
let _returnObj = filenameVerification(file);
if (_returnObj.type === 'error') {
message.error(_returnObj.content);
return false;
}
return schema.renderTo === 'Image';
}} rotate locale={'zh-cn'} modalTitle={'编辑图片'} modalOk={'确定'}
modalCancel={'取消'}>*/}
<Upload
disabled={schema.disabled}
{...option}
fileList={showList ? JSON.parse(showList) : []}
multiple
style={{ width: '100%' }}
className={_isRecordingOrVideo ? 'formUploadVideoOrRecording' : 'formUpload'}
showUploadList={{
showPreviewIcon: _isRecordingOrVideo,
showDownloadIcon: true,
downloadIcon: <DownloadOutlined />,
}}
accept={returnFileTypeString(schema.renderTo)}
>
{!_isRecordingOrVideo ? (
<Button disabled={schema.disabled} icon={<UploadOutlined />}>
Upload
</Button>
) : (
'+ Upload'
)}
</Upload>
{/* </ImgCrop>*/}
<Modal
style={{ width: '30%' }}
bodyStyle={{ textAlign: 'center' }}
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={handleCancel}
>
{_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>
</>
);
};
export default FileUpload;
@import '~antd/es/style/themes/default.less';
@parse-form-prefix-cls: ~'@{ant-prefix}-parse-form';
.@{parse-form-prefix-cls} {
.formUpload {
width: 100%;
.@{ant-prefix}-upload-list-item {
float: left;
width: 49%;
max-width: 300px; // 增加了最大宽度,防止样式失效导致布局错误的问题
margin-right: 1%;
}
}
.@{ant-prefix}-animate {
}
.formUploadVideoOrRecording {
width: 100%;
.@{ant-prefix}-upload-list-item {
float: left;
width: 99%;
margin-right: 1%;
}
}
}
...@@ -8,11 +8,15 @@ import React, { useEffect, useState } from 'react'; ...@@ -8,11 +8,15 @@ import React, { useEffect, useState } from 'react';
import { Form, Input, DatePicker, InputNumber, Space, Row, Col, Button, message } from 'antd'; import { Form, Input, DatePicker, InputNumber, Space, Row, Col, Button, message } from 'antd';
import moment from 'moment'; import moment from 'moment';
import { submitReportData } from '../api/service/report'; import { submitReportData } from '../api/service/report';
import FileUpload from './Components/fileUpload/fileUpload';
import { reportService } from '../api';
// 类型 // 类型
const USER_ID = window.globalConfig.userInfo.OID;
const DATE_PICKER_ARRAY = ['日期']; const DATE_PICKER_ARRAY = ['日期'];
const DATE_TIME_PICKER_ARRAY = ['日期时刻']; const DATE_TIME_PICKER_ARRAY = ['日期时刻'];
const DATE_TYPE = ['日期', '日期时刻']; // 用来匹配是否需要转为日期对象; const DATE_TYPE = ['日期', '日期时刻']; // 用来匹配是否需要转为日期对象;
const NUMBER_ARRAY = ['数值', '金额']; const NUMBER_ARRAY = ['数值', '金额'];
const FILE_ARRAY = ['附件'];
// 形态对应组件 // 形态对应组件
// 对应关系 // 对应关系
...@@ -24,8 +28,9 @@ const NUMBER_ARRAY = ['数值', '金额']; ...@@ -24,8 +28,9 @@ const NUMBER_ARRAY = ['数值', '金额'];
* @params: reportDetails 各个字段的配置列表 * @params: reportDetails 各个字段的配置列表
* data 与reportDetails对应的值 * data 与reportDetails对应的值
*/ */
const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName }) => { const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName, modalType }) => {
if (!reportData || Object.keys(reportData).length === 0) return <>未传递表单数据</>; // if (!reportData || Object.keys(reportData).length === 0) return <>未传递表单数据</>;
const [fileAlias, setFileAlias] = useState([]); // 附件需要单独处理提交的数据
const [form] = Form.useForm(); const [form] = Form.useForm();
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
...@@ -52,77 +57,135 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName }) => ...@@ -52,77 +57,135 @@ const ReportEditForm = ({ reportDetails, reportData, onCancel, reportName }) =>
return <DatePicker />; return <DatePicker />;
} else if (NUMBER_ARRAY.includes(config.type)) { } else if (NUMBER_ARRAY.includes(config.type)) {
return <InputNumber />; return <InputNumber />;
} else if (FILE_ARRAY.includes(config.type)) {
return <FileUpload schema={{ renderTo: 'File' }} />;
} else { } else {
return <Input />; return <Input />;
} }
}; };
const submitReportForm = () => { const submitReportForm = () => {
let _data = form.getFieldsValue(); form.validateFields().then((values) => {
console.log(_data); let _data = values;
// { let final = [];
// "key": "string", Object.keys(_data).forEach((key) => {
// "fieldName": "string", let value = reportData[key];
// "fieldValue": "string" let _value = _data[key];
// } if (moment.isMoment(_data[key])) {
let final = []; _value = moment(_data[key]).format('YYYY-MM-DD HH:mm:ss');
Object.keys(_data).forEach((key) => { }
let value = reportData[key]; if (value !== _value) {
let _value = _data[key]; if (fileAlias.includes(key)) {
if (moment.isMoment(_data[key])) { _value = _value
_value = moment(_data[key]).format('YYYY-MM-DD HH:mm:ss'); ? JSON.parse(_value)
.reduce((final, curr) => {
final.push(curr.sourcePath);
return final;
}, [])
.join(',')
: '';
}
let _finalData = {
fieldAlias: key,
fieldValue: _value,
};
if (modalType === '编辑') {
_finalData.key = reportData.Key;
}
final.push(_finalData);
}
});
if (modalType === '新增') {
reportService
.addReportData({
reportName: reportName,
userId: USER_ID,
reportDatas: final,
})
.then((res) => {
if (res.code === 0) {
message.success('保存成功!');
onCancel();
} else if (res.code === -1) {
message.error(res.msg);
} else if (res.code === -2) {
message.error('系统故障,请找管理员查看故障!');
}
});
} }
if (value !== _value) { if (modalType === '编辑') {
final.push({ submitReportData(
key: reportData.Key, {},
fieldName: key, {
fieldValue: _value, editDatas: final,
reportName: reportName,
userId: USER_ID,
},
).then((res) => {
if (res.code === 0) {
message.success('保存成功!');
onCancel();
} else if (res.code === -1) {
message.error(res.msg);
} else if (res.code === -2) {
message.error('系统故障,请找管理员查看故障!');
}
}); });
} }
}); });
console.log(final);
// reportService.updateReportData()
submitReportData(
{
reportName: reportName,
userId: window.globalConfig.userInfo.OID,
},
final,
).then((res) => {
if (res.code === 0) {
message.success('保存成功!');
onCancel();
}
});
}; };
useEffect(() => {
if (reportDetails && reportDetails.length) {
let _fileAlias = [...fileAlias];
reportDetails.forEach((item) => {
if (item.type === '附件') _fileAlias.push(item.fieldAlias);
});
setFileAlias(_fileAlias);
}
}, [reportDetails]);
useEffect(() => { useEffect(() => {
if (reportData && Object.keys(reportData).length) if (reportData && Object.keys(reportData).length)
form.setFieldsValue(handleDate(reportDetails, reportData)); form.setFieldsValue(handleDate(reportDetails, reportData));
}, [reportData]); }, [reportData]);
return ( return (
<div> <div style={{ position: 'relative' }}>
<Form {...formItemLayout} form={form}> <div>
<Row> <Form {...formItemLayout} form={form}>
{reportDetails && <Row style={{ overflowY: 'scroll', maxHeight: 'calc(100vh - 300px)' }}>
reportDetails.map((config) => { {reportDetails &&
return ( reportDetails.map((config) => {
<Col span={8}> return (
<Form.Item label={config.fieldAlias} name={config.fieldAlias}> <Col span={8} key={config.fieldAlias}>
{componentMap(config)} <Form.Item
</Form.Item> label={config.fieldAlias}
</Col> name={config.fieldAlias}
); rules={[
})} {
</Row> required: config.isRequired,
<Row> message: `${config.fieldAlias}必填`,
<Col span={24} style={{ textAlign: 'right' }}> },
{/*<Form.Item style={{textAlign:'right'}}>*/} ]}
<Button type={'primary'} onClick={submitReportForm}> >
提交 {componentMap(config)}
</Button> </Form.Item>
{/*</Form.Item>*/} </Col>
</Col> );
</Row> })}
</Form> </Row>
<Row>
<Col span={24} style={{ textAlign: 'right' }}>
{/*<Form.Item style={{textAlign:'right'}}>*/}
<Button
style={{ position: 'sticky', bottom: 0 }}
type={'primary'}
onClick={submitReportForm}
>
提交
</Button>
{/*</Form.Item>*/}
</Col>
</Row>
</Form>
</div>
</div> </div>
); );
}; };
......
...@@ -12,62 +12,47 @@ ...@@ -12,62 +12,47 @@
* @params: <ReportManage * @params: <ReportManage
* params={{reportName,pageSize,filterFields,filterValues,state,customerState}}> * params={{reportName,pageSize,filterFields,filterValues,state,customerState}}>
* *
*
*
*
* reportName: 报表名称; * reportName: 报表名称;
* pageSize: 按需配置,默认加载100; * pageSize: 按需配置,默认加载100;
* filterFields:
* *
* ***************filterFields/filterValues多用于以组件的形式嵌入,将需要过滤的条件传入********************
* *
* * filterFields: 需要默认加载的过滤条件的field,英文逗号分割,与filterValues一一对应;
* 需要默认加载的过滤条件的field,英文逗号分割,与filterValues一一对应;
* filterValues: * filterValues:
*
*
*
* 需要默认加载的过滤条件的value,英文逗号分割,与filterFields一一对应; * 需要默认加载的过滤条件的value,英文逗号分割,与filterFields一一对应;
* state: delete|edit|scan 各种权限;
*
*
* *
* *************************************************************************************************
* *
* customerState: ['filters','sortBtn','exportBtn','editBtn','deleteBtn','pagination']; * state: delete|edit|scan 各种权限;
* customerState:
* ['filters','sortBtn','exportBtn','editBtn','deleteBtn','pagination'];
* sortFields:
* '排序字段1,排序字段2,排序字段3'
* *
* ---------------------------------------权限过滤-----------------------------------------
* *
* permissionType: 部门|站点|用户
* permissionField: 【字段名】
* *
* * ---------------------------------------------------------------------------------------
* sortFields: '排序字段1,排序字段2,排序字段3'
* @config: * @config:
* 【数值】 [prefix]_d%|0.00|_d%[suffix]|金额 = 前缀|精度|后缀|金额类的数据(千分位),可分别设置。 * 【数值】 [prefix]_d%|0.00|_d%[suffix]|金额 = 前缀|精度|后缀|金额类的数据(千分位),可分别设置。
* 【标签】 split=, * 【标签】 split=, 分隔符。
*
*
* 分隔符。
* *
* 【功能】 功能配置框内,配置需要跳转功能所需参数,type、url是必须功能,需要type判断类型,需要通过url去解析加载组件。 * 【功能】 功能配置框内,配置需要跳转功能所需参数,type、url是必须功能,需要type判断类型,需要通过url去解析加载组件。
* @type: * @type:
* 【文本】普通文本 * 【文本】普通文本
* 【数值】数值类型的文本 * 【数值】数值类型的文本
* 【标签】文本渲染成标签,具有不同颜色; * 【标签】文本渲染成标签,具有不同颜色;
* 【功能】“功能”会在当前页内去展示,会卸载掉列表页,加载功能组件。配置 type * 【功能】“功能”会在当前页内去展示,会卸载掉列表页,加载功能组件。配置 type +
* + * url + 自定义字段 的配置项,自行解析加载即可;
* * -------------------- 待需求提出后开发 -----------------
*
* url + 自定义字段 的配置项,自行解析加载即可;
* -------------------- 待需求提出后开发
* -----------------
*
*
* *
* 【链接】内链外链,点击可跳转;配置规则:配置链接即可; * 【链接】内链外链,点击可跳转;配置规则:配置链接即可;
* * 【弹窗】modal弹窗弹出,弹窗内的具体业务自行配置;配置规则:[function_name];[...params];
* 【弹窗】modal弹窗弹出,弹窗内的具体业务自行配置;配置规则:[function_name];[...params];
*
*
*
* *
* ------------------------------------------------------ * ------------------------------------------------------
* 【附件】
* @table: * @table:
* 表头:表头需要支持多级表头、合并; * 表头:表头需要支持多级表头、合并;
* 列:列支持设置筛选; * 列:列支持设置筛选;
...@@ -75,15 +60,9 @@ ...@@ -75,15 +60,9 @@
* @control: * @control:
* 固定筛选:拥有固定筛选框,根据配置显示可搜索字段; * 固定筛选:拥有固定筛选框,根据配置显示可搜索字段;
* *
*
*
*
* 可配置筛选框:根据字段配置,将字段设置成筛选条件,枚举出该字段所有值,提供用户进行选择,然后进行筛选;筛选框具体形态可根据配置字段来渲染; * 可配置筛选框:根据字段配置,将字段设置成筛选条件,枚举出该字段所有值,提供用户进行选择,然后进行筛选;筛选框具体形态可根据配置字段来渲染;
* 导出功能:各类导出功能按钮; * 导出功能:各类导出功能按钮;
* *
*
*
*
* 时间筛选框:单表唯一;需要变更,支持多时间参数的筛选 * 时间筛选框:单表唯一;需要变更,支持多时间参数的筛选
* @state: 参考台账权限 delete 全部权限 * @state: 参考台账权限 delete 全部权限
* edit 除删除外的权限 * edit 除删除外的权限
...@@ -103,13 +82,20 @@ import { ...@@ -103,13 +82,20 @@ import {
Dropdown, Dropdown,
Menu, Menu,
Spin, Spin,
Popconfirm,
Tooltip,
} from 'antd'; } from 'antd';
import { import {
SortAscendingOutlined, SortAscendingOutlined,
MinusCircleOutlined, MinusCircleOutlined,
ExportOutlined, ExportOutlined,
FormOutlined, FormOutlined,
PlusOutlined,
DeleteOutlined, DeleteOutlined,
QuestionCircleOutlined,
HeartTwoTone,
HeartOutlined,
DownOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import BasicTable from '@wisdom-components/basictable'; import BasicTable from '@wisdom-components/basictable';
import ReturnControlComponent from './Components/Control'; import ReturnControlComponent from './Components/Control';
...@@ -135,6 +121,7 @@ import { ...@@ -135,6 +121,7 @@ import {
import { hasMoney, isArray, isNumber, isString, returnHandledNumber } from './utils/utils'; import { hasMoney, isArray, isNumber, isString, returnHandledNumber } from './utils/utils';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ReportEditForm from './ReportEditForm'; import ReportEditForm from './ReportEditForm';
import { exportJPG } from '../api/service/report';
const ControlsType = ['下拉', '多选', '日期']; const ControlsType = ['下拉', '多选', '日期'];
const fieldSplitStr = '-'; // fieldGroup用来分割 const fieldSplitStr = '-'; // fieldGroup用来分割
...@@ -144,6 +131,7 @@ const initDateModel = 'all'; ...@@ -144,6 +131,7 @@ const initDateModel = 'all';
let timer = null; let timer = null;
const PERMISSION = { const PERMISSION = {
delete: [ delete: [
'addBtn',
'filters', 'filters',
'pagination', 'pagination',
// 操作条按钮 // 操作条按钮
...@@ -154,6 +142,7 @@ const PERMISSION = { ...@@ -154,6 +142,7 @@ const PERMISSION = {
'deleteBtn', 'deleteBtn',
], ],
edit: [ edit: [
'addBtn',
'filters', 'filters',
'pagination', 'pagination',
'sortBtn', 'sortBtn',
...@@ -161,11 +150,22 @@ const PERMISSION = { ...@@ -161,11 +150,22 @@ const PERMISSION = {
// 操作列 // 操作列
'editBtn', 'editBtn',
], ],
scan: ['filters', 'pagination'], scan: ['filters', 'pagination', 'sortBtn'],
}; };
const USER_ID = window.globalConfig.userInfo.OID;
const MODEL = ['all', 'year', 'quarter', 'month', 'week', 'day'];
const ReportsManage = (props) => { const ReportsManage = (props) => {
const { reportName, pageSize, filterFields, filterValues, state, customerState, sortFields } = const {
props.params; reportName,
pageSize,
filterFields,
filterValues,
state,
customerState,
sortFields,
permissionType,
permissionField,
} = props.params;
const permission = const permission =
customerState && isArray(customerState) ? customerState : PERMISSION[state || 'delete']; customerState && isArray(customerState) ? customerState : PERMISSION[state || 'delete'];
const tableWrapperRef = useRef(); const tableWrapperRef = useRef();
...@@ -174,9 +174,12 @@ const ReportsManage = (props) => { ...@@ -174,9 +174,12 @@ const ReportsManage = (props) => {
return ( return (
<div className={style.lackParams}>未配置reportName,请完善配置并重新登陆后查看页面!</div> <div className={style.lackParams}>未配置reportName,请完善配置并重新登陆后查看页面!</div>
); );
const [isInit, setIsInit] = useState(true);
const [firstToGetData, setFirstToGetData] = useState(false);
const [tableStruct, setTableStruct] = useState([]); // 临时使用,看后续是否需要保留 const [tableStruct, setTableStruct] = useState([]); // 临时使用,看后续是否需要保留
const [columns, setColumns] = useState([]); // 表头设置 const [columns, setColumns] = useState([]); // 表头设置
const [tableData, setTableData] = useState([]); // 表数据 const [tableData, setTableData] = useState([]); // 表数据
const [reportConfigs, setReportConfigs] = useState([]); // 表设置
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
total: 0, total: 0,
...@@ -191,14 +194,14 @@ const ReportsManage = (props) => { ...@@ -191,14 +194,14 @@ const ReportsManage = (props) => {
_pagination.current = current; _pagination.current = current;
_pagination.pageSize = size; _pagination.pageSize = size;
setPagination(_pagination); setPagination(_pagination);
getData(false, _pagination); getData(_pagination);
}, },
onChange: (current, size) => { onChange: (current, size) => {
let _pagination = { ...pagination }; let _pagination = { ...pagination };
_pagination.current = current; _pagination.current = current;
_pagination.pageSize = size; _pagination.pageSize = size;
setPagination(_pagination); setPagination(_pagination);
getData(false, _pagination); getData(_pagination);
}, },
}); });
const [controls, setControls] = useState([]); // 用来存储操作控件 const [controls, setControls] = useState([]); // 用来存储操作控件
...@@ -240,9 +243,10 @@ const ReportsManage = (props) => { ...@@ -240,9 +243,10 @@ const ReportsManage = (props) => {
// const [detailsComponent, setDetailsComponent] = useState(<></>); // 详情组件 // const [detailsComponent, setDetailsComponent] = useState(<></>); // 详情组件
const [detailsComponentVisible, setDetailsComponentVisible] = useState(false); // 是否显示详情组件 const [detailsComponentVisible, setDetailsComponentVisible] = useState(false); // 是否显示详情组件
const [editComponentVisible, setEditComponentVisible] = useState(false); // 是否显示编辑组件 const [modalType, setModalType] = useState('');
// const [editComponentVisible, setEditComponentVisible] = useState(false); // 是否显示编辑组件
const [currentData, setCurrentData] = useState({}); // 设置当前编辑数据 const [currentData, setCurrentData] = useState({}); // 设置当前编辑数据
const [sroter, setSorter] = useState({}); const [sorterObject, setSorterObject] = useState({});
const [detailConfig, setDetailConfig] = useState({ url: '', type: '', params: {} }); const [detailConfig, setDetailConfig] = useState({ url: '', type: '', params: {} });
const [controlsHeight, setControlsHeight] = useState(44); const [controlsHeight, setControlsHeight] = useState(44);
const menu = () => { const menu = () => {
...@@ -275,6 +279,20 @@ const ReportsManage = (props) => { ...@@ -275,6 +279,20 @@ const ReportsManage = (props) => {
), ),
key: 'exportExcel', key: 'exportExcel',
}, },
{
label: (
<Button
size="middle"
loading={exportLoading}
type="text"
onClick={() => exportImage()}
icon={<ExportOutlined />}
>
导出JPG
</Button>
),
key: 'excelPdf',
},
]; ];
return <Menu items={_item} />; return <Menu items={_item} />;
}; };
...@@ -315,8 +333,38 @@ const ReportsManage = (props) => { ...@@ -315,8 +333,38 @@ const ReportsManage = (props) => {
setExportLoading(false); setExportLoading(false);
}); });
}; };
const exportImage = () => {
exportJPG(
{ responseType: 'blob' },
{
reportName,
pageIndex: 1,
pageSize: 10,
sortFields: '',
content: '',
filters: [
{
fieldAlias: '',
fieldValue: '',
},
],
},
).then((res) => {
if (res && res.code === -1) return message.error(res.msg);
const url = window.URL.createObjectURL(
new Blob([res], { type: 'application/zip;charset=UTF-8' }),
);
const a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.download = `${reportName}${moment().format('YYYY-MM-DD-HH-mm-ss').replaceAll('-', '')}.zip`;
a.click();
a.remove();
setExportLoading(false);
});
};
const searchData = (e) => { const searchData = (e) => {
getData(false, pagination); getData(pagination);
}; };
const controlSelectChange = (fieldAlias, e) => { const controlSelectChange = (fieldAlias, e) => {
let _filterArray = { ...filterObject }; let _filterArray = { ...filterObject };
...@@ -326,9 +374,13 @@ const ReportsManage = (props) => { ...@@ -326,9 +374,13 @@ const ReportsManage = (props) => {
const searchInputChange = (e) => { const searchInputChange = (e) => {
setSearchContent(e.target.value); setSearchContent(e.target.value);
}; };
const setConfig = (config, summary, data) => { /* const setConfig = (config, summary) => {
getControlsBarConfig(config);
getTableSummaryConfig(config, summary);
getTableLevel(config);
};*/
const setConfig = (config) => {
getControlsBarConfig(config); getControlsBarConfig(config);
getTableSummaryConfig(config, summary);
getTableLevel(config); getTableLevel(config);
}; };
const addFilterAndSearchParams = (data) => { const addFilterAndSearchParams = (data) => {
...@@ -373,6 +425,17 @@ const ReportsManage = (props) => { ...@@ -373,6 +425,17 @@ const ReportsManage = (props) => {
}); });
}); });
} }
// 表格上的自定义排序的按钮
if (sorterObject && sorterObject.order) {
_data.sortFields = `${sorterObject.columnKey} ${
sorterObject.order === 'ascend' ? 'asc' : 'desc'
}`;
}
// 增加权限过滤的参数
if (permissionType && permissionField) {
_data.filterType = permissionType;
_data.filterField = permissionField;
}
// 并入 _data // 并入 _data
if (_filters.length) _data.filters = _filters; if (_filters.length) _data.filters = _filters;
return _data; return _data;
...@@ -426,7 +489,7 @@ const ReportsManage = (props) => { ...@@ -426,7 +489,7 @@ const ReportsManage = (props) => {
}); });
return _merge; return _merge;
}; };
const getData = (isInit, pagination) => { const getData = (pagination) => {
setTableLoading(true); setTableLoading(true);
const { pageSize, current } = pagination; const { pageSize, current } = pagination;
// 搜索条件附加到params // 搜索条件附加到params
...@@ -434,6 +497,7 @@ const ReportsManage = (props) => { ...@@ -434,6 +497,7 @@ const ReportsManage = (props) => {
reportName: reportName, reportName: reportName,
pageIndex: current, pageIndex: current,
pageSize: pageSize, pageSize: pageSize,
userId: USER_ID,
}); });
// sortFields // sortFields
reportService reportService
...@@ -444,8 +508,9 @@ const ReportsManage = (props) => { ...@@ -444,8 +508,9 @@ const ReportsManage = (props) => {
let _statisticalValues = res.data.statisticalValues; let _statisticalValues = res.data.statisticalValues;
let _tableData = res.data.data.list; let _tableData = res.data.data.list;
let _sortString = res.data.sortString; let _sortString = res.data.sortString;
if (isInit) { if (isInit || !summaryArray.length) {
setConfig(_reportDetails, _statisticalValues, _tableData); getTableSummaryConfig(_reportDetails, _statisticalValues);
setIsInit(false);
} }
getTableHeaderConfig(_reportDetails, _tableData); getTableHeaderConfig(_reportDetails, _tableData);
let _pagination = { ...pagination }; let _pagination = { ...pagination };
...@@ -471,6 +536,7 @@ const ReportsManage = (props) => { ...@@ -471,6 +536,7 @@ const ReportsManage = (props) => {
setPagination(_pagination); setPagination(_pagination);
setTableData([]); setTableData([]);
} }
if (isInit) setIsInit(false);
setTableLoading(false); setTableLoading(false);
}) })
.catch((err) => { .catch((err) => {
...@@ -478,6 +544,17 @@ const ReportsManage = (props) => { ...@@ -478,6 +544,17 @@ const ReportsManage = (props) => {
setTableLoading(false); setTableLoading(false);
}); });
}; };
const getConfigs = () => {
reportService.getReportDetails({ reportName }).then((res) => {
if (res.code === 0) {
setReportConfigs(res.data);
setConfig(res.data);
setColumns(returnColumn(res.data));
setTableStruct(res.data);
setFirstToGetData(true);
}
});
};
/** @description: 在配置项中,isFilter: true 用来渲染控制框;filterRule: 下拉/文本/多选 */ /** @description: 在配置项中,isFilter: true 用来渲染控制框;filterRule: 下拉/文本/多选 */
const getControlsBarConfig = (config) => { const getControlsBarConfig = (config) => {
let _data = config.filter((item) => item.isFilter); let _data = config.filter((item) => item.isFilter);
...@@ -488,11 +565,12 @@ const ReportsManage = (props) => { ...@@ -488,11 +565,12 @@ const ReportsManage = (props) => {
setSearchPlaceholder(_searchPlaceholder); setSearchPlaceholder(_searchPlaceholder);
let _controls = _data.filter((item) => ControlsType.includes(item.filterRule)); let _controls = _data.filter((item) => ControlsType.includes(item.filterRule));
setControls(_controls); setControls(_controls);
handleDate(_controls); handleControls(_controls); // 处理控制条,设定默认值
handleDate(_controls); // 处理日期, 设定默认值
}; };
const getTableHeaderConfig = (config, data) => { const getTableHeaderConfig = (config, data) => {
setTableStruct(config); // setTableStruct(config);
setColumns(returnColumn(config, data)); // setColumns(returnColumn(config, data));
setAllSortFields(returnSortFields(config)); setAllSortFields(returnSortFields(config));
}; };
const getTableSummaryConfig = (config, summary) => { const getTableSummaryConfig = (config, summary) => {
...@@ -601,7 +679,7 @@ const ReportsManage = (props) => { ...@@ -601,7 +679,7 @@ const ReportsManage = (props) => {
} }
let _tempObj = {}; let _tempObj = {};
let _fieldAliasArray = handleDataToGetRowSpanArray(_config, data); // 需要向下合并的字段 let _fieldAliasArray = data ? handleDataToGetRowSpanArray(_config, data) : false; // 需要向下合并的字段
_config.forEach((item) => { _config.forEach((item) => {
let _item = { let _item = {
title: item.fieldAlias, title: item.fieldAlias,
...@@ -612,7 +690,7 @@ const ReportsManage = (props) => { ...@@ -612,7 +690,7 @@ const ReportsManage = (props) => {
// console.log('Record: ', record); // record是这条记录,index是rowIndex // console.log('Record: ', record); // record是这条记录,index是rowIndex
// 1. 如果该字段是需要向下合并的,则进入判断 // 1. 如果该字段是需要向下合并的,则进入判断
let _obj = {}; let _obj = {};
if (_fieldAliasArray[item.fieldAlias]) { if (_fieldAliasArray && _fieldAliasArray[item.fieldAlias]) {
_obj.rowSpan = _fieldAliasArray[item.fieldAlias][rowIndex]; _obj.rowSpan = _fieldAliasArray[item.fieldAlias][rowIndex];
} }
return _obj; return _obj;
...@@ -639,7 +717,7 @@ const ReportsManage = (props) => { ...@@ -639,7 +717,7 @@ const ReportsManage = (props) => {
return mapHandleType(item.type)(item, value || '', record, ...rest); return mapHandleType(item.type)(item, value || '', record, ...rest);
}, },
}; };
_item.width = item.columnWidth || 200; // 列宽,不设置时默认给200; _item.width = (!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 = (item.fieldGroup || item.fieldAlias || item.fieldName).split(fieldSplitStr); let _keyArray = (item.fieldGroup || item.fieldAlias || item.fieldName).split(fieldSplitStr);
...@@ -682,26 +760,68 @@ const ReportsManage = (props) => { ...@@ -682,26 +760,68 @@ const ReportsManage = (props) => {
render: (text, record) => { render: (text, record) => {
return ( return (
<Space className={style.handleColumnWrapper}> <Space className={style.handleColumnWrapper}>
{record.IsAllow ? (
<Popconfirm
placement="topRight"
title={'确认取消关注吗'}
icon={<QuestionCircleOutlined />}
onConfirm={() => focusProject(record)}
>
<Tooltip title={'点击取消关注'}>
<HeartTwoTone twoToneColor="#eb2f96" />
</Tooltip>
</Popconfirm>
) : (
<Popconfirm
placement="topRight"
title={'确认关注项目吗'}
icon={<QuestionCircleOutlined />}
onConfirm={() => focusProject(record)}
>
<Tooltip title={'点击添加关注'}>
<HeartOutlined />
</Tooltip>
</Popconfirm>
)}
{permission.includes('editBtn') ? ( {permission.includes('editBtn') ? (
<FormOutlined <FormOutlined
className={style.editButton} className={style.editButton}
onClick={() => { onClick={() => {
setEditComponentVisible(true); // setEditComponentVisible(true);
setModalType('编辑');
setCurrentData(record); setCurrentData(record);
}} }}
/> />
) : ( ) : (
'' ''
)} )}
{/* { {permission.includes('deleteBtn') ? (
permission.includes('deleteBtn') ? <DeleteOutlined
<DeleteOutlined disabled className={style.deleteButton} onClick={() => { disabled
Modal.confirm({ className={style.deleteButton}
content: '你确定要删除改数据吗?', onClick={() => {
onOK: () => message.error('您无法删除数据!'), Modal.confirm({
}); content: '你确定要删除该数据吗?',
}} /> : '' onOk: () => {
}*/} reportService
.delReportData({
reportName: reportName,
userId: USER_ID,
key: record.Key,
})
.then((res) => {
if (res.code === 0) {
message.success('删除成功!');
getData(pagination);
}
});
},
});
}}
/>
) : (
''
)}
</Space> </Space>
); );
}, },
...@@ -773,15 +893,31 @@ const ReportsManage = (props) => { ...@@ -773,15 +893,31 @@ const ReportsManage = (props) => {
} }
}); });
}; };
/** @description: 判断是否存在【时间】类型的选择,并返回组件 */ /** @description: 判断是否存在【时间】类型的选择,并返回组件;并记录默认值 */
const handleControls = (controls) => {
// 过滤出非日期的字段,存储默认值
let _controls = controls.filter((item) => item.type !== '日期');
_controls.forEach((item) => {
let _configItems = item.configItems.split('|');
_configItems.forEach((str) => {
if (str.includes('defaultValue=')) {
controlSelectChange(item.fieldAlias, str.replace('defaultValue=', ''));
}
});
});
};
const handleDate = (obj) => { const handleDate = (obj) => {
let _typeObj = obj.find((item) => item.filterRule === '日期'); let _typeObj = obj.find((item) => item.filterRule === '日期');
setHasDatePicker(_typeObj ? _typeObj.fieldAlias : ''); setHasDatePicker(_typeObj ? _typeObj.fieldAlias : '');
const _configItems = _typeObj?.configItems.split('|') || []; const _configItems = _typeObj?.configItems.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 =
_configItems.find((item) => item.includes('defaultModel='))?.replace('defaultModel=', '') ||
'year';
_defaultDate = MODEL.includes(_defaultModel) ? _defaultDate : 'year'; // 确保值符合要求
if (_defaultDate && _defaultDate.length > 1) { if (_defaultDate && _defaultDate.length > 1) {
_defaultDate = { dateFrom: moment(_defaultDate[0]), dateTo: moment(_defaultDate[1]) }; _defaultDate = { dateFrom: moment(_defaultDate[0]), dateTo: moment(_defaultDate[1]) };
} else if (_defaultDate && _defaultDate.length === 1) { } else if (_defaultDate && _defaultDate.length === 1) {
...@@ -789,22 +925,47 @@ const ReportsManage = (props) => { ...@@ -789,22 +925,47 @@ const ReportsManage = (props) => {
} else { } else {
_defaultDate = { dateFrom: moment(), dateTo: moment() }; _defaultDate = { dateFrom: moment(), dateTo: moment() };
} }
// 给定默认值,初始化时可以加载
changeDate(
{
dateFrom: _defaultDate?.dateFrom.clone().startOf(_defaultModel).format(dateFormat),
dateTo: _defaultDate?.dateTo.clone().endOf(_defaultModel).format(dateFormat),
},
_defaultModel,
);
setDefaultDateConfig({ setDefaultDateConfig({
defaultDate: _defaultDate?.dateFrom, defaultDate: _defaultDate?.dateFrom,
defaultModel: defaultModel: _defaultModel,
_configItems.find((item) => item.includes('defaultModel='))?.replace('defaultModel=', '') ||
'year',
}); });
}; };
const focusProject = (record) => {
reportService
.setReportAllow({
userId: USER_ID,
reportName: reportName,
reportKey: record.Key,
})
.then((res) => {
if (res.code === 0) {
message.success(`${record.IsAllow ? '取消' : '关注'}成功!`);
getData(pagination);
} else {
message.error(`${record.IsAllow ? '取消' : '关注'}失败!`);
}
});
};
useEffect(() => { useEffect(() => {
getData(true, pagination); getConfigs();
}, []); }, []);
useEffect(() => {
if (firstToGetData) getData(pagination);
}, [firstToGetData]);
useEffect(() => { useEffect(() => {
if (tableHeaderLevel) setTableHeight(); if (tableHeaderLevel) setTableHeight();
}, [tableHeaderLevel]); }, [tableHeaderLevel]);
useEffect(() => { useEffect(() => {
getData(false, pagination); if (!isInit) getData(pagination);
}, [timeFrom, timeTo, filterObject]); }, [timeFrom, timeTo, sorterObject, filterObject]);
useEffect(() => { useEffect(() => {
function getRefHeight() { function getRefHeight() {
if (timer) clearTimeout(timer); if (timer) clearTimeout(timer);
...@@ -832,94 +993,112 @@ const ReportsManage = (props) => { ...@@ -832,94 +993,112 @@ const ReportsManage = (props) => {
{/* 为方便阅读,分开两部分代码 */} {/* 为方便阅读,分开两部分代码 */}
{!detailsComponentVisible ? ( {!detailsComponentVisible ? (
<div className={style.contentWrapper}> <div className={style.contentWrapper}>
{state !== 'scan' ? ( <Row className={style.controlRow} ref={controlRef}>
<Row className={style.controlRow} ref={controlRef}> <Space style={{ flex: 1 }} size={8} wrap={true}>
<Space style={{ flex: 1 }} size={8} wrap={true}> {/*时间搜索控件,确保时间搜索控件在第一个,单独匹配*/}
{/*时间搜索控件,确保时间搜索控件在第一个,单独匹配*/} {hasDatePicker &&
{hasDatePicker && defaultDateConfig.defaultDate !== null &&
defaultDateConfig.defaultDate !== null && permission.includes('filters') ? (
permission.includes('filters') ? ( <DatePickerGroup
<DatePickerGroup showModels={['all', 'month', 'quarter', 'year', 'custom']}
showModels={['all', 'month', 'quarter', 'year', 'custom']} onChange={changeDate}
onChange={changeDate} format={dateFormat}
format={dateFormat} defaultModel={defaultDateConfig.defaultModel}
defaultModel={defaultDateConfig.defaultModel} defaultDate={defaultDateConfig.defaultDate}
defaultDate={defaultDateConfig.defaultDate} />
) : (
''
)}
{controls && controls.length && permission.includes('filters')
? controls
.filter((control) => ['下拉', '多选'].includes(control.filterRule))
.map((control) => {
return (
<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)}
/>
</Form.Item>
);
})
: ''}
{permission.includes('filters') ? (
<Form.Item label="快速索引" key={'快速搜索控件'}>
<ReturnControlComponent
placeholder={`请输入${
searchPlaceholder.length ? searchPlaceholder.join(',') : '关键字'
}搜索`}
style={{ width: 240 }}
type={'文本'}
onChange={(e) => {
searchInputChange(e);
}}
onSearch={searchData}
/> />
) : ( </Form.Item>
''
)}
{controls && controls.length && permission.includes('filters')
? controls
.filter((control) => ['下拉', '多选'].includes(control.filterRule))
.map((control) => {
return (
<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)}
/>
</Form.Item>
);
})
: ''}
{permission.includes('filters') ? (
<Form.Item label="快速索引" key={'快速搜索控件'}>
<ReturnControlComponent
placeholder={`请输入${
searchPlaceholder.length ? searchPlaceholder.join(',') : '关键字'
}搜索`}
style={{ width: 240 }}
type={'文本'}
onChange={(e) => {
searchInputChange(e);
}}
onSearch={searchData}
/>
</Form.Item>
) : (
''
)}
</Space>
{permission.includes('sortBtn') ? (
<div style={{ width: 200, textAlign: 'end' }}>
<Space size={8} nowrap={true}>
{sortModalVisible && permission.includes('sortBtn') ? (
<Form.Item>
<Button
type={'primary'}
title={'自定义排序'}
icon={<SortAscendingOutlined />}
onClick={() => setModalVisible(true)}
>
排序
</Button>
</Form.Item>
) : (
''
)}
{permission.includes('exportBtn') ? (
<Form.Item>
<Dropdown.Button style={{ float: 'right' }} overlay={menu}>
导出
</Dropdown.Button>
</Form.Item>
) : (
''
)}
</Space>
</div>
) : ( ) : (
'' ''
)} )}
</Row> </Space>
) : ( {permission.includes('sortBtn') || permission.includes('exportBtn') ? (
'' <div style={{ width: 270, textAlign: 'end' }}>
)} <Space size={8} nowrap>
{permission.includes('addBtn') ? (
<Form.Item>
<Button
type={'primary'}
title={'自定义排序'}
icon={<PlusOutlined />}
onClick={() => {
setModalType('新增');
setCurrentData({});
}}
>
添加
</Button>
</Form.Item>
) : (
''
)}
{sortModalVisible && permission.includes('sortBtn') ? (
<Form.Item>
<Button
type={'primary'}
title={'自定义排序'}
icon={<SortAscendingOutlined />}
onClick={() => setModalVisible(true)}
>
排序
</Button>
</Form.Item>
) : (
''
)}
{permission.includes('exportBtn') ? (
<Form.Item>
<Dropdown style={{ float: 'right' }} overlay={menu}>
<Button>
<Space>
导出
<DownOutlined />
</Space>
</Button>
</Dropdown>
</Form.Item>
) : (
''
)}
</Space>
</div>
) : (
''
)}
</Row>
<div <div
className={style.tableContent} className={style.tableContent}
style={{ height: `calc(100% - ${controlsHeight || 0}px)` }} style={{ height: `calc(100% - ${controlsHeight || 0}px)` }}
...@@ -932,8 +1111,7 @@ const ReportsManage = (props) => { ...@@ -932,8 +1111,7 @@ const ReportsManage = (props) => {
dataSource={tableData} dataSource={tableData}
columns={columns} columns={columns}
onChange={(pagination, filters, sorter, extra) => { onChange={(pagination, filters, sorter, extra) => {
console.log(sorter); setSorterObject(sorter);
setSorter(sorter);
}} }}
pagination={permission.includes('pagination') ? pagination : false} pagination={permission.includes('pagination') ? pagination : false}
// 237是内置图片高度 // 237是内置图片高度
...@@ -1058,7 +1236,7 @@ const ReportsManage = (props) => { ...@@ -1058,7 +1236,7 @@ const ReportsManage = (props) => {
<Button <Button
type={'primary'} type={'primary'}
onClick={() => { onClick={() => {
saveReportListSortFields(() => getData(false, pagination)); saveReportListSortFields(() => getData(pagination));
setModalVisible(false); setModalVisible(false);
}} }}
> >
...@@ -1114,19 +1292,21 @@ const ReportsManage = (props) => { ...@@ -1114,19 +1292,21 @@ const ReportsManage = (props) => {
</Modal> </Modal>
{/* 编辑表单 */} {/* 编辑表单 */}
<Modal <Modal
title={'编辑报表信息'} title={`${modalType}报表信息`}
visible={editComponentVisible} visible={!!modalType}
width={'80%'} width={'80%'}
footer={null} footer={null}
// visible={true} // visible={true}
onCancel={() => setEditComponentVisible(false)} destroyOnClose
onCancel={() => setModalType('')}
> >
<ReportEditForm <ReportEditForm
modalType={modalType}
reportDetails={tableStruct} reportDetails={tableStruct}
reportData={currentData} reportData={currentData}
onCancel={() => { onCancel={() => {
setEditComponentVisible(false); setModalType('');
getData(false, pagination); getData(pagination);
}} }}
reportName={reportName} reportName={reportName}
/> />
......
...@@ -15,6 +15,8 @@ import { ...@@ -15,6 +15,8 @@ import {
MenuOutlined, MenuOutlined,
SettingOutlined, SettingOutlined,
LeftOutlined, LeftOutlined,
ExportOutlined,
ImportOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { import {
Button, Button,
...@@ -30,6 +32,8 @@ import { ...@@ -30,6 +32,8 @@ import {
Space, Space,
Switch, Switch,
Tooltip, Tooltip,
Table,
Upload,
} from 'antd'; } from 'antd';
import { SketchPicker } from 'react-color'; import { SketchPicker } from 'react-color';
import { arrayMoveImmutable } from 'array-move'; import { arrayMoveImmutable } from 'array-move';
...@@ -38,16 +42,23 @@ import { reportService } from '../api'; ...@@ -38,16 +42,23 @@ import { reportService } from '../api';
import style from './ReportsSetting.less'; import style from './ReportsSetting.less';
import classname from 'classnames'; import classname from 'classnames';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import { addReportDetailInfoIndex } from '../api/service/report'; import {
addReportDetailInfoIndex,
API,
exportReportConfig,
importReportConfig,
} from '../api/service/report';
import { isNumber, isString } from './utils/utils'; import { isNumber, isString } from './utils/utils';
import moment from 'moment'; import moment from 'moment';
import CustomerColorPicker from './Components/customerColorPicker/CustomerColorPicker';
const { Option } = Select; const { Option } = Select;
const { TextArea } = Input; const { TextArea } = Input;
const { Search } = Input; const { Search } = Input;
const typeArray = ['文本', '标签', '数值', '链接', '功能', '弹窗', '日期', '日期时间']; const typeArray = ['文本', '标签', '数值', '链接', '功能', '弹窗', '日期', '日期时间', '附件'];
const filterRule = ['文本', '下拉', '多选', '日期']; const filterRule = ['文本', '下拉', '多选', '日期'];
const publicSplit = '&split;'; const publicSplit = '&split;';
const USER_ID = window.globalConfig.userInfo.OID;
const ReportsSetting = () => { const ReportsSetting = () => {
// 报表列表 // 报表列表
const layout = { const layout = {
...@@ -120,6 +131,7 @@ const ReportsSetting = () => { ...@@ -120,6 +131,7 @@ const ReportsSetting = () => {
clickReport(record.id); clickReport(record.id);
}} }}
/> />
<ExportOutlined title={'导出配置'} onClick={() => exportConfigs(record)} />
<DeleteOutlined <DeleteOutlined
title={'删除报表'} title={'删除报表'}
style={{ color: 'red' }} style={{ color: 'red' }}
...@@ -161,8 +173,41 @@ const ReportsSetting = () => { ...@@ -161,8 +173,41 @@ const ReportsSetting = () => {
const [patchSubTableBtnLoading, setPatchSubTableBtnLoading] = useState(false); const [patchSubTableBtnLoading, setPatchSubTableBtnLoading] = useState(false);
const [isEditing, setIsEditing] = useState(true); const [isEditing, setIsEditing] = useState(true);
const [detailTableLoading, setDetailTableLoading] = useState(false); const [detailTableLoading, setDetailTableLoading] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
const SortableItem = SortableElement((props) => <tr {...props} />); const SortableItem = SortableElement((props) => <tr {...props} />);
const SortableBody = SortableContainer((props) => <tbody {...props} />); const SortableBody = SortableContainer((props) => <tbody {...props} />);
// 导入配置
const uploadProps = {
action: `${window.location.origin}${API.IMPORT_REPORT_DATA}`,
multiple: false,
showUploadList: false,
headers: {
'Content-Type': 'multipart/form-data',
},
withCredentials: true,
customRequest({ action, file, headers, withCredentials }) {
const formData = new FormData();
formData.append('file', file);
const _data = { userId: USER_ID };
importReportConfig(
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
{ ..._data },
formData,
).then((res) => {
if (res.code === 0) {
message.success('导入成功!');
getData();
} else {
message.info(res.msg);
}
});
},
};
const onSortEnd = ({ oldIndex, newIndex }) => { const onSortEnd = ({ oldIndex, newIndex }) => {
if (oldIndex !== newIndex) { if (oldIndex !== newIndex) {
const newData = arrayMoveImmutable(detailData.slice(), oldIndex, newIndex).filter( const newData = arrayMoveImmutable(detailData.slice(), oldIndex, newIndex).filter(
...@@ -217,33 +262,19 @@ const ReportsSetting = () => { ...@@ -217,33 +262,19 @@ const ReportsSetting = () => {
dataIndex: 'fieldName', dataIndex: 'fieldName',
key: 'fieldName', key: 'fieldName',
}, },
{ /* {
title: '别名', title: '别名',
dataIndex: 'fieldAlias', dataIndex: 'fieldAlias',
key: 'fieldAlias', key: 'fieldAlias',
render: (text, record, index) => { render: (text, record, index) => {
return isEditing ? ( return isEditing ? <Form.Item name={[index, 'fieldAlias']} rules={[{
<Form.Item required: true,
name={[index, 'fieldAlias']} message: '请输入字段别名',
rules={[ }]}><Input key={`fieldAlias_${index}`} value={record?.fieldAlias}
{ className={returnEqual(index, 'fieldAlias') ? style.boxShadow : ''}
required: true, onChange={(e) => modifyDetailData('fieldAlias', e.target.value, record, index)} /></Form.Item> : text;
message: '请输入字段别名',
}, },
]} }, */
>
<Input
key={`fieldAlias_${index}`}
value={record?.fieldAlias}
className={returnEqual(index, 'fieldAlias') ? style.boxShadow : ''}
onChange={(e) => modifyDetailData('fieldAlias', e.target.value, record, index)}
/>
</Form.Item>
) : (
text
);
},
},
{ {
title: '字段组', title: '字段组',
dataIndex: 'fieldGroup', dataIndex: 'fieldGroup',
...@@ -429,15 +460,58 @@ const ReportsSetting = () => { ...@@ -429,15 +460,58 @@ const ReportsSetting = () => {
}, },
}, },
{ {
title: '操作', title: '是否必填',
dataIndex: 'isRequired',
key: 'isRequired',
width: 100, width: 100,
render: (text, record, index) => {
return isEditing ? (
<Form.Item name={[index, 'isRequired']} valuePropName="checked">
<Switch
onChange={(e) => modifyDetailData('isRequired', e, record, index)}
checkedChildren="是"
unCheckedChildren="否"
defaultChecked={false}
className={returnEqual(index, 'isRequired') ? style.boxShadowOfSwitch : ''}
/>
</Form.Item>
) : text ? (
'是'
) : (
'否'
);
},
},
{
title: '操作',
width: 80,
render: (text, record) => { render: (text, record) => {
return ( return (
<DeleteOutlined onClick={() => deleteReportDetail(record.id)} style={{ color: 'red' }} /> <div style={{ display: 'flex', justifyContent: 'space-around' }}>
<FormOutlined
onClick={() => {
setActiveID(record.id);
if (record.color) setBackgroundColor(record.color);
form.setFieldsValue(setDefaultValue(record));
setCurrentField(setDefaultValue(record));
setDetailVisible(true);
}}
/>
<DeleteOutlined
onClick={() => deleteReportDetail(record.id)}
style={{ color: 'red' }}
/>
</div>
); );
}, },
}, },
]; ];
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRowKeys(selectedRowKeys);
setSelectedRows(selectedRows);
},
};
const wordInputWidth = 300; const wordInputWidth = 300;
const numberInputWidth = 120; const numberInputWidth = 120;
const rangeWidth = 500; const rangeWidth = 500;
...@@ -586,7 +660,9 @@ const ReportsSetting = () => { ...@@ -586,7 +660,9 @@ const ReportsSetting = () => {
if (callback) callback(); if (callback) callback();
}); });
}; };
// 切换字段的同时,如果数据有修改,那需要保存之前的数据 edit by ChenLong 2022年8月29日
const changeField = (record) => { const changeField = (record) => {
// submitReportDetails('changeField').then(res => {
setActiveID(record.id); setActiveID(record.id);
form.setFieldsValue(setDefaultValue(record)); form.setFieldsValue(setDefaultValue(record));
// 手动设置文字的颜色 // 手动设置文字的颜色
...@@ -595,6 +671,7 @@ const ReportsSetting = () => { ...@@ -595,6 +671,7 @@ const ReportsSetting = () => {
setNumberColorPickerArray(record.numericalConfigs); setNumberColorPickerArray(record.numericalConfigs);
setLabelColorPickerArray(record.labelConfigs); setLabelColorPickerArray(record.labelConfigs);
setCurrentField(setDefaultValue(record)); // 为了确保前后数据统一 setCurrentField(setDefaultValue(record)); // 为了确保前后数据统一
// });
}; };
const setDefaultValue = (record) => { const setDefaultValue = (record) => {
if (!record) return {}; if (!record) return {};
...@@ -618,9 +695,10 @@ const ReportsSetting = () => { ...@@ -618,9 +695,10 @@ const ReportsSetting = () => {
if (!_record.alignType) _record.alignType = 'left'; if (!_record.alignType) _record.alignType = 'left';
return _record; return _record;
}; };
const submitReportDetails = () => { // @params: {type:String} 不传入type,默认提交表单、关闭表单;传入type时
const submitReportDetails = (type) => {
// 1.表单内的值;2.标签值 // 1.表单内的值;2.标签值
form.validateFields().then((value) => { return form.validateFields().then((value) => {
// 需要将表单内带索引的fieldGroup_[number]处理成后端需要的fieldGroups: a-b-c-d; // 需要将表单内带索引的fieldGroup_[number]处理成后端需要的fieldGroups: a-b-c-d;
value.fieldGroup = Object.values(value.fieldGroup).join('-'); value.fieldGroup = Object.values(value.fieldGroup).join('-');
let _value = Object.assign(currentField, value); let _value = Object.assign(currentField, value);
...@@ -670,7 +748,9 @@ const ReportsSetting = () => { ...@@ -670,7 +748,9 @@ const ReportsSetting = () => {
message.error(res.msg); message.error(res.msg);
} }
setSubmitFieldLoading(false); setSubmitFieldLoading(false);
setDetailVisible(false); if (!type) {
setDetailVisible(false);
}
clickReport(currentReport.id); clickReport(currentReport.id);
}); });
}); });
...@@ -763,6 +843,7 @@ const ReportsSetting = () => { ...@@ -763,6 +843,7 @@ const ReportsSetting = () => {
// setIsEditing(true); // setIsEditing(true);
}; };
const submitDetailFromTable = () => { const submitDetailFromTable = () => {
setDetailTableLoading(true);
editDetailForm.validateFields().then((res) => { editDetailForm.validateFields().then((res) => {
// 去除掉标签、数字区间内的id // 去除掉标签、数字区间内的id
let _detailData = detailData.map((item) => { let _detailData = detailData.map((item) => {
...@@ -779,17 +860,22 @@ const ReportsSetting = () => { ...@@ -779,17 +860,22 @@ const ReportsSetting = () => {
}); });
return _item; return _item;
}); });
reportService.addReportDetailInfo({ reportDetails: _detailData }).then((res) => { reportService
if (res.code === 0) { .addReportDetailInfo({ reportDetails: _detailData })
message.success('提交成功!'); .then((res) => {
// setIsEditing(false); if (res.code === 0) {
} else { message.success('提交成功!');
message.error(res.msg); } else {
} message.error(res.msg);
setSubmitFieldLoading(false); }
setDetailVisible(false); setDetailTableLoading(false);
clickReport(currentReport.id); setSubmitFieldLoading(false);
}); setDetailVisible(false);
clickReport(currentReport.id);
})
.catch((err) => {
setDetailTableLoading(false);
});
}); });
}; };
const patchSubTable2MainTable = () => { const patchSubTable2MainTable = () => {
...@@ -882,7 +968,11 @@ const ReportsSetting = () => { ...@@ -882,7 +968,11 @@ const ReportsSetting = () => {
} }
let { data, callback, index, key } = currentColorPicker; let { data, callback, index, key } = currentColorPicker;
let _data = [...data]; let _data = [...data];
_data[index][key] = `rgba(${_rgb.r},${_rgb.g},${_rgb.b},${_rgb.a})`; if (currentColorPicker.key === 'tagColor') {
_data[index].color = e;
} else {
_data[index][key] = `rgba(${_rgb.r},${_rgb.g},${_rgb.b},${_rgb.a})`;
}
callback(_data); callback(_data);
}; };
const returnCurrentColor = () => { const returnCurrentColor = () => {
...@@ -890,67 +980,6 @@ const ReportsSetting = () => { ...@@ -890,67 +980,6 @@ const ReportsSetting = () => {
if (key === 'init') return 'rgba(0,0,0,.65)'; if (key === 'init') return 'rgba(0,0,0,.65)';
return data[index][key]; return data[index][key];
}; };
// 颜色卡片组件
//[
// "rgb(255, 255, 255)",
// "rgb(214, 229, 255)",
// "rgb(214, 241, 255)",
// "rgb(211, 243, 226)",
// "rgb(255, 220, 219)",
// "rgb(255, 236, 219)",
// "rgb(255, 245, 204)",
// "rgb(251, 219, 255)",
// "rgb(255, 219, 234)",
// "rgb(220, 223, 228)",
// "rgb(173, 203, 255)",
// "rgb(173, 228, 255)",
// "rgb(172, 226, 197)",
// "rgb(255, 181, 179)",
// "rgb(255, 206, 163)",
// "rgb(255, 234, 153)",
// "rgb(231, 180, 255)",
// "rgb(255, 179, 220)",
// "rgb(129, 134, 143)",
// "rgb(41, 114, 244)",
// "rgb(0, 163, 245)",
// "rgb(69, 176, 118)",
// "rgb(222, 60, 54)",
// "rgb(248, 136, 37)",
// "rgb(231, 180, 255)",
// "rgb(154, 56, 215)",
// "rgb(221, 64, 151)"
// ]
const ColorBoard = ({ value, onChange }) => {
return (
<>
<div
style={{
padding: 4,
display: 'inline-flex',
borderRadius: '2px',
border: '1px solid rgba(0,0,0,.35)',
}}
>
<div
style={{
width: '36px',
height: '14px',
borderRadius: '2px',
border: '1px solid rgba(0,0,0,.35)',
backgroundColor: value || 'rgba(0,0,0,.65)',
}}
onClick={(e) => {
clickColorCard(e);
setCurrentColorPicker({
key: 'wordColor',
callback: onChange,
});
}}
/>
</div>
</>
);
};
// 拖拽 // 拖拽
const DragHandle = SortableHandle(() => ( const DragHandle = SortableHandle(() => (
<MenuOutlined <MenuOutlined
...@@ -993,6 +1022,42 @@ const ReportsSetting = () => { ...@@ -993,6 +1022,42 @@ const ReportsSetting = () => {
_data.splice(index, 1, _record); _data.splice(index, 1, _record);
setDetailData(_data); setDetailData(_data);
}; };
const batchModify = (key, value) => {
console.log(key, ':', value);
// 1.取出勾选的字段;2.批量修改值
if (selectedRowKeys.length === 0) return message.info('未勾选任何字段');
let _detailData = detailData.map((item) => {
let _item = { ...item };
if (selectedRowKeys.includes(_item.id)) {
_item[key] = value;
}
return _item;
});
setDetailData(_detailData);
editDetailForm.setFieldsValue(_detailData);
};
const exportConfigs = (record) => {
setTableLoading(true);
exportReportConfig(
{ responseType: 'blob' },
{
reportId: record.id,
},
).then((res) => {
setTableLoading(false);
if (res && res.code === -1) return message.error(res.msg);
const url = window.URL.createObjectURL(
new Blob([res], { type: 'application/json;charset=UTF-8' }),
);
const a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.download = `${record.reportName}.json`;
a.click();
a.remove();
});
};
useEffect(() => { useEffect(() => {
getData(); getData();
}, []); }, []);
...@@ -1026,42 +1091,32 @@ const ReportsSetting = () => { ...@@ -1026,42 +1091,32 @@ const ReportsSetting = () => {
top: colorCardPosition.top, top: colorCardPosition.top,
}} }}
> >
<SketchPicker {currentColorPicker.key === 'tagColor' ? (
width={240} <CustomerColorPicker
presetColors={[ clickColorPicker={(backgroundColor) => {
'rgb(255, 255, 255)', changeBackgroundColor(backgroundColor);
'rgb(214, 229, 255)', }}
'rgb(214, 241, 255)', />
'rgb(211, 243, 226)', ) : (
'rgb(255, 220, 219)', <SketchPicker
'rgb(255, 236, 219)', width={240}
'rgb(255, 245, 204)', presetColors={[
'rgb(251, 219, 255)', 'rgb(129, 134, 143)',
'rgb(255, 219, 234)', 'rgb(41, 114, 244)',
'rgb(220, 223, 228)', 'rgb(0, 163, 245)',
'rgb(173, 203, 255)', 'rgb(69, 176, 118)',
'rgb(173, 228, 255)', 'rgb(222, 60, 54)',
'rgb(172, 226, 197)', 'rgb(248, 136, 37)',
'rgb(255, 181, 179)', 'rgb(216,180,255)',
'rgb(255, 206, 163)', 'rgb(154, 56, 215)',
'rgb(255, 234, 153)', 'rgb(221, 64, 151)',
'rgb(231, 180, 255)', ]}
'rgb(255, 179, 220)', color={
'rgb(129, 134, 143)', currentColorPicker.key === 'wordColor' ? backgroundColor : returnCurrentColor()
'rgb(41, 114, 244)', }
'rgb(0, 163, 245)', onChange={(e) => changeBackgroundColor(e)}
'rgb(69, 176, 118)', />
'rgb(222, 60, 54)', )}
'rgb(248, 136, 37)',
'rgb(216,180,255)',
'rgb(154, 56, 215)',
'rgb(221, 64, 151)',
]}
color={
currentColorPicker.key === 'wordColor' ? backgroundColor : returnCurrentColor()
}
onChange={(e) => changeBackgroundColor(e)}
/>
</div> </div>
</div> </div>
<Modal <Modal
...@@ -1199,7 +1254,7 @@ const ReportsSetting = () => { ...@@ -1199,7 +1254,7 @@ const ReportsSetting = () => {
)} )}
key={item.id} key={item.id}
> >
{item.fieldName} {item.fieldAlias}
</li> </li>
))} ))}
</ul> </ul>
...@@ -1228,13 +1283,18 @@ const ReportsSetting = () => { ...@@ -1228,13 +1283,18 @@ const ReportsSetting = () => {
> >
<Input disabled style={{ width: wordInputWidth }} /> <Input disabled style={{ width: wordInputWidth }} />
</Form.Item> </Form.Item>
{/* <Form.Item label={'别名'} name={'fieldAlias'} rules={[ <Form.Item
{ label={'别名'}
required: true, message: '别名必填且不可重复', name={'fieldAlias'}
}, rules={[
]}> {
<Input style={{ width: wordInputWidth }} /> required: true,
</Form.Item>*/} message: '别名必填且不可重复',
},
]}
>
<Input style={{ width: wordInputWidth }} />
</Form.Item>
<Form.Item <Form.Item
label={'表头级数'} label={'表头级数'}
name={'level'} name={'level'}
...@@ -1314,13 +1374,6 @@ const ReportsSetting = () => { ...@@ -1314,13 +1374,6 @@ const ReportsSetting = () => {
/> />
</div> </div>
</Form.Item> </Form.Item>
{/* <Form.Item label={'对齐方式'} name={'alignType'}>
<Radio.Group>
<Radio value={'left'}>左</Radio>
<Radio value={'center'}>中</Radio>
<Radio value={'right'}>右</Radio>
</Radio.Group>
</Form.Item>*/}
{watchType === '数值' ? ( {watchType === '数值' ? (
<Form.Item label={'区间设置'}> <Form.Item label={'区间设置'}>
{numberColorPickerArray.map((item, index) => { {numberColorPickerArray.map((item, index) => {
...@@ -1535,7 +1588,7 @@ const ReportsSetting = () => { ...@@ -1535,7 +1588,7 @@ const ReportsSetting = () => {
data: labelColorPickerArray, data: labelColorPickerArray,
callback: setLabelColorPickerArray, callback: setLabelColorPickerArray,
index, index,
key: 'color', key: 'tagColor',
}); });
}} }}
/> />
...@@ -1623,7 +1676,9 @@ const ReportsSetting = () => { ...@@ -1623,7 +1676,9 @@ const ReportsSetting = () => {
/> />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button onClick={showPatchSubModal}>附加子表</Button> <Button style={{ marginRight: 8 }} onClick={showPatchSubModal}>
附加子表
</Button>
<Button onClick={addVirtualColumn}>添加虚拟字段</Button> <Button onClick={addVirtualColumn}>添加虚拟字段</Button>
</Form.Item> </Form.Item>
</div> </div>
...@@ -1641,6 +1696,7 @@ const ReportsSetting = () => { ...@@ -1641,6 +1696,7 @@ const ReportsSetting = () => {
</Button> </Button>
<Button <Button
type={'primary'} type={'primary'}
loading={detailTableLoading}
style={{ marginRight: 8 }} style={{ marginRight: 8 }}
onClick={submitDetailFromTable} onClick={submitDetailFromTable}
> >
...@@ -1661,10 +1717,6 @@ const ReportsSetting = () => { ...@@ -1661,10 +1717,6 @@ const ReportsSetting = () => {
)} )}
</Form.Item> </Form.Item>
</Form> </Form>
{/* <Button type={'primary'} onClick={() => {
setDetailTableVisible(false);
// setIsEditing(false);
}}>返回</Button>*/}
</Row> </Row>
<div className={style.tableContent}> <div className={style.tableContent}>
<Form className={style.tableForm} form={editDetailForm}> <Form className={style.tableForm} form={editDetailForm}>
...@@ -1672,10 +1724,102 @@ const ReportsSetting = () => { ...@@ -1672,10 +1724,102 @@ const ReportsSetting = () => {
loading={detailTableLoading} loading={detailTableLoading}
pagination={false} pagination={false}
bordered bordered
scroll={{ y: 'calc(100% - 40px)' }} scroll={{ y: `calc(100% - ${selectedRowKeys.length ? 90 : 40}px)` }} // 需要考虑总结栏的高度
rowKey={'id'} rowKey={'id'}
columns={detailColumns} columns={detailColumns}
rowSelection={
isEditing
? {
type: 'checkbox',
...rowSelection,
}
: null
}
dataSource={detailData.filter((item) => item.visible)} dataSource={detailData.filter((item) => item.visible)}
summary={(pageData) => {
if (detailData && detailData.length > 1 && isEditing && selectedRows.length) {
return (
<Table.Summary fixed>
<Table.Summary.Row>
<Table.Summary.Cell colSpan={7}>批量操作</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<InputNumber onChange={(e) => batchModify('columnWidth', e)} />
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Select onChange={(e) => batchModify('alignType', e)}>
<Option value={'left'}></Option>
<Option value={'center'}></Option>
<Option value={'right'}></Option>
</Select>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Select onChange={(e) => batchModify('fixedColumn', e)}>
<Option value={''}>不固定</Option>
<Option value={'left'}></Option>
<Option value={'right'}></Option>
</Select>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Switch
checkedChildren="显示"
unCheckedChildren="不显示"
defaultChecked={false}
onChange={(e) => batchModify('isShow', e)}
/>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
defaultChecked={false}
onChange={(e) => batchModify('isFilter', e)}
/>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Select onChange={(e) => batchModify('isFilter', e)}>
<Option value={''}>不过滤</Option>
<Option value={'文本'}>文本</Option>
<Option value={'下拉'}>下拉</Option>
<Option value={'多选'}>多选</Option>
<Option value={'日期'}>日期</Option>
</Select>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
defaultChecked={false}
onChange={(e) => batchModify('isMerge', e)}
/>
</Form.Item>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Form.Item>
<DeleteOutlined
onClick={() => batchModify('delete')}
title={'批量删除'}
style={{ color: 'red' }}
/>
</Form.Item>
</Table.Summary.Cell>
</Table.Summary.Row>
</Table.Summary>
);
}
}}
components={ components={
isEditing isEditing
? '' ? ''
...@@ -1689,6 +1833,7 @@ const ReportsSetting = () => { ...@@ -1689,6 +1833,7 @@ const ReportsSetting = () => {
onRow={(record) => { onRow={(record) => {
return { return {
onDoubleClick: (e) => { onDoubleClick: (e) => {
return false;
if ( if (
e.target.cellIndex === detailColumns.length - 1 || e.target.cellIndex === detailColumns.length - 1 ||
e.target.cellIndex === void 0 e.target.cellIndex === void 0
...@@ -1794,6 +1939,7 @@ const ReportsSetting = () => { ...@@ -1794,6 +1939,7 @@ const ReportsSetting = () => {
<Form.Item> <Form.Item>
<Button <Button
type={'primary'} type={'primary'}
style={{ marginRight: 8 }}
onClick={() => onClick={() =>
openCreateModal(() => { openCreateModal(() => {
setCurrentReport({}); setCurrentReport({});
...@@ -1809,6 +1955,11 @@ const ReportsSetting = () => { ...@@ -1809,6 +1955,11 @@ const ReportsSetting = () => {
> >
新增 新增
</Button> </Button>
<Upload {...uploadProps}>
<Button size="middle" icon={<ImportOutlined />}>
导入
</Button>
</Upload>
</Form.Item> </Form.Item>
</Form> </Form>
</Row> </Row>
......
const tagColors = [
'rgb(129, 134, 143)',
'rgb(41, 114, 244)',
'rgb(0, 163, 245)',
'rgb(69, 176, 118)',
'rgb(222, 60, 54)',
'rgb(248, 136, 37)',
'rgb(189, 160, 20)',
'rgb(154, 56, 215)',
'rgb(221, 64, 151)',
];
const tagBackgroundColors = [
'rgb(220, 223, 228)',
'rgb(214, 229, 255)',
'rgb(214, 241, 255)',
'rgb(211, 243, 226)',
'rgb(255, 220, 219)',
'rgb(255, 236, 219)',
'rgb(255, 245, 204)',
'rgb(251, 219, 255)',
'rgb(255, 219, 234)',
];
export { tagColors, tagBackgroundColors };
...@@ -8,7 +8,8 @@ import { hasMoney, isNumber } from './utils'; ...@@ -8,7 +8,8 @@ import { hasMoney, isNumber } from './utils';
* @params: config下的数值的configRule结构如下,[{最大值: 10,最小值: 0,颜色:'#AAAAAA'}]; * @params: config下的数值的configRule结构如下,[{最大值: 10,最小值: 0,颜色:'#AAAAAA'}];
* @business: configRule有值,则按照configRule设置;没有,按照color设置; 有最大值,无最小值;['', 1] * @business: configRule有值,则按照configRule设置;没有,按照color设置; 有最大值,无最小值;['', 1]
* 有最大值,有最小值;[2, * 有最大值,有最小值;[2,
* 10] *
* 10]
* 有最小值,无最大值;[11,''] * 有最小值,无最大值;[11,'']
*/ */
// 链接 功能 弹窗功能待提出需求 // 链接 功能 弹窗功能待提出需求
...@@ -76,7 +77,8 @@ const returnOpacity = (rgba) => { ...@@ -76,7 +77,8 @@ const returnOpacity = (rgba) => {
}; };
export const handleNumber = (config, number) => { export const handleNumber = (config, number) => {
let _color = ''; let _color = '';
if (number) number = Number(number); // 当设置精度后,会被转成字符串 let _number;
if (number) _number = Number(number); // 当设置精度后,会被转成字符串
if (config.numericalConfigs && config.numericalConfigs.length) { if (config.numericalConfigs && config.numericalConfigs.length) {
config.numericalConfigs.forEach((item) => { config.numericalConfigs.forEach((item) => {
// 接口对于数值类型的返回为null // 接口对于数值类型的返回为null
...@@ -84,11 +86,11 @@ export const handleNumber = (config, number) => { ...@@ -84,11 +86,11 @@ export const handleNumber = (config, number) => {
let _max = item.maxValue || ''; let _max = item.maxValue || '';
let _min = item.minValue || ''; let _min = item.minValue || '';
if (_max !== '' && _min === '') { if (_max !== '' && _min === '') {
_color = number <= Number(_max) ? '' : item.color; _color = _number <= Number(_max) ? '' : item.color;
} else if (_min !== '' && _max === '') { } else if (_min !== '' && _max === '') {
_color = number > Number(_min) ? item.color : ''; _color = _number > Number(_min) ? item.color : '';
} else { } else {
_color = number > Number(_min) && number <= Number(_max) ? item.color : ''; _color = _number > Number(_min) && _number <= Number(_max) ? item.color : '';
} }
} }
}); });
...@@ -144,15 +146,23 @@ export const handleTag = (config, text) => { ...@@ -144,15 +146,23 @@ export const handleTag = (config, text) => {
} }
_color = config.color || 'rgba(0,0,0,.85)'; _color = config.color || 'rgba(0,0,0,.85)';
// String(text)为了解决可能存在数值类型的数据需要设置成标签的需求 // String(text)为了解决可能存在数值类型的数据需要设置成标签的需求
// return String(text).split(_configMap['分隔符']).map(item => <Tag color={_map[item]}>{item}</Tag>);
return String(text) return String(text)
.split(_configMap['分隔符']) .split(_configMap['分隔符'])
.map((item) => <Tag color={_map[item]}>{item}</Tag>); .map((item) => (
/* return String(text).split(_configMap['分隔符']).map(item => <Tag style={{ <Tag
background: _map[item] || _color, style={{
border: `1px solid ${_map[item]}`, background: _map[item] || _color,
borderRadius: 4, border: `1px solid ${_map[item] || _color}`,
color: `rgba(0, 0, 0, .65)`, borderRadius: 2,
}}>{item}</Tag>);*/ color: _map[item]
? tagColors[tagBackgroundColors.findIndex((c) => c === _map[item])]
: `rgba(0, 0, 0, .65)`,
}}
>
{item}
</Tag>
));
}; };
export const handleText = (config, text) => { export const handleText = (config, text) => {
return <span style={{ color: config.color || 'rgba(0,0,0,.85)' }}>{text}</span>; return <span style={{ color: config.color || 'rgba(0,0,0,.85)' }}>{text}</span>;
......
...@@ -24,7 +24,9 @@ const hasMoney = (configItems) => { ...@@ -24,7 +24,9 @@ const hasMoney = (configItems) => {
const returnHandledNumber = (configItems, num) => { const returnHandledNumber = (configItems, num) => {
// 精度、前缀、后缀、倍率 // 精度、前缀、后缀、倍率
// $_d|_d%|_d*0.0001|金额|0.00 // $_d|_d%|_d*0.0001|金额|0.00
if (isNaN(num)) return '-';
if (!configItems) return num; if (!configItems) return num;
num = Number(num);
let _items = configItems.split('|'); let _items = configItems.split('|');
/* let prefix = ''; /* let prefix = '';
let suffix = '';*/ let suffix = '';*/
...@@ -58,8 +60,10 @@ const returnHandledNumber = (configItems, num) => { ...@@ -58,8 +60,10 @@ const returnHandledNumber = (configItems, num) => {
* @author: ChenLong * @author: ChenLong
* @params: * @params:
* configItems 报表字段的配置 例如 defaultValue=智慧水务 * configItems 报表字段的配置 例如 defaultValue=智慧水务
*
* defaultDateModel=customer|defaultDateValue=2022-01-01,2022-12-31; * defaultDateModel=customer|defaultDateValue=2022-01-01,2022-12-31;
* keysArray * keysArray
*
* 所需要返回的值的key的集合,比如你需要获取configItems中的’defaultValue‘,那么keysArray=['defaultValue']; * 所需要返回的值的key的集合,比如你需要获取configItems中的’defaultValue‘,那么keysArray=['defaultValue'];
* @returns: * @returns:
* defaultValue 通用参数 默认值 * defaultValue 通用参数 默认值
...@@ -74,6 +78,42 @@ const returnDefaultValueOrConfigs = (configItems = '', keysArray = []) => { ...@@ -74,6 +78,42 @@ const returnDefaultValueOrConfigs = (configItems = '', keysArray = []) => {
}); });
return _map; return _map;
}; };
function downloadFunc(url, name, target = '_self') {
const a = document.createElement('a');
a.href = url;
a.target = target;
a.download = name;
a.click();
a.remove();
}
/**
* @description: 校验文件的名称是否包含特殊字符
* @params: {Object: File} file file对象 { special:Boolean } 是否去除/的匹配
* @date: 2021/12/8
* @author: ChenLong
* @returns {Object} {type: error | success ,content: 提示...}
*/
function filenameVerification(file, special) {
debugger;
// 文件名含有特殊字符 提示不能上传 {+,:/?#[]@!$&\\*+;=}
// 规则对象(flag)
var flag = !special
? new RegExp("[`~!@#$^&*=|{}':;',\\[\\]/?~!@#¥&*——|{}【】‘;:”“'。,、?]")
: new RegExp("[`~!@#$^&*=|{}':;',[\\]?~!@#¥&*——|{}【】‘;:”“'。,、?]");
if (flag.test(file.name)) {
return {
type: 'error',
content: `文件名格式错误,请检查文件名是否含有特殊字符${"~!@#$^&*=|{}':;',\\[\\]/?~!@#¥&*——|{}【】‘;:”“'。,、?"}`,
};
}
return {
type: 'success',
content: `上传成功!`,
};
}
export { export {
isObject, isObject,
isString, isString,
...@@ -82,4 +122,6 @@ export { ...@@ -82,4 +122,6 @@ export {
isArray, isArray,
returnHandledNumber, returnHandledNumber,
returnDefaultValueOrConfigs, returnDefaultValueOrConfigs,
downloadFunc,
filenameVerification,
}; };
...@@ -24,6 +24,13 @@ export const API = { ...@@ -24,6 +24,13 @@ export const API = {
SAVE_REPORT_LIST_SORT_FIELDS: `${BASEURL}/SaveReportListSortFields`, // 保存排序 SAVE_REPORT_LIST_SORT_FIELDS: `${BASEURL}/SaveReportListSortFields`, // 保存排序
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`, // 获取报表配置
DEL_REPORT_DATA: `${BASEURL}/DelReportData`, // 删除报表数据
SET_REPORT_ALLOW: `${BASEURL}/SetReportAllow`, // 设置关注
ADD_REPORT_DATA: `${BASEURL}/AddReportData`, // 添加报表数据
EXPORT_JPG: `${BASEURL}/ExportJPGAccountData`,
EXPORT_REPORT_CONFIG: `${BASEURL}/ExportReportConfig`,
IMPORT_REPORT_CONFIG: `${BASEURL}/ImportReportConfig`,
}; };
const reportService = { const reportService = {
getReportInfo: { getReportInfo: {
...@@ -96,6 +103,41 @@ const reportService = { ...@@ -96,6 +103,41 @@ const reportService = {
method: constants.REQUEST_METHOD_POST, method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST, type: constants.REQUEST_METHOD_POST,
}, },
getReportDetails: {
url: API.GET_REPORT_DETAILS,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
delReportData: {
url: API.DEL_REPORT_DATA,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
setReportAllow: {
url: API.SET_REPORT_ALLOW,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
addReportData: {
url: API.ADD_REPORT_DATA,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
exportJPG: {
url: API.EXPORT_JPG,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
exportConfig: {
url: API.EXPORT_REPORT_CONFIG,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_METHOD_GET,
},
importConfig: {
url: API.IMPORT_REPORT_CONFIG,
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_METHOD_POST,
},
}; };
export const submitReportData = (params, data) => export const submitReportData = (params, data) =>
request({ request({
...@@ -112,10 +154,33 @@ export const exportAccountData = (options, params, data) => ...@@ -112,10 +154,33 @@ export const exportAccountData = (options, params, data) =>
params, params,
data, data,
}); });
export const exportJPG = (options, data) => {
return request({
url: API.EXPORT_JPG,
method: 'post',
...options,
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) =>
request({
url: API.IMPORT_REPORT_CONFIG,
method: 'post',
...options,
params,
data,
});
export const exportReportConfig = (options, params) =>
request({
url: API.EXPORT_REPORT_CONFIG,
method: 'get',
...options,
params,
});
export default reportService; export default reportService;
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