用固定资产管理系统中的资产模块进行示范批量导入导出功能
下面我将提供一个完整的SpringBoot + Vue实现的固定资产批量导入导出功能,这个项目大家也可以进行看我的资产管理系统
项目:https://blog.csdn.net/qq_46108094

下面开始项目实操
后端实现 (Spring Boot)
1. 实体类 (Assets.java)
java
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("assets")
public class Assets {
@TableId(type = IdType.AUTO)
private Long id;
private String assetCode; // 资产编码
private String assetName; // 资产名称
private String assetType; // 资产类型
private String specification; // 规格型号
private BigDecimal unitPrice; // 单价
private Integer quantity; // 数量
private BigDecimal totalValue; // 总价值
private String department; // 使用部门
private String user; // 使用人
private String location; // 存放地点
private String status; // 状态:在用、闲置、报废等
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
2. 控制器 (AssetsController.java)
java
package com.example.demo.controller;
import com.example.demo.entity.Assets;
import com.example.demo.service.AssetsService;
import com.example.demo.common.Result;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
@RestController
@RequestMapping("/assets")
public class AssetsController {
@Resource
private AssetsService assetsService;
/**
* 导出所有资产数据
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception {
// 查询所有数据
List<Assets> list = assetsService.list();
// 在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
// 自定义标题别名
writer.addHeaderAlias("assetCode", "资产编码");
writer.addHeaderAlias("assetName", "资产名称");
writer.addHeaderAlias("assetType", "资产类型");
writer.addHeaderAlias("specification", "规格型号");
writer.addHeaderAlias("unitPrice", "单价");
writer.addHeaderAlias("quantity", "数量");
writer.addHeaderAlias("totalValue", "总价值");
writer.addHeaderAlias("department", "使用部门");
writer.addHeaderAlias("user", "使用人");
writer.addHeaderAlias("location", "存放地点");
writer.addHeaderAlias("status", "状态");
writer.addHeaderAlias("createTime", "创建时间");
// 一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list, true);
// 设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("固定资产列表", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
out.close();
writer.close();
}
/**
* 导入Excel数据
*/
@PostMapping("/import")
public Result importData(MultipartFile file) throws Exception {
InputStream inputStream = file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
// 通过JavaBean的方式读取Excel内的对象,要求表头必须是英文,与JavaBean的属性对应
List<Assets> list = reader.readAll(Assets.class);
if (CollUtil.isEmpty(list)) {
return Result.error("Excel文件中没有数据");
}
// 批量保存
boolean result = assetsService.saveBatch(list);
return result ? Result.success("导入成功") : Result.error("导入失败");
}
/**
* 下载导入模板
*/
@GetMapping("/template")
public void downloadTemplate(HttpServletResponse response) throws Exception {
// 创建空数据示例
Assets template = new Assets();
template.setAssetCode("ASSET-001");
template.setAssetName("示例资产");
template.setAssetType("电子设备");
template.setSpecification("标准型号");
template.setUnitPrice(new BigDecimal("1000.00"));
template.setQuantity(1);
template.setTotalValue(new BigDecimal("1000.00"));
template.setDepartment("行政部");
template.setUser("张三");
template.setLocation("A区101室");
template.setStatus("在用");
List<Assets> list = CollUtil.newArrayList(template);
// 在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
// 自定义标题别名
writer.addHeaderAlias("assetCode", "资产编码");
writer.addHeaderAlias("assetName", "资产名称");
writer.addHeaderAlias("assetType", "资产类型");
writer.addHeaderAlias("specification", "规格型号");
writer.addHeaderAlias("unitPrice", "单价");
writer.addHeaderAlias("quantity", "数量");
writer.addHeaderAlias("totalValue", "总价值");
writer.addHeaderAlias("department", "使用部门");
writer.addHeaderAlias("user", "使用人");
writer.addHeaderAlias("location", "存放地点");
writer.addHeaderAlias("status", "状态");
// 只导出别名字段
writer.setOnlyAlias(true);
// 一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list, true);
// 设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("固定资产导入模板", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
out.close();
writer.close();
}
}
3. 服务接口 (AssetsService.java)
java
package com.example.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Assets;
import java.util.List;
public interface AssetsService extends IService<Assets> {
// 可以添加自定义方法
}
4. 服务实现 (AssetsServiceImpl.java)
java
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.Assets;
import com.example.demo.mapper.AssetsMapper;
import com.example.demo.service.AssetsService;
import org.springframework.stereotype.Service;
@Service
public class AssetsServiceImpl extends ServiceImpl<AssetsMapper, Assets> implements AssetsService {
// 实现自定义方法
}
5. 统一返回结果类 (Result.java)
java
package com.example.demo.common;
import lombok.Data;
import java.io.Serializable;
@Data
public class Result implements Serializable {
private int code;
private String msg;
private Object data;
public static Result success() {
Result result = new Result();
result.setCode(200);
result.setMsg("成功");
return result;
}
public static Result success(Object data) {
Result result = new Result();
result.setCode(200);
result.setMsg("成功");
result.setData(data);
return result;
}
public static Result success(String msg, Object data) {
Result result = new Result();
result.setCode(200);
result.setMsg(msg);
result.setData(data);
return result;
}
public static Result error() {
Result result = new Result();
result.setCode(500);
result.setMsg("失败");
return result;
}
public static Result error(String msg) {
Result result = new Result();
result.setCode(500);
result.setMsg(msg);
return result;
}
public static Result error(int code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
前端实现 (Vue + Element UI)
1. 固定资产页面 (Assets.vue)
vue
<template>
<div class="app-container">
<!-- 搜索和操作区域 -->
<div class="filter-container">
<el-input v-model="listQuery.assetName" placeholder="资产名称" style="width: 200px;" class="filter-item" />
<el-select v-model="listQuery.department" placeholder="使用部门" clearable style="width: 200px" class="filter-item">
<el-option v-for="item in departmentOptions" :key="item" :label="item" :value="item" />
</el-select>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
搜索
</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
新增
</el-button>
<el-button type="success" plain @click="exportData">批量导出</el-button>
<el-button type="info" plain @click="handleImport">批量导入</el-button>
</div>
<!-- 数据表格 -->
<el-table
:key="tableKey"
v-loading="listLoading"
:data="list"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column label="ID" prop="id" align="center" width="80">
<template slot-scope="{row}">
<span>{{ row.id }}</span>
</template>
</el-table-column>
<el-table-column label="资产编码" width="120" align="center">
<template slot-scope="{row}">
<span>{{ row.assetCode }}</span>
</template>
</el-table-column>
<el-table-column label="资产名称" min-width="150">
<template slot-scope="{row}">
<span>{{ row.assetName }}</span>
</template>
</el-table-column>
<el-table-column label="资产类型" width="120" align="center">
<template slot-scope="{row}">
<span>{{ row.assetType }}</span>
</template>
</el-table-column>
<el-table-column label="规格型号" width="120" align="center">
<template slot-scope="{row}">
<span>{{ row.specification }}</span>
</template>
</el-table-column>
<el-table-column label="单价" width="100" align="center">
<template slot-scope="{row}">
<span>¥{{ row.unitPrice }}</span>
</template>
</el-table-column>
<el-table-column label="数量" width="80" align="center">
<template slot-scope="{row}">
<span>{{ row.quantity }}</span>
</template>
</el-table-column>
<el-table-column label="总价值" width="120" align="center">
<template slot-scope="{row}">
<span>¥{{ row.totalValue }}</span>
</template>
</el-table-column>
<el-table-column label="使用部门" width="120" align="center">
<template slot-scope="{row}">
<span>{{ row.department }}</span>
</template>
</el-table-column>
<el-table-column label="使用人" width="100" align="center">
<template slot-scope="{row}">
<span>{{ row.user }}</span>
</template>
</el-table-column>
<el-table-column label="存放地点" width="120" align="center">
<template slot-scope="{row}">
<span>{{ row.location }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
<template slot-scope="{row,$index}">
<el-button type="primary" size="mini" @click="handleUpdate(row)">
编辑
</el-button>
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<!-- 批量导入对话框 -->
<el-dialog title="批量导入资产" :visible.sync="importVisible" width="40%" :close-on-click-modal="false" destroy-on-close>
<div style="margin-bottom: 20px;">
<el-alert
title="导入说明"
type="info"
:closable="false"
show-icon>
<div slot="default">
<p>1. 请先<a href="javascript:void(0);" @click="downloadTemplate" style="color: #409EFF; text-decoration: underline;">下载模板</a>,按照模板格式填写数据</p>
<p>2. 仅支持xlsx格式文件,且文件大小不超过10MB</p>
<p>3. 每次最多导入1000条数据</p>
</div>
</el-alert>
</div>
<el-upload
class="upload-demo"
drag
action="#"
:auto-upload="false"
:on-change="handleUploadChange"
:before-upload="beforeUpload"
:file-list="fileList"
:limit="1"
accept=".xlsx,.xls"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传xlsx/xls文件,且不超过10MB</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button @click="importVisible = false">取 消</el-button>
<el-button type="primary" :loading="importLoading" @click="submitImport">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { fetchList, importAssets } from '@/api/assets'
import Pagination from '@/components/Pagination'
export default {
name: 'AssetsList',
components: { Pagination },
filters: {
statusFilter(status) {
const statusMap = {
'在用': 'success',
'闲置': 'info',
'报废': 'danger'
}
return statusMap[status]
}
},
data() {
return {
tableKey: 0,
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
assetName: undefined,
department: undefined
},
departmentOptions: ['行政部', '财务部', '技术部', '市场部', '人事部'],
importVisible: false,
importLoading: false,
fileList: [],
uploadFile: null
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleCreate() {
// 新增资产逻辑
},
handleUpdate(row) {
// 编辑资产逻辑
},
handleDelete(row, index) {
// 删除资产逻辑
},
// 导出数据
exportData() {
window.open(`${process.env.VUE_APP_BASE_API}/assets/export`)
},
// 打开导入对话框
handleImport() {
this.importVisible = true
this.fileList = []
},
// 下载模板
downloadTemplate() {
window.open(`${process.env.VUE_APP_BASE_API}/assets/template`)
},
// 上传文件前的校验
beforeUpload(file) {
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.type === 'application/vnd.ms-excel'
const isLt10M = file.size / 1024 / 1024 < 10
if (!isExcel) {
this.$message.error('只能上传Excel文件!')
}
if (!isLt10M) {
this.$message.error('上传文件大小不能超过10MB!')
}
return isExcel && isLt10M
},
// 文件改变时的回调
handleUploadChange(file, fileList) {
const isExcel = file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.raw.type === 'application/vnd.ms-excel'
if (!isExcel) {
this.$message.error('只能上传Excel文件!')
this.fileList = []
return false
}
this.uploadFile = file.raw
},
// 提交导入
submitImport() {
if (!this.uploadFile) {
this.$message.warning('请先选择文件')
return
}
this.importLoading = true
const formData = new FormData()
formData.append('file', this.uploadFile)
importAssets(formData).then(response => {
this.importLoading = false
this.$message({
message: response.msg || '导入成功',
type: 'success'
})
this.importVisible = false
this.getList() // 刷新列表
}).catch(() => {
this.importLoading = false
})
}
}
}
</script>
<style scoped>
.filter-container {
margin-bottom: 20px;
}
.upload-demo {
text-align: center;
}
</style>
2. API接口 (assets.js)
javascript
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/assets/list',
method: 'get',
params: query
})
}
export function importAssets(data) {
return request({
url: '/assets/import',
method: 'post',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
使用说明
1. 后端依赖
在pom.xml中添加以下依赖:
xml
<!-- Hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.17</version>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2. 功能特点
- 批量导出:将数据库中的固定资产数据导出为Excel文件
- 批量导入:通过Excel模板批量导入固定资产数据
- 模板下载:提供标准导入模板,确保数据格式一致性
- 数据校验:前端和后端都对上传文件进行校验
- 用户体验:提供清晰的操作指引和反馈
3. 使用流程
- 点击"批量导入"按钮打开导入对话框
- 下载导入模板并按照格式填写数据
- 将填写好的Excel文件拖拽或点击上传
- 点击确定开始导入,系统会显示导入结果
- 导入成功后,页面数据自动刷新
这个实现方案提供了完整的固定资产批量导入导出功能 ,包含了前后端代码 。
大家可以通过这个进行修改然后直接集成到自己的SpringBoot + Vue项目中。