vue+spring boot实现文件上传

  • Post author:
  • Post category:vue



目录


1.前端画面


2.后端


3.总结


1.前端画面

<template>
  <div class="app-container">

    <el-descriptions title="入区信息" border style="margin-bottom: 20px">
      <el-descriptions-item label="申报平台名称">{{ showDescription.companyName }}</el-descriptions-item>
      <el-descriptions-item label="主管关区">
      <dict-tag :options="dict.type.master_cuscd" :value="showDescription.masterCuscd"/>
      </el-descriptions-item>
      <el-descriptions-item label="核放单编号">{{ showDescription.passportNo }}</el-descriptions-item>
      <el-descriptions-item label="承运车牌号">{{ showDescription.vehicleNo }}</el-descriptions-item>
      <el-descriptions-item label="申报入区日期">{{ showDescription.declInputDate }}</el-descriptions-item>
    </el-descriptions>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['goodsManage:storageInDetail:remove']"
        >删除
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-upload"
          size="mini"
          @click="handleImport"
        >导入
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"
          v-hasPermi="['goodsManage:storageInDetail:export']"
        >导出
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="storageInDetailList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="序号" align="center" type="index" fixed/>
      <el-table-column label="核放单编号" align="center" prop="passportNo" width="240" />
      <el-table-column label="申报平台名称" align="center" prop="companyName" width="200" show-overflow-tooltip/>
      <el-table-column label="料号" align="center" prop="itemRecordNo" width="150"/>
      <el-table-column label="商品编码" align="center" prop="gcode" width="150"/>
      <el-table-column label="商品名称" align="center" prop="gname" width="150" show-overflow-tooltip/>
      <el-table-column label="申报计量单位" align="center" prop="unit" width="100"/>
      <el-table-column label="申报数量" align="center" prop="qty"/>
      <el-table-column label="货物毛重" align="center" prop="grossWeight"/>
      <el-table-column label="货物净重" align="center" prop="netWeight"/>
      <el-table-column label="备注" align="center" prop="note"/>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['goodsManage:storageInDetail:edit']"
            v-if="false"
          >修改
          </el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['goodsManage:storageInDetail:remove']"
          >删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改国内低值货物入区子表对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body :close-on-click-modal="false">
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="入区主表id" prop="pid">
          <el-input v-model="form.pid" placeholder="请输入入区主表id"/>
        </el-form-item>
        <el-form-item label="核放单编号" prop="passportNo">
          <el-input v-model="form.passportNo" placeholder="请输入核放单编号"/>
        </el-form-item>
        <el-form-item label="申报平台id" prop="companyId">
          <el-input v-model="form.companyId" placeholder="请输入申报平台id"/>
        </el-form-item>
        <el-form-item label="申报平台名称" prop="companyName">
          <el-input v-model="form.companyName" placeholder="请输入申报平台名称"/>
        </el-form-item>
        <el-form-item label="料号" prop="itemRecordNo">
          <el-input v-model="form.itemRecordNo" placeholder="请输入料号"/>
        </el-form-item>
        <el-form-item label="商品编码" prop="gcode">
          <el-input v-model="form.gcode" placeholder="请输入商品编码"/>
        </el-form-item>
        <el-form-item label="商品名称" prop="gname">
          <el-input v-model="form.gname" placeholder="请输入商品名称"/>
        </el-form-item>
        <el-form-item label="申报计量单位" prop="unit">
          <el-input v-model="form.unit" placeholder="请输入申报计量单位"/>
        </el-form-item>
        <el-form-item label="申报数量" prop="qty">
          <el-input v-model="form.qty" placeholder="请输入申报数量"/>
        </el-form-item>
        <el-form-item label="货物毛重" prop="grossWeight">
          <el-input v-model="form.grossWeight" placeholder="请输入货物毛重"/>
        </el-form-item>
        <el-form-item label="货物净重" prop="netWeight">
          <el-input v-model="form.netWeight" placeholder="请输入货物净重"/>
        </el-form-item>
        <el-form-item label="备注" prop="note">
          <el-input v-model="form.note" type="textarea" placeholder="请输入内容"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
    <!-- 上传文件对话框 -->
    <upload-dialog :info="info" ref="uploadDialog"/>
  </div>
</template>

<script>
import {
  listStorageInDetail,
  getStorageInDetail,
  delStorageInDetail,
  addStorageInDetail,
  updateStorageInDetail
} from "@/api/goodsManage/storageInDetail";
import {getStorageInMaster} from "@/api/goodsManage/storageInMaster";

export default {
  name: "StorageInDetail",
  dicts: ['master_cuscd'],
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 国内低值货物入区子表表格数据
      storageInDetailList: [],
      //画面头显示
      showDescription: {},
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      /**导入相关参数*/
      info: {
        url: "/goodsManage/storageInDetail/import",
        title: "入区货物明细导入",
        open: false,
        params: null
      },
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        pid: null,
        passportNo: null,
        companyId: null,
        companyName: null,
        itemRecordNo: null,
        gcode: null,
        gname: null,
        unit: null,
        qty: null,
        grossWeight: null,
        netWeight: null,
        note: null,
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {}
    };
  },
  created() {
    this.queryParams.pid = this.$route.params && this.$route.params.id;
    this.getMasterInfo();
    this.getList();
  },
  watch: {
    //监听上传组件弹框关闭
    'info.open': {
      immediate: false,
      handler: function (val) {
        if (!val) {
          //刷新列表
          this.loading = true;
          setTimeout(this.handleQuery, 2000);
        }
      }
    }
  },
  methods: {
    /** 查询国内低值货物入区主表列表 */
    getMasterInfo(){
      getStorageInMaster(this.queryParams.pid).then(response =>{
        this.showDescription = response.data;
      })
    },
    /** 查询国内低值货物入区子表列表 */
    getList() {
      this.loading = true;
      listStorageInDetail(this.queryParams).then(response => {
        this.storageInDetailList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: null,
        pid: null,
        passportNo: null,
        companyId: null,
        companyName: null,
        itemRecordNo: null,
        gcode: null,
        gname: null,
        unit: null,
        qty: null,
        grossWeight: null,
        netWeight: null,
        note: null,
        createBy: null,
        createTime: null,
        updateBy: null,
        updateTime: null
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /**导入处理*/
    handleImport() {
      this.info.open = true;
      this.info.params = {pid: this.queryParams.pid};
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.id)
      this.single = selection.length !== 1
      this.multiple = !selection.length
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加国内低值货物入区子表";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const id = row.id || this.ids
      getStorageInDetail(id).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改国内低值货物入区子表";
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.id != null) {
            updateStorageInDetail(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            addStorageInDetail(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$modal.confirm('是否确认删除国内低值货物入区子表编号为"' + ids + '"的数据项?').then(function () {
        return delStorageInDetail(ids);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {
      });
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('goodsManage/storageInDetail/export', {
        ...this.queryParams
      }, `storageInDetail_${new Date().getTime()}.xlsx`)
    },
    selectCompany(val) {
      this.queryParams.companyId = val;
      this.handleQuery();
    }
  }
};
</script>

上传组件

<template>
  <el-dialog v-dialog-drag ref="uploadDialog" :title="info.title" :visible.sync="info.open" width="500px"
             append-to-body>
    <el-upload
      drag
      accept=".xls, .xlsx"
      action="#"
      :show-file-list="false"
      :http-request="requestUpload"
      :before-upload="beforeUpload">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div slot="tip" class="el-upload__tip">只能上传xls/xlsx文件</div>
    </el-upload>
  </el-dialog>
</template>
<script>
import {upload} from "@/api/tool/upload";

export default {
  props: {
    info: {
      url: null,
      title: null,
      open: Boolean,
      params: null
    }
  },
  data() {
    return {
      // 上传参数
      upload: {
        file: null,
        updateSupport: true,
        params: null
      }
    };
  },
  methods: {
    beforeUpload(file) {
      this.upload.file = file;
      if (this.info.params) {
        this.upload.params = JSON.stringify(this.info.params);
      }
    },
    requestUpload() {
      let formData = new FormData();
      formData.append("file", this.upload.file);
      formData.append("updateSupport", this.upload.updateSupport);
      formData.append("params", this.upload.params);
      upload(this.info.url, formData).then(response => {
        this.info.open = false;
        this.$modal.msgSuccess("导入成功");
      });
    },
  }
};
</script>

上传组件js

import request from "@/utils/request";

/**
 * 上传文件
 * @param data
 * @returns {AxiosPromise}
 */
export function upload(url,data) {
  return request({
    url: url,
    method: 'post',
    data: data
  })
}

2.后端

    /**
     * @param file
     * @param params
     * @return
     * @throws Exception
     */
    @PreAuthorize("@ss.hasPermi('goodsManage:storageInDetail:add')")
    @Log(title = "国内低值货物入区子表", businessType = BusinessType.IMPORT)
    @PostMapping("/import")
    public AjaxResult importExcel(@RequestParam("file") MultipartFile file, String params) throws Exception {
        storageInDetailService.importMaster(file, params);
        return AjaxResult.success();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int importMaster(MultipartFile file, String params) throws Exception {
        //导入校验是否主表已经审核通过,审核通过不允许再次导入
        checkMasterApproveType(params);
        //读取Excel信息
        DynamicHeaderListener parseListener = new DynamicHeaderListener();
        EasyExcel.read(file.getInputStream(), parseListener).sheet("表体").headRowNumber(2).doReadSync();
        List<Map<Integer, String>> dataList = parseListener.getList();
        if (CollUtil.isEmpty(dataList)) {
            log.error("读入的入区明细为空");
            throw new MultipartException("读入的入区明细为空,请重新导入");
        }
        JSONObject param = JSON.parseObject(params);
        Long pid = param.getLongValue("pid");
        //查询主表信息
        StorageInMaster storageInMaster = storageInMasterService.getById(pid);
        if (ObjectUtil.isEmpty(storageInMaster)) {
            log.error("入区信息不存在");
            throw new MultipartException("入区信息不存在,请确认");
        }
        List<StorageInDetail> details = new ArrayList<>();
        dataList.forEach(item -> {
            StorageInDetail storageInDetail = new StorageInDetail();
            //入区主表id
            storageInDetail.setPid(pid);
            //核放单编号
            storageInDetail.setPassportNo(storageInMaster.getPassportNo());
            //申报平台id
            storageInDetail.setCompanyId(storageInMaster.getCompanyId());
            //申报平台名称
            storageInDetail.setCompanyName(storageInMaster.getCompanyName());
            //料号
            storageInDetail.setItemRecordNo(item.get(2));
            //商品编码
            storageInDetail.setGcode(item.get(3));
            //商品名称
            storageInDetail.setGname(item.get(4));
            //申报计量单位
            storageInDetail.setUnit(item.get(5));
            //申报数量
            storageInDetail.setQty(BigDecimal.valueOf(Long.parseLong(item.get(6))));
            //货物毛重
            storageInDetail.setGrossWeight(Convert.toBigDecimal(item.get(7), BigDecimal.ZERO));
            //货物净重
            storageInDetail.setNetWeight(Convert.toBigDecimal(item.get(8), BigDecimal.ZERO));
            //备注
            storageInDetail.setNote(item.get(9));
            //创建人
            storageInDetail.setCreateBy(SecurityUtils.getUserId().toString());

            details.add(storageInDetail);
        });
        BigDecimal declQty = new BigDecimal(0);
        for (StorageInDetail insertData : details) {
            this.save(insertData);
            declQty = declQty.add(insertData.getQty());
        }
        StorageInMaster updateMaster = new StorageInMaster();
        updateMaster.setDeclQty(declQty);
        updateMaster.setId(pid);
        storageInMasterService.updateById(updateMaster);
        return 1;
    }

3.总结

上传文件时,要使用 FormData() 构造函数,将上传的文件及参数封装在FormData()中,同时浏览器会自动识别并添加请求头 “Content-Type: multipart/form-data”,且参数依然像是表单提交时的那种键值对儿,此外 FormData() 构造函数 new 时可以直接传入 form 表单的 dom 节点。注意使用post请求,后端接收的时候可以使用requestParam,也可以不使用。

Content-Type: multipart/form-data,这种类型是后来追加的。为什么要新增一个类型,而不使用旧有的

application/x-www-form-urlencoded

:因为旧有类型不适合用于传输大型二进制数据或者包含非ASCII字符的数据。平常我们使用这个类型都是把表单数据使用url编码后传送给后端,二进制文件当然没办法一起编码进去了。所以

multipart/form-data

就诞生了,专门用于有效的传输文件。



版权声明:本文为m0_72167535原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。