excel导入导出国际化

This commit is contained in:
chenjianxing 2020-05-22 16:08:22 +08:00
parent f415a121a5
commit 6977ee496b
11 changed files with 121 additions and 42 deletions

View File

@ -2,14 +2,18 @@ package io.metersphere.config;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validator; import javax.validation.Validator;
@Configuration @Configuration
public class I18nConfig { public class I18nConfig {
@ -33,7 +37,14 @@ public class I18nConfig {
@Bean @Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) { public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setProviderClass(HibernateValidator.class);
localValidatorFactoryBean.setValidationMessageSource(messageSource); localValidatorFactoryBean.setValidationMessageSource(messageSource);
return localValidatorFactoryBean; return localValidatorFactoryBean;
} }
@Bean
public Validator validator(LocalValidatorFactoryBean localValidatorFactoryBean){
return localValidatorFactoryBean.getValidator();
}
} }

View File

@ -12,33 +12,33 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15) @ColumnWidth(15)
public class TestCaseExcelData { public class TestCaseExcelData {
@NotBlank @NotBlank(message = "{cannot_be_null}")
@Length(max=50) @Length(max=50)
@ExcelProperty("{test_case_name}") @ExcelProperty("{test_case_name}")
private String name; private String name;
@NotBlank @NotBlank(message = "{cannot_be_null}")
@Length(max=1000) @Length(max=1000)
@ExcelProperty("{test_case_module}") @ExcelProperty("{test_case_module}")
@ColumnWidth(30) @ColumnWidth(30)
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}") @Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
private String nodePath; private String nodePath;
@NotBlank @NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_type}") @ExcelProperty("{test_case_type}")
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}") @Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
private String type; private String type;
@NotBlank @NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_maintainer}") @ExcelProperty("{test_case_maintainer}")
private String maintainer; private String maintainer;
@NotBlank @NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_priority}") @ExcelProperty("{test_case_priority}")
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}") @Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
private String priority; private String priority;
@NotBlank @NotBlank(message = "{cannot_be_null}")
@ExcelProperty("{test_case_method}") @ExcelProperty("{test_case_method}")
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}") @Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
private String method; private String method;

View File

@ -61,7 +61,7 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
if (!StringUtils.isEmpty(errMsg)) { if (!StringUtils.isEmpty(errMsg)) {
ExcelErrData excelErrData = new ExcelErrData(t, rowIndex, ExcelErrData excelErrData = new ExcelErrData(t, rowIndex,
Translator.get("number") + rowIndex + Translator.get("row") + Translator.get("error") Translator.get("number")+ " " + rowIndex + " " + Translator.get("row") + Translator.get("error")
+ "" + errMsg); + "" + errMsg);
errList.add(excelErrData); errList.add(excelErrData);
} else { } else {

View File

@ -7,6 +7,7 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.groups.Default; import javax.validation.groups.Default;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Set; import java.util.Set;
@ -17,11 +18,11 @@ public class ExcelValidateHelper {
private static ExcelValidateHelper excelValidateHelper; private static ExcelValidateHelper excelValidateHelper;
@Resource @Resource
LocalValidatorFactoryBean localValidatorFactoryBean; Validator validator;
public static <T> String validateEntity(T obj) throws NoSuchFieldException { public static <T> String validateEntity(T obj) throws NoSuchFieldException {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
Set<ConstraintViolation<T>> set = excelValidateHelper.localValidatorFactoryBean.getValidator().validate(obj, Default.class); Set<ConstraintViolation<T>> set = excelValidateHelper.validator.validate(obj, Default.class);
if (set != null && !set.isEmpty()) { if (set != null && !set.isEmpty()) {
for (ConstraintViolation<T> cv : set) { for (ConstraintViolation<T> cv : set) {
Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString()); Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());
@ -33,9 +34,12 @@ public class ExcelValidateHelper {
return result.toString(); return result.toString();
} }
/**
* 在静态方法中调用
*/
@PostConstruct @PostConstruct
public void initialize() { public void initialize() {
excelValidateHelper = this; excelValidateHelper = this;
excelValidateHelper.localValidatorFactoryBean = this.localValidatorFactoryBean; excelValidateHelper.validator = this.validator;
} }
} }

View File

@ -9,6 +9,7 @@ import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.excel.domain.ExcelErrData; import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelResponse; import io.metersphere.excel.domain.ExcelResponse;
@ -190,6 +191,7 @@ public class TestCaseService {
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead(); EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList(); errList = easyExcelListener.getErrList();
} catch (Exception e) { } catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(e.getMessage()); MSException.throwException(e.getMessage());
} finally { } finally {
easyExcelListener.close(); easyExcelListener.close();

View File

@ -1,6 +1,10 @@
#commons #commons
error_lang_invalid=Invalid language parameter error_lang_invalid=Invalid language parameter
file_cannot_be_null=File cannot be empty! file_cannot_be_null=File cannot be empty!
cannot_be_null=\tCannot be empty
number=Number
row=row
error=error
#user related #user related
user_email_already_exists=User email already exists user_email_already_exists=User email already exists
user_name_is_null=User name cannot be null user_name_is_null=User name cannot be null
@ -43,23 +47,20 @@ test_case_node_level=level
test_case_node_level_tip=The node tree maximum depth is test_case_node_level_tip=The node tree maximum depth is
test_case_module_not_null=The owned module cannot be empty test_case_module_not_null=The owned module cannot be empty
test_case_create_module_fail=Failed to create module test_case_create_module_fail=Failed to create module
test_case_import_template_name=Test case templates test_case_import_template_name=Test_case_templates
test_case_import_template_sheet=Template test_case_import_template_sheet=Template
module_not_null=The module must not be blank module_not_null=The module must not be blank
user_not_exists=The user in this workspace is not exists user_not_exists=The user in this workspace is not exists
test_case_already_exists=The test case in this project is exists test_case_already_exists=The test case in this project is exists
parse_data_error=Parse data error parse_data_error=Parse data error
missing_header_information=Missing header information missing_header_information=Missing header information
number=Number
row=row
error=error
test_case_exist=A test case already exists under this project: test_case_exist=A test case already exists under this project:
node_deep_limit=The node depth does not exceed 5 layers! node_deep_limit=The node depth does not exceed 5 layers!
before_delete_plan=There is an associated test case under this plan, please unlink it first! before_delete_plan=There is an associated test case under this plan, please unlink it first!
incorrect_format=Incorrect format incorrect_format=\tincorrect format
test_case_type_validate=must be functional, performance, api test_case_type_validate=\tmust be functional, performance, api
test_case_priority_validate=must be P0, P1, P2, P3 test_case_priority_validate=\tmust be P0, P1, P2, P3
test_case_method_validate=must be manual, auto test_case_method_validate=\tmust be manual, auto
test_case_name=Name test_case_name=Name
test_case_type=Type test_case_type=Type
test_case_maintainer=Maintainer test_case_maintainer=Maintainer

View File

@ -1,6 +1,10 @@
#commons #commons
error_lang_invalid=语言参数错误 error_lang_invalid=语言参数错误
file_cannot_be_null=文件不能为空! file_cannot_be_null=文件不能为空!
cannot_be_null=不能为空
number=
row=
error=出错
#user related #user related
user_email_already_exists=用户邮箱已存在 user_email_already_exists=用户邮箱已存在
user_name_is_null=用户名不能为空 user_name_is_null=用户名不能为空
@ -50,9 +54,6 @@ user_not_exists=该工作空间下无该用户
test_case_already_exists=该项目下已存在该测试用例 test_case_already_exists=该项目下已存在该测试用例
parse_data_error=解析数据出错 parse_data_error=解析数据出错
missing_header_information=缺少头部信息 missing_header_information=缺少头部信息
number=
row=
error=出错
test_case_exist=该项目下已存在用例: test_case_exist=该项目下已存在用例:
node_deep_limit=节点深度不超过5层 node_deep_limit=节点深度不超过5层
before_delete_plan=该计划下存在关联测试用例,请先取消关联! before_delete_plan=该计划下存在关联测试用例,请先取消关联!

View File

@ -1,6 +1,10 @@
#commons #commons
error_lang_invalid=語言參數錯誤 error_lang_invalid=語言參數錯誤
file_cannot_be_null=文件不能為空! file_cannot_be_null=文件不能為空!
cannot_be_null=不能為空
number=
row=
error=出錯
#user related #user related
user_email_already_exists=用戶郵箱已存在 user_email_already_exists=用戶郵箱已存在
user_name_is_null=用戶名不能為空 user_name_is_null=用戶名不能為空
@ -50,9 +54,6 @@ user_not_exists=該工作空間下無該用戶
test_case_already_exists=該項目下已存在該測試用例 test_case_already_exists=該項目下已存在該測試用例
parse_data_error=解析數據出錯 parse_data_error=解析數據出錯
missing_header_information=缺少頭部信息 missing_header_information=缺少頭部信息
number=
row=
error=出錯
test_case_exist=該項目下已存在用例: test_case_exist=該項目下已存在用例:
node_deep_limit=節點深度不超過5層 node_deep_limit=節點深度不超過5層
before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯! before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯!

View File

@ -135,9 +135,9 @@
}); });
}, },
saveAndRun() { saveAndRun() {
if (!this.validTestPlan()) { // if (!this.validTestPlan()) {
return; // return;
} // }
let options = this.getSaveOption(); let options = this.getSaveOption();

View File

@ -4,21 +4,23 @@
<el-row> <el-row>
<el-link type="primary" class="download-template" <el-link type="primary" class="download-template"
href="/test/case/export/template">{{$t('test_track.case.import.download_template')}}</el-link></el-row> @click="downloadTemplate"
>{{$t('test_track.case.import.download_template')}}</el-link></el-row>
<el-row> <el-row>
<el-upload <el-upload
v-loading="isLoading" v-loading="result.loading"
:element-loading-text="$t('test_track.case.import.importing')" :element-loading-text="$t('test_track.case.import.importing')"
element-loading-spinner="el-icon-loading" element-loading-spinner="el-icon-loading"
class="upload-demo" class="upload-demo"
:action="'/test/case/import/' + projectId"
multiple multiple
:limit="1" :limit="1"
action=""
:on-exceed="handleExceed" :on-exceed="handleExceed"
:beforeUpload="UploadValidate" :beforeUpload="UploadValidate"
:on-success="handleSuccess" :on-success="handleSuccess"
:on-error="handleError" :on-error="handleError"
:show-file-list="false" :show-file-list="false"
:http-request="upload"
:file-list="fileList"> :file-list="fileList">
<template v-slot:trigger> <template v-slot:trigger>
<el-button size="mini" type="success" plain>{{$t('test_track.case.import.click_upload')}}</el-button> <el-button size="mini" type="success" plain>{{$t('test_track.case.import.click_upload')}}</el-button>
@ -49,6 +51,7 @@
components: {ElUploadList, MsTableButton}, components: {ElUploadList, MsTableButton},
data() { data() {
return { return {
result: {},
dialogVisible: false, dialogVisible: false,
fileList: [], fileList: [],
errList: [], errList: [],
@ -80,16 +83,16 @@
return true; return true;
}, },
handleSuccess(response) { handleSuccess(response) {
this.isLoading = false;
let res = response.data; // let res = response.data;
if (res.success) { // if (res.success) {
this.$success(this.$t('test_track.case.import.success')); // this.$success(this.$t('test_track.case.import.success'));
this.dialogVisible = false; // this.dialogVisible = false;
this.$emit("refresh"); // this.$emit("refresh");
} else { // } else {
this.errList = res.errList; // this.errList = res.errList;
} // }
this.fileList = []; // this.fileList = [];
}, },
handleError(err, file, fileList) { handleError(err, file, fileList) {
this.isLoading = false; this.isLoading = false;
@ -102,6 +105,28 @@
open() { open() {
this.dialogVisible = true; this.dialogVisible = true;
}, },
downloadTemplate() {
// this.$get('/test/case/export/template');
// fileDownload('/test/case/export/template', {});
this.$fileDownload('/test/case/export/template');
},
upload(file) {
this.isLoading = false;
this.fileList.push(file.file);
this.result = this.$fileUpload('/test/case/import/' + this.projectId, this.fileList,response => {
let res = response.data;
if (res.success) {
this.$success(this.$t('test_track.case.import.success'));
this.dialogVisible = false;
this.$emit("refresh");
} else {
this.errList = res.errList;
}
this.fileList = [];
}, erro => {
this.fileList = [];
});
}
} }
} }
</script> </script>

View File

@ -35,8 +35,7 @@ export default {
function then(success, response, result) { function then(success, response, result) {
if (!response.data) { if (!response.data) {
success(response); success(response);
} } else if (response.data.success) {
if (response.data.success) {
success(response.data); success(response.data);
} else { } else {
window.console.warn(response.data); window.console.warn(response.data);
@ -108,5 +107,40 @@ export default {
if (array.length < 1) return; if (array.length < 1) return;
axios.all(array).then(axios.spread(callback)); axios.all(array).then(axios.spread(callback));
}; };
Vue.prototype.$fileDownload = function(url) {
axios({
method: 'get',
url: url,
responseType: 'blob',
}).then(response => {
let fileName = window.decodeURI(response.headers['content-disposition'].split('=')[1]);
let link = document.createElement("a");
link.href = window.URL.createObjectURL(new Blob([response.data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"}));
link.download = fileName;
link.click();
})
};
Vue.prototype.$fileUpload = function(url, fileList, success, failure) {
let result = {loading: true};
let formData = new FormData();
if (fileList.length > 0) {
fileList.forEach(f => {
formData.append("file", f);
});
}
axios.post(url, formData, { headers: { "Content-Type": "multipart/form-data" }})
.then(response => {
then(success, response, result);
}).catch(error => {
exception(error, result);
if (failure) {
then(failure, error, result);
}
});
return result;
}
} }
} }