diff --git a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java index 1aae87f3de..3466cadd6e 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java @@ -48,6 +48,7 @@ public class ShiroServiceImpl implements ShiroService { filterChainDefinitionMap.put("/plugin/theme/themes", ANON); filterChainDefinitionMap.put("/plugin/theme/items/**", ANON); filterChainDefinitionMap.put("/plugin/view/types", ANON); + filterChainDefinitionMap.put("/static-resource/**", ANON); // 验证链接 filterChainDefinitionMap.put("/api/link/validate**", ANON); diff --git a/backend/src/main/java/io/dataease/commons/utils/FileUtils.java b/backend/src/main/java/io/dataease/commons/utils/FileUtils.java new file mode 100644 index 0000000000..64760511b0 --- /dev/null +++ b/backend/src/main/java/io/dataease/commons/utils/FileUtils.java @@ -0,0 +1,26 @@ +package io.dataease.commons.utils; + +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +public class FileUtils { + + public static void createIfAbsent(@NonNull Path path) throws IOException { + Assert.notNull(path, "Path must not be null"); + + if (Files.notExists(path)) { + // Create directories + Files.createDirectories(path); + LogUtil.debug("Created directory: [{}]", path); + } + } +} diff --git a/backend/src/main/java/io/dataease/commons/utils/StaticResourceUtils.java b/backend/src/main/java/io/dataease/commons/utils/StaticResourceUtils.java new file mode 100644 index 0000000000..cd644a98a0 --- /dev/null +++ b/backend/src/main/java/io/dataease/commons/utils/StaticResourceUtils.java @@ -0,0 +1,54 @@ +package io.dataease.commons.utils; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +public class StaticResourceUtils { + public static final String URL_SEPARATOR = "/"; + + private static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + public static String ensureBoth(@NonNull String string, @NonNull String bothfix) { + return ensureBoth(string, bothfix, bothfix); + } + + public static String ensureBoth(@NonNull String string, @NonNull String prefix, + @NonNull String suffix) { + return ensureSuffix(ensurePrefix(string, prefix), suffix); + } + + /** + * Ensures the string contain prefix. + * + * @param string string must not be blank + * @param prefix prefix must not be blank + * @return string contain prefix specified + */ + public static String ensurePrefix(@NonNull String string, @NonNull String prefix) { + Assert.hasText(string, "String must not be blank"); + Assert.hasText(prefix, "Prefix must not be blank"); + + return prefix + StringUtils.removeStart(string, prefix); + } + + + /** + * Ensures the string contain suffix. + * + * @param string string must not be blank + * @param suffix suffix must not be blank + * @return string contain suffix specified + */ + public static String ensureSuffix(@NonNull String string, @NonNull String suffix) { + Assert.hasText(string, "String must not be blank"); + Assert.hasText(suffix, "Suffix must not be blank"); + + return StringUtils.removeEnd(string, suffix) + suffix; + } +} diff --git a/backend/src/main/java/io/dataease/config/DeMvcConfig.java b/backend/src/main/java/io/dataease/config/DeMvcConfig.java new file mode 100644 index 0000000000..04f73c5217 --- /dev/null +++ b/backend/src/main/java/io/dataease/config/DeMvcConfig.java @@ -0,0 +1,52 @@ +package io.dataease.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static io.dataease.commons.utils.StaticResourceUtils.ensureBoth; +import static io.dataease.commons.utils.StaticResourceUtils.ensureSuffix; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +@Configuration +public class DeMvcConfig implements WebMvcConfigurer { + private static final String FILE_PROTOCOL = "file://"; + public static final String FILE_SEPARATOR = File.separator; + public static final String USER_HOME = "/opt/dataease/data"; + + private static String WORK_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "static-resource" + FILE_SEPARATOR; + + + /** + * Upload prefix. + */ + private final static String UPLOAD_URL_PREFIX = "static-resource"; + + + /** + * url separator. + */ + public static final String URL_SEPARATOR = "/"; + + /** + * Configuring static resource path + * + * @param registry registry + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + String workDir = FILE_PROTOCOL + ensureSuffix(WORK_DIR, FILE_SEPARATOR); + String uploadUrlPattern = ensureBoth(UPLOAD_URL_PREFIX, URL_SEPARATOR) + "**"; + registry.addResourceHandler(uploadUrlPattern) + .addResourceLocations(workDir); + + } +} diff --git a/backend/src/main/java/io/dataease/config/properties/StaticResourceProperties.java b/backend/src/main/java/io/dataease/config/properties/StaticResourceProperties.java new file mode 100644 index 0000000000..9e49adeb24 --- /dev/null +++ b/backend/src/main/java/io/dataease/config/properties/StaticResourceProperties.java @@ -0,0 +1,18 @@ +package io.dataease.config.properties; + +import lombok.Data; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +@Data +public class StaticResourceProperties { + + /** + * Upload prefix. + */ + private String uploadUrlPrefix = "static-resource"; + +} diff --git a/backend/src/main/java/io/dataease/controller/staticResource/StaticResourceController.java b/backend/src/main/java/io/dataease/controller/staticResource/StaticResourceController.java new file mode 100644 index 0000000000..8f4297289f --- /dev/null +++ b/backend/src/main/java/io/dataease/controller/staticResource/StaticResourceController.java @@ -0,0 +1,28 @@ +package io.dataease.controller.staticResource; + +import io.dataease.service.staticResource.StaticResourceService; +import io.swagger.annotations.ApiOperation; +import org.pentaho.ui.xul.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +@RestController +@RequestMapping("/static/resource") +public class StaticResourceController { + + @Resource + StaticResourceService staticResourceService; + + @PostMapping("upload/{fileId}") + @ApiOperation("Uploads static file") + public void upload(@PathVariable("fileId") String fileId, @RequestPart("file") MultipartFile file) { + staticResourceService.upload(fileId,file); + } +} diff --git a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java index f8dc176372..c3d1e31871 100644 --- a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java +++ b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java @@ -503,6 +503,9 @@ public class ChartViewService { // 如果是插件视图 走插件内部的逻辑 if (ObjectUtils.isNotEmpty(view.getIsPlugin()) && view.getIsPlugin()) { Map> fieldMap = new HashMap<>(); + List xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken>() { + }.getType()); + fieldMap.put("xAxisExt",xAxisExt); fieldMap.put("xAxis", xAxis); fieldMap.put("yAxis", yAxis); fieldMap.put("extStack", extStack); diff --git a/backend/src/main/java/io/dataease/service/staticResource/StaticResourceService.java b/backend/src/main/java/io/dataease/service/staticResource/StaticResourceService.java new file mode 100644 index 0000000000..61cc19bacd --- /dev/null +++ b/backend/src/main/java/io/dataease/service/staticResource/StaticResourceService.java @@ -0,0 +1,48 @@ +package io.dataease.service.staticResource; + +import io.dataease.commons.utils.FileUtils; +import io.dataease.commons.utils.LogUtil; +import io.dataease.exception.DataEaseException; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Author: wangjiahao + * Date: 2022/4/24 + * Description: + */ +@Service +public class StaticResourceService { + + private final Path staticDir = Paths.get("/opt/dataease/data/static-resource/"); + + public void upload(String fileId,MultipartFile file) { + // check if the path is valid (not outside staticDir) + Assert.notNull(file, "Multipart file must not be null"); + try { + String originName = file.getOriginalFilename(); + String newFileName = fileId+originName.substring(originName.lastIndexOf("."),originName.length()); + Path uploadPath = Paths.get(staticDir.toString(), newFileName); + // create dir is absent + FileUtils.createIfAbsent(Paths.get(staticDir.toString())); + Files.createFile(uploadPath); + file.transferTo(uploadPath); + } catch (IOException e) { + LogUtil.error("文件上传失败",e); + DataEaseException.throwException("文件上传失败"); + } catch (Exception e){ + DataEaseException.throwException(e); + } + } +} diff --git a/backend/src/main/java/io/dataease/service/sys/PluginService.java b/backend/src/main/java/io/dataease/service/sys/PluginService.java index e1ec18e6e2..7b19dac5a8 100644 --- a/backend/src/main/java/io/dataease/service/sys/PluginService.java +++ b/backend/src/main/java/io/dataease/service/sys/PluginService.java @@ -1,6 +1,7 @@ package io.dataease.service.sys; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ZipUtil; import com.google.gson.Gson; import io.dataease.dto.MyPluginDTO; import io.dataease.ext.ExtSysPluginMapper; @@ -72,8 +73,9 @@ public class PluginService { //2.解压目标文件dest 得到plugin.json和jar String folder = pluginDir + "folder/"; try { - ZipUtils.unzip(dest, folder); - } catch (IOException e) { + ZipUtil.unzip(dest.getAbsolutePath(), folder); + // ZipUtils.unzip(dest, folder); + } catch (Exception e) { DeFileUtils.deleteFile(pluginDir + "temp/"); DeFileUtils.deleteFile(folder); // 需要删除文件 diff --git a/frontend/src/api/staticResource/staticResource.js b/frontend/src/api/staticResource/staticResource.js new file mode 100644 index 0000000000..de424b0680 --- /dev/null +++ b/frontend/src/api/staticResource/staticResource.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' +import { uuid } from 'vue-uuid' +import store from '@/store' + +export function uploadFile(fileId, file) { + const param = new FormData() + param.append('file', file.file) + return request({ + url: '/static/resource/upload/' + fileId, + method: 'post', + headers: { 'Content-Type': 'multipart/form-data' }, + data: param, + loading: false + }) +} + +export function uploadFileResult(file, callback) { + const fileId = uuid.v1() + const fileName = file.file.name + const newFileName = fileId + fileName.substr(fileName.lastIndexOf('.'), fileName.length) + const fileUrl = store.state.staticResourcePath + newFileName + uploadFile(fileId, file).then(() => { + callback(fileUrl) + }) +} + diff --git a/frontend/src/components/canvas/components/Editor/Preview.vue b/frontend/src/components/canvas/components/Editor/Preview.vue index 5d11fc0b66..0469c8d67c 100644 --- a/frontend/src/components/canvas/components/Editor/Preview.vue +++ b/frontend/src/components/canvas/components/Editor/Preview.vue @@ -31,14 +31,14 @@ class="dialog-css" :destroy-on-close="true" > - + {{ $t('chart.export') }} Excle - {{ $t('chart.image') }} + {{ $t('chart.image') }} @@ -137,6 +137,9 @@ export default { created() { }, computed: { + showExportImgButton() { + return this.showChartInfo.type && !this.showChartInfo.type.includes('table') + }, canvasInfoMainStyle() { if (this.backScreenShot) { return { diff --git a/frontend/src/components/canvas/components/Editor/SettingMenu.vue b/frontend/src/components/canvas/components/Editor/SettingMenu.vue index 6c64133a79..c765fdcf1b 100644 --- a/frontend/src/components/canvas/components/Editor/SettingMenu.vue +++ b/frontend/src/components/canvas/components/Editor/SettingMenu.vue @@ -12,7 +12,7 @@ {{ $t('panel.upComponent') }} {{ $t('panel.downComponent') }} {{ $t('panel.linkage_setting') }} - {{ $t('panel.add_tab') }} + {{ $t('panel.add_tab') }} {{ $t('panel.setting_jump') }} {{ $t('panel.component_style') }} diff --git a/frontend/src/components/canvas/components/Editor/index.vue b/frontend/src/components/canvas/components/Editor/index.vue index 4dcc72c839..231ba944cb 100644 --- a/frontend/src/components/canvas/components/Editor/index.vue +++ b/frontend/src/components/canvas/components/Editor/index.vue @@ -153,16 +153,16 @@ :destroy-on-close="true" :show-close="true" > - + - - {{ $t('chart.export') }} - - - Excle - {{ $t('chart.image') }} - - + + {{ $t('chart.export') }} + + + Excle + {{ $t('chart.image') }} + +