SpringBoot+Vue豆寶社群前後端分離專案手把手實戰系列教程14---帖子列表分頁功能實現
SpringBoot+Vue豆寶社群前後端分離專案手把手實戰系列教程14---帖子列表分頁功能實現
豆寶社群專案實戰教程簡介
本專案實戰教程配有免費視訊教程,配套程式碼完全開源。手把手從零開始搭建一個目前應用最廣泛的Springboot+Vue前後端分離多使用者社群專案。本專案難度適中,為便於大家學習,每一集視訊教程對應在Github上的每一次提交。
專案首頁截圖
程式碼開源地址
視訊教程地址
前端技術棧
Vue Vuex Vue Router Axios Bulma Buefy Element Vditor DarkReader
後端技術棧
Spring Boot Mysql Mybatis MyBatis-Plus Spring Security JWT Lombok
分頁前端
1.src\components建立Pagination\index.vue
<template> <div :class="{ hidden: hidden }" class="pagination-container"> <el-pagination :background="background" :current-page.sync="currentPage" :page-size.sync="pageSize" :layout="layout" :page-sizes="pageSizes" :total="total" v-bind="$attrs" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </template> <script> import {scrollTo} from "@/utils/scroll-to"; export default { name: "Pagination", props: { total: { required: true, type: Number, }, page: { type: Number, default: 1, }, limit: { type: Number, default: 10, }, pageSizes: { type: Array, default() { return [5, 10, 20, 30, 50]; }, }, layout: { type: String, default: "total, sizes, prev, pager, next, jumper", // default: 'sizes, prev, pager, next, jumper' }, background: { type: Boolean, default: true, }, autoScroll: { type: Boolean, default: true, }, hidden: { type: Boolean, default: false, }, }, computed: { currentPage: { get() { return this.page; }, set(val) { this.$emit("update:page", val); }, }, pageSize: { get() { return this.limit; }, set(val) { this.$emit("update:limit", val); }, }, }, methods: { handleSizeChange(val) { this.$emit("pagination", { page: this.currentPage, limit: val }); if (this.autoScroll) { scrollTo(0, 800); } }, handleCurrentChange(val) { this.$emit("pagination", { page: val, limit: this.pageSize }); if (this.autoScroll) { scrollTo(0, 800); } }, }, }; </script> <style scoped> .pagination-container { /* background: #fff; */ padding: 5px 0px; } .pagination-container.hidden { display: none; } </style> 複製程式碼
2.src\utils建立scroll-to.js
Math.easeInOutQuad = function(t, b, c, d) { t /= d / 2 if (t < 1) { return c / 2 * t * t + b } t-- return -c / 2 * (t * (t - 2) - 1) + b } // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts var requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } })() /** * Because it's so ****ing difficult to detect the scrolling element, just move them all * @param {number} amount */ function move(amount) { document.documentElement.scrollTop = amount document.body.parentNode.scrollTop = amount document.body.scrollTop = amount } function position() { return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop } /** * @param {number} to * @param {number} duration * @param {Function} callback */ export function scrollTo(to, duration, callback) { const start = position() const change = to - start const increment = 20 let currentTime = 0 duration = (typeof (duration) === 'undefined') ? 500 : duration var animateScroll = function() { // increment the time currentTime += increment // find the value with the quadratic in-out easing function var val = Math.easeInOutQuad(currentTime, start, change, duration) // move the document.body move(val) // do the animation unless its over if (currentTime < duration) { requestAnimFrame(animateScroll) } else { if (callback && typeof (callback) === 'function') { // the animation is done so lets callback callback() } } } animateScroll() } 複製程式碼
3.修改src\views\post\index.vue
以下是index.vue的全部內容
<template> <div> <el-card shadow="never"> <div slot="header" class="clearfix"> <el-tabs v-model="activeName" @tab-click="handleClick"> <el-tab-pane label="最新主題" name="latest"> <article v-for="(item, index) in articleList" :key="index" class="media"> <div class="media-left"> <figure class="image is-48x48"> <img :src="`http://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`" style="border-radius: 5px;"> </figure> </div> <div class="media-content"> <div class=""> <p class="ellipsis is-ellipsis-1"> <el-tooltip class="item" effect="dark" :content="item.title" placement="top"> <router-link :to="{name:'post-detail',params:{id:item.id}}"> <span class="is-size-6">{{ item.title }}</span> </router-link> </el-tooltip> </p> </div> <nav class="level has-text-grey is-mobile is-size-7 mt-2"> <div class="level-left"> <div class="level-left"> <router-link class="level-item" :to="{ path: `/member/${item.username}/home` }"> {{ item.alias }} </router-link> <span class="mr-1"> 釋出於:{{ dayjs(item.createTime).format("YYYY/MM/DD") }} </span> <span v-for="(tag, index) in item.tags" :key="index" class="tag is-hidden-mobile is-success is-light mr-1" > <router-link :to="{ name: 'tag', params: { name: tag.name } }"> {{ "#" + tag.name }} </router-link> </span> <span class="is-hidden-mobile">瀏覽:{{ item.view }}</span> </div> </div> </nav> </div> <div class="media-right" /> </article> </el-tab-pane> <el-tab-pane label="熱門主題" name="hot"> <article v-for="(item, index) in articleList" :key="index" class="media"> <div class="media-left"> <figure class="image is-48x48"> <img :src="`http://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`" style="border-radius: 5px;"> </figure> </div> <div class="media-content"> <div class=""> <p class="ellipsis is-ellipsis-1"> <el-tooltip class="item" effect="dark" :content="item.title" placement="top"> <router-link :to="{name:'post-detail',params:{id:item.id}}"> <span class="is-size-6">{{ item.title }}</span> </router-link> </el-tooltip> </p> </div> <nav class="level has-text-grey is-mobile is-size-7 mt-2"> <div class="level-left"> <div class="level-left"> <router-link class="level-item" :to="{ path: `/member/${item.username}/home` }"> {{ item.alias }} </router-link> <span class="mr-1"> 釋出於:{{ dayjs(item.createTime).format("YYYY/MM/DD") }} </span> <span v-for="(tag, index) in item.tags" :key="index" class="tag is-hidden-mobile is-success is-light mr-1" > <router-link :to="{ name: 'tag', params: { name: tag.name } }"> {{ "#" + tag.name }} </router-link> </span> <span class="is-hidden-mobile">瀏覽:{{ item.view }}</span> </div> </div> </nav> </div> <div class="media-right" /> </article> </el-tab-pane> <el-tab-pane label="最近修改" name="update"> <article v-for="(item, index) in articleList" :key="index" class="media"> <div class="media-left"> <figure class="image is-48x48"> <img :src="`http://cn.gravatar.com/avatar/${item.userId}?s=164&d=monsterid`" style="border-radius: 5px;"> </figure> </div> <div class="media-content"> <div class=""> <p class="ellipsis is-ellipsis-1"> <el-tooltip class="item" effect="dark" :content="item.title" placement="top"> <router-link :to="{name:'post-detail',params:{id:item.id}}"> <span class="is-size-6">{{ item.title }}</span> </router-link> </el-tooltip> </p> </div> <nav class="level has-text-grey is-mobile is-size-7 mt-2"> <div class="level-left"> <div class="level-left"> <router-link class="level-item" :to="{ path: `/member/${item.username}/home` }"> {{ item.alias }} </router-link> <span class="mr-1"> 釋出於:{{ dayjs(item.createTime).format("YYYY/MM/DD") }} </span> <span v-for="(tag, index) in item.tags" :key="index" class="tag is-hidden-mobile is-success is-light mr-1" > <router-link :to="{ name: 'tag', params: { name: tag.name } }"> {{ "#" + tag.name }} </router-link> </span> <span class="is-hidden-mobile">瀏覽:{{ item.view }}</span> </div> </div> </nav> </div> <div class="media-right" /> </article> </el-tab-pane> </el-tabs> </div> <!--分頁--> <pagination v-show="page.total > 0" :total="page.total" :page.sync="page.current" :limit.sync="page.size" @pagination="init" /> </el-card> </div> </template> <script> import { getList } from '@/api/post' import Pagination from '@/components/Pagination' export default { name: 'TopicList', components: { Pagination }, data() { return { activeName: 'latest', articleList: [], page: { current: 1, size: 10, total: 0, tab: 'latest' } } }, created() { this.init(this.tab) }, methods: { init(tab) { getList(this.page.current, this.page.size, tab).then((response) => { const { data } = response this.page.current = data.current this.page.total = data.total this.page.size = data.size this.articleList = data.records }) }, handleClick(tab) { this.init(tab.name) } } } </script> <style scoped> </style> 複製程式碼
4.測試問題
測試頁面還沒分頁,需要後端完成分頁後顯示
分頁後端
MybatisPlusConfig
@Configuration public class MybatisPlusConfig { /** * 新的分頁外掛,一緩和二緩遵循mybatis的規則, * 需要設定 MybatisConfiguration#useDeprecatedExecutor = false 避免快取出現問題(該屬性會在舊外掛移除後一同移除) */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } } 複製程式碼
測試頁面
「其他文章」
- 稀土掘金點石成金公益計劃系列-合集
- 一份非官方的稀土掘金社群活動攻略
- 順豐疫情管控區域-截止至0914
- 稀土掘金小鋪-獎品回收活動
- 我又聽到有人說:主要原因是人不行
- 功能管理(Feature management)中的 Keystone 模式
- 美團簡單版動態執行緒池原始碼實現
- 基於Svelte Ui中後臺解決方案SvelteAdmin
- 記一次圖片中繁體文字轉簡體的嘗試
- Elasticsearch:分詞器中的 token 過濾器使用示例
- 如果稀土掘金App要更新“釋出文章”功能,那麼入口會設定在哪裡?
- 企業級GIT分支管控方案
- [極致使用者體驗] 讓你的網頁,適配微信大字號模式!體驗超好,快來收藏
- Webpack最佳入門實踐
- NodeJS 基於 Dapr 構建雲原生微服務應用,從 0 到 1 快速上手指南
- Figma自編教程第三篇(也是做產品實習生的第三天)
- 回憶曾跨過的坎:開局就被要求給頁面做效能優化
- 快上車!搭建一個屬於自己的元件庫!
- 中國程式設計師的技術管理之痛:技術、管理兩不靠,核心價值從何找?丨掘金夜談
- 卷不動了的大前端,未來趨勢在哪裡?丨掘金夜談