Vue + Element UI + Lumen 实现通用表格功
前言
最近在做一个前后端分离的项目,前端使用 Vue
+ Element UI
,而后端则使用 Lumen
做接口开发,其中分页是必不可少的一部分,本文就介绍如何基于以上环境做一个简单、可复用的分页功能。
先说后端
后端做的事情不多,只需要接受几个参数,根据参数来获取数据即可。
需要获取的参数如下:
-
pageSize
(一页数据的数量) -
pageIndex
(第几页的数据)
然后就可以根据这两个参数计算出偏移量,再从数据库中取出相应的数据。
假如现在给出的参数为:pageSize=10
,pageIndex = 2
,也就是说每一页要10条记录,要第二页。
计算偏移量的公式为:pageSize * (pageIndex - 1)
。
基本代码如下:
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="php" contenteditable="false" cid="n16" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public function getUser(Request pageSize =
pageIndex =
offset =
pageIndex - 1);
return User::offset(pageSize)
->get();
}</pre>
后端基本上只需要做这么多,就可以完成一个分页的功能了,但还是有几处地方需要改进一下:
-
给参数一个默认值
-
前端还需要知道整个表的数据的总数
-
把分页做成一个公用的函数
改进后的代码如下:
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="php" contenteditable="false" cid="n26" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">private default_page_index = 1;
// 公用分页
public function pagination(list) {
request->input('pageSize',
pageIndex =
this->default_page_index);
pageSize * (
total =
list =
offset)
->limit(list->get(),
'total' => request) {
this->pagination(
list);
}</pre>
再说前端
前端相对于需要做的事情就比较多了,需要考虑几点:
-
获取数据时需要带上分页的参数
-
分页参数需要进行本地持久化,以免刷新页面回到第一页(后端设置的默认值)
-
同样要抽象出一个通用的分页组件
获取数据
这里我们用 vuex
来管理状态,然后在请求时带上分页数据:
store.js:
注意:
- 这里为了方便展示代码,并没有使用模块化,项目中,最好将使用模块化方便管理。
- 这里默认读者清楚 ES6 的语法
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="javascript" contenteditable="false" cid="n46" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">export default new vuex.Store({
state : {
user : {
list: [],
total: 0,
pageIndex: 1,
pageSize: 10,
}
},
mutations: {
updateUser(state, data) {
state.user = {
...state.user,
...data,
}
},
},
actions: {
async getUser({commit,state,getters}) {
// axios.get('/user',getters.requestData(state.user))
commit('updateUser',res);
},
},
getters:{
requestData(state) {
return (origin) => {
const {
pageIndex,
pageSize,
} = origin;
const data = {
pageIndex,
pageSize,
};
return data;
}
},
}
})</pre>
数据持久化
现在如何获取数据已经搞定了,数据持久化我使用 vuex-localstorage,安装后,只需要在上面代码的基础上添加:
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="javascript" contenteditable="false" cid="n49" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import createPersist from 'vuex-localstorage'
export default new vuex.Store({
// 接着上面的
plugins: [createPersist({
namespace: 'studio-user',
initialState: {},
// ONE_WEEK
expires: 7 * 24 * 60 * 60 * 1e3
})]
})</pre>
公用分页组件
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="html" contenteditable="false" cid="n51" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><template>
<el-Pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="module.total"
:current-page.sync="module.pageIndex"
:page-sizes="module.pageSizes"
:page-size.sync="module.pageSize"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
</el-Pagination>
</template>
<script>
export default {
props: {
module: Object
},
methods: {
getData() {
this.$emit("get-data");
},
handleCurrentChange() {
this.getData();
},
handleSizeChange(val) {
this.getData();
}
}
};
</script></pre>
使用分页组件
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="html" contenteditable="false" cid="n53" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><template>
<div class="container">
<el-table
:data="user.list"
style="width: 100%;"
<el-table-column
v-for="(item,index) in columns"
:key="index"
:prop="item.prop"
:label="item.label"
align="center"
/>
</el-table>
<pagination
:module="user"
@get-data="getData"
/>
</div>
</template>
<script>
import Pagination from "@/common/components/Pagination";
import { mapActions, mapState } from "vuex";
export default {
components: {
Pagination,
},
data: () => ({
columns: [
{
prop: "name",
label: "姓名"
},
{
prop: "性别",
label: "sex"
},
{
prop: "年龄",
label: "age"
},
]
}),
created() {
this.getData();
},
methods: {
...mapActions({
getData : "getUser",
})
},
computed: {
...mapState(["user"])
}
};
</script></pre>
后记
将一些常用的功能抽象出来,打造一个自己的工具库,从而使开发效率更高。
最后,贴上刚放假时立的 Flag:寒假待写文章。
最后的最后,安利一下文章开头说的项目:清技背单词,上面的代码就是从这个项目中copy
出来后略作修改的。
感谢观看,希望我的文章能对您有一些帮助!