首先我这边得到的需求是系统升级的固件上传,即多个固件上传的同时携带不同的参数,并在后端绑定分组+升级顺序。
(ps:本人是个纯正的后端java开发,只是意外来做了一部分前端的内容,各位大佬要是有更好的方法,可以评论告诉我,轻喷,栓q。) 比如A固件是A厂商、A品牌、A产品型号、mesh模块,升级顺序是1,B固件是A厂商、B品牌、C产品型号、gateway模块、升级顺序2.这时候我们要把这两个固件同时上传到后端,并匹配其前端选择的数据,进行分组和存储。
前端实现
用到了element组件库里的el-tabs组件,官网实现如下图:
但是我们这里的需求仅靠这个无法实现,因为我们这里的上传需要依靠多表单的提交才能实现,所以在el-tabs的基础上需要加上一个表单的循环。
具体前端实现如下
<el-dialog :title="$t('common.addSystemUpdate')" :visible.sync="systemUpgradeDialogVisible" width="900px"
append-to-body>
<div style="margin-bottom: 20px;">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addTab(editableTabsValue)">{{
$t('common.add')
}}</el-button>
</div>
<el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab" @tab-click="clickTab">
<el-tab-pane v-for="(item, index) in editableTabs" :key="item.name" :label="item.title" :name="item.name">
<el-form ref="form" :model="form"
label-width="auto">
<el-row>
<el-col :span="12">
<el-form-item :label="$t('manufacturer.manufacturerName')" prop="manufacturerName">
<el-select v-model="item.form.manufacturerName" :placeholder="$t('brand.manufacturerHolder')"
@change=selectChanged(item.form)>
<el-option v-for="dict in manufacturerList" :key="dict.manufacturerName"
:label="dict.manufacturerName" :value="dict.manufacturerName"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('brand.brandName')" prop="brandName">
<el-select v-model="item.form.brandName" :placeholder="$t('category.brandHolder')"
@change=brandSelectChanged(item.form)>
<el-option v-for="dict in brandList" :key="dict.brandName" :label="dict.brandName"
:value="dict.brandName"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('firmware.firmwareNumberCode')" prop="firmwareNumberCode">
<el-select v-model="item.form.firmwareId" :placeholder="$t('firmware.firmwareNumberCodeHolder')"
@change=firmwareSelectChanged(item.form)>
<el-option v-for="dict in firmwareNumberList" :key="dict.id" :label="dict.firmwareNumberName"
:value="dict.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('model.module')" prop="type">
<el-select v-model="item.form.moduleId" :placeholder="$t('model.moduleHolder')"
@change=moduleSelectChanged(item.form)>
<el-option v-for="dict in moduleList" :key="dict.moduleId" :label="dict.moduleName"
:value="dict.moduleId"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('device.firmware.upgradeType')">
<el-select v-model="item.form.upgradeType" :placeholder="$t('device.firmware.upgradeTypeHolder')">
<el-option v-for="dict in dict.type.firmware_upgrade_type" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('device.firmware.upOrder')">
<el-input-number v-model="item.form.upOrder" :min="1" :max="10" controls-position="right"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('device.firmware.content')">
<el-input v-model="item.form.content" type="textarea"
:placeholder="$t('device.firmware.contentHolder')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('device.firmware.head')">
<el-transfer :titles="['Source', 'Target']" v-model="item.form.headList" :data="data"></el-transfer>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('device.firmware.uploadFirmware')">
<el-upload ref="systemUpload" name="file" class="upload-demo" drag action="#" :auto-upload="true" :multiple="false"
:http-request="systemHttpRequest" :on-change="changeUpload">
<i class="el-icon-upload"></i>
<div class="el-upload__text">{{ $t('device.firmware.uploadTip') }}</div>
<div class="el-upload__tip" slot="tip" style="color:red">{{ $t('device.firmware.uploadText') }}</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitSystemForm" v-loading.fullscreen.lock="fullscreenLoading"
:element-loading-text="$t('common.uploading')">{{ $t('tooltip.ok') }}</el-button>
<el-button @click="systemCancel">{{ $t('tooltip.cancel') }}</el-button>
</div>
</el-dialog>
data部分
data() {
return {
// 遮罩层
loading: true,
fullscreenLoading: false,
systemUpgradeDialogVisible: false,
editableTabsValue: '1',
editableTabs: [{
title: 标题,
name: '1',
form: {
//表单字段
}
}],
tabIndex: 1
};
js部分关键方法 这里我们的处理方法是把所有携带的字段属性拼接成一个json,然后作为file的key赋值,然后存进filelist传给后端。
// 新增系统升级相关方法
handleSystemAdd() {
this.editableTabs = []
this.editableTabs.push({
title: this.$t('device.firmware.addFirmware'),
name: "1",
form:{
firmwareName: undefined,
versionCode: undefined,
versionName: undefined,
status: "0",
upOrder: 1,
content: undefined,
firmwareId: undefined,
file: [],
headList:[1, 2, 3, 6, 8, 9, 10]
}
});
list(this.query).then(res => {
this.manufacturerList = res.data.list
})
this.data = []
this.dict.type.firmware_upload_title.forEach(dict => {
this.data.push({
key: parseInt(dict.value),
label: dict.label,
disabled: this.headList.indexOf(parseInt(dict.value)) >= 0
})
})
this.systemUpgradeDialogVisible = true;
this.title = this.$t('common.addSystemUpdate');
},
addTab(targetName) {
let newTabName = ++this.tabIndex + '';
this.editableTabs.push({
title: this.$t('device.firmware.addFirmware'),
name: newTabName,
form:{
firmwareName: undefined,
versionCode: undefined,
versionName: undefined,
status: "0",
upOrder: 1,
content: undefined,
firmwareId: undefined,
file: [],
headList:[1, 2, 3, 6, 8, 9, 10]
}
});
this.editableTabsValue = newTabName;
},
clickTab(tab,event) {
this.editableTabsValue = tab.name;
},
removeTab(targetName) {
let tabs = this.editableTabs;
let activeName = this.editableTabsValue;
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
let nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
activeName = nextTab.name;
}
}
});
}
this.editableTabsValue = activeName;
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
},
submitSystemForm() {
this.fullscreenLoading = true
const params = new FormData()
var upOrderArr = []
// 判断你上传固件顺序是否有重复
upOrderArr = this.editableTabs.map(tab => {
return tab.form.upOrder
})
for (var i = 0; i < upOrderArr.length; i++) {
if (upOrderArr.indexOf(upOrderArr[i]) !== upOrderArr.lastIndexOf(upOrderArr[i])) {
this.fullscreenLoading = false
this.$modal.msgError(this.$t('device.firmware.msg1'));
return
}
}
// 处理标签列表数据
this.editableTabs.forEach((tab, index) => {
let form = tab.form
var data = {}
if (form.moduleId) {
data.moduleId = form.moduleId
data.moduleName = form.moduleName
}
data.firmwareId = form.firmwareId
data.upgradeType = form.upgradeType
data.headPayloads = form.headList
data.content = form.content
data.upOrder = form.upOrder
params.append(JSON.stringify(data),tab.form.file[0].file)
})
let config = {
headers: {
"Content-Type": "multipart/form-data",
"repeatSubmit": false,
"Authorization": 'Bearer ' + getToken()
}
}
axios.post(process.env.VUE_APP_BASE_API + "你的上传接口的路径", params, config)
.then(response => {
if (response.data.code === 200) {
this.$modal.msgSuccess(response.data.msg === "" ? "操作成功" : response.data.msg);
} else {
this.$modal.msgError(response.data.msg === "" ? "操作失败" : response.data.msg);
}
//上传完成后刷新页面
this.systemUpgradeDialogVisible = false
this.getList()
this.fullscreenLoading = false
this.systemCancel()
})
},
systemCancel() {
this.tabIndex = 1
this.editableTabsValue = '1',
this.editableTabs = []
this.systemUpgradeDialogVisible = false;
this.data = []
}
后端接收处理
因为后端接收的时候我们接收的是一个List,所以这里需要做一些额外的处理。 大佬同事帮我写了一个文件注解过滤器。作用是处理前端传递过来的文件集合。主要方法如下:
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
Collection<Part> parts = servletRequest.getParts();
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
Map<String, MultipartFile> result = new HashMap<>();
for (Part part : parts) {
List<MultipartFile> files = multipartRequest.getFiles(part.getName());
result.put(part.getName(), files.get(0));
}
return result;
}else if (List.class.isAssignableFrom(parameter.getParameterType())){
List<MultipartFile> result = new ArrayList<>();
for (Part part : parts) {
List<MultipartFile> files = multipartRequest.getFiles(part.getName());
result.addAll(files);
}
return result;
}
}
return null;
}
然后就可以对接收到的文件列表进行处理了
@Override
@Transactional
public AjaxResult systemAdd(List<MultipartFile> files) throws IOException {
for (MultipartFile file : files) {
try {
String data = URLDecoder.decode(file.getName(),"utf-8");
// 取出来的data就是你前端传递的form表单的数据,然后可以进行具体的业务逻辑了
// ps:这里的数据需要进行编码,不然就会出现乱码
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
......
return AjaxResult.success();
}