如何实现前后端交互?
这里就拿做jeecgboot项目时的一个功能举例,该功能要实现的目标是后端查询数据库中的所需要显示的数据的条数,并显示到前端界面。数据库的内容变化的时候,前端显示也会实时改变。
数据库设计:
首先看一下数据库,数据库中有一个待办表tobedone,表结构如下:

图1 待办表的表结构
还有一个年度关键指标表,kpi表。表结构如下:

图2 年度关键指标表的结构
实体类设计:
根据设计好的表结构,分别设计两个实体类,将各个字段都写到实体类中去。这个是待办表的实体类。
java
package org.jeecg.modules.demo.tobedone.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 待办
* @Author: jeecg-boot
* @Date: 2025-08-07
* @Version: V1.0
*/
@Data
@TableName("yx_tobedone")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="待办")
public class Tobedone implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
/**所属部门*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
/**完成时间*/
@Excel(name = "待办完成时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "待办完成时间")
private java.util.Date tobedoneFinishedtime;
/**指标id*/
@Excel(name = "指标id", width = 15)
@Schema(description = "指标id")
private java.lang.String kpiId;
/*任务状态*/
@Excel(name = "任务状态", width = 15)
@Schema(description = "任务状态")
private java.lang.String taskstatus;
/*配合部门*/
@Excel(name = "配合部门", width = 15)
@Schema(description = "配合部门")
private java.lang.String coopDepart;
/*任务类型*/
@Excel(name = "任务类型", width = 15)
@Schema(description = "任务类型")
private java.lang.String tasktype;
/*任务类型*/
@Excel(name = "驳回情况", width = 15)
@Schema(description = "驳回情况")
private java.lang.String refusestatus;
/*任务类型*/
@Excel(name = "汇报月份", width = 15)
@Dict(dicCode = "month_type")
@Schema(description = "汇报月份")
private java.lang.String reportMonth;
/*任务类型*/
@Excel(name = "完成进度", width = 15)
@Schema(description = "完成进度")
private java.lang.String completionProgress;
/*任务类型*/
@Excel(name = "是否预警", width = 15)
@Schema(description = "是否预警")
private java.lang.String whetherWarn;
/*任务类型*/
@Excel(name = "审批月份", width = 15)
@Dict(dicCode = "month_type")
@Schema(description = "审批月份")
private java.lang.String approvalMonth;
}
别看这个字段这么多,其实做一个简单的交互就用了其中一部分字段。
然后是年度关键指标表的实体类:
java
package org.jeecg.modules.demo.kpi.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 年度关键指标
* @Author: jeecg-boot
* @Date: 2025-08-07
* @Version: V1.0
*/
@Data
@TableName("yx_kpi")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="年度关键指标")
public class Kpi implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
/**所属部门*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
/**指标类型*/
@Excel(name = "指标类型", width = 15, dicCode = "index_type")
@Dict(dicCode = "index_type")
@Schema(description = "指标类型")
private java.lang.String indextype;
/**考核指标*/
@Excel(name = "考核指标", width = 15)
@Schema(description = "考核指标")
private java.lang.String assessindex;
/**权重*/
@Excel(name = "权重", width = 15)
@Schema(description = "权重")
private java.lang.String weight;
/**指标定义及公式*/
@Excel(name = "指标定义及公式", width = 15)
@Schema(description = "指标定义及公式")
private java.lang.String indexdefineandtime;
/**评价细则*/
@Excel(name = "评价细则", width = 15)
@Schema(description = "评价细则")
private java.lang.String commentrules;
/**责任部门*/
@Excel(name = "责任部门", width = 15, dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
@Dict(dictTable = "sys_depart", dicText = "depart_name", dicCode = "id")
@Schema(description = "责任部门")
private java.lang.String respondepartment;
/**基本目标值*/
@Excel(name = "基本目标值", width = 15)
@Schema(description = "基本目标值")
private java.lang.String basetargetvalue;
/**激励目标值*/
@Excel(name = "激励目标值", width = 15)
@Schema(description = "激励目标值")
private java.lang.String motivatetargetvalue;
/**完成时间*/
@Excel(name = "指标完成时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "指标完成时间")
private java.util.Date kpiFinishedtime;
/**配合部门*/
@Excel(name = "配合部门", width = 15, dictTable = "sys_depart where depart_name like '%部' or depart_name like '%处'", dicText = "depart_name", dicCode = "id")
@Dict(dictTable = "sys_depart where depart_name like '%部' or depart_name like '%处'", dicText = "depart_name", dicCode = "id")
@Schema(description = "配合部门")
private java.lang.String cooperatedept;
/**创建人部门id*/
@Excel(name = "部门id", width = 15)
@Schema(description = "部门id")
private java.lang.String createByDepartId;
}
数据访问层和服务层设计:
现在数据库和实体类都设计好了,就开始编写数据访问层和服务层,然后编写控制类实现接口,准备提供给前端。
要实现查询数据库中的所需要显示的数据的条数,写一个xml,编写sql语句查询。
java
<select id="ReceiveSumOfKpi" resultType="integer" parameterType="string">
SELECT COUNT(*) as total_count
from yx_kpi as k INNER JOIN yx_tobedone as t
where t.kpi_id=k.id and t.coop_depart=#{orgId} and t.taskstatus=#{status};
</select>
进行表连接,然后根据where语句去查配合部门为该部门,状态为传入状态的数据,并使用count进行计数。
有了xml就实现数据访问层和服务层接口,都编写一个方法:
java
//记录接收年度关键指标记录的总数
int ReceiveSumOfKpi(String orgId,String status);
这里注意mapper接口要使用@Mapper注解。
然后实现服务层接口,编写一个实现类:
java
/*接收年度关键指标记录的总数
* */
@Override
public int ReceiveSumOfKpi(String orgId, String status) {
return tobedoneMapper.ReceiveSumOfKpi(orgId,status);
}
控制类设计:
最后实现控制类,加上权限管理。
java
/*
* 接收年度关键指标列表总数的记录
* */
@RequiresPermissions("Receive_kpi:yx_Receive_kpi:ReceiveSumOfKpi")
@Transactional//事务处理注解
@RequestMapping(value = "/ReceiveSumOfKpi",method = {RequestMethod.GET,RequestMethod.POST})
public int ReceiveSumOfKpi(){
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
int sum=tobedoneService.ReceiveSumOfKpi(sysUser.getOrgId(),Constants.TODO);
System.out.println(sum);
return sum;
}
这样后端的接口就写好了,这里控制类没有写全,接口是/tobedone/tobedone下的ReceiveSumOfKpi。
前端设计:
我在前端和后端都实现了权限管理,这里不主要说权限管理的方法,主要说交互的方法。
api设计:
首先想往前端传数据,前端得拿到后端的接口,这就编写一个api用来调用后端的接口。
java
import axios from 'axios';
import { getToken } from '/@/utils/auth';
import { useGlobSetting } from '/@/hooks/setting';
const globSetting = useGlobSetting();
const baseUrl = globSetting.apiUrl;
// 创建axios实例
const api = axios.create({
baseURL: baseUrl + '/tobedone/tobedone', // 使用全局配置的API基础URL
headers: {
'Content-Type': 'application/json'
},
timeout: 15000 // 请求超时时间
});
// 请求拦截器 - 添加授权令牌
api.interceptors.request.use(
(config) => {
const token = getToken();
if (token) {
// 在请求头中添加token
config.headers['X-Access-Token'] = token;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器 - 处理错误
api.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
console.error('API请求错误:', error);
return Promise.reject(error);
}
);
// 待办看板相关api
export const KanBanApi = {
// 获取接收年度关键指标列表的总数
ReceiveSumOfKpi: () => api.get('/ReceiveSumOfKpi'),
// 获取汇报年度关键指标列表的总数
ReportSumOfKpi: () => api.get('/ReportSumOfKpi'),
// 获取审批年度关键指标列表的总数
ApprovalSumOfKpi: () => api.post('/ApprovalSumOfKpi'),
//年度关键指标记录的总数
KpiSum: () => api.get('/KpiSum'),
};
export default api;
主要就是创建axios实例,请求拦截器和响应拦截器。核心功能还是export导出的相关api。
前面有点像方法名,用来前端调用这个方法,然后会使用api.get去调用后端的接口,这里没有/tobedone/tobedone是因为在创建axios实例的时候设置了baseUrl。
现在api也设计好了,就开始写前端页面。
页面设计:
html
<template>
<div class="card-container">
<div class="card-item" v-auth="'Receive_kpi:yx_Receive_kpi:ReceiveSumOfKpi'">
<div class="text">{{cardList[0].title}}</div>
<div class="count">{{cardList[0].count}}</div>
</div>
<div class="card-item" v-auth="'Report_kpi:yx_Report_kpi:ReportSumOfKpi'">
<div class="text">{{cardList[1].title}}</div>
<div class="count">{{cardList[1].count}}</div>
</div>
<div class="card-item" v-auth="'Approval_kpi:yx_Approval_kpi:ApprovalSumOfKpi'">
<div class="text">{{cardList[2].title}}</div>
<div class="count">{{cardList[2].count}}</div>
</div>
<div class="card-item" v-auth="'kpi:yx_kpi:KpiSum'">
<div class="text">{{cardList[3].title}}</div>
<div class="count">{{cardList[3].count}}</div>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, ref} from 'vue';
import {KanBanApi} from "@/views/ToDoKanBan/ToDoKanBan.api";
const cardList=ref([
{title:"接收年度关键指标",count:null},
{title:"汇报年度关键指标",count:null},
{title:"审批年度关键指标",count:null},
{title:"年度关键指标列表", count:null}
]);
// 页面加载时获取接收年度关键指标记录的总数
onMounted(async () => {
try {
const responseOfReceiveSumOfKpi = await KanBanApi.ReceiveSumOfKpi();
const responseOfReportSumOfKpi = await KanBanApi.ReportSumOfKpi();
const responseOfApprovalSumOfKpi = await KanBanApi.ApprovalSumOfKpi();
const responseKpiSum= await KanBanApi.KpiSum();
// 如果返回的直接就是数字
cardList.value[0].count = responseOfReceiveSumOfKpi;
cardList.value[1].count = responseOfReportSumOfKpi;
cardList.value[2].count = responseOfApprovalSumOfKpi;
cardList.value[3].count = responseKpiSum;
} catch (error) {
console.error('获取指标数量失败:', error);
}
});
</script>
<style scoped lang="less">
.card-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
}
.card-item {
width: calc(25% - 20px); /* 每行4个,可根据需求调整 */
background-color: #0b3b9c;
border-radius: 8px;
color: #fff;
text-align: center;
padding: 15px;
box-sizing: border-box;
}
.text {
margin-bottom: 6px;
}
.count {
font-size: 14px;
}
</style>
前端模板主要用来显示cardList的title和count,而count的显示方法在页面加载的时候就进行方法挂载了。调用了四个api,分别赋值给cardList.value[0].count等。这样就可以显示了。实现了前后端的交互。
传参的时候传字段和传对象的区别和方法?
传参的时候传递字段:
传递字段就是将多个独立的字段作为方法的参数逐个传递。
演示代码:
java
// 传递单个字段
public class UserService {
// 方法接收多个独立字段作为参数
public void createUser(String username, int age, String email) {
System.out.println("创建用户: " + username + ", 年龄: " + age + ", 邮箱: " + email);
}
}
// 调用方式
public class Main {
public static void main(String[] args) {
UserService service = new UserService();
// 逐个传递字段
service.createUser("张三", 25, "zhangsan@example.com");
}
}
传递字段的好处就是简单直接,适合参数数量少的且关联性低的场景,调用的时候直观,无需创建额外的对象。缺点就是参数数量多的时候方法签名冗长,当参数需要新增或者修改的时候需要修改签名,违反开闭原则,并且如果参数顺序错误也会导致错误。
传参的时候传递对象:
传递对象就是将多个字段封装到一个对象中,方法仅接收该对象作为参数。
演示代码:
java
// 1. 定义封装参数的对象
public class User {
private String username;
private int age;
private String email;
// 构造方法、getter和setter
public User(String username, int age, String email) {
this.username = username;
this.age = age;
this.email = email;
}
// getter和setter省略...
}
// 2. 方法接收对象作为参数
public class UserService {
// 仅接收一个User对象
public void createUser(User user) {
System.out.println("创建用户: " + user.getUsername() + ", 年龄: " + user.getAge() + ", 邮箱: " + user.getEmail());
}
}
// 3. 调用方式
public class Main {
public static void main(String[] args) {
UserService service = new UserService();
// 创建对象并传递
User user = new User("张三", 25, "zhangsan@example.com");
service.createUser(user);
}
}
传递参数的时候关联性强,无需修改方法的签名,参数增减的时候只需要扩展字段就可以了。避免参数顺序混淆,能够降低出错概率。缺点就是需要额外定义对象类,增加代码量。