Merge pull request #3662 from dataease/pr@dev@feat_dataset_export

feat(数据集): 数据集导出Excel
This commit is contained in:
王嘉豪 2022-11-07 16:16:53 +08:00 committed by GitHub
commit 4f3a17178f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 269 additions and 16 deletions

View File

@ -46,7 +46,7 @@ public class ChartViewFieldController {
BeanUtils.copyProperties(datasetTable, dataSetTableRequest);
DatasetTableField datasetTableField = new DatasetTableField();
BeanUtils.copyProperties(chartViewField, datasetTableField);
dataSetTableService.getPreviewData(dataSetTableRequest, 1, 1, Collections.singletonList(datasetTableField));
dataSetTableService.getPreviewData(dataSetTableRequest, 1, 1, Collections.singletonList(datasetTableField), null);
} catch (Exception e) {
DEException.throwException(Translator.get("i18n_calc_field_error"));
}

View File

@ -12,6 +12,8 @@ import io.dataease.commons.constants.SysLogConstants;
import io.dataease.commons.utils.PageUtils;
import io.dataease.commons.utils.Pager;
import io.dataease.controller.ResultHolder;
import io.dataease.controller.handler.annotation.I18n;
import io.dataease.controller.request.dataset.DataSetExportRequest;
import io.dataease.controller.request.dataset.DataSetTableRequest;
import io.dataease.controller.response.DataSetDetail;
import io.dataease.dto.dataset.DataSetTableDTO;
@ -29,6 +31,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -139,7 +142,7 @@ public class DataSetTableController {
@ApiOperation("查询预览数据")
@PostMapping("getPreviewData/{page}/{pageSize}")
public Map<String, Object> getPreviewData(@RequestBody DataSetTableRequest dataSetTableRequest, @PathVariable Integer page, @PathVariable Integer pageSize) throws Exception {
return dataSetTableService.getPreviewData(dataSetTableRequest, page, pageSize, null);
return dataSetTableService.getPreviewData(dataSetTableRequest, page, pageSize, null, null);
}
@ApiOperation("db数据库表预览数据")
@ -249,4 +252,11 @@ public class DataSetTableController {
public List<String> getDatasetNameFromGroup(@PathVariable String sceneId) {
return dataSetTableService.getDatasetNameFromGroup(sceneId);
}
@ApiOperation("数据集导出")
@PostMapping("/exportDataset")
@I18n
public void exportDataset(@RequestBody DataSetExportRequest request, HttpServletResponse response) throws Exception {
dataSetTableService.exportDataset(request, response);
}
}

View File

@ -125,7 +125,7 @@ public class DataSetTableFieldController {
DatasetTable datasetTable = dataSetTableService.get(datasetTableField.getTableId());
DataSetTableRequest dataSetTableRequest = new DataSetTableRequest();
BeanUtils.copyProperties(datasetTable, dataSetTableRequest);
dataSetTableService.getPreviewData(dataSetTableRequest, 1, 1, Collections.singletonList(datasetTableField));
dataSetTableService.getPreviewData(dataSetTableRequest, 1, 1, Collections.singletonList(datasetTableField), null);
} catch (Exception e) {
DEException.throwException(Translator.get("i18n_calc_field_error"));
}
@ -177,7 +177,7 @@ public class DataSetTableFieldController {
DecodedJWT jwt = JWT.decode(linkToken);
Long userId = jwt.getClaim("userId").asLong();
multFieldValuesRequest.setUserId(userId);
return dataSetFieldService.fieldValues(multFieldValuesRequest.getFieldIds(), multFieldValuesRequest.getSort(), multFieldValuesRequest.getUserId(), true, true,false);
return dataSetFieldService.fieldValues(multFieldValuesRequest.getFieldIds(), multFieldValuesRequest.getSort(), multFieldValuesRequest.getUserId(), true, true, false);
}
@ApiIgnore

View File

@ -0,0 +1,12 @@
package io.dataease.controller.request.dataset;
import lombok.Data;
/**
* @Author Junjun
*/
@Data
public class DataSetExportRequest extends DataSetTableRequest {
private String filename;
private String expressionTree;
}

View File

@ -11,6 +11,7 @@ import io.dataease.commons.constants.*;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.utils.*;
import io.dataease.controller.ResultHolder;
import io.dataease.controller.request.dataset.DataSetExportRequest;
import io.dataease.controller.request.dataset.DataSetGroupRequest;
import io.dataease.controller.request.dataset.DataSetTableRequest;
import io.dataease.controller.request.dataset.DataSetTaskRequest;
@ -32,11 +33,13 @@ import io.dataease.plugins.common.base.domain.*;
import io.dataease.plugins.common.base.mapper.*;
import io.dataease.plugins.common.constants.DatasetType;
import io.dataease.plugins.common.constants.DatasourceTypes;
import io.dataease.plugins.common.constants.DeTypeConstants;
import io.dataease.plugins.common.dto.dataset.SqlVariableDetails;
import io.dataease.plugins.common.dto.datasource.DataSourceType;
import io.dataease.plugins.common.dto.datasource.TableField;
import io.dataease.plugins.common.request.datasource.DatasourceRequest;
import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO;
import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeObj;
import io.dataease.plugins.datasource.provider.Provider;
import io.dataease.plugins.datasource.query.QueryProvider;
import io.dataease.plugins.loader.ClassloaderResponsity;
@ -61,8 +64,8 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@ -72,6 +75,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
@ -558,7 +562,7 @@ public class DataSetTableService {
}
public Map<String, Object> getPreviewData(DataSetTableRequest dataSetTableRequest, Integer page, Integer pageSize,
List<DatasetTableField> extFields) throws Exception {
List<DatasetTableField> extFields, DatasetRowPermissionsTreeObj extTree) throws Exception {
Map<String, Object> map = new HashMap<>();
String syncStatus = "";
DatasetTableField datasetTableField = DatasetTableField.builder().tableId(dataSetTableRequest.getId())
@ -576,6 +580,12 @@ public class DataSetTableService {
DatasetTable datasetTable = datasetTableMapper.selectByPrimaryKey(dataSetTableRequest.getId());
// 行权限
List<DataSetRowPermissionsTreeDTO> rowPermissionsTree = permissionsTreeService.getRowPermissionsTree(fields, datasetTable, null);
// ext filter
if (extTree != null) {
DataSetRowPermissionsTreeDTO dto = new DataSetRowPermissionsTreeDTO();
dto.setTree(extTree);
rowPermissionsTree.add(dto);
}
// 列权限
List<String> desensitizationList = new ArrayList<>();
fields = permissionService.filterColumnPermissions(fields, desensitizationList, datasetTable.getId(), null);
@ -2869,4 +2879,85 @@ public class DataSetTableService {
public void updateDatasetInfo(DatasetTable datasetTable) {
datasetTableMapper.updateByPrimaryKeySelective(datasetTable);
}
public void exportDataset(DataSetExportRequest request, HttpServletResponse response) throws Exception {
try {
DatasetRowPermissionsTreeObj tree = null;
if (StringUtils.isNotEmpty(request.getExpressionTree())) {
Gson gson = new Gson();
tree = gson.fromJson(request.getExpressionTree(), DatasetRowPermissionsTreeObj.class);
}
Map<String, Object> previewData = getPreviewData(request, 1, 100000, null, tree);
List<DatasetTableField> fields = (List<DatasetTableField>) previewData.get("fields");
List<Map<String, Object>> data = (List<Map<String, Object>>) previewData.get("data");
// 构建Excel数据格式
List<List<String>> details = new ArrayList<>();
List<String> header = new ArrayList<>();
for (DatasetTableField field : fields) {
header.add(field.getName());
}
details.add(header);
for (Map<String, Object> obj : data) {
List<String> row = new ArrayList<>();
for (DatasetTableField field : fields) {
String string = (String) obj.get(field.getDataeaseName());
row.add(string);
}
details.add(row);
}
// 操作Excel
Workbook wb = new XSSFWorkbook();
// Sheet
Sheet detailsSheet = wb.createSheet("数据");
//给单元格设置样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
//设置字体大小
font.setFontHeightInPoints((short) 12);
//设置字体加粗
font.setBold(true);
//给字体设置样式
cellStyle.setFont(font);
//设置单元格背景颜色
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//设置单元格填充样式(使用纯色背景颜色填充)
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
if (CollectionUtils.isNotEmpty(details)) {
for (int i = 0; i < details.size(); i++) {
Row row = detailsSheet.createRow(i);
List<String> rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.size(); j++) {
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(rowData.get(j));
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else {
if ((fields.get(j).getDeType() == DeTypeConstants.DE_INT || fields.get(j).getDeType() == DeTypeConstants.DE_FLOAT) && StringUtils.isNotEmpty(rowData.get(j))) {
try {
cell.setCellValue(Double.valueOf(rowData.get(j)));
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
} else {
cell.setCellValue(rowData.get(j));
}
}
}
}
}
}
OutputStream outputStream = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");
//文件名称
response.setHeader("Content-disposition", "attachment;filename=" + request.getFilename() + ".xlsx");
wb.write(outputStream);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
DataEaseException.throwException(e);
}
}
}

View File

@ -249,4 +249,15 @@ export function dsTable(page, size, id) {
method: 'post'
})
}
export default { loadTable, getScene, addGroup, delGroup, addTable, delTable, groupTree, checkCustomDs }
export function exportDataset(data) {
// 初始化仪表板视图缓存
return request({
url: 'dataset/table/exportDataset',
method: 'post',
data: data,
loading: true,
responseType: 'blob'
})
}
export default { loadTable, getScene, addGroup, delGroup, addTable, delTable, groupTree, checkCustomDs, exportDataset }

View File

@ -1693,7 +1693,11 @@ export default {
input_edit_name: 'Input field name',
edit_search: 'Search by name',
na: 'None',
date_format: 'Time format, default: year-month-day hour:minute:second'
date_format: 'Time format, default: year-month-day hour:minute:second',
export_dataset: 'Export',
filename: 'Filename',
export_filter: 'Filter',
pls_input_filename: 'Please input filename'
},
driver: {
driver: 'Driver',

View File

@ -1693,7 +1693,11 @@ export default {
input_edit_name: '請輸入字段名稱',
edit_search: '通過名稱搜索',
na: '暫無',
date_format: '時間格式,默認:年-月-日 時:分:秒'
date_format: '時間格式,默認:年-月-日 時:分:秒',
export_dataset: '數據集導出',
filename: '文件名稱',
export_filter: '篩選條件',
pls_input_filename: '請輸入文件名稱'
},
driver: {
driver: '驅動',

View File

@ -1692,7 +1692,11 @@ export default {
input_edit_name: '请输入字段名称',
edit_search: '通过名称搜索',
na: '暂无',
date_format: '时间格式,默认: 年-月-日 时:分:秒'
date_format: '时间格式,默认: 年-月-日 时:分:秒',
export_dataset: '数据集导出',
filename: '文件名称',
export_filter: '筛选条件',
pls_input_filename: '请输入文件名称'
},
driver: {
driver: '驱动',

View File

@ -55,12 +55,19 @@
</el-popover>
</el-col>
<el-col
v-if="hasDataPermission('manage', param.privileges)"
style="text-align: right"
:span="8"
>
<deBtn
:disabled="!previewDataSuccess"
type="primary"
icon="el-icon-download"
@click="exportDataset"
>
{{ $t('dataset.export_dataset') }}
</deBtn>
<el-dropdown
v-if="table.type === 'excel'"
v-if="table.type === 'excel' && hasDataPermission('manage', param.privileges)"
size="small"
trigger="click"
placement="bottom-end"
@ -81,7 +88,7 @@
</el-dropdown-menu>
</el-dropdown>
<deBtn
v-if="['sql', 'union'].includes(table.type)"
v-if="['sql', 'union'].includes(table.type) && hasDataPermission('manage', param.privileges)"
type="primary"
@click="editDataset(table.type)"
>
@ -177,11 +184,66 @@
/>
</el-tab-pane>
</el-tabs>
<!--导出数据集弹框-->
<el-dialog
v-dialogDrag
:visible.sync="showExport"
width="600px"
class="de-dialog-form"
:title="$t('dataset.export_dataset')"
append-to-body
>
<el-form
ref="exportForm"
class="de-form-item"
:model="exportForm"
:rules="exportFormRules"
:before-close="closeExport"
@submit.native.prevent
@keypress.enter.native="exportDatasetRequest"
>
<el-form-item
:label="$t('dataset.filename')"
prop="name"
>
<el-input
v-model.trim="exportForm.name"
:placeholder="$t('dataset.pls_input_filename')"
/>
</el-form-item>
<el-form-item
:label="$t('dataset.export_filter')"
prop="expressionTree"
>
<!--TODO 下面的input需用行权限的树形过滤组件替换-->
<el-input
v-model.trim="exportForm.expressionTree"
placeholder="请输入筛选条件"
/>
</el-form-item>
</el-form>
<span class="tip">提示最多支持导出10万条数据</span>
<div
slot="footer"
class="dialog-footer"
>
<deBtn
secondary
@click="closeExport"
>{{ $t('dataset.cancel') }}</deBtn>
<deBtn
type="primary"
@click="exportDatasetRequest"
>{{ $t('dataset.confirm') }}
</deBtn>
</div>
</el-dialog>
</div>
</template>
<script>
import { post } from '@/api/dataset/dataset'
import { exportDataset, post } from '@/api/dataset/dataset'
import TabDataPreview from './TabDataPreview'
import UpdateInfo from './UpdateInfo'
import DatasetDetail from '../common/DatasetDetail'
@ -224,7 +286,27 @@ export default {
row: 1000
},
tabStatus: false,
isPluginLoaded: false
isPluginLoaded: false,
showExport: false,
exportForm: {
name: '',
expressionTree: ''
},
exportFormRules: {
name: [
{
required: true,
message: this.$t('commons.input_content'),
trigger: 'change'
},
{
max: 50,
message: this.$t('commons.char_can_not_more_50'),
trigger: 'change'
}
]
},
previewDataSuccess: false
}
},
computed: {
@ -294,11 +376,14 @@ export default {
this.data = response.data.data
this.page = response.data.page
this.syncStatus = response.data.syncStatus
this.previewDataSuccess = true
if (response.data.status === 'warnning') {
this.$warning(response.data.msg, 3000)
this.previewDataSuccess = false
}
if (response.data.status === 'error') {
this.$error(response.data.msg, 3000)
this.previewDataSuccess = false
}
this.lastRequestComplete = true
})
@ -311,6 +396,7 @@ export default {
pageSize: 1000,
show: 0
}
this.previewDataSuccess = false
})
}
},
@ -365,6 +451,32 @@ export default {
this.initTable(this.param.id)
}
}
},
exportDataset() {
this.showExport = true
this.exportForm.name = this.table.name
this.exportForm.expressionTree = ''
},
closeExport() {
this.showExport = false
},
exportDatasetRequest() {
if (this.table.id) {
this.table.row = 100000
this.table.filename = this.exportForm.name
this.table.expressionTree = this.exportForm.expressionTree
exportDataset(this.table).then((res) => {
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const link = document.createElement('a')
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
link.download = this.exportForm.name + '.xlsx' //
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
}
}
}
}
@ -378,6 +490,11 @@ export default {
.blackTheme .icon-class {
color: #cccccc;
}
.tip {
color: #F56C6C;
font-size: 12px;
}
</style>
<style lang="scss" scoped>
.view-table {