Vue实战篇二十四:分页显示

系列文章目录

Vue基础篇一:编写第一个Vue程序
Vue基础篇二:Vue组件的核心概念
Vue基础篇三:Vue的计算属性与侦听器
Vue基础篇四:Vue的生命周期(秒杀案例实战)
Vue基础篇五:Vue的指令
Vue基础篇六:Vue使用JSX进行动态渲染
Vue提高篇一:使用Vuex进行状态管理
Vue提高篇二:使用vue-router实现静态路由
Vue提高篇三:使用vue-router实现动态路由
Vue提高篇四:使用Element UI组件库
Vue提高篇五:使用Jest进行单元测试
Vue提高篇六: 使用Vetur+ESLint+Prettier插件提升开发效率
Vue实战篇一: 使用Vue搭建注册登录界面
Vue实战篇二: 实现邮件验证码发送
Vue实战篇三:实现用户注册
Vue实战篇四:创建多步骤表单
Vue实战篇五:实现文件上传
Vue实战篇六:表格渲染动态数据
Vue实战篇七:表单校验
Vue实战篇八:实现弹出对话框进行交互
Vue实战篇九:使用省市区级联选择插件
Vue实战篇十:响应式布局
Vue实战篇十一:父组件获取子组件数据的常规方法
Vue实战篇十二:多项选择器的实际运用
Vue实战篇十三:实战分页组件
Vue实战篇十四:前端excel组件实现数据导入
Vue实战篇十五:表格数据多选在实际项目中的技巧
Vue实战篇十六:导航菜单
Vue实战篇十七:用树型组件实现一个知识目录
Vue实战篇十八:搭建一个知识库框架
Vue实战篇十九:使用printjs打印表单
Vue实战篇二十:自定义表格合计
Vue实战篇二十一:实战Prop的双向绑定
Vue实战篇二十二:生成二维码
Vue实战篇二十三:卡片风格与列表风格的切换


# 一、背景

  • 时候前端通过某个API接口发起的查询,后端会返回大量的数据。为了保证前后端的响应效率及前端的显示效果,我们需要实现分页显示。
    在这里插入图片描述

二、后端实现

  • 在后端Spring Boot项目中,应用了Mybatis-plus 3.4.0框架,我们将通过Mybatis-plus的分页插件来实现。
  • 以下代码我们以用户查询做为例子。

2.1 配置分页插件

  • 首先我们定义一个分页插件的配置类,并让Spirng进行管理。
/**
 * MybatisPlus分页插件配置类
 *
 * @author zhuhuix
 * @date 2022-04-07
 */

@Configuration
@ConditionalOnClass(value = {MybatisPlusInterceptor.class})
@Mapper
public class MybatisPlusConfig {
    // 3.4.0 版本以上使用MybatisPlusInterceptor
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2.2 定义与前端交互的查询条件传输对象

  • 定义一个与前端交互的传输对象类,在这个传输对象类中除了必要的查询条件外,还要增加当前查询第几页及每页查询条数。
/**
 * 用户查询条件
 *
 * @author zhuhuix
 * @date 2021-09-28
 *
 * @date 2022-04-04
 * 增加分页传入参数
 */
@ApiModel(value = "用户查询条件")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysUserQueryDto {

    @ApiModelProperty(value = "用户名")
    private String userName;

    @ApiModelProperty(value = "注册起始时间")
    private Long createTimeStart;

    @ApiModelProperty(value = "注册结束时间")
    private Long createTimeEnd;

    @ApiModelProperty(value = "当前页数")
    private Integer currentPage;

    @ApiModelProperty(value = "每页条数")
    private Integer pageSize;
}

2.3 定义返回给前端的数据传输对象

  • 除了以上查询条件外,我们再定义一个返回的数据传输对象类,在这个对象类中,需要增加分页的一些信息。
/**
 * 用户分页返回数据
 *
 * @author zhuhuix
 * @date 2022-04-07
 */

@ApiModel(value = "用户分页数据")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysUserDto {

    // 当前查询第几页
    private Integer currentPage;

	// 每页显示数量 
    private Integer pageSize;

	// 总页数=总数据量/每页显示数量
    private Long totalPage;

    private List<SysUser> sysUserList;

}

2.4 用户分页查询的接口及实现

  • 接下来我们来写分页查询的实现类
/**
 * 用户信息接口
 *
 * @author zhuhuix
 * @date 2020-04-03
 * 
 * @date 2022-04-07
 * 增加分页查询接口
 */
public interface SysUserService {

	...

    /**
     * 根据条件查询用户信息,并返回分页用户列表
     * @param sysUserQueryDto 查询条件
     * @return 分页用户数据
     */
    SysUserDto page(SysUserQueryDto sysUserQueryDto);


}

/**
 * 用户接口实现类
 *
 * @author zhuhuix
 * @date 2020-04-03
 * 
 * @date 2022-04-07
 * 实现分页查询
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class SysUserServiceImpl implements SysUserService {

    private final SysUserMapper sysUserMapper;
   
   	...
	
    @Override
    public SysUserDto page(SysUserQueryDto sysUserQueryDto) {
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(sysUserQueryDto.getUserName())) {
            queryWrapper.lambda().like(SysUser::getUserName, sysUserQueryDto.getUserName())
                    .or().like(SysUser::getNickName, sysUserQueryDto.getUserName());
        }
        if (!StringUtils.isEmpty(sysUserQueryDto.getCreateTimeStart())
                && !StringUtils.isEmpty(sysUserQueryDto.getCreateTimeEnd())) {
            queryWrapper.and(wrapper -> wrapper.lambda().between(SysUser::getCreateTime,
                    new Timestamp(sysUserQueryDto.getCreateTimeStart()),
                    new Timestamp(sysUserQueryDto.getCreateTimeEnd())));
        }

		// 根据前端的分页查询参数进行分页查询
        Page<SysUser> page = new Page<>(sysUserQueryDto.getCurrentPage(), sysUserQueryDto.getPageSize());
        sysUserMapper.selectPage(page, queryWrapper);

        SysUserDto sysUserDto = new SysUserDto();
        sysUserDto.setCurrentPage(sysUserQueryDto.getCurrentPage());
        sysUserDto.setPageSize(sysUserQueryDto.getPageSize());
        sysUserDto.setTotalPage(page.getTotal());
        sysUserDto.setSysUserList(page.getRecords());

        return sysUserDto;

    }

}

  • 在Controller层定义分页查询的API接口
/**
 * api用户信息
 *
 * @author zhuhuix
 * @date 2021-08-16
 * 
 * @date 2022-04-07
 * 增加分页查询接口
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/api/user")
@Api(tags = "用户信息接口")
public class SysUserController {

    private final SysUserService sysUserService;

    ...

    @ApiOperation("根据条件查询返回用户分页列表")
    @PostMapping("/page")
    public ResponseEntity<Object> getSysUserPage(@RequestBody SysUserQueryDto sysUserQueryDto) {
        return ResponseEntity.ok(sysUserService.page(sysUserQueryDto));
    }


}

在这里插入图片描述

三、前端实现

  • 我们需要将后端分页查询接口获取的数据,在用户查询的前端页面中使用Element UIel-pagination分页组件进行渲染。

3.1 前端添加Ap接口

import request from '@/utils/request'

...

// 获取用户分页信息接口
export function getUserPageList(params) {
  return request({
    url: '/api/user/page',
    method: 'post',
    data: JSON.stringify(params)
  })
}

3.2 前端页面添加分页组件进行渲染

  • 我们先看下前端分页组件的用法
      <el-pagination
            background    // 显示背景色 
            :current-page="currentPage"  // 当前页数
            :page-sizes="[5, 10, 15, 20]" // 每页显示条数的选项列表
            :page-size="pageSize"  // 每页显示条数
            layout="sizes,prev, pager, next" // 布局,分别为显示每页条数选项列表,前一页,当前页,后一页
            :total="totalPage" //总共页数
            @size-change="handleSizeChange" // 每页显示条数变化触发事件
            @current-change="handleCurrentChange" // 翻页后触发事件
          />
  • 掌握了分页组件的用法,我们只需要把分页组件添加到用户页面,并用新的分页查询替代原来的查询,将分页查询返回的分页信息赋值给分页组件对应的参数,这样就完整地实现了前端的分页查询。
  • 以下是该页面的完整代码
<template>
  <div class="app-container">
    <!--工具栏-->
    <div class="head-container">
      <!-- 搜索 -->
      <el-input
        v-model="userName"
        size="small"
        clearable
        placeholder="输入账号或用户名称搜索"
        style="width: 200px"
        class="filter-item"
        @keyup.enter.native="doQuery"
      />
      <el-date-picker
        v-model="createTime"
        :default-time="['00:00:00', '23:59:59']"
        type="daterange"
        range-separator=":"
        size="small"
        class="date-item"
        value-format="yyyy-MM-dd HH:mm:ss"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
      />
      <el-button
        class="filter-item"
        size="mini"
        type="success"
        icon="el-icon-search"
        @click="doQuery"
      >搜索</el-button>
      <el-button
        class="filter-item"
        size="mini"
        type="danger"
        icon="el-icon-circle-plus-outline"
        :disabled="selections.length === 0"
        @click="doDelete"
      >删除</el-button>
    </div>

    <el-row>
      <!--角色分配表单-->
      <el-dialog
        append-to-body
        :close-on-click-modal="false"
        :visible.sync="showDialog"
        title="角色分配"
        width="600px"
      >
        <el-form
          ref="form"
          :inline="true"
          :model="form"
          size="small"
          label-width="76px"
        >
          <el-form-item label="登录账号" prop="userName">
            <el-input v-model="form.userName" :disabled="true" />
          </el-form-item>
          <el-form-item label="昵称" prop="nickName">
            <el-input v-model="form.nickName" :disabled="true" />
          </el-form-item>
          <el-form-item style="margin-bottom: 0" label="角色" prop="userRoles">
            <el-select
              v-model="userRoles"
              style="width: 455px"
              multiple
              filterable
              placeholder="请选择"
              @remove-tag="deleteTag"
              @change="changeRole"
            >
              <el-option
                v-for="item in roles"
                :key="item.roleCode"
                :label="item.roleName"
                :value="item.id"
              />
            </el-select>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button type="text" @click="doCancel">取消</el-button>
          <el-button
            :loading="formLoading"
            type="primary"
            @click="doSubmit"
          >确认</el-button>
        </div>
      </el-dialog>
      <el-tabs v-model="activeName" type="border-card">
        <el-tab-pane label="用户列表" name="userList">
          <el-table
            ref="table"
            v-loading="loading"
            :data="users"
            style="width: 100%; font-size: 12px"
            @selection-change="selectionChangeHandler"
          >
            <el-table-column type="selection" width="55" />
            <el-table-column
              :show-overflow-tooltip="true"
              width="150"
              prop="userName"
              label="登录账号"
            />
            <el-table-column
              :show-overflow-tooltip="true"
              width="150"
              prop="nickName"
              label="用户昵称"
            />
            <el-table-column prop="gender" width="60" label="性别">
              <template slot-scope="scope">
                <el-tag v-if="scope.row.gender === 1" type="success"></el-tag>
                <el-tag v-if="scope.row.gender === 2" type="warning"></el-tag>
                <el-tag v-if="scope.row.gender === 0" type="info">未知</el-tag>
              </template>
            </el-table-column>
            <el-table-column
              :show-overflow-tooltip="true"
              prop="phone"
              width="150"
              label="电话"
            />
            <el-table-column
              :show-overflow-tooltip="true"
              prop="city"
              label="所在地区"
            >
              <template slot-scope="scope">
                <span>{{ scope.row.province }} {{ scope.row.city }}
                  {{ scope.row.country }}</span>
              </template>
            </el-table-column>
            <el-table-column
              :show-overflow-tooltip="true"
              prop="avatarUrl"
              width="80"
              label="头像"
            >
              <template slot-scope="scope">
                <img
                  :src="
                    scope.row.avatarUrl
                      ? baseApi + '/file/' + scope.row.avatarUrl
                      : Avatar
                  "
                  class="avatar"
                >
              </template>
            </el-table-column>
            <el-table-column
              :show-overflow-tooltip="true"
              prop="createTime"
              width="155"
              label="注册日期"
            >
              <template slot-scope="scope">
                <span>{{ parseTime(scope.row.createTime) }}</span>
              </template>
            </el-table-column>
            <el-table-column
              label="操作"
              width="160"
              align="center"
              fixed="right"
            >
              <template slot-scope="scope">
                <el-button
                  size="mini"
                  type="text"
                  round
                  @click="doAssignRole(scope.row.id)"
                >分配角色</el-button>
              </template>
            </el-table-column>
          </el-table>
          <!-- 分页组件 -->
          <el-pagination
            class="page"
            background
            :current-page="currentPage"
            :page-sizes="[5, 10, 15, 20]"
            :page-size="pageSize"
            layout="sizes,prev, pager, next"
            :total="totalPage"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
          />
        </el-tab-pane>
      </el-tabs>
    </el-row>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import Avatar from '@/assets/images/avatar.png'
import { parseTime } from '@/utils/index'
import { getUserPageList, deleteUser, getInfoById, getUserRoles, saveUserRoles } from '@/api/user'
import { getRoleList } from '@/api/role'
export default {
  name: '用户管理',
  data() {
    return {
      Avatar: Avatar,
      activeName: 'userList',
      showDialog: false,
      loading: false,
      formLoading: true,
      form: {},
      totalPage: 0,
      currentPage: 1,
      pageSize: 5,
      users: [],
      selections: [],
      userName: '',
      createTime: null,
      roles: [],
      userRoles: []
    }
  },
  computed: {
    ...mapGetters([
      'baseApi'
    ])
  },
  created() {

  },
  methods: {
    parseTime,
    doQuery() {
      this.users = []
      var param = { userName: this.userName }
      if (this.createTime != null) {
        param.createTimeStart = Date.parse(this.createTime[0])
        param.createTimeEnd = Date.parse(this.createTime[1])
      }
      param.pageSize = this.pageSize
      param.currentPage = this.currentPage
      // 替换成分页查询接口
      getUserPageList(param).then(res => {
        if (res) {
          this.totalPage = res.totalPage
          this.currentPage = res.currentPage
          this.users = res.sysUserList
        }
      })
    },
    // 每页条数改变
    handleSizeChange(val) {
      this.pageSize = val
      this.doQuery()
    },
    // 翻页
    handleCurrentChange(val) {
      this.currentPage = val
      this.doQuery()
    },
    doDelete() {
      const ids = []
      this.selections.forEach((res) => {
        ids.push(res.id)
      })
      this.$confirm(`确认删除这些用户吗?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() =>
        deleteUser(ids).then(res => {
          if (res) {
            this.$notify({
              title: '删除成功',
              type: 'success',
              duration: 2500
            })
            this.doQuery()
          }
        })
      ).catch(() => {
      })
    },
    // 选择改变
    selectionChangeHandler(val) {
      this.selections = val
    },
    doAssignRole(id) {
      this.form = {}
      this.userRoles = []
      this.roles = []
      this.showDialog = true
      this.formLoading = true
      getInfoById(id).then((res) => {
        this.form = { id: res.id, userName: res.userName, nickName: res.nickName, gender: res.gender, phone: res.phone }
        var param = {}
        getRoleList(param).then(res => {
          if (res) {
            this.roles = res
          }
          getUserRoles(id).then((res) => {
            if (res) {
              res.forEach(role => {
                this.userRoles.push(role.id)
              })
            }
            this.formLoading = false
          })
        })
      })
    },
    doCancel() {
      this.showDialog = false
      this.form = {}
    },
    doSubmit() {
      this.formLoading = true
      saveUserRoles(this.form.id, this.userRoles).then(() => {
        this.showDialog = false
        this.$notify({
          title: '保存成功',
          type: 'success',
          duration: 2500
        })
      })
    },
    deleteTag(value) {
      this.userRoles.forEach(function(data, index) {
        if (data.id === value) {
          this.userRoles.splice(index, value)
        }
      })
    },
    changeRole(value) {
      // console.log(this.userRoles)
    }
  }
}

</script>

<style rel="stylesheet/scss" lang="scss">
.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
}
.page{
  float: right;
  margin-top: 5px;
}
</style>

<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
  text-align: left;
}
</style>

四、 实现效果

在这里插入图片描述

五、源码

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>