/** * 钟是志 * 2021年5月25日 10:56:50 * 学工 学生管理 自定义统计 功能 通用版 * * */ import React, { Fragment } from 'react'; import { Select, Button, Divider, Table, Row, Col, Tag, Alert, Form, message } from 'antd'; import { connect } from 'dva'; import { getToken } from '@/utils/authority'; import config from '@/config/config'; import ButtonDiy from '@/baseComponent/ButtonDiy'; import QueryItem from './QueryItem'; import styles from './Ability.css'; import FormdataWrapper from '@/utils/object-to-formdata-custom'; import moment from 'moment'; import { controlNotification } from '@/baseComponent/utils'; import { getSassApiHeader, getSysCode } from '@/webPublic/one_stop_public/2023yunshangguizhou/utils'; const Option = Select.Option; const nameSpan = { small: 9, big: 7, }; const nameSpan2 = { small: 0, big: 0, }; const nameSpan3 = { small: 3, big: 2, }; var keyX = 1; function getLableNameByValue(options = [], searchKey = '') { const findItem = options.find((x) => { return x.field === searchKey; }); return findItem.name; } const FormItem = Form.Item; @connect(({ cms, ExportFile, loading }) => ({ cms, ExportFile, loading: loading.models.cms || loading.models.ExportFile, })) @Form.create() export default class StatisticsInfo extends React.Component { constructor(props) { super(props); this.state = { hisData: { list: [], pagination: {}, }, formValues: {}, exportLoading: false, visiable: false, queryVisiable: false, sortVisiable: false, currentQueryKey: null, currentGroupKey: null, orderVisiable: false, currentOrderKey: null, querys: [], orders: [], groups: [], gs: [], os: [], qs: [], x: null, y: null, z: null, currentKey: null, XxX: null, mockData: [], mockXZData: [], targetKeys: [], sourceSelectedKeys: [], targetSelectedKeys: [], infos: {}, columns: [], dataSource: [], flag: false, showHistory: false, showSearchItem: true, }; } componentDidMount() { if (this.props.isShow) { this.open(); this.getPage({ pageNo: 1, pageSize: 10, }); } } getPage = (params, values) => { const pagination = this.state.hisData.pagination; if (!values) { values = this.state.formValues; } if (params == null) { params = { pageNo: pagination.current ? pagination.current : 1, pageSize: pagination.pageSize ? pagination.pageSize : 10, ...values, }; } this.props.dispatch({ type: 'ExportFile/fetch', payload: { ...params, isExport: false, keyword: this.props.keyword ? this.props.keyword : this.props.modelClass, }, callback: (payload) => { this.setState({ hisData: { list: payload.rows, pagination: { current: payload.pageNo, pageSize: payload.pageSize, total: payload.total, }, }, }); }, }); }; columns = [ { title: '序号', render: (text, record, index) => index + 1, }, { title: '开始时间', dataIndex: 'createTime', render: (val) => moment(val).format('YYYY-MM-DD HH:mm:ss'), }, { title: '结束时间', dataIndex: 'endTime', render: (val) => (val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : null), }, { title: '状态', render: (val, record) => (record.endTime ? '已完成' : '生成中'), }, { title: '操作', render: (text, record) => record.endTime ? ( <Fragment> <a target="_blank" href={config.dfs + record.path}> 下载 </a> <Divider type="vertical" /> <a onClick={this.delete.bind(this, record.id)}>删除</a> </Fragment> ) : ( <Fragment> <a onClick={this.delete.bind(this, record.id)}>删除</a> </Fragment> ), }, ]; delete = (id) => { const { dispatch } = this.props; dispatch({ type: 'ExportFile/remove', payload: { ids: [id], }, callback: () => { this.getPage({ pageNo: 1, pageSize: 10 }); }, }); }; batchDelete = (e) => { const { dispatch } = this.props; const { selectedRows } = this.state; if (!selectedRows) return; dispatch({ type: 'ExportFile/remove', payload: { ids: selectedRows.map((row) => row.id).join(','), }, callback: () => { this.setState({ selectedRows: [], }); this.getPage({ pageNo: 1, pageSize: 10 }); }, }); }; deleteQuery = (i) => { const querys = this.state.querys; querys.splice(i); this.setState({ querys }); }; deleteGroup = (i) => { const groups = this.state.groups; groups.splice(i); this.setState( { groups, }, () => { this.finish(); }, ); }; open = () => { const { dispatch } = this.props; dispatch({ type: 'cms/getExportInfo', payload: { modelClass: this.props.modelClass, }, callback: (infos) => { const filterXZKeys = this.props.filterXZKeys; const mockData = []; const mockXZData = []; const qs = []; const gs = []; const os = []; for (var key in infos) { const x = infos[key]; mockData.push({ key: x.field, title: x.name, chosen: false, hql: x.hql, }); if (filterXZKeys != null) { if (filterXZKeys.includes(key)) { mockXZData.push({ key: x.field, title: x.name, chosen: false, hql: x.hql, }); } } else { mockXZData.push({ key: x.field, title: x.name, chosen: false, hql: x.hql, }); } if (x.canQuery) { qs.push(x); } if (x.canGroup) { gs.push(x); } if (x.canOrder) { os.push(x); } } this.setState({ mockData, mockXZData, infos, visiable: true, qs, gs, os, }); this.props.setShow(true); }, }); }; onCancle = () => { this.setState({ visiable: false }); }; handleChange = (targetKeys, direction, moveKeys) => { this.setState({ targetKeys }); }; okGroup = () => { const groups = this.state.groups; if (!this.state.currentGroupKey) { message.info('请选择一个聚合条件'); return false; } groups.push(this.state.infos[this.state.currentGroupKey]); this.setState({ groups, groupVisiable: false, currentGroupKey: null, }); }; addQuery = () => { this.setState({ queryVisiable: true }); }; okQuery = () => { if (!this.state.currentQueryKey) { message.info('请选择一个查询条件'); return false; } const querys = this.state.querys; querys.push(this.state.infos[this.state.currentQueryKey]); this.setState({ querys, queryVisiable: false, currentQueryKey: null }); }; finish = () => { const { dispatch, beanName, daoClass } = this.props; const { x, y, z, infos, XxX, currentKey, groups } = this.state; if (x == null) { message.info('请选择横轴'); return; } if (y == null) { message.info('请选择纵轴'); return; } this.props.form.validateFields((err, fieldsValue) => { if (err) return; const qqs = this.props.mustQuerys || []; const ggs = []; for (var key in fieldsValue) { var xx = key.indexOf('__'); let kk = key.substr(xx + 2); // 拿取查询星系 qqs.push({ notes: this.state.infos[kk].notes, hql: this.state.infos[kk].hql, c: this.state.infos[kk].type, x: fieldsValue[key].stringX, v: fieldsValue[key].string, }); } if (groups.length === 0) { message.info('请至少选择一个聚合条件'); return; } for (var i = 0; i < groups.length; i++) { ggs.push({ hql: groups[i].hql }); } const tx = infos[x]; const ty = infos[y]; var ttx; var tty; var ttz; if (tx.notes) { if (tx.notes.indexOf('com.') === -1) { ttx = infos[x].hql.replace('.id', '.dictName'); } else { ttx = infos[x].hql; } } else { ttx = infos[x].hql; } if (ty.notes) { if (ty.notes.indexOf('com.') === -1) { tty = infos[y].hql.replace('.id', '.dictName'); } else { tty = infos[y].hql; } } else { tty = infos[y].hql; } if (z != null) { const tz = infos[z]; if (tz && tz.notes) { if (tz.notes.indexOf('com.') === -1) { ttz = infos[z].hql.replace('.id', '.dictName'); } else { ttz = infos[z].hql; } } else { ttz = infos[z].hql; } } dispatch({ type: 'cms/getStatistics', payload: { x: ttx, y: tty, z: ttz, beanName, daoClass, xXx: XxX, hql: currentKey && infos[currentKey] ? infos[currentKey].hql : null, querys: JSON.stringify(qqs), groups: JSON.stringify(ggs), }, callback: (data) => { this.setState({ dataSource: data.dataSource, columns: data.columns, showSearchItem: false, }); }, }); }); }; downloadFile(url, params) { this.setState({ exportLoading: true }); fetch(url, { method: 'POST', body: FormdataWrapper(params), headers: getSassApiHeader(), }) .then((res) => { if (res.status != '200') { return res.json(); } else { return res.blob(); } }) .then((data) => { if (data instanceof Blob) { let a = document.createElement('a'); let url = window.URL.createObjectURL(data); let filename = (this.props.fileName ? this.props.fileName : '统计文件') + '.xlsx'; a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); this.getPage({ pageNo: 1, pageSize: 10 }); a = null; } else { controlNotification({ message: `文件导出错误`, description: data.errMsg, }); } }) .catch((err) => { controlNotification({ message: `网络请求超时`, }); }) .finally(() => { this.setState({ exportLoading: false }); }); } export = () => { const { beanName, daoClass } = this.props; const { x, y, z, infos, XxX, currentKey, groups, qs } = this.state; console.log(this.state, '----state---'); console.log(this.props, '----props---'); if (x == null) { message.info('请选择第一统计项'); return; } if (y == null) { message.info('请选择行数据'); return; } this.props.form.validateFields((err, fieldsValue) => { if (err) return; const qqs = this.props.mustQuerys || []; const ggs = []; for (var key in fieldsValue) { var xx = key.indexOf('__'); let kk = key.substr(xx + 2); qqs.push({ notes: this.state.infos[kk].notes, hql: this.state.infos[kk].hql, c: this.state.infos[kk].type, x: fieldsValue[key].stringX, v: fieldsValue[key].string, }); } if (groups.length === 0) { message.info('请至少选择一个聚合条件'); return; } for (var i = 0; i < groups.length; i++) { ggs.push({ hql: groups[i].hql }); } const tx = infos[x]; const ty = infos[y]; var ttx; var tty; var ttz; if (tx.notes) { if (tx.notes.indexOf('com.') === -1) { ttx = infos[x].hql.replace('.id', '.dictName'); } else { ttx = infos[x].hql; } } else { ttx = infos[x].hql; } if (ty.notes) { if (ty.notes.indexOf('com.') === -1) { tty = infos[y].hql.replace('.id', '.dictName'); } else { tty = infos[y].hql; } } else { tty = infos[y].hql; } if (z != null) { const tz = infos[z]; if (tz && tz.notes) { if (tz.notes.indexOf('com.') === -1) { ttz = infos[z].hql.replace('.id', '.dictName'); } else { ttz = infos[z].hql; } } else { ttz = infos[z].hql; } } const param = { x: ttx, y: tty, z: ttz, xname: getLableNameByValue(qs, x), yname: getLableNameByValue(qs, y), zname: getLableNameByValue(qs, z), beanName, daoClass, xXx: XxX, keyword: this.props.keyword ? this.props.keyword : this.props.modelClass, hql: currentKey && infos[currentKey] ? infos[currentKey].hql : null, querys: JSON.stringify(qqs), groups: JSON.stringify(ggs), }; let downloadUrl = config.httpServer + '/CmsApi/exportStatistics?'; const token = getToken() != null && getToken() != 'null' ? getToken() : '0000'; downloadUrl = `${downloadUrl}token=${token}`; this.downloadFile(downloadUrl, param); }); }; cancelGroup = () => { this.setState({ currentGroupKey: null, groupVisiable: false }); }; cancelQuery = () => { this.setState({ currentQueryKey: null, queryVisiable: false }); }; cancelOrder = () => { this.setState({ currentOrderKey: null, orderVisiable: false }); }; selectOrder = (e) => { this.setState({ currentOrderKey: e }); }; selectQuery = (e) => { this.setState({ currentQueryKey: e, flag: !this.state.flag }); }; selectGroup = (e) => { this.setState({ currentGroupKey: e, flag: !this.state.flag }); }; changeX = (x) => { this.setState({ x, flag: !this.state.flag, }); }; changeY = (y) => { this.setState( { y, flag: !this.state.flag, }, () => { this.finish(); }, ); }; changeZ = (z) => { this.setState({ z, flag: !this.state.flag }); }; cancelZ = () => { this.setState({ z: null, flag: !this.state.flag }); }; selectXxX = (XxX) => { this.setState({ XxX, flag: !this.state.flag }); }; selectCurrentKey = (currentKey) => { this.setState({ currentKey, XxX: null, flag: !this.state.flag }); }; cancelCurrentKey = () => { this.setState({ currentKey: null, XxX: null, flag: !this.state.flag }); }; pageConfig = (item = 0) => { const { visiable, queryVisiable, querys, groups, currentKey, XxX, infos, mockData, mockXZData, columns, dataSource, qs, os, gs, x, y, z, currentQueryKey, currentGroupKey, groupVisiable, } = this.state; let xxxs = []; if (currentKey != null) { if ( infos[currentKey].type == 'java.lang.Integer' || infos[currentKey].type == 'java.lang.Double' || infos[currentKey].type == 'java.lang.Long' ) { xxxs = [ { label: '数量', value: 'count' }, { label: '最大值', value: 'max' }, { label: '最小值', value: 'min', }, { label: '平均值', value: 'avg' }, { label: '求和', value: 'sum' }, ]; } else { xxxs = [{ label: '数量', value: 'count' }]; } } let optionQs = []; for (let item of qs) { let add = true; for (let i = 0; i < querys.length; i++) { if (querys[i].field === item.field) { add = false; break; } } if (add) { optionQs.push(item); } } let optionGs = []; for (let item of gs) { let add = true; for (let i = 0; i < groups.length; i++) { if (groups[i].field === item.field) { add = false; break; } } if (add) { optionGs.push(item); } } const config = [ { required: true, selectConfig: [ { name: '横轴', value: x, onChange: this.changeX, options: mockXZData, optionKey: 'key', optionName: 'title', }, ], }, { required: false, selectConfig: [ { name: '第二横轴', value: z, onChange: this.changeZ, options: mockXZData, optionKey: 'key', optionName: 'title', }, ], buttonConfig: [ { name: '取消', handleClick: this.cancelZ, className: 'defaultRed', key: 'cancel', }, ], }, { required: true, selectConfig: [ { name: '纵轴', value: y, onChange: this.changeY, options: mockXZData, optionKey: 'key', optionName: 'title', }, ], }, { required: false, selectConfig: [ { name: '统计指标', value: currentKey, onChange: this.selectCurrentKey, options: mockXZData, optionKey: 'key', optionName: 'title', }, { name: '', value: XxX, onChange: this.selectXxX, options: xxxs, optionKey: 'value', optionName: 'label', }, ], buttonConfig: [ { name: '取消', handleClick: this.cancelCurrentKey, className: 'defaultRed', key: 'cancel', }, ], }, { required: false, selectConfig: [ { name: '查询条件', value: currentQueryKey, onChange: this.selectQuery, options: optionQs, optionKey: 'field', optionName: 'name', }, ], buttonConfig: [ { name: '确定', handleClick: this.okQuery, className: 'defaultBlue', key: 'confirm', }, { name: '取消', handleClick: this.cancelQuery, className: 'defaultRed', key: 'cancel', }, ], }, { required: false, selectConfig: [ { name: '聚合条件', value: currentGroupKey, onChange: this.selectGroup, options: optionGs, optionKey: 'field', optionName: 'name', }, ], buttonConfig: [ { name: '确定', handleClick: this.okGroup, className: 'defaultBlue', key: 'confirm', }, { name: '取消', handleClick: this.cancelGroup, className: 'defaultRed', key: 'cancel', }, ], }, ]; return config[item]; }; selectDom = (item) => { const config = this.pageConfig(item); return ( <Row> {config.selectConfig.map((selectConfigItem, dataIndex) => { const nameSpanThis = dataIndex > 0 ? nameSpan2 : nameSpan; return ( <Col span={5} style={{ paddingLeft: '15px' }} key={dataIndex}> <FormItem label={selectConfigItem.name} required={config.required || false} colon={false} labelCol={{ xl: nameSpanThis.small, xxl: nameSpanThis.big, }} wrapperCol={{ xl: 24 - nameSpanThis.small, xxl: 24 - nameSpanThis.big, }}> <Select style={{ width: '100%' }} onChange={selectConfigItem.onChange} showSearch={true} optionFilterProp={'children'} value={selectConfigItem.value}> {selectConfigItem.options.map((r) => { return ( <Option key={r[selectConfigItem.optionKey]} value={r[selectConfigItem.optionKey]}> {r[selectConfigItem.optionName]} </Option> ); })} </Select> </FormItem> </Col> ); })} {typeof config.buttonConfig !== 'undefined' && config.buttonConfig.length ? config.buttonConfig.map((buttonDs) => { return ( <Col span={2} key={buttonDs.key} style={{ paddingLeft: '15px' }}> <FormItem colon={false} labelCol={{ xl: nameSpan.small, xxl: nameSpan.big, }} wrapperCol={{ xl: 24 - nameSpan.small, xxl: 24 - nameSpan.big, }}> <ButtonDiy name={buttonDs.name} type="default" type={buttonDs.name === '确定' ? 'primary' : 'default'} className={buttonDs.className} handleClick={buttonDs.handleClick} /> </FormItem> </Col> ); }) : null} </Row> ); }; render() { const { form, loading, isShow } = this.props; const { querys, groups, currentKey, exportLoading, infos, columns, dataSource, x, y, z, showHistory, showSearchItem, } = this.state; const loadingxxx = loading || exportLoading; let columnsTemp = columns; if (columnsTemp.length > 0) { if (z == null) { columnsTemp[0].title = ( <div className={styles.out2} style={{ fontSize: 14 }}> <span className={styles.title1}>{infos[x] ? infos[x].name : ''}</span>{' '} <span className={styles.title3}>{infos[y] ? infos[y].name : ''}</span> </div> ); columnsTemp[0].width = 200; } else { columnsTemp[0].title = ( <div className={styles.out} style={{ fontSize: 14 }}> <span className={styles.title1}>{infos[x] ? infos[x].name : ''}</span>{' '} <span className={styles.title2}>{infos[z] ? infos[z].name : ''}</span>{' '} <span className={styles.title3}>{infos[y] ? infos[y].name : ''}</span> </div> ); columnsTemp[0].width = 200; } } if (!isShow) { return ( <span> <ButtonDiy name="自定义统计" type="default" className="defaultBlue" handleClick={() => this.props.setShow(true)} /> </span> ); } return ( <div style={{ backgroundColor: '#fff', padding: '20px', }}> {/* 暂时屏蔽 */} <ButtonDiy name="返回" type="default" className="defaultBlue" handleClick={() => this.props.setShow(false)} /> <div id="downloadDiv" style={{ display: 'none' }} /> <Alert style={{ marginTop: 10, marginBottom: 10 }} message="如出现超时情况,表明数据量过大,可在等候一段时间后在下方我的历史导出列表中下载" type="warning" /> <Row> <Col span={24}> <div style={{ display: showSearchItem ? 'block' : 'none' }}> {this.selectDom(0)} {this.selectDom(1)} {this.selectDom(2)} {this.selectDom(3)} {this.selectDom(4)} <div style={{ textAlign: 'left', paddingLeft: '25px' }}> {querys.map((r, i) => ( <FormItem key={r.field} labelCol={{ // 查询条件 xl: nameSpan3.small, xxl: nameSpan3.big, }} wrapperCol={{ xl: 24 - nameSpan3.small, xxl: 24 - nameSpan3.big, }} label={r.name}> {form.getFieldDecorator(i + 'q__' + r.field, { initialValue: { stringX: '=' }, rules: [ { validator: (rule, value, callback) => { var errors = []; if (value == null) { errors.push(new Error('请输入查询值!', rule.field)); } else { if ( value.stringX !== 'IS NOT NULL' && value.stringX !== 'IS NULL' && value.string == null ) { errors.push(new Error('请输入查询值!', rule.field)); } } callback(errors); }, }, ], })(<QueryItem obj={r} deleteQuery={this.deleteQuery.bind(this, i)} />)} </FormItem> ))} </div> {this.selectDom(5)} <Row style={{ textAlign: 'left', paddingLeft: '80px', paddingTop: '8px', }}> {groups.map((r, i) => ( <Tag closable={true} key={i + r.field} onChange={this.deleteGroup.bind(this, i)}> {'按' + r.name + '聚合'} </Tag> ))} </Row> </div> <Row style={{ paddingTop: '20px' }}> <Col span={24} style={{ textAlign: 'left' }}> <ButtonDiy handleClick={this.finish} name={'开始统计'} type={'primary'} loading={loadingxxx} /> <ButtonDiy handleClick={() => { this.setState({ showSearchItem: !showSearchItem }); }} name={showSearchItem ? '隐藏搜索条件' : '显示统计条件'} /> {dataSource.length > 0 ? ( <ButtonDiy handleClick={this.export} name={'导出结果'} className="defaultBlue" loading={loadingxxx} /> ) : ( '' )} </Col> {dataSource.length > 0 ? ( <Col span={24}> <Table size="small" style={{ marginTop: 10 }} bordered style={{ scrollX: 'auto', maxWidth: '100%' }} dataSource={dataSource} rowKey={'col0'} pagination={{ defaultPageSize: 30, }} scroll={{ x: 'max-content' /*y: 'max-content'*/ }} columns={columnsTemp} /> </Col> ) : ( '' )} </Row> </Col> </Row> <Row> <Col span={24}> <h1> 我的历史统计 <a onClick={() => { this.setState({ showHistory: !showHistory }); }}> {showHistory ? '隐藏' : '显示'} </a> </h1> {showHistory ? ( <Fragment> <div style={{ textAlign: 'left', marginBottom: 5, marginTop: 5 }}> <Button type="primary" size="small" onClick={this.getPage.bind( this, { pageNo: 1, pageSize: 10, }, this.state.formValues, )}> 刷新列表 </Button> </div> <Table size="small" rowKey="id" columns={this.columns} dataSource={this.state.hisData.list} pagination={{ ...this.state.hisData.pagination, onChange: this.changeHisPage }} /> </Fragment> ) : null} </Col> </Row> </div> ); } }