Merge branch 'dev' of github.com:dataease/dataease into dev

This commit is contained in:
taojinlong 2022-04-25 13:16:43 +08:00
commit 8d4ff99917
19 changed files with 293 additions and 30 deletions

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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";
}

View File

@ -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);
}
}

View File

@ -503,6 +503,9 @@ public class ChartViewService {
// 如果是插件视图 走插件内部的逻辑
if (ObjectUtils.isNotEmpty(view.getIsPlugin()) && view.getIsPlugin()) {
Map<String, List<ChartViewFieldDTO>> fieldMap = new HashMap<>();
List<ChartViewFieldDTO> xAxisExt = new Gson().fromJson(view.getXAxisExt(), new TypeToken<List<ChartViewFieldDTO>>() {
}.getType());
fieldMap.put("xAxisExt",xAxisExt);
fieldMap.put("xAxis", xAxis);
fieldMap.put("yAxis", yAxis);
fieldMap.put("extStack", extStack);

View File

@ -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);
}
}
}

View File

@ -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);
// 需要删除文件

View File

@ -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)
})
}

View File

@ -12,7 +12,7 @@
<el-dropdown-item icon="el-icon-arrow-up" @click.native="upComponent">{{ $t('panel.upComponent') }}</el-dropdown-item>
<el-dropdown-item icon="el-icon-arrow-down" @click.native="downComponent">{{ $t('panel.downComponent') }}</el-dropdown-item>
<el-dropdown-item v-if="'view'===curComponent.type" icon="el-icon-link" @click.native="linkageSetting">{{ $t('panel.linkage_setting') }}</el-dropdown-item>
<el-dropdown-item v-if="'de-tabs'===curComponent.type" icon="el-icon-link" @click.native="addTab">{{ $t('panel.add_tab') }}</el-dropdown-item>
<el-dropdown-item v-if="'de-tabs'===curComponent.type" icon="el-icon-plus" @click.native="addTab">{{ $t('panel.add_tab') }}</el-dropdown-item>
<el-dropdown-item v-if="'view'===curComponent.type" icon="el-icon-connection" @click.native="linkJumpSet">{{ $t('panel.setting_jump') }}</el-dropdown-item>
<el-dropdown-item icon="el-icon-magic-stick" @click.native="boardSet">{{ $t('panel.component_style') }}</el-dropdown-item>
<el-dropdown-item @click.native="hyperlinksSet">

View File

@ -21,7 +21,14 @@
:ref="element.propValue.id"
:component-name="chart.type + '-view'"
:obj="{chart, trackMenu, searchCount, terminalType: scaleCoefficientType}"
:chart="chart"
:track-menu="trackMenu"
:search-count="searchCount"
:terminal-type="scaleCoefficientType"
:scale="scale"
class="chart-class"
@onChartClick="chartClick"
@onJumpClick="jumpClick"
/>
<chart-component
v-else-if="charViewShowFlag"

View File

@ -7,6 +7,7 @@
v-if="chart.isPlugin"
:component-name="chart.type + '-view'"
:obj="{chart: mapChart || chart}"
:chart="mapChart || chart"
class="chart-class"
/>
<chart-component v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'echarts'" class="chart-class" :chart="mapChart || chart" />

View File

@ -5,6 +5,7 @@
v-if="chart.isPlugin"
:component-name="chart.type + '-view'"
:obj="{chart: mapChart || chart}"
:chart="mapChart || chart"
class="chart-class"
/>
<chart-component v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'echarts'" class="chart-class" :chart="mapChart || chart" />

View File

@ -109,7 +109,9 @@ const data = {
// 仪表板视图明细
panelViewDetailsInfo: {},
// 当前tab页内组件
curActiveTabInner: null
curActiveTabInner: null,
// static resource local path
staticResourcePath: '/static-resource/'
},
mutations: {
...animation.mutations,

View File

@ -64,7 +64,6 @@
:on-remove="handleRemove"
:http-request="upload"
:file-list="fileList"
:on-change="onChange"
>
<i class="el-icon-plus" />
</el-upload>
@ -108,9 +107,9 @@
import { queryBackground } from '@/api/background/background'
import BackgroundItem from '@/views/background/BackgroundItem'
import { mapState } from 'vuex'
import eventBus from '@/components/canvas/utils/eventBus'
import { deepCopy } from '@/components/canvas/utils/utils'
import { COLOR_PANEL } from '@/views/chart/chart/chart'
import { uploadFileResult } from '@/api/staticResource/staticResource'
export default {
name: 'Background',
@ -179,7 +178,7 @@ export default {
},
handleRemove(file, fileList) {
this.uploadDisabled = false
this.panel.imageUrl = null
this.curComponent.commonBackground.outerImage = null
this.fileList = []
this.commitStyle()
},
@ -187,16 +186,11 @@ export default {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
onChange(file, fileList) {
var _this = this
_this.uploadDisabled = true
const reader = new FileReader()
reader.onload = function() {
_this.curComponent.commonBackground.outerImage = reader.result
}
reader.readAsDataURL(file.raw)
},
upload(file) {
const _this = this
uploadFileResult(file, (fileUrl) => {
_this.curComponent.commonBackground.outerImage = fileUrl
})
// console.log('this is upload')
}

View File

@ -962,7 +962,10 @@
ref="dynamicChart"
:component-name="chart.type + '-view'"
:obj="{chart}"
:chart-id="chart.id"
:chart="chart"
class="chart-class"
@onChartClick="chartClick"
/>
<chart-component
v-else-if="httpRequest.status && chart.type && !chart.type.includes('table') && !chart.type.includes('text') && chart.type !== 'label' && renderComponent() === 'echarts'"

View File

@ -25,12 +25,11 @@
accept=".jpeg,.jpg,.png,.gif"
class="avatar-uploader"
list-type="picture-card"
:http-request="upload"
:class="{disabled:uploadDisabled}"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:http-request="upload"
:file-list="fileList"
:on-change="onChange"
>
<i class="el-icon-plus" />
</el-upload>
@ -51,6 +50,7 @@
import { mapState } from 'vuex'
import { deepCopy } from '@/components/canvas/utils/utils'
import { COLOR_PANEL } from '@/views/chart/chart/chart'
import { uploadFileResult } from '@/api/staticResource/staticResource'
export default {
name: 'BackgroundSelector',
@ -98,19 +98,13 @@ export default {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
onChange(file, fileList) {
var _this = this
_this.uploadDisabled = true
const reader = new FileReader()
reader.onload = function() {
_this.panel.imageUrl = reader.result
this.commitStyle()
}
this.$store.state.styleChangeTimes++
reader.readAsDataURL(file.raw)
},
upload(file) {
// console.log('this is upload')
const _this = this
uploadFileResult(file, (fileUrl) => {
_this.$store.state.styleChangeTimes++
_this.panel.imageUrl = fileUrl
_this.commitStyle()
})
}
}
}

View File

@ -5,6 +5,8 @@
:ref="refId"
:url="url"
:obj="obj"
v-bind="$attrs"
v-on="$listeners"
@execute-axios="executeAxios"
@on-add-languanges="addLanguages"
@plugin-call-back="pluginCallBack"
@ -26,6 +28,7 @@ export default {
components: {
AsyncComponent
},
inheritAttrs: true,
props: {
componentName: {
type: String,