1.技术要点
mybatisplus;vue3,springboot2;javaguns框架
2实现导入功能
后端实现:
①controller层
@RestController
@ApiResource(name = "项目评价")
public class ProjectAppraisalController {
/**
* 导入excel表数据
* @param file
* @return
*/
@PostResource(name = "数据导入" ,path = "/projectAppraisal/import")
public ResponseData<?> importData(MultipartFile file){
return new SuccessResponseData<>(projectAppraisalService.importData(file));
}
}
-
这段代码是一个使用了Spring Boot框架的控制器方法。它使用了
@PostResource
注解来定义一个POST请求的资源,并指定了资源的名称为"数据导入",路径为"/awardPatent/import"。该方法接收一个
MultipartFile
类型的文件作为参数,表示要导入的文件。MultipartFile
是Spring框架提供的一个类,用于处理文件上传。在方法体内,调用了
awardPatentService.importData(file)
来处理文件导入的逻辑。awardPatentService
是一个服务类的实例,通过调用其importData
方法来处理文件导入操作。最后,将导入操作的结果封装在一个
SuccessResponseData
对象中,并作为响应返回。SuccessResponseData
是一个自定义的响应数据类,表示成功的响应,并包含了导入操作的结果数据。总结来说,这段代码定义了一个用于处理文件导入的控制器方法。它接收一个文件作为参数,调用服务类的方法来处理文件导入操作,并将结果封装在响应数据中返回。具体的实现逻辑需要根据代码的上下文来确定。
②service层:
public interface ProjectAppraisalService extends IService<ProjectAppraisal> {
ResponseData<?> importData(MultipartFile file);
}
-
ResponseData<?> importData(MultipartFile file)
是一个方法声明,其中ResponseData<?>
表示返回类型为ResponseData
,而MultipartFile file
是该方法的参数,表示接收一个文件作为输入。具体来说,这个方法可能是用于处理文件导入的逻辑。它接收一个
MultipartFile
类型的文件作为参数,表示要导入的文件。MultipartFile
是Spring框架提供的一个类,用于处理文件上传。ResponseData
是一个自定义的响应数据类,可能包含了请求的处理结果和相关的数据。<?>
表示泛型,表示可以是任意类型的数据。这个方法的作用可能是将接收到的文件进行处理,然后返回一个包含处理结果和相关数据的
ResponseData
对象。总结来说,
ResponseData<?> importData(MultipartFile file)
是一个方法声明,用于处理文件导入操作。它接收一个文件作为参数,并返回一个包含处理结果和相关数据的ResponseData
对象。具体的实现逻辑需要根据代码的上下文来确定。
③实体层:
@TableName("project_appraisal")
@Data
@ExcelIgnoreUnannotated
@EqualsAndHashCode(callSuper = true)
public class ProjectAppraisal extends BaseEntity {
@ExcelProperty(value = "序号",index = 0)
@TableField(exist = false)
private Integer serialNum;
/**
* 主键
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
@ChineseDescription("主键")
private Long id;
/**
* 流程ID
*/
@TableField("process_id")
@ChineseDescription("流程ID")
private String processId;
/**
* 管理编号
*/
@TableField("manage_code")
@ChineseDescription("管理编号")
private String manageCode;
/**
* 案件状态
*/
@TableField("status")
@ChineseDescription("案件状态")
private String status;
@TableField(exist = false)
@ChineseDescription("工作流相关")
private WorkflowModel workflowModel;
/**
* 项目名称
*/
@TableField("project_name")
@ExcelProperty(value = "项目名称",index = 1)
@ChineseDescription("项目名称")
private String projectName;
/**
* 投产单位
*/
@TableField("production_unit")
@ChineseDescription("投产单位")
@ExcelProperty(value = "投产单位",index = 2)
private String productionUnit;
/**
* 评价单位
*/
@TableField("evaluation_unit")
@ChineseDescription("评价单位")
@ExcelProperty(value = "评价单位",index = 3)
private String evaluationUnit;
/**
* 评价方式
*/
@TableField("evaluation_method")
@ChineseDescription("评价方式")
@ExcelProperty(value = "评价方式",index = 4)
private String evaluationMethod;
/**
* 评价时间
*/
@DateTimeFormat("yyyy-MM-dd")
@TableField("evaluation_time")
@ChineseDescription("评价时间")
@ExcelProperty(value = "评价时间",index = 5)
private Date evaluationTime;
/**
* 评价结果
*/
@TableField("evaluation_result")
@ChineseDescription("评价结果")
@ExcelProperty(value = "评价结果",index = 6)
private String evaluationResult;
/**
* 附件
*/
@TableField("attachment")
@ChineseDescription("附件")
private String attachment;
}
④serviceImpl
@Service
public class ProjectAppraisalServiceImpl extends ServiceImpl<ProjectAppraisalMapper, ProjectAppraisal> implements ProjectAppraisalService {
@Override
public ResponseData<?> importData(MultipartFile file) {
if (file != null){
List<ProjectAppraisal> projectAppraisalList = null;
try {
//创建监听器
GeneralListener<ProjectAppraisal> listener = new GeneralListener<>();
//执行Excel的读取,read方法需要三个参数 ,文件(流) 大的Class实例 监听器
EasyExcel.read(file.getInputStream() , ProjectAppraisal.class , listener).sheet().doRead();
//获取监听器的数据
projectAppraisalList = listener.getList();
} catch (IOException e) {
return new ErrorResponseData<>("F0002" ,"数据格式有误");
}
//处理表头的第一行数据、
projectAppraisalList.stream().filter(item -> Pattern.matches("//d+", String.valueOf(item.getSerialNum())));
//添加表中数据
projectAppraisalList.forEach(projectAppraisal -> {
String projectName = projectAppraisal.getProjectName();
String productionUnit = projectAppraisal.getProductionUnit();
LambdaQueryWrapper<ProjectAppraisal> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProjectAppraisal::getProjectName ,projectName);
wrapper.eq(ProjectAppraisal::getProductionUnit,productionUnit);
List<ProjectAppraisal> projectAppraisals = projectAppraisalService.list(wrapper);
//如果不存在,加入
if (projectAppraisals.isEmpty()){
projectAppraisal.setStatus("完成");
projectAppraisal.setCreateTime(new Date());
projectAppraisalService.save(projectAppraisal);
}
});
return new SuccessResponseData<>("00000" ,"上传成功" ,null);
}else {
return new ErrorResponseData<>("F0003" ,"上传失败");
}
}
}
这段代码是一个实现了importData
方法的具体实现。该方法接收一个MultipartFile
类型的文件作为参数,并返回一个ResponseData<?>
类型的响应数据。
在方法体内,首先判断传入的文件是否为空。如果不为空,则继续执行文件导入的逻辑。
在文件导入的逻辑中,首先创建一个GeneralListener<ProjectAppraisal>
类型的监听器对象。然后使用EasyExcel库的read
方法,传入文件流、ProjectAppraisal
类的实例和监听器对象,来读取Excel文件的内容。通过监听器对象获取读取到的数据,将其赋值给projectAppraisalList
变量。
接下来,对projectAppraisalList
进行处理。首先使用stream()
方法和filter()
方法,过滤出表头的第一行数据。然后使用forEach()
方法遍历projectAppraisalList
,对每个ProjectAppraisal
对象进行处理。根据项目名称和生产单位查询数据库中是否存在相同的记录,如果不存在,则将该记录保存到数据库中。
最后,根据处理结果返回相应的响应数据。如果文件上传成功,返回一个SuccessResponseData
对象,包含成功的响应码和消息。如果文件上传失败,返回一个ErrorResponseData
对象,包含失败的响应码和消息
3.前端代码:
<template>
<div class="ele-body">
<!-- 搜索表单 -->
<div class="block-interval">
<a-card :bordered="false">
<a-form :model="where">
<a-row :gutter="20">
<a-col>
<a-form-item label="项目名称:">
<a-input v-model:value.trim="where.projectName" placeholder="请输入项目名称" allow-clear />
</a-form-item>
</a-col>
<a-col>
<a-form-item label="评价单位:">
<a-input v-model:value.trim="where.evaluationUnit" placeholder="请输入评价单位" allow-clear />
</a-form-item>
</a-col>
<a-col>
<a-space class="ele-text-center">
<a-button type="primary" @click="reload">查询</a-button>
<a-button @click="reset">重置</a-button>
</a-space>
</a-col>
</a-row>
</a-form>
</a-card>
</div>
<!-- 表格 -->
<div>
<a-card :bordered="false" class="table-height">
<ele-pro-table
ref="table"
row-key="id"
:datasource="datasource"
:columns="columns"
:where="where"
v-model:selection="selection"
:scroll="{ x: 'max-content' }"
>
<!-- table上边工具栏 -->
<template #toolbar>
<a-space>
<a-button type="primary" @click="openEdit()" v-permission="'PROJECTEVALUATION_ADD_BUTTON'">
<template #icon>
<plus-outlined />
</template>
<span>新建</span>
</a-button>
<a-button class="export-excel-button" type="primary" @click="exportExcel" v-permission="'PROJECTEVALUATION_EXPORT_BUTTON'">
<template #icon>
<download-outlined />
</template>
<span>导出</span>
</a-button>
<a-button @click="showImport()" v-if="hasRole()">
<upload-outlined />
导入
</a-button>
</a-space>
<a-modal v-model:visible="isShowImport" title="导入数据" :footer="null">
<a-upload-dragger
ref="fileImport"
v-model:file-list="fileList"
name="file"
:multiple="true"
:accept="importAccept"
:action="baseUrl + '/projectAppraisal/import'"
:headers="headers"
@change="handleChange"
@reject="handleReject"
:before-upload="beforeImport"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">点击选择文件或拖拽文件到此处</p>
</a-upload-dragger>
<p style="margin-top: 20px; text-align: center; color: #ccc">
请上传Excel格式文件,<a href="/static/template/projectappraisal.xlsx" download="项目评价数据导入模板.xlsx">下载模板</a>
</p>
</a-modal>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'ended'">
<a-tag color="green" v-if="(record.workflowModel ? record.workflowModel.isEnd : '') === 1">是</a-tag>
<a-tag color="red" v-else> 否</a-tag>
</template>
<!-- 当前节点 -->
<template v-if="column.key === 'active'">
<span v-if="record.workflowModel">
<a @click="tracking(record)" v-if="record.workflowModel.isEnd === 1">结束</a>
<a @click="tracking(record)" v-else>{{ record.workflowModel.actName }}</a>
</span>
<span v-else> - </span>
</template>
<!-- 申报结果 -->
<template v-if="column.key === 'evaluationResult'">
<span v-if="record.evaluationResult">
<a-tag color="green" v-if="record.evaluationResult === '是'">通过</a-tag>
<a-tag color="red" v-else>未通过</a-tag>
</span>
<span v-else> - </span>
</template>
<!-- table操作栏按钮 -->
<template v-if="column.key === 'action'">
<a-space>
<a @click="openDetail(record)">查看</a>
<a-space
v-permission="'PROJECTEVALUATION_UPDATE_BUTTON'"
v-if="(record.workflowModel && record.workflowModel.isEnd) !== 1 || hasRole()"
>
<a-divider type="vertical" />
<a @click="openEdit(record)">修改</a>
</a-space>
<a-space v-permission="'PROJECTEVALUATION_UPDATE_BUTTON'" v-if="record.status === '完成' && !record.evaluationResult">
<a-divider type="vertical" />
<a-popconfirm
title="该记录是否通过申报?"
ok-text="通过"
cancel-text="不通过"
@confirm="resultFun(record, '是')"
@cancel="resultFun(record, '否')"
>
<a>结果</a>
</a-popconfirm>
</a-space>
<a-space v-permission="'PROJECTEVALUATION_DEL_BUTTON'" v-if="record.status === '暂存' || hasRole()">
<a-divider type="vertical" />
<a-popconfirm title="确定要删除此记录吗?" @confirm="remove(record)">
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
</div>
<common-tracking ref="tracking" />
<!-- 编辑弹窗 -->
<projectAppraisal-edit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</template>
<script>
import { message } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import ProjectAppraisalEdit from './project_appraisal_edit.vue';
import { ProjectAppraisalApi } from '@/api/bohua/ProjectAppraisalApi';
import CommonTracking from '@/views/workflow/tracking/common-tracking.vue';
import { useUserStore } from '@/store/modules/user';
import { hasRole } from '@/utils/permission';
import { toDateString } from 'ele-admin-pro';
import { ref } from 'vue';
import { getToken } from '@/utils/token-util';
export default {
name: 'ProjectAppraisal',
components: {
PlusOutlined,
ProjectAppraisalEdit,
CommonTracking
},
async mounted() {},
data() {
return {
isShowImport: ref(false),
fileList: [],
importAccept: ['.xlsx', '.xls'],
baseUrl: import.meta.env.VITE_API_URL,
// 表格列配置
columns: [
{
key: 'index',
title: '序号',
align: 'center',
width: 48,
customRender: ({ index }) => this.$refs.table.tableIndex + index
},
{
title: '项目名称',
dataIndex: 'projectName'
},
{
title: '投产单位',
dataIndex: 'productionUnit'
},
{
title: '评价单位',
dataIndex: 'evaluationUnit'
},
{
title: '评价方式',
dataIndex: 'evaluationMethod'
},
{
title: '评价时间',
dataIndex: 'evaluationTime',
customRender: ({ text }) => {
return !text ? '-' : toDateString(text, 'yyyy-MM-dd');
}
},
{
title: '评价结果',
dataIndex: 'evaluationResult',
key: 'evaluationResult'
},
{
ellipsis: true,
resizable: true,
title: '审批节点',
dataIndex: 'workflowModel.actName',
key: 'active'
},
{
title: '流程结束',
dataIndex: 'workflowModel.isEnd',
key: 'ended'
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center'
}
],
// 表格搜索条件
where: {},
// 表格选中数据
selection: [],
// 当前编辑数据
current: null,
// 是否显示编辑弹窗
showEdit: false,
// 是否查看
isDetail: false,
headers: {
authorization: getToken()
}
};
},
methods: {
showImport() {
this.isShowImport = true;
},
isAllowedFile(fileName, allowedExtensions) {
const fileExtension = '.' + fileName.split('.').pop().toLowerCase();
return allowedExtensions.includes(fileExtension);
},
hasRole() {
return hasRole('superAdmin');
},
handleChange(info) {
if (!this.isAllowedFile(info.file.name, this.importAccept) || info.file.status !== 'uploading') {
// 如果文件格式支持或不在上传
if (info.file.status == 'done' || info.file.status == 'error') {
const success = info.file?.response?.success || false;
const msg = info.file?.response?.message || '';
if (!success || info.file.status == 'error') {
message.error(info.file.name + '导入失败!' + msg);
} else if (info.file.status == 'done') {
message.success(info.file.name + '导入成功!');
}
}
//console.log('移除上传完成、上传错误或不允许上传的文件');
this.fileList = info.fileList.filter(file => file.uid !== info.file.uid);
}
},
handleReject() {
message.error('上传文件格式不正确!');
},
beforeImport(file) {
if (!this.isAllowedFile(file.name, this.importAccept)) {
message.error('上传文件只能是' + this.importAccept.join('、') + '格式!');
return false; // 阻止上传
}
return true; // 允许上传
},
/**
* 打开追踪界面
*
*
*/
tracking(row) {
this.$refs.tracking.tracking(row.processId);
},
/**
* 搜索按钮
*
*
*/
reload() {
this.selection = [];
this.$refs.table.reload({ page: 1 });
},
/**
* 重置搜索
*
*
*/
reset() {
this.reloadPage = 1;
this.where.projectName = '';
this.where.evaluationUnit = '';
this.$nextTick(() => {
this.reload();
});
},
/**
* 删除
*
* @author zhihui
* @date 2023/12/26 09:38
*/
async remove(row) {
const result = await ProjectAppraisalApi.delete({ id: row.id });
message.success(result.message);
this.reload();
},
/**
* 打开新增或编辑弹窗
*
*
*/
openEdit(row) {
if (row) this.current = row;
else {
//新建时,默认企业名称为用户所在单位
const loginUserInfo = useUserStore().info;
this.current = { companyName: loginUserInfo.orgName };
}
this.showEdit = true;
this.isDetail = false;
},
openDetail(row) {
this.current = row;
this.showEdit = true;
this.isDetail = true;
},
/**
* 获取表格数据
*
*
*/
datasource({ page, limit, where, orders }) {
return ProjectAppraisalApi.findPage({ ...where, ...orders, pageNo: page, pageSize: limit });
},
resultFun(record, i) {
ProjectAppraisalApi.edit({ id: record.id, evaluationResult: i }).then(result => {
message.success(result.message);
this.reload();
});
},
exportExcel() {
ProjectAppraisalApi.exportExcel(this.where);
}
}
};
</script>