项目笔记,教学视频来源于B站青戈
https://www.bilibili.com/video/BV1H14y1S7YV
保证前面的都功能都实现后,接着往下走。
查 +分页
接下来,实现前端页面分页功能。
前端分页组件
打开elementplus官网,找到合适的分页组件,其对应代码如下
<template><el-pagination background layout="prev, pager, next" :total="1000" />
</template>
放到前端的表格组件下方,加个div包裹。
js加个变量,因为total总记录数量,应该是从数据库拿到的,是动态的。
<!-- 分页组件--><div><el-pagination background layout="prev, pager, next" :total="total" /></div>const total = ref(0) //总记录数,初始为0
然后打开后端项目,实现分页查询,需要与SQL语句配合,而且浏览器的路由也可以修改后缀。
后端SQL语句修改
首先是数据库的SQL语句,复制上个SQL代码,稍加修改,让其实现分页,借助LIMIT
这个分页函数后面的两个参数,第一个是开始序号,第二个是每页放置多少条记录。
select * from user where name like ‘%张%’ or date like ‘%张%’ or address like ‘%张%’ or user_no like ‘%张%’ LIMIT 1,2
接着尝试修改后端的SQL代码,
在UserMapper新增SQL代码;
注意@Param
在导入包的时候,要选择有apache
单词的包,选错会导致报错。
@Select("select * from user where name like concat('%',#{name},'%') or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%') limit #{start}, #{pageSize}")
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数
在UserController这个文件添加以下代码
@GetMapping("/page") //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){List<User> userList = userMapper.selectPage( name,start,pageSize);return Result.success(userList);}
前端SQL相关修改
后端配置得差不多,前端也进行相应修改,来到HomeView.vue ,把数据请求的路由更改成与后端SQL语句还参数一致的
const load = () =>{request.get('user/page?name=' + input.value + '&start=1&pageSize=2').then(res =>{ // request.get('user/all?name=' + input.value).then(res =>{ //name改成inputconsole.log(res)state.tableData = res.data})
}
然后数据库、前端、后端,都运行,打开前端项目生成的本地网址,若表格出现数据,则这一步成功。
但是上面的分页参数&start=1&pageSize=2
,是静态的写死的,我们希望这个分页参数是可以根据用户手动选择的,于是要考虑运用分页组件的参数。
找到elementplus前端分页组件的代码,加上两个数据绑定
原来
<el-pagination layout="prev, pager, next" :total="total"/>修改
<el-pagination layout="prev, pager, next" v-model:current-page="pageNum" v-model:page-size="pageSize" :page-size="[1,2,5]" :total="total"/>
在js加上这两个变量,为了把页码的两个参数变成动态的,初步修改如下
const pageNum = ref(1) //页码。最小的是1
const pageSize = ref(1) //页码大小,实际上数据库无记录可以显示零条,但是理论上最起码显示一条const load = () => {let start = pageNum.value - 1 //数据库的记录序号从0开始,但是前端页面数据从1开始request.get('user/page?name=' + input.value + '&start='+ start +'&pageSize=' + pageSize.value).then(res => { //省略代码})
}
去看前端的分页,但是发现没反应。
首先因为组件的total 变量数值默认是 零,但数据库目前的总数量是2,故手动修改。
const total = ref(2) //总记录数
其次,分页没有触发load函数故,回到elementplus官网,找到分页切换触发的两个函数,所以修改如下
原来(其他属性省略没写)<el-pagination layout="prev, pager, next" / >修改后<el-pagination layout="sizes,prev, pager, next" @size-change="load" @current-change="load"/>
补充:
layout
属性有多个属性值,它们有不同的功能,从左到右依次为
- total,表示后端数据的总条数,即页面上的 “ 共400条 ”
- sizes,表示每页能容纳多少条数据,即页面上的下拉选框 “ 100条/页 ”
- prev,表示向前翻一页,即页面上的 “ < ”
- pager,表示指定翻到哪一页,即页面上的 “ 1 2 3 4 ”
- next,表示向后翻一页,即页面上的 “ > ”
- jumper,表示直接跳去哪一页,即页面上的 “ 前往 3 页 ”
二个核心事件
- @size-change事件,当你使用下拉选框改变page-size属性时,它能监听到page-size属性的变化,并立刻将最新的值自动传给的事件处理函数,你甚至无需给它手动传参
- @current-change事件,当你改变current-page属性时,它能监听到current-page属性的变化,并立刻将最新的值自动传给相应的事件处理函数,你甚至无需给它手动传参
然后,发现,分页组件的文案都是英文,想改成中文,可以使用elementplus的国际化功能。来到前端项目的main.js ,添加下面的代码
import zhCn from 'element-plus/dist/locale/zh-cn.min'// app.use(ElementPlus) //原来的
app.use(ElementPlus,{ //修改的locale: zhCn,
})
一个新的问题,随着数据库记录数量的增多,发现最后一条记录会被吞掉,原因是数据库页的起始序号计算有问题
原来
const load = () => {let start = pageNum.value - 1 //数据库的记录序号从0开始,但是前端页面数据从1开始//省略代码
}修改
const load = () => {let start = (pageNum.value - 1)*pageSize.value //数据库的记录序号从0开始,但是前端页面数据从1开始,为了避免吞数据,需要乘每页的数量//省略代码
}
动态total 修改
下一个问题,total的数值,目前还是静态写死的,为了改成动态的,需要从数据库中查出具体数值并显示到前端上。
第一步,后端项目,在UserMapper文件新增代码(复制粘贴上面的SQL语句,然后修改)如下
@Select("select count(id) from user where name like concat('%',#{name},'%') or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%')")
Integer selectTotal(@Param("name") String name); //一个参数,函数返回值是integer类型
第二步,后端项目,UseController文件,新增代码,主要是增加total这个变量,把数值顺利传递到前端,修改如下
原来
@GetMapping("/page") //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】
public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){List<User> userList = userMapper.selectPage( name,start,pageSize);return Result.success(userList);
}修改
@GetMapping("/page") //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】
public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){List<User> userList = userMapper.selectPage( name,start,pageSize);Integer total = userMapper.selectTotal(name);Map<String,Object> map= new HashMap<>();map.put("list",userList);map.put("total",total);return Result.success(map);
}
然后,重新访问前端页面,发现表格里面的数据没有显示出来。
为什么?因为刚才修改的Controller文件,把返回的数据包裹成map类型(key:value),里面有两个key,如下图,data里面有两个对象,需要进一步访问。
所以,前端访问data的代码要稍加修改,在前端项目,HomeView.vue修改
原来
<el-pagination layout="sizes,prev, pager, next"/>const load = () => {//省略代码({state.tableData = res.data})
}修改(layout加上total这个布局,展示一共有多少条数据)
<el-pagination layout="sizes,prev, pager, next,total"/>const load = () => {//省略代码({state.tableData = res.data.list //修改这里total.value = res.data.total //添加这个语句 })
}
补充说明:layout等号右边的单词,排序也有是有讲究的,会根据你排放的顺序调整哦
增
刚开始前端的增删改查,都是在前端单方面的简易模拟,接下来与数据库挂钩。
先做增加功能。
增加的数据中,有“日期”信息,之前为了简单,只用了字符串String类型,但是实际上是用专门的日期组件,更加方便美观。
在elementplus找到日期选择器组件,复制粘贴然后根据自身需求修改代码,如下
<el-date-picker v-model="state.form.data" type="date" placeholder="选择一个日期" />
来到后端,把数据库SQL语句写好,来到UseMapper,新增代码如下
@Insert("insert into user(name,date,address,user_no) values( #{name} , #{date} ,#{address},#{userNo} )")
void insert(User user);
来到UseController,新增代码如下
@PostMapping("/save")public Result save(@RequestBody User user){userMapper.insert(user);return Result.success();}
至此,后端代码ok,需要修改前端的相应代码。
来到前端项目HomeViews.vue ,修改save()函数
原来
const save = () => {if (globalIndex.value > -1) { //编辑数据state.tableData[globalIndex.value] = state.form;globalIndex = ref(-1)} else { //新增数据// 向表格添加数据state.tableData.push(state.form);}// 关闭弹窗dialogFormVisible.value = false;
}修改
const save = () => {console.log(state.form)//打印当前弹窗的数据内容request.post('/user/save',state.form).then(res=>{if (res.code === '200'){ElMessage.success("保存成功")// 关闭弹窗dialogFormVisible.value = false;}else {ElMessage.error(res.msg)}},err=>{console.log("发送post请求失败:",err) //现在是后端返回错误})
}
但是运行的时候出现错误,如下
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.example.springboot.entity.User]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.springboot.entity.User` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]] with root cause
找了师兄才解决这个问题
在后端UseMapper添加三个注解,并且引入相关的包
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
接着就可以在数据库中查看,数据是否被增加进去。
发现,虽然添加成功了,但是添加的时间格式有问题
故需要修改时间格式,解决很简单,elementplus有时间格式的属性,修改如下
原来<el-date-pickerv-model="state.form.date"type="date"placeholder="选择日期"/>修改<el-date-pickerv-model="state.form.date"type="date"placeholder="选择日期"value-format="YYYY-MM-DD"/>
改
下一个功能,修改数据
跟“增加数据”步骤相似
后端项目,修改SQL语句,打开UseMapper,新增下面的代码(一定不能写错变量的名字!!!)
@Update("update user set name = #{name}, date = #{date}, address = #{address} ,user_no = #{userNo} where id = #{id} ")
void update(User user);
然后打开UseController,新增代码如下
@PutMapping("/update")public Result update(@RequestBody User user){ //函数名原来是saveuserMapper.update(user);return Result.success();}
相应地,前端网络请求也要进行相应修改,来到HomeView.vue
原来
const save = () => {console.log(state.form)request.post('/user/save',state.form).then(res=>{if (res.code === '200'){ElMessage.success("操作成功")// 关闭弹窗dialogFormVisible.value = false;}else {ElMessage.error(res.msg)}},err=>{console.log("发送post请求失败:",err)})
} 修改
const save = () => {console.log(state.form)request({url: state.form.id ? '/user/update':'/user/save',method: state.form.id ? 'PUT' :'POST',data: state.form}).then(res=>{if (res.code === '200'){ElMessage.success("操作成功")dialogFormVisible.value = false}else{ElMessage.error("操作失败", res.msg)}})
}
更新数据操作成功后,发现,虽然数据库的记录更新了,
但是前端页面还没有及时更新,所以调用load()
const save = () => {//省略代码if (res.code === '200'){ElMessage.success("操作成功")dialogFormVisible.value = falseload() //调用查询方法,及时更新数据 }//省略代码
}
补充一个细节,希望新增的记录能够显示在最前面,所以,SQL语句查询的时候,增加一个倒序排序
order by id desc
原来
@Select("select * from user where name like concat('%',#{name},'%') or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%') limit #{start}, #{pageSize}")
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数,注意相应类型修改
@Select("select * from user where name like concat('%',#{name},'%') or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%') order by desc limit #{start}, #{pageSize}")
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数,注意相应类型
删
后端
UseMapper新增代码
@Delete("delete from user where id = #{id}")void delete(Integer id);
UseController新增代码
@DeleteMapping("/del")public Result delete(@RequestParam Integer id){userMapper.delete(id);return Result.success();}
前端HomeView.vue (多余的代码省略了)
原来
<el-button @click.prevent="remove(scope.$index)">删除</el-button>
// 删除数据
const remove = (index) => {// 从index的位置开始,删除一行state.tableData.splice(index, 1)
}修改
<el-button @click.prevent="remove(scope.row.id)">删除</el-button>
// 删除数据
const remove = (id) => {request.delete('user/del?id='+id).then(res=>{if (res.code === '200'){ElMessage.success("操作成功")load() //调用查询方法,及时更新数据}else{ElMessage.error("操作失败", res.msg)}})
}
以上就是小白入门增删改查的基础操作了