!8 importexport

Merge pull request !8 from Foming/dev
Foming 4 years ago committed by Gitee
commit 6eb3a7b717

@ -51,6 +51,7 @@ module.exports = {
{title: '数据源', path: '/guide/datasource'},
{title: '数据集', path: '/guide/dataset'},
{title: '大屏设计', path: '/guide/dashboard'},
{title: '导入导出', path: '/guide/importexport'},
]
},
{

@ -88,7 +88,9 @@
**如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
### 散点图
**开发中** <br>
**规划中** <br>
### 对比图
**开发中** <br>
柱状对比图数据集需要3个字段其中一个作为对比的字段只能为2种值只有2种值作为对比的字段要选择“y轴字段”字典。因为底层的解析用的是堆叠图的解析这里的y轴字段并不是指的图表上面的y轴还请注意有强迫症可以自行修改源码的解析剩下的2个字段对应字典看图<br>
![img18](../picture/dashboard/img_18.png)
**如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**

@ -0,0 +1,17 @@
**注导入导出目前是初始版本报错没有细化如果导入导出过程中页面无反应请F12**
**注:“导入成功/失败”的提示不一定对应当前真实导入导出情况,请根据实际导入导出的结果进行判断**
## 导出
![img](../picture/imexport/img.png) <br>
导出会生成zip文件包含图表、样式、图片等不会带有该大屏的名称和code。<br>
### 导出数据集
适用于同一系统内部使用
### 导出不含有数据集
导出的图表会使用默认的静态数据集,适用于跨系统,请注意,如果你的大屏图表有部分图表是在对方系统不存在的,那么目前整个大屏是不会显示出来的,后续会进行兼容,不存在的图表留空。<br>
## 导入
![img1](../picture/imexport/img_1.png) <br>
选择一个导出的zip文件导入即可。注意导入会覆盖当前大屏请新建一张空白的大屏进行导入。<br>
**注:如果你导入的大屏中含有你当前系统不存在的图表,整个大屏是不会显示的。** <br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -9,7 +9,12 @@ import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.Char
import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.ReportDashboardObjectDto;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @desc controller
@ -62,4 +67,30 @@ public class ReportDashboardController {
return ResponseBean.builder().data(reportDashboardService.getChartData(dto)).build();
}
/**
*
* @param reportCode
* @return
*/
@GetMapping("/export")
@Permission(code = "view", name = "导出大屏")
public ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response,
@RequestParam("reportCode") String reportCode, @RequestParam(value = "showDataSet",required = false, defaultValue = "1") Integer showDataSet) {
return reportDashboardService.exportDashboard(request, response, reportCode, showDataSet);
}
/**
*
* @param file zip
* @param reportCode
* @return
*/
@PostMapping("/import/{reportCode}")
@Permission(code = "design", name = "导入大屏")
public ResponseBean importDashboard(@RequestParam("file") MultipartFile file, @PathVariable("reportCode") String reportCode) {
reportDashboardService.importDashboard(file, reportCode);
return ResponseBean.builder().build();
}
}

@ -6,6 +6,11 @@ import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.Char
import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.ReportDashboardObjectDto;
import com.anjiplus.template.gaea.business.modules.dashboard.controller.param.ReportDashboardParam;
import com.anjiplus.template.gaea.business.modules.dashboard.dao.entity.ReportDashboard;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @desc ReportDashboard
@ -35,4 +40,22 @@ public interface ReportDashboardService extends GaeaBaseService<ReportDashboardP
* @return
*/
Object getChartData(ChartDto dto);
/**
* zip
* @param request
* @param response
* @param reportCode
* @return
*/
ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response, String reportCode, Integer showDataSet);
/**
* zip
* @param file
* @param reportCode
* @return
*/
void importDashboard(MultipartFile file, String reportCode);
}

@ -13,6 +13,9 @@ import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.Repo
import com.anjiplus.template.gaea.business.modules.dashboard.dao.ReportDashboardMapper;
import com.anjiplus.template.gaea.business.modules.dashboard.service.ChartStrategy;
import com.anjiplus.template.gaea.business.modules.dashboard.service.ReportDashboardService;
import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
import com.anjiplus.template.gaea.business.modules.file.service.GaeaFileService;
import com.anjiplus.template.gaea.business.modules.file.util.FileUtils;
import com.anjiplus.template.gaea.business.util.DateUtil;
import com.anjiplus.template.gaea.business.modules.dashboardwidget.controller.dto.ReportDashboardWidgetDto;
import com.anjiplus.template.gaea.business.modules.dashboardwidget.controller.dto.ReportDashboardWidgetValueDto;
@ -22,17 +25,30 @@ import com.anjiplus.template.gaea.business.modules.dashboardwidget.service.Repor
import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.DataSetDto;
import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.OriginalDataDto;
import com.anjiplus.template.gaea.business.modules.dataset.service.DataSetService;
import com.anjiplus.template.gaea.business.util.FileUtil;
import com.anjiplus.template.gaea.business.util.UuidUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
@ -42,6 +58,7 @@ import java.util.*;
* @date 2021-04-12 14:52:21.761
**/
@Service
@Slf4j
//@RequiredArgsConstructor
public class ReportDashboardServiceImpl implements ReportDashboardService, InitializingBean, ApplicationContextAware {
@ -54,6 +71,18 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
@Autowired
private DataSetService dataSetService;
@Autowired
private GaeaFileService gaeaFileService;
@Value("${customer.file.downloadPath:''}")
private String fileDownloadPath;
@Value("${customer.file.dist-path:''}")
private String dictPath;
private final static String ZIP_PATH = "/zip/";
private final static String JSON_PATH = "dashboard.json";
private Map<String, ChartStrategy> queryServiceImplMap = new HashMap<>();
private ApplicationContext applicationContext;
@ -105,7 +134,7 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
* @param dto
*/
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public void insertDashboard(ReportDashboardObjectDto dto) {
String reportCode = dto.getReportCode();
GaeaAssert.notEmpty(reportCode, ResponseCode.PARAM_IS_NULL, "reportCode");
@ -121,6 +150,7 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
} else {
//更新
dashboard.setId(reportDashboard.getId());
dashboard.setVersion(null);
this.update(dashboard);
}
@ -165,6 +195,194 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
// return getTarget(chartType).transform(dto, result.getData());
}
/**
* zip
*
* @param request
* @param response
* @param reportCode
* @return
*/
@Override
public ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response, String reportCode, Integer showDataSet) {
String userAgent = request.getHeader("User-Agent");
boolean isIeBrowser = userAgent.indexOf("MSIE") > 0;
ReportDashboardObjectDto detail = getDetail(reportCode);
List<ReportDashboardWidgetDto> widgets = detail.getDashboard().getWidgets();
detail.setWidgets(widgets);
detail.getDashboard().setWidgets(null);
//1.组装临时目录,/app/disk/upload/zip/临时文件夹
String path = dictPath + ZIP_PATH + UuidUtil.generateShortUuid();
//将涉及到的图片保存下来1.背景图2.组件为图片的)
String backgroundImage = detail.getDashboard().getBackgroundImage();
zipLoadImage(backgroundImage, path);
detail.getWidgets().stream()
.filter(reportDashboardWidgetDto -> "widget-image".equals(reportDashboardWidgetDto.getType()))
.forEach(reportDashboardWidgetDto -> {
String imageAddress = reportDashboardWidgetDto.getValue().getSetup().getString("imageAdress");
zipLoadImage(imageAddress, path);
});
//showDataSet == 0 代表不包含数据集
if (0 == showDataSet) {
detail.getWidgets().forEach(reportDashboardWidgetDto -> {
ReportDashboardWidgetValueDto value = reportDashboardWidgetDto.getValue();
JSONObject data = value.getData();
if (null != data && data.containsKey("dataType")) {
reportDashboardWidgetDto.getValue().getData().put("dataType", "staticData");
}
});
}
//2.将大屏设计到的json文件保存
String jsonPath = path + "/" + JSON_PATH;
FileUtil.WriteStringToFile(jsonPath, JSONObject.toJSONString(detail));
//将path文件夹打包zip
String zipPath = path + ".zip";
FileUtil.compress(path, zipPath);
File file = new File(zipPath);
ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
builder.contentLength(file.length());
//application/octet-stream 二进制数据流(最常见的文件下载)
builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
if (isIeBrowser) {
builder.header("Content-Disposition", "attachment; filename=" + reportCode + ".zip");
} else {
builder.header("Content-Disposition", "attacher; filename*=UTF-8''" + reportCode + ".zip");
}
ResponseEntity<byte[]> body = builder.body(FileUtils.readFileToByteArray(file));
//删除zip文件
file.delete();
//删除path临时文件夹
FileUtil.delete(path);
log.info("删除临时文件:{}{}", zipPath, path);
return body;
}
/**
* zip
*
* @param file
* @param reportCode
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void importDashboard(MultipartFile file, String reportCode) {
log.info("导入开始,{}", reportCode);
//1.组装临时目录,/app/disk/upload/zip/临时文件夹
String path = dictPath + ZIP_PATH + UuidUtil.generateShortUuid();
//2.解压
FileUtil.decompress(file, path);
// path/uuid/
File parentPath = new File(path);
//获取打包的第一层目录
File firstFile = parentPath.listFiles()[0];
File[] files = firstFile.listFiles();
//定义map
Map<String, String> fileMap = new HashMap<>();
String content = "";
for (int i = 0; i < files.length; i++) {
File childFile = files[i];
if (JSON_PATH.equals(childFile.getName())) {
//json文件
content = FileUtil.readFile(childFile);
} else if ("image".equals(childFile.getName())) {
File[] imageFiles = childFile.listFiles();
//所有需要上传的图片
for (File imageFile : imageFiles) {
//查看是否存在此image
String fileName = imageFile.getName().split("\\.")[0];
//根据fileId从gaea_file中读出filePath
LambdaQueryWrapper<GaeaFile> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(GaeaFile::getFileId, fileName);
GaeaFile gaeaFile = gaeaFileService.selectOne(queryWrapper);
if (null == gaeaFile) {
GaeaFile upload = gaeaFileService.upload(imageFile, fileName);
log.info("存入图片: {}", upload.getFilePath());
fileMap.put(fileName, upload.getUrlPath());
}
}
}
}
//解析cotent
ReportDashboardObjectDto detail = JSONObject.parseObject(content, ReportDashboardObjectDto.class);
//将涉及到的图片路径替换1.背景图2.组件为图片的)
String backgroundImage = detail.getDashboard().getBackgroundImage();
detail.getDashboard().setBackgroundImage(replaceUrl(backgroundImage, fileMap));
detail.getWidgets().stream()
.filter(reportDashboardWidgetDto -> "widget-image".equals(reportDashboardWidgetDto.getType()))
.forEach(reportDashboardWidgetDto -> {
String imageAddress = reportDashboardWidgetDto.getValue().getSetup().getString("imageAdress");
String address = replaceUrl(imageAddress, fileMap);
reportDashboardWidgetDto.getValue().getSetup().put("imageAdress", address);
reportDashboardWidgetDto.getOptions().getJSONArray("setup").getJSONObject(4).put("value", address);
});
//将新的大屏编码赋值
detail.setReportCode(reportCode);
//解析结束,删除临时文件夹
FileUtil.delete(path);
log.info("解析成功,开始存入数据库...");
insertDashboard(detail);
}
private String replaceUrl(String imageAddress, Map<String, String> fileMap) {
String fileId = imageAddress.substring(imageAddress.trim().length() - 36);
String orDefault = fileMap.getOrDefault(fileId, null);
if (StringUtils.isBlank(orDefault)) {
return imageAddress;
}
return orDefault;
}
/**
*
* @param imageAddress
* @param path
*/
private void zipLoadImage(String imageAddress, String path) {
//http://10.108.26.197:9095/file/download/1d9bcd35-82a1-4f08-9465-b66b930b6a8d
if (imageAddress.trim().startsWith(fileDownloadPath)) {
//以fileDownloadPath为前缀的代表为上传的图片
String fileName = imageAddress.substring(fileDownloadPath.length() + 1);
//根据fileId从gaea_file中读出filePath
LambdaQueryWrapper<GaeaFile> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(GaeaFile::getFileId, fileName);
GaeaFile gaeaFile = gaeaFileService.selectOne(queryWrapper);
if (null != gaeaFile) {
String fileType = gaeaFile.getFileType();
path = path + "/image/" + fileName + "." + fileType;
//path = /app/disk/upload/zip/UUID/image
//原始文件的路径
String filePath = gaeaFile.getFilePath();
FileUtil.copyFileUsingFileChannels(filePath, path);
}
}
}
public ChartStrategy getTarget(String type) {
for (String s : queryServiceImplMap.keySet()) {
if (s.contains(type)) {

@ -8,6 +8,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
/**
* (GaeaFile)Service
@ -17,15 +18,33 @@ import javax.servlet.http.HttpServletResponse;
*/
public interface GaeaFileService extends GaeaBaseService<GaeaFileParam, GaeaFile> {
/**
*
*
* @param multipartFile
* @param file
* @param customFileName null
* @return
*/
GaeaFile upload(MultipartFile multipartFile, File file, String customFileName);
/**
*
*
* @param file
* @return 访
* @param multipartFile
* @return
*/
GaeaFile upload(MultipartFile file);
GaeaFile upload(MultipartFile multipartFile);
/**
*
*
* @param file
* @param customFileName
* @return
*/
GaeaFile upload(File file, String customFileName);
/**
* fileId
*

@ -10,6 +10,7 @@ import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
import com.anjiplus.template.gaea.business.modules.file.service.GaeaFileService;
import com.anjiplus.template.gaea.business.modules.file.util.FileUtils;
import com.anjiplus.template.gaea.business.modules.file.util.StringPatternUtil;
import com.anjiplus.template.gaea.business.util.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
@ -64,11 +65,18 @@ public class GaeaFileServiceImpl implements GaeaFileService {
return gaeaFileMapper;
}
@Override
@Transactional(rollbackFor = Exception.class)
public GaeaFile upload(MultipartFile file) {
public GaeaFile upload(MultipartFile multipartFile, File file, String customFileName) {
try {
String fileName = file.getOriginalFilename();
String fileName = "";
if (null != multipartFile) {
fileName = multipartFile.getOriginalFilename();
}else {
fileName = file.getName();
}
if (StringUtils.isBlank(fileName)) {
throw BusinessExceptionBuilder.build(ResponseCode.FILE_EMPTY_FILENAME);
}
@ -82,7 +90,12 @@ public class GaeaFileServiceImpl implements GaeaFileService {
throw BusinessExceptionBuilder.build(ResponseCode.FILE_SUFFIX_UNSUPPORTED);
}
// 生成文件唯一性标识
String fileId = UUID.randomUUID().toString();
String fileId;
if (StringUtils.isBlank(customFileName)) {
fileId = UUID.randomUUID().toString();
} else {
fileId = customFileName;
}
String newFileName = fileId + suffixName;
// 本地文件保存路径
String filePath = dictPath + newFileName;
@ -102,7 +115,11 @@ public class GaeaFileServiceImpl implements GaeaFileService {
if (!parentFile.exists()) {
parentFile.mkdirs();
}
file.transferTo(dest);
if (null != multipartFile) {
multipartFile.transferTo(dest);
}else {
FileUtil.copyFileUsingFileChannels(file, dest);
}
// 将完整的http访问路径返回
return gaeaFile;
} catch (Exception e) {
@ -112,6 +129,29 @@ public class GaeaFileServiceImpl implements GaeaFileService {
}
}
/**
*
*
* @param multipartFile
* @return
*/
@Override
public GaeaFile upload(MultipartFile multipartFile) {
return upload(multipartFile, null, null);
}
/**
*
*
* @param file
* @param customFileName
* @return
*/
@Override
public GaeaFile upload(File file, String customFileName) {
return upload(null, file, customFileName);
}
@Override
public ResponseEntity<byte[]> download(HttpServletRequest request, HttpServletResponse response, String fileId) {
try {

@ -0,0 +1,415 @@
package com.anjiplus.template.gaea.business.util;
import com.anji.plus.gaea.code.ResponseCode;
import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Enumeration;
import java.util.zip.*;
/**
* Created by raodeming on 2021/8/23.
*/
@Slf4j
public class FileUtil {
//链接url下载图片
public static void downloadPicture(String urlPath, String path) {
URL url = null;
try {
url = new URL(urlPath);
DataInputStream dataInputStream = new DataInputStream(url.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(path);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = dataInputStream.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
fileOutputStream.write(output.toByteArray());
dataInputStream.close();
fileOutputStream.close();
log.info("链接下载图片:{},临时路径:{}", urlPath, path);
} catch (IOException e) {
log.error("根据链接下载失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
/**
*
*
* @param source
* @param dest
* @throws IOException
*/
public static void copyFileUsingFileChannels(File source, File dest) {
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} catch (IOException e) {
log.error("复制文件失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
} finally {
try {
inputChannel.close();
outputChannel.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
}
/**
*
*
* @param source
* @param dest
* @throws IOException
*/
public static void copyFileUsingFileChannels(String source, String dest) {
copyFileUsingFileChannels(new File(source), new File(dest));
}
public static void WriteStringToFile(String filePath, String content) {
try {
//不存在创建文件
File file = new File(filePath);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
FileWriter fw = new FileWriter(filePath);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
fw.close();
} catch (Exception e) {
log.error("写入文件失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
/**
*
*
* @param file
* @return
*/
public static String readFile(File file) {
BufferedReader reader = null;
StringBuilder sbf = new StringBuilder();
try {
reader = new BufferedReader(new FileReader(file));
String tempStr;
while ((tempStr = reader.readLine()) != null) {
sbf.append(tempStr);
}
reader.close();
return sbf.toString();
} catch (IOException e) {
log.error("读文件失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e1.getMessage());
}
}
}
}
/**
*
*
* @param filePath
* @return
*/
public static String readFile(String filePath) {
File file = new File(filePath);
return readFile(file);
}
static final int BUFFER = 8192;
/**
* zip
*
* @param srcPath
* @param dstPath
* @throws IOException
*/
public static void compress(String srcPath, String dstPath) {
File srcFile = new File(srcPath);
File dstFile = new File(dstPath);
FileOutputStream out = null;
ZipOutputStream zipOut = null;
try {
out = new FileOutputStream(dstFile);
CheckedOutputStream cos = new CheckedOutputStream(out, new CRC32());
zipOut = new ZipOutputStream(cos);
String baseDir = "";
compress(srcFile, zipOut, baseDir);
} catch (IOException e) {
log.error("压缩文件夹失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
} finally {
if (null != zipOut) {
try {
zipOut.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
out = null;
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
}
}
private static void compress(File file, ZipOutputStream zipOut, String baseDir) {
if (file.isDirectory()) {
compressDirectory(file, zipOut, baseDir);
} else {
compressFile(file, zipOut, baseDir);
}
}
/**
*
*/
private static void compressDirectory(File dir, ZipOutputStream zipOut, String baseDir) {
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
compress(files[i], zipOut, baseDir + dir.getName() + "/");
}
}
/**
*
*/
private static void compressFile(File file, ZipOutputStream zipOut, String baseDir) {
if (!file.exists()) {
return;
}
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(file));
ZipEntry entry = new ZipEntry(baseDir + file.getName());
zipOut.putNextEntry(entry);
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
zipOut.write(data, 0, count);
}
} catch (IOException e) {
log.error("压缩文件夹失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
} finally {
if (null != bis) {
try {
bis.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
}
}
public static void decompress(String zipFile, String dstPath) {
try {
decompress(new ZipFile(zipFile), dstPath);
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
public static void decompress(MultipartFile zipFile, String dstPath) {
try {
File file = new File(dstPath + File.separator + zipFile.getOriginalFilename());
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
zipFile.transferTo(file);
decompress(new ZipFile(file), dstPath);
//解压完删除
file.delete();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
/**
* zip
*
* @param zip
* @param dstPath
* @throws IOException
*/
public static void decompress(ZipFile zip, String dstPath) {
log.info("解压zip{},临时目录:{}", zip.getName(), dstPath);
File pathFile = new File(dstPath);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
try {
for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String zipEntryName = entry.getName();
InputStream in = null;
OutputStream out = null;
try {
in = zip.getInputStream(entry);
String outPath = (dstPath + "/" + zipEntryName).replaceAll("\\*", "/");
;
//判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
}
//判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
if (new File(outPath).isDirectory()) {
continue;
}
out = new FileOutputStream(outPath);
byte[] buf1 = new byte[1024];
int len;
while ((len = in.read(buf1)) > 0) {
out.write(buf1, 0, len);
}
} catch (IOException e) {
log.error("解压失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
log.error("", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
}
}
zip.close();
} catch (IOException e) {
log.error("解压失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
/**
*
* @param ins
* @param file
*/
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
log.error("获取流文件失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
/**
*
*
* @param path
*/
public static void delete(String path) {
Path directory = Paths.get(path);
try {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
Files.delete(file); // this will work because it's always a File
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir); //this will work because Files in the directory are already deleted
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
log.error("删除文件失败", e);
throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
}
}
public static void main(String[] args) throws Exception {
// String targetFolderPath = "D:\\aa";
// String rawZipFilePath = "D:\\aa.zip";
// String newZipFilePath = "D:\\aa.zip";
//
//
// //将目标目录的文件压缩成Zip文件
// FileUtil.compress(targetFolderPath, newZipFilePath);
//
// //将Zip文件解压缩到目标目录
// FileUtil.decompress(rawZipFilePath, targetFolderPath);
// FileUtil.downloadPicture("http://10.108.26.197:9095/file/download/fd20d563-00aa-45e2-b5db-aff951f814ec", "D:\\abc.png");
// delete("D:\\aa");
}
}

@ -8,3 +8,4 @@ spring:
customer:
file:
dist-path: D:\Workspace\AJ-Report\report-core\upload
downloadPath: http://127.0.0.1:9095/file/download

@ -66,7 +66,7 @@ INSERT INTO `aj_report`.`gaea_report_data_set`(`set_code`, `set_name`, `set_desc
use
aj_report_init;
CREATE TABLE `aj_report_barstack`
CREATE TABLE if not exists `aj_report_barstack`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` date DEFAULT NULL,

@ -0,0 +1,21 @@
use aj_report_init;
CREATE TABLE if not exists `aj_report_comparestack` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` date DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`nums` bigint(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (1, '2021-08-23', '成功', 12);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (2, '2021-08-23', '失败', 1);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (3, '2021-08-24', '成功', 24);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (4, '2021-08-24', '失败', 5);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (5, '2021-08-25', '成功', 13);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (6, '2021-08-25', '失败', 8);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (7, '2021-08-26', '成功', 19);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (8, '2021-08-26', '失败', 3);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (9, '2021-08-27', '成功', 9);
INSERT INTO `aj_report_init`.`aj_report_comparestack`(`id`, `time`, `type`, `nums`) VALUES (10, '2021-08-27', '失败', 15);
INSERT INTO `aj_report`.`gaea_report_data_set`(`set_code`, `set_name`, `set_desc`, `source_code`, `dyn_sentence`, `case_result`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('compare_ajreport', '柱状对比图示例数据', '', 'mysql_ajreport', 'SELECT time,type,nums from aj_report_comparestack', '[{\"time\":\"2021-08-23\",\"type\":\"成功\",\"nums\":12},{\"time\":\"2021-08-23\",\"type\":\"失败\",\"nums\":1},{\"time\":\"2021-08-24\",\"type\":\"成功\",\"nums\":24},{\"time\":\"2021-08-24\",\"type\":\"失败\",\"nums\":5},{\"time\":\"2021-08-25\",\"type\":\"成功\",\"nums\":13},{\"time\":\"2021-08-25\",\"type\":\"失败\",\"nums\":8},{\"time\":\"2021-08-26\",\"type\":\"成功\",\"nums\":19},{\"time\":\"2021-08-26\",\"type\":\"失败\",\"nums\":3},{\"time\":\"2021-08-27\",\"type\":\"成功\",\"nums\":9},{\"time\":\"2021-08-27\",\"type\":\"失败\",\"nums\":15}]', 1, 0, 'admin', '2021-08-27 13:48:33', 'admin', '2021-08-27 13:48:33', 1);

@ -1,5 +1,6 @@
import request from '@/utils/request'
import { getShareToken, getToken } from "@/utils/auth";
import axios from 'axios';
// 保存大屏设计
export function insertDashboard(data) {
@ -45,3 +46,30 @@ export function getData(data) {
data,
})
}
// 导出大屏
export function exportDashboard(data) {
return new Promise((resolve) =>{
axios({
method:'get',
url: process.env.BASE_API + '/reportDashboard/export',
headers: { 'Authorization': getToken() },
params:data,
responseType:'blob'
}).then(res =>{
resolve(res.data);
}).catch(err =>{
resolve('error');
})
})
}
// 导入大屏
export function importDashboard(data) {
return request({
url: 'reportDashboard/import',
method: 'post',
data,
})
}

@ -54,6 +54,30 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe618;</span>
<div class="name">导出</div>
<div class="code-name">&amp;#xe618;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe608;</span>
<div class="name">导入</div>
<div class="code-name">&amp;#xe608;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9ca;</span>
<div class="name">对比图谱</div>
<div class="code-name">&amp;#xe9ca;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe635;</span>
<div class="name">折线</div>
<div class="code-name">&amp;#xe635;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe621;</span>
<div class="name">堆叠图</div>
@ -762,9 +786,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1629425895962') format('woff2'),
url('iconfont.woff?t=1629425895962') format('woff'),
url('iconfont.ttf?t=1629425895962') format('truetype');
src: url('iconfont.woff2?t=1629797734566') format('woff2'),
url('iconfont.woff?t=1629797734566') format('woff'),
url('iconfont.ttf?t=1629797734566') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -790,6 +814,42 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icondaochu"></span>
<div class="name">
导出
</div>
<div class="code-name">.icondaochu
</div>
</li>
<li class="dib">
<span class="icon iconfont icondaoru"></span>
<div class="name">
导入
</div>
<div class="code-name">.icondaoru
</div>
</li>
<li class="dib">
<span class="icon iconfont iconduibitupu"></span>
<div class="name">
对比图谱
</div>
<div class="code-name">.iconduibitupu
</div>
</li>
<li class="dib">
<span class="icon iconfont iconzhexian"></span>
<div class="name">
折线
</div>
<div class="code-name">.iconzhexian
</div>
</li>
<li class="dib">
<span class="icon iconfont iconbianzu23"></span>
<div class="name">
@ -1852,6 +1912,38 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icondaochu"></use>
</svg>
<div class="name">导出</div>
<div class="code-name">#icondaochu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icondaoru"></use>
</svg>
<div class="name">导入</div>
<div class="code-name">#icondaoru</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconduibitupu"></use>
</svg>
<div class="name">对比图谱</div>
<div class="code-name">#iconduibitupu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconzhexian"></use>
</svg>
<div class="name">折线</div>
<div class="code-name">#iconzhexian</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconbianzu23"></use>

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 1513211 */
src: url('iconfont.woff2?t=1629425895962') format('woff2'),
url('iconfont.woff?t=1629425895962') format('woff'),
url('iconfont.ttf?t=1629425895962') format('truetype');
src: url('iconfont.woff2?t=1629797734566') format('woff2'),
url('iconfont.woff?t=1629797734566') format('woff'),
url('iconfont.ttf?t=1629797734566') format('truetype');
}
.iconfont {
@ -13,6 +13,22 @@
-moz-osx-font-smoothing: grayscale;
}
.icondaochu:before {
content: "\e618";
}
.icondaoru:before {
content: "\e608";
}
.iconduibitupu:before {
content: "\e9ca";
}
.iconzhexian:before {
content: "\e635";
}
.iconbianzu23:before {
content: "\e621";
}

File diff suppressed because one or more lines are too long

@ -5,6 +5,34 @@
"css_prefix_text": "icon",
"description": "",
"glyphs": [
{
"icon_id": "14325372",
"name": "导出",
"font_class": "daochu",
"unicode": "e618",
"unicode_decimal": 58904
},
{
"icon_id": "15040056",
"name": "导入",
"font_class": "daoru",
"unicode": "e608",
"unicode_decimal": 58888
},
{
"icon_id": "18140536",
"name": "对比图谱",
"font_class": "duibitupu",
"unicode": "e9ca",
"unicode_decimal": 59850
},
{
"icon_id": "23013771",
"name": "折线",
"font_class": "zhexian",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "18888301",
"name": "堆叠图",

@ -41,9 +41,6 @@ import Avue from '@smallwei/avue';
import '@smallwei/avue/lib/index.css';
Vue.use(Avue);
import VueSuperSlide from 'vue-superslide'
Vue.use(VueSuperSlide)
// enable element zh-cn
Vue.use(ElementUI, { zhLocale })

@ -59,7 +59,7 @@
:style="{ width: widthLeftForToolsHideButton + 'px' }"
@click="toolIsShow = !toolIsShow"
>
<i class="el-icon-arrow-right" />
<i class="el-icon-arrow-right"/>
</div>
<div
@ -87,36 +87,87 @@
<i class="iconfont iconyulan" @click="viewScreen"></i>
</el-tooltip>
</span>
<!-- <span class="btn border-left">
<span class="btn">
<el-tooltip
class="item"
effect="dark"
content="导入"
placement="bottom"
>
<el-upload class="el-upload"
ref="upload"
:action="uploadUrl"
:headers="headers"
accept=".zip"
:on-success="handleUpload"
:on-error="handleError"
:show-file-list="false"
:limit="1">
<i class="iconfont icondaoru"></i>
</el-upload>
</el-tooltip>
</span>
<span class="btn border-left">
<ul class="nav">
<li>
<i class="el-icon-brush"></i><i class="el-icon-arrow-down"></i>
<i class="iconfont icondaochu"></i><i class="el-icon-arrow-down"></i>
<ul>
<li>
<div>
<i class="el-icon-full-screen mr10"></i>边框
<i class="el-icon-arrow-right ml20"></i>
</div>
<ul class="three-level">
<li><a href="#">边框1</a></li>
<li><a href="#">边框2</a></li>
<li><a href="#">边框3</a></li>
</ul>
<el-tooltip
class="item"
effect="dark"
content="适合当前系统"
placement="right"
>
<div @click="exportDashboard(1)">()</div>
</el-tooltip>
</li>
<li>
<div>
<i class="el-icon-magic-stick mr10"></i>装饰<i
class="el-icon-arrow-right ml20"
></i>
</div>
<ul class="three-level">
<li><a href="#">装饰1</a></li>
</ul>
<el-tooltip
class="item"
effect="dark"
content="适合跨系统"
placement="right"
>
<div @click="exportDashboard(0)">()</div>
</el-tooltip>
</li>
</ul>
</li>
</ul>
</span> -->
</span>
<!-- <span class="btn border-left">
<ul class="nav">
<li>
<i class="el-icon-brush"></i><i class="el-icon-arrow-down"></i>
<ul>
<li>
<div>
<i class="el-icon-full-screen mr10"></i>边框
<i class="el-icon-arrow-right ml20"></i>
</div>
<ul class="three-level">
<li><a href="#">边框1</a></li>
<li><a href="#">边框2</a></li>
<li><a href="#">边框3</a></li>
</ul>
</li>
<li>
<div>
<i class="el-icon-magic-stick mr10"></i>装饰<i
class="el-icon-arrow-right ml20"
></i>
</div>
<ul class="three-level">
<li><a href="#">装饰1</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</span>-->
</div>
<div
class="workbench-container"
@ -226,13 +277,15 @@
</template>
<script>
import { insertDashboard, detailDashboard } from "@/api/bigscreen";
import { widgetTools, getToolByCode } from "./tools";
import {insertDashboard, detailDashboard, importDashboard, exportDashboard} from "@/api/bigscreen";
import {widgetTools, getToolByCode} from "./tools";
import widget from "./widget/widget.vue";
import dynamicForm from "./form/dynamicForm.vue";
import draggable from "vuedraggable";
import VueRulerTool from "vue-ruler-tool"; //
import contentMenu from "./form/contentMenu";
import {getToken} from "@/utils/auth";
export default {
name: "Login",
components: {
@ -244,6 +297,7 @@ export default {
},
data() {
return {
uploadUrl: process.env.BASE_API + '/reportDashboard/import/' + this.$route.query.reportCode,
grade: false,
layerWidget: [],
widgetTools: widgetTools, // js
@ -310,6 +364,11 @@ export default {
};
},
computed: {
headers() {
return {
Authorization: getToken(), // token
}
},
//
middleWidth() {
var widthLeftAndRight = 0;
@ -382,7 +441,7 @@ export default {
},
async initEchartData() {
const reportCode = this.$route.query.reportCode;
const { code, data } = await detailDashboard(reportCode);
const {code, data} = await detailDashboard(reportCode);
if (code != 200) return;
const processData = this.handleInitEchartsData(data);
const screenData = this.handleBigScreen(data.dashboard);
@ -482,7 +541,7 @@ export default {
},
widgets: this.widgets
};
const { code, data } = await insertDashboard(screenData);
const {code, data} = await insertDashboard(screenData);
if (code == "200") {
this.$message.success("保存成功!");
}
@ -491,10 +550,50 @@ export default {
viewScreen() {
var routeUrl = this.$router.resolve({
path: "/bigscreen/viewer",
query: { reportCode: this.$route.query.reportCode }
query: {reportCode: this.$route.query.reportCode}
});
window.open(routeUrl.href, "_blank");
},
//
async exportDashboard(val) {
const fileName = this.$route.query.reportCode + ".zip"
const param = {
reportCode:this.$route.query.reportCode,
showDataSet:val
}
exportDashboard(param).then(res => {
const blob = new Blob([res], {type: "application/octet-stream"});
if (window.navigator.msSaveOrOpenBlob) {//msSaveOrOpenBlobbool
navigator.msSaveBlob(blob, fileName);//
} else {
const link = document.createElement('a');//a
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
window.URL.revokeObjectURL(link.href);
}
})
},
//
handleUpload(response, file, fileList) {
//el-upload
this.$refs.upload.clearFiles();
//
this.initEchartData();
this.$message({
message: '导入成功!',
type: 'success',
})
},
handleError() {
this.$message({
message: '上传失败!',
type: 'error',
})
},
//
getPXUnderScale(px) {
return this.bigscreenScaleInWorkbench * px;
@ -750,22 +849,28 @@ export default {
.mr10 {
margin-right: 10px;
}
.ml20 {
margin-left: 20px;
}
.border-right {
border-right: 1px solid #273b4d;
}
.border-left {
border-left: 1px solid #273b4d;
}
.el-icon-arrow-down {
font-size: 10px;
}
.is-active {
background: #31455d !important;
color: #bfcbd9 !important;
}
.layout {
display: -webkit-box;
display: -ms-flexbox;
@ -775,6 +880,7 @@ export default {
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
.layout-left {
display: inline-block;
height: 100%;
@ -807,6 +913,7 @@ export default {
border: 1px solid #3a4659;
background: #282a30;
}
.tools-item-text {
}
}
@ -823,6 +930,7 @@ export default {
background-color: #242a30;
cursor: pointer;
padding-top: 26%;
i {
font-size: 18px;
width: 18px;
@ -844,18 +952,21 @@ export default {
align-items: center;
vertical-align: middle;
text-align: center;
.top-button {
display: flex;
flex-direction: row;
height: 40px;
line-height: 40px;
margin-left: 9px;
.btn {
color: #788994;
width: 55px;
text-align: center;
display: block;
cursor: pointer;
.el-icon-arrow-down {
transform: rotate(0deg);
-ms-transform: rotate(0deg); /* IE 9 */
@ -864,8 +975,10 @@ export default {
-o-transform: rotate(0deg); /* Opera */
transition: all 0.4s ease-in-out;
}
&:hover {
background: rgb(25, 29, 34);
.el-icon-arrow-down {
transform: rotate(180deg);
-ms-transform: rotate(180deg); /* IE 9 */
@ -877,6 +990,7 @@ export default {
}
}
}
.workbench-container {
position: relative;
-webkit-transform-origin: 0 0;
@ -885,10 +999,12 @@ export default {
box-sizing: border-box;
margin: 0;
padding: 0;
.vueRuler {
width: 100%;
padding: 18px 0px 0px 18px;
}
.workbench {
background-color: #1e1e1e;
position: relative;
@ -901,6 +1017,7 @@ export default {
margin: 0;
padding: 0;
}
.bg-grid {
position: absolute;
top: 0;
@ -911,11 +1028,12 @@ export default {
background-image: linear-gradient(
hsla(0, 0%, 100%, 0.1) 1px,
transparent 0
),
linear-gradient(90deg, hsla(0, 0%, 100%, 0.1) 1px, transparent 0);
),
linear-gradient(90deg, hsla(0, 0%, 100%, 0.1) 1px, transparent 0);
// z-index: 2;
}
}
.bottom-text {
width: 100%;
color: #a0a0a0;
@ -930,36 +1048,43 @@ export default {
height: 100%;
}
/deep/.el-tabs--border-card {
/deep/ .el-tabs--border-card {
border: 0;
.el-tabs__header {
.el-tabs__nav {
.el-tabs__item {
background-color: #242f3b;
border: 0px;
}
.el-tabs__item.is-active {
background-color: #31455d;
}
}
}
.el-tabs__content {
background-color: #242a30;
height: calc(100vh - 39px);
overflow-x: hidden;
overflow-y: auto;
.el-tab-pane {
color: #bfcbd9;
}
&::-webkit-scrollbar {
width: 5px;
height: 14px;
}
&::-webkit-scrollbar-track,
&::-webkit-scrollbar-thumb {
border-radius: 1px;
border: 0 solid transparent;
}
&::-webkit-scrollbar-track-piece {
/*修改滚动条的背景和圆角*/
background: #29405c;
@ -969,14 +1094,17 @@ export default {
&::-webkit-scrollbar-track {
box-shadow: 1px 1px 5px rgba(116, 148, 170, 0.5) inset;
}
&::-webkit-scrollbar-thumb {
min-height: 20px;
background-clip: content-box;
box-shadow: 0 0 0 5px rgba(116, 148, 170, 0.5) inset;
}
&::-webkit-scrollbar-corner {
background: transparent;
}
/*修改垂直滚动条的样式*/
&::-webkit-scrollbar-thumb:vertical {
background-color: #00113a;
@ -991,34 +1119,41 @@ export default {
}
}
}
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
.nav {
width: 40px;
padding: 0;
list-style: none;
/* overflow: hidden; */
}
.nav {
zoom: 1;
}
.nav:before,
.nav:after {
content: "";
display: table;
}
.nav:after {
clear: both;
}
.nav li {
width: 55px;
text-align: center;
position: relative;
}
.nav li a {
float: left;
padding: 12px 30px;
@ -1026,9 +1161,11 @@ li {
font: bold 12px;
text-decoration: none;
}
.nav li:hover {
color: #788994;
}
.nav li ul {
visibility: hidden;
position: absolute;
@ -1043,14 +1180,17 @@ li {
width: 120px;
transition: all 0.2s ease-in-out;
}
.nav li:hover > ul {
opacity: 1;
visibility: visible;
margin: 0;
li:hover {
background-color: rgb(25, 29, 34);
}
}
.nav ul li {
float: left;
display: block;
@ -1058,6 +1198,7 @@ li {
width: 100%;
font-size: 12px;
}
.nav ul a {
padding: 10px;
width: 100%;
@ -1068,12 +1209,15 @@ li {
background-color: rgb(25, 29, 34);
transition: all 0.2s ease-in-out;
}
.nav ul a:hover {
border: 1px solid #3c5e88;
}
.nav ul li:first-child > a:hover:before {
border-bottom-color: #04acec;
}
.nav ul ul {
top: 0;
left: 120px;
@ -1083,6 +1227,7 @@ li {
padding: 10px;
_margin: 0;
}
.nav ul ul li {
width: 120px;
height: 120px;
@ -1090,10 +1235,12 @@ li {
display: block;
float: left;
}
/deep/.vue-ruler-h {
/deep/ .vue-ruler-h {
opacity: 0.3;
}
/deep/.vue-ruler-v {
/deep/ .vue-ruler-v {
opacity: 0.3;
}
</style>

@ -7006,6 +7006,648 @@ const widgetTools = [
],
},
},
{
code: 'widgetBarCompareChart',
type: 'chart',
label: '柱状对比图',
icon: 'iconduibitupu',
options: {
// 配置
setup: [
{
type: 'el-input-text',
label: '图层名称',
name: 'layerName',
required: false,
placeholder: '',
value: '柱状对比图',
},
{
type: 'vue-color',
label: '背景颜色',
name: 'background',
required: false,
placeholder: '',
value: ''
},
[
{
name: '柱体设置',
list: [
{
type: 'el-slider',
label: '最大宽度',
name: 'maxWidth',
required: false,
placeholder: '',
value: 15,
},
{
type: 'el-slider',
label: '圆角',
name: 'radius',
require: false,
placeholder: '',
value: 5,
},
],
},
{
name: '标题设置',
list: [
{
type: 'el-switch',
label: '标题',
name: 'isNoTitle',
required: false,
placeholder: '',
value: true,
},
{
type: 'el-input-text',
label: '标题',
name: 'titleText',
required: false,
placeholder: '',
value: '',
},
{
type: 'vue-color',
label: '字体颜色',
name: 'textColor',
required: false,
placeholder: '',
value: '#FFD700'
},
{
type: 'el-select',
label: '字体粗细',
name: 'textFontWeight',
required: false,
placeholder: '',
selectOptions: [
{code: 'normal', name: '正常'},
{code: 'bold', name: '粗体'},
{code: 'bolder', name: '特粗体'},
{code: 'lighter', name: '细体'}
],
value: 'normal'
},
{
type: 'el-input-number',
label: '字体大小',
name: 'textFontSize',
required: false,
placeholder: '',
value: 20
},
{
type: 'el-select',
label: '字体位置',
name: 'textAlign',
required: false,
placeholder: '',
selectOptions: [
{code: 'center', name: '居中'},
{code: 'left', name: '左对齐'},
{code: 'right', name: '右对齐'},
],
value: 'center'
},
],
},
{
name: '左X轴设置',
list: [
{
type: 'el-switch',
label: '显示',
name: 'hideXLeft',
required: false,
placeholder: '',
value: true,
},
{
type: 'el-input-number',
label: '数值间隔',
name: 'splitNumberLeft',
required: false,
placeholder: '',
value: ''
},
{
type: 'vue-color',
label: '数值颜色',
name: 'XcolorLeft',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '数值字号',
name: 'fontSizeXLeft',
required: false,
placeholder: '',
value: 14,
},
{
type: 'el-switch',
label: '刻度线',
name: 'tickLineLeft',
require: false,
placeholder: '',
value: false,
},
{
type: 'el-switch',
label: 'X轴线',
name: 'xLineLeft',
require: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '轴颜色',
name: 'lineColorXLeft',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-switch',
label: '竖分割线',
name: 'SplitLineLeft',
require: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '分割线颜色',
name: 'SplitLineColorLeft',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '分割线宽度',
name: 'SplitLinefontSizeLeft',
required: false,
placeholder: '',
value: 1,
},
{
type: 'el-switch',
label: '边框线',
name: 'frameLineLeft',
require: false,
placeholder: '',
value: false,
},
],
},
{
name: '右X轴设置',
list: [
{
type: 'el-switch',
label: '显示',
name: 'hideXRight',
required: false,
placeholder: '',
value: true,
},
{
type: 'el-input-number',
label: '数值间隔',
name: 'splitNumberRight',
required: false,
placeholder: '',
value: ''
},
{
type: 'vue-color',
label: '数值颜色',
name: 'XcolorRight',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '数值字号',
name: 'fontSizeXRight',
required: false,
placeholder: '',
value: 14,
},
{
type: 'el-switch',
label: '刻度线',
name: 'tickLineRight',
require: false,
placeholder: '',
value: false,
},
{
type: 'el-switch',
label: 'X轴线',
name: 'xLineRight',
require: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '轴颜色',
name: 'lineColorXRight',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-switch',
label: '竖分割线',
name: 'SplitLineRight',
require: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '分割线颜色',
name: 'SplitLineColorRight',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '分割线宽度',
name: 'SplitLinefontSizeRight',
required: false,
placeholder: '',
value: 1,
},
{
type: 'el-switch',
label: '边框线',
name: 'frameLineRight',
require: false,
placeholder: '',
value: false,
},
],
},
{
name: 'Y轴设置',
list: [
{
type: 'el-switch',
label: '显示',
name: 'hideY',
required: false,
placeholder: '',
value: true,
},
{
type: 'vue-color',
label: '数值颜色',
name: 'colorY',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '数值字号',
name: 'fontSizeY',
required: false,
placeholder: '',
value: 14,
},
{
type: 'el-select',
label: '数值对齐',
name: 'textAlign',
required: false,
placeholder: '',
selectOptions: [
{code: 'center', name: '居中'},
{code: 'left', name: '左对齐'},
{code: 'right', name: '右对齐'},
],
value: 'center'
},
{
type: 'el-switch',
label: '刻度线',
name: 'tickLineY',
require: false,
placeholder: '',
value: false,
},
{
type: 'el-switch',
label: 'Y轴线',
name: 'lineY',
require: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '轴颜色',
name: 'lineColorY',
required: false,
placeholder: '',
value: '#fff',
},
],
},
{
name: '数值设定',
list: [
{
type: 'el-switch',
label: '显示',
name: 'isShow',
required: false,
placeholder: '',
value: true
},
{
type: 'el-input-number',
label: '字体大小',
name: 'fontSize',
required: false,
placeholder: '',
value: 14
},
{
type: 'vue-color',
label: '字体颜色',
name: 'subTextColor',
required: false,
placeholder: '',
value: '#fff'
},
{
type: 'el-select',
label: '字体粗细',
name: 'fontWeight',
required: false,
placeholder: '',
selectOptions: [
{code: 'normal', name: '正常'},
{code: 'bold', name: '粗体'},
{code: 'bolder', name: '特粗体'},
{code: 'lighter', name: '细体'}
],
value: 'normal'
},
],
},
{
name: '提示语设置',
list: [
{
type: 'el-input-number',
label: '字体大小',
name: 'tipsFontSize',
required: false,
placeholder: '',
value: 16
},
{
type: 'vue-color',
label: '字体颜色',
name: 'lineColor',
required: false,
placeholder: '',
},
],
},
{
name: '坐标轴边距设置',
list: [
{
type: 'el-slider',
label: '左右边距(像素)',
name: 'marginLeftRight',
required: false,
placeholder: '',
value: 10,
},
{
type: 'el-slider',
label: '顶边距(像素)',
name: 'marginTop',
required: false,
placeholder: '',
value: 40,
},
{
type: 'el-slider',
label: '底边距(像素)',
name: 'marginBottom',
required: false,
placeholder: '',
value: 10,
},
],
},
{
name: '图例操作',
list: [
{
type: 'el-switch',
label: '显示',
name: 'isShowLegend',
required: false,
placeholder: '',
value: true,
},
{
type: 'vue-color',
label: '字体颜色',
name: 'lengedColor',
required: false,
placeholder: '',
value: '#fff',
},
{
type: 'el-input-number',
label: '字体大小',
name: 'lengedFontSize',
required: false,
placeholder: '',
value: 16,
},
{
type: 'el-input-number',
label: '图例宽度',
name: 'lengedWidth',
required: false,
placeholder: '',
value: 15,
},
{
type: 'el-select',
label: '横向位置',
name: 'lateralPosition',
required: false,
placeholder: '',
selectOptions: [
{code: 'center', name: '居中'},
{code: 'left', name: '左对齐'},
{code: 'right', name: '右对齐'},
],
value: 'center'
},
{
type: 'el-select',
label: '纵向位置',
name: 'longitudinalPosition',
required: false,
placeholder: '',
selectOptions: [
{code: 'top', name: '顶部'},
{code: 'bottom', name: '底部'},
],
value: 'top'
},
{
type: 'el-select',
label: '布局前置',
name: 'layoutFront',
required: false,
placeholder: '',
selectOptions: [
{code: 'vertical', name: '竖排'},
{code: 'horizontal', name: '横排'},
],
value: 'horizontal'
},
],
},
{
name: '自定义配色',
list: [
{
type: 'customColor',
label: '',
name: 'customColor',
required: false,
value: [{color: '#36c5e7'}, {color: '#e68b55'}],
},
],
},
],
],
// 数据
data: [
{
type: 'el-radio-group',
label: '数据类型',
name: 'dataType',
require: false,
placeholder: '',
selectValue: true,
selectOptions: [
{
code: 'staticData',
name: '静态数据',
},
{
code: 'dynamicData',
name: '动态数据',
},
],
value: 'staticData',
},
{
type: 'el-input-number',
label: '刷新时间(毫秒)',
name: 'refreshTime',
relactiveDom: 'dataType',
relactiveDomValue: 'dynamicData',
value: 5000
},
{
type: 'el-button',
label: '静态数据',
name: 'staticData',
required: false,
placeholder: 'px',
relactiveDom: 'dataType',
relactiveDomValue: 'staticData',
value: [
{"axis":"07-25","name":"success","data":"2"},
{"axis":"07-25","name":"fail","data":"10"},
{"axis":"07-26","name":"success","data":"5"},
{"axis":"07-26","name":"fail","data":"20"},
{"axis":"07-27","name":"success","data":"15"},
{"axis":"07-27","name":"fail","data":"30"},
{"axis":"07-28","name":"success","data":"10"},
{"axis":"07-28","name":"fail","data":"12"},
{"axis":"07-29","name":"success","data":"9"},
{"axis":"07-29","name":"fail","data":"16"},
],
},
{
type: 'dycustComponents',
label: '',
name: 'dynamicData',
required: false,
placeholder: 'px',
relactiveDom: 'dataType',
relactiveDomValue: 'dynamicData',
chartType: 'widget-stackchart',
dictKey: 'STACK_PROPERTIES',
value: '',
},
],
// 坐标
position: [
{
type: 'el-input-number',
label: '左边距',
name: 'left',
required: false,
placeholder: 'px',
value: 0,
},
{
type: 'el-input-number',
label: '上边距',
name: 'top',
required: false,
placeholder: 'px',
value: 0,
},
{
type: 'el-input-number',
label: '宽度',
name: 'width',
required: false,
placeholder: '该容器在1920px大屏中的宽度',
value: 400,
},
{
type: 'el-input-number',
label: '高度',
name: 'height',
required: false,
placeholder: '该容器在1080px大屏中的高度',
value: 200,
},
],
},
},
]
const getToolByCode = function (code) {

@ -0,0 +1,620 @@
<template>
<div :style="styleObj">
<v-chart :options="options" autoresize/>
</div>
</template>
<script>
export default {
name: "WidgetBarCompareChart",
// https://www.makeapie.com/editor.html?c=xrJwcCF3NZ
components: {},
props: {
value: Object,
ispreview: Boolean
},
data() {
return {
options: {
title: {
//text: '',
x: 'center',
textStyle: {
color: '#ffffff',
},
},
//
grid: [
{//
show: false,//线
left: '4%',
top: 60,
bottom: 10,
containLabel: true,
width: '40%'
},
{//
show: false,
left: '50.5%',
top: 60,
bottom: 25,
width: '0%'
},
{//
show: false,
right: '4%',
top: 60,
bottom: 10,
containLabel: true,
width: '40%'
},
],
//
legend: {
textStyle: {
color: '#fff',
textAlign: 'center'
},
//itemGap:80,
//itemWidth: 0
},
xAxis: [
{//
splitNumber: 2,
type: 'value',
inverse: true,
axisLine: {//线
show: false,
},
axisTick: {
show: false,
},
position: 'bottom',
axisLabel: { // x
show: true,
textStyle: {
color: '#ffffff',
fontSize: 12
}
},
splitLine: { // 线
show: true,
lineStyle: {
color: '#57617f',
width: 1,
type: 'solid'
}
}
},
{
gridIndex: 1,
show: false,
},
{//
gridIndex: 2,
type: 'value',
axisLine: {
show: false,
},
axisTick: {
show: false,
},
position: 'bottom',
axisLabel: {
show: true,
textStyle: {
color: '#ffffff',
fontSize: 12,
},
},
splitLine: {
show: true,
lineStyle: {
color: '#57617f',
width: 1,
type: 'solid',
},
},
},
],
yAxis: [
{
type: 'category',
inverse: true,
position: 'right',
axisLine: {
show: false,
},
axisTick: {
show: false
},
axisLabel: {
show: false,
},
data: [],
},
{//
gridIndex: 1,
type: 'category',
inverse: true,
position: 'left',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: true,
textStyle: {
align: 'center',
color: '#ffffff',
fontSize: 14,
}
},
data: [],
},
{
gridIndex: 2,
type: 'category',
inverse: true,
position: 'left',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false,
},
},
],
series: [
{
name: '',
type: 'bar',
barGap: 20,
barWidth: 15,
label: {
normal: {
show: true,
color: 'red',
position: 'insideLeft',
textStyle: {
color: '#ffffff',
}
},
emphasis: {
show: false,
},
},
itemStyle: {
normal: {
color: '#36c5e7',
barBorderRadius: [8, 0, 0, 8],
},
emphasis: {
show: false,
},
},
data: [],
},
{
name: '',
type: 'bar',
barGap: 20,
barWidth: 15,
xAxisIndex: 2,
yAxisIndex: 2,
label: {
normal: {
show: true,
color: 'red',
position: 'insideRight',
textStyle: {
color: '#ffffff',
}
},
},
itemStyle: {
normal: {
color: '#e68b55',
barBorderRadius: [0, 8, 8, 0],
},
emphasis: {
show: false,
},
},
data: [],
},
]
},
optionsStyle: {},
optionsData: {},
optionsSetup: {},
flagInter: null
};
},
computed: {
styleObj() {
return {
position: this.ispreview ? "absolute" : "static",
width: this.optionsStyle.width + "px",
height: this.optionsStyle.height + "px",
left: this.optionsStyle.left + "px",
top: this.optionsStyle.top + "px",
background: this.optionsSetup.background
};
}
},
watch: {
value: {
handler(val) {
this.optionsStyle = val.position;
this.optionsData = val.data;
this.optionsSetup = val.setup;
this.optionsSetup = val.setup;
this.editorOptions();
},
deep: true
}
},
mounted() {
this.optionsStyle = this.value.position;
this.optionsData = this.value.data;
this.optionsCollapse = this.value.setup;
this.optionsSetup = this.value.setup;
this.editorOptions();
},
methods: {
// options
editorOptions() {
this.setOptionsTitle();
this.setOptionsXLeft();
this.setOptionsXRight();
this.setOptionsY();
this.setOptionsTop();
this.setOptionsTooltip();
this.setOptionsGrid();
this.setOptionsLegend();
this.setOptionsColor();
this.setOptionsData();
},
//
setOptionsTitle() {
const optionsCollapse = this.optionsSetup;
const title = {};
title.text = optionsCollapse.titleText;
title.show = optionsCollapse.isNoTitle;
title.left = optionsCollapse.textAlign;
title.textStyle = {
color: optionsCollapse.textColor,
fontSize: optionsCollapse.textFontSize,
fontWeight: optionsCollapse.textFontWeight
};
this.options.title = title;
},
// X
setOptionsXLeft() {
const optionsSetup = this.optionsSetup;
const xAxisLeft = {
splitNumber: optionsSetup.splitNumberLeft,
type: 'value',
inverse: true,
axisLine: {//X线
show: optionsSetup.xLineLeft,
lineStyle: {
color: optionsSetup.lineColorXLeft,
},
},
axisTick: {
show: optionsSetup.tickLineLeft,
},
position: 'bottom',
axisLabel: { // x
show: optionsSetup.hideXLeft,
textStyle: {
color: optionsSetup.XcolorLeft,
fontSize: optionsSetup.fontSizeXLeft
}
},
splitLine: { // 线
show: optionsSetup.SplitLineLeft,
lineStyle: {
color: optionsSetup.SplitLineColorLeft,
width: optionsSetup.SplitLinefontSizeLeft,
type: 'solid'
}
}
}
this.options.xAxis[0] = xAxisLeft;
},
// X
setOptionsXRight() {
const optionsSetup = this.optionsSetup;
const xAxisRight = {
gridIndex: 2,
splitNumber: optionsSetup.splitNumberRight,
type: 'value',
axisLine: {//X线
show: optionsSetup.xLineRight,
lineStyle: {
color: optionsSetup.lineColorXRight,
},
},
axisTick: {
show: optionsSetup.tickLineRight,
},
position: 'bottom',
axisLabel: { // x
show: optionsSetup.hideXRight,
textStyle: {
color: optionsSetup.XcolorRight,
fontSize: optionsSetup.fontSizeXRight
}
},
splitLine: { // 线
show: optionsSetup.SplitLineRight,
lineStyle: {
color: optionsSetup.SplitLineColorRight,
width: optionsSetup.SplitLinefontSizeRight,
type: 'solid'
}
}
}
this.options.xAxis[2] = xAxisRight;
},
// Y
setOptionsY() {
const optionsSetup = this.optionsSetup;
const axisLine = {
show: optionsSetup.lineY,
lineStyle: {
color: optionsSetup.lineColorY
}
};
const axisTick = {
show: optionsSetup.tickLineY
};
const axisLabel = {
show: optionsSetup.hideY,
textStyle: {
align: optionsSetup.textAlign,
color: optionsSetup.colorY,
fontSize: optionsSetup.fontSizeY,
}
};
this.options.yAxis[1]['axisLine'] = axisLine;
this.options.yAxis[1]['axisTick'] = axisTick;
this.options.yAxis[1]['axisLabel'] = axisLabel;
},
//
setOptionsTop() {
const optionsSetup = this.optionsSetup;
const series = this.options.series;
for (const key in series) {
if (series[key].type == "bar") {
series[0].label = {
normal: {
show: optionsSetup.isShow,
position: 'insideLeft',
textStyle: {
fontSize: optionsSetup.fontSize,
color: optionsSetup.subTextColor,
fontWeight: optionsSetup.fontWeight
}
},
emphasis: {
show: false,
},
},
series[1].label = {
normal: {
show: optionsSetup.isShow,
color: 'red',
position: 'insideRight',
textStyle: {
fontSize: optionsSetup.fontSize,
color: optionsSetup.subTextColor,
fontWeight: optionsSetup.fontWeight
}
},
emphasis: {
show: false,
},
},
series[key].barWidth = optionsSetup.maxWidth;
}
}
this.options.series = series;
},
// tooltip
setOptionsTooltip() {
const optionsSetup = this.optionsSetup;
const tooltip = {
trigger: "item",
show: true,
textStyle: {
color: optionsSetup.lineColor,
fontSize: optionsSetup.fontSize
}
};
this.options.tooltip = tooltip;
},
//
getOptionsBottom(){
const optionsSetup = this.optionsSetup;
let bottom = optionsSetup.marginBottom;
if (optionsSetup.hideXLeft) {
bottom = optionsSetup.marginBottom + 15
}else if (optionsSetup.hideXRight){
bottom = optionsSetup.marginBottom + 15
}
return bottom
},
setOptionsGrid() {
const optionsSetup = this.optionsSetup;
const grid = [
{//
show: optionsSetup.frameLineLeft,
left: optionsSetup.marginLeftRight,
top: optionsSetup.marginTop,
bottom: optionsSetup.marginBottom,
containLabel: true,
width: '40%'
},
{//
show: false,
left: "51%",
top: optionsSetup.marginTop,
bottom: this.getOptionsBottom(),
width: '0%'
},
{//
show: optionsSetup.frameLineRight,
right: optionsSetup.marginLeftRight,
top: optionsSetup.marginTop,
bottom: optionsSetup.marginBottom,
containLabel: true,
width: '40%'
},
]
this.options.grid = grid;
},
//
setOptionsLegend() {
const optionsSetup = this.optionsSetup;
const legend = this.options.legend;
legend.show = optionsSetup.isShowLegend;
legend.left = optionsSetup.lateralPosition;
legend.top = optionsSetup.longitudinalPosition == "top" ? 0 : "auto";
legend.bottom =
optionsSetup.longitudinalPosition == "bottom" ? 0 : "auto";
legend.orient = optionsSetup.layoutFront;
legend.textStyle = {
color: optionsSetup.lengedColor,
fontSize: optionsSetup.lengedFontSize
};
legend.itemWidth = optionsSetup.lengedWidth;
},
//
setOptionsColor() {
const optionsSetup = this.optionsSetup;
const customColor = optionsSetup.customColor;
if (!customColor) return;
const itemStyleLeft = {
normal: {
color: customColor[0].color,
barBorderRadius: [optionsSetup.radius, 0, 0, optionsSetup.radius]
},
emphasis: {
show: false,
},
}
const itemStyleRight = {
normal: {
color: customColor[1].color,
barBorderRadius: [0, optionsSetup.radius, optionsSetup.radius, 0]
},
emphasis: {
show: false,
},
}
this.options.series[0].itemStyle = itemStyleLeft;
this.options.series[1].itemStyle = itemStyleRight;
},
//
setOptionsData() {
const optionsSetup = this.optionsSetup;
const optionsData = this.optionsData; // or
optionsData.dataType == "staticData"
? this.staticDataFn(optionsData.staticData, optionsSetup)
: this.dynamicDataFn(
optionsData.dynamicData,
optionsData.refreshTime,
optionsSetup
);
},
//
setUnique(arr) {
let newArr = [];
arr.forEach(item => {
return newArr.includes(item) ? '' : newArr.push(item);
});
return newArr;
},
//
staticDataFn(val) {
//
let xAxisList = [];
let yAxisList = [];
let arrayList = [];
for (const i in val) {
xAxisList[i] = val[i].axis
yAxisList[i] = val[i].name
}
xAxisList = this.setUnique(xAxisList)
yAxisList = this.setUnique(yAxisList)
for (const i in yAxisList) {
const data = new Array(yAxisList.length).fill(0)
for (const j in xAxisList) {
for (const k in val) {
if (val[k].name == yAxisList[i]) {
if (val[k].axis == xAxisList[j]) {
data[j] = val[k].data
}
}
}
}
arrayList.push({
name: yAxisList[i],
data: data
})
}
this.options.series[0]['name'] = arrayList[0].name
this.options.series[0]['data'] = arrayList[0].data
this.options.series[1]['name'] = arrayList[1].name
this.options.series[1]['data'] = arrayList[1].data
this.options.yAxis[1]['data'] = xAxisList
},
//
dynamicDataFn(val, refreshTime, optionsSetup) {
if (!val) return;
if (this.ispreview) {
this.getEchartData(val, optionsSetup);
this.flagInter = setInterval(() => {
this.getEchartData(val, optionsSetup);
}, refreshTime);
} else {
this.getEchartData(val, optionsSetup);
}
},
getEchartData(val, optionsSetup) {
const data = this.queryEchartsData(val);
data.then(res => {
this.renderingFn(optionsSetup, res);
});
},
renderingFn(optionsSetup, val) {
this.options.yAxis[1]['data'] = val.xAxis
if (val.series[0].type == "bar"){
this.options.series[0]['name'] = val.series[0].name
this.options.series[0]['data'] = val.series[0].data
this.options.series[1]['name'] = val.series[1].name
this.options.series[1]['data'] = val.series[1].data
}
}
}
};
</script>
<style scoped lang="scss">
.echarts {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>

@ -33,6 +33,7 @@ import widgetPiePercentageChart from "./pie/widgetPiePercentageChart";
import widgetAirBubbleMap from "./map/widgetAirBubbleMap";
import widgetBarStackChart from "./bar/widgetBarStackChart";
import widgetLineStackChart from "./line/widgetLineStackChart";
import widgetBarCompareChart from "./bar/widgetBarCompareChart";
export default {
name: "WidgetTemp",
@ -58,7 +59,8 @@ export default {
widgetPiePercentageChart,
widgetAirBubbleMap,
widgetBarStackChart,
widgetLineStackChart
widgetLineStackChart,
widgetBarCompareChart
},
model: {
prop: "value",

@ -43,6 +43,7 @@ import widgetPiePercentageChart from "./pie/widgetPiePercentageChart";
import widgetAirBubbleMap from "./map/widgetAirBubbleMap";
import widgetBarStackChart from "./bar/widgetBarStackChart";
import widgetLineStackChart from "./line/widgetLineStackChart";
import widgetBarCompareChart from "./bar/widgetBarCompareChart";
export default {
name: "Widget",
@ -68,7 +69,8 @@ export default {
widgetPiePercentageChart,
widgetAirBubbleMap,
widgetBarStackChart,
widgetLineStackChart
widgetLineStackChart,
widgetBarCompareChart
},
model: {
prop: "value",

@ -27,7 +27,11 @@
</div>
</template>
<script>
import VueSuperSlide from "vue-superslide";
export default {
components: {
VueSuperSlide
},
props: {
value: Object,
ispreview: Boolean

Binary file not shown.
Loading…
Cancel
Save