背景
我们做的大量都是后台管理系统,对于这种系统,我们会发现,里面的业务其实相当单一。
大量都是类似这种, 搜索条件,表格,分页
每个页面都要处理上下翻页,搜索,mounted加载首页table数据等等逻辑
虽然不复杂,但很累。
因此,我们需要封装一些DSL
来降低我们的重复工作量
啥是CompositionAPI/hook
看看文档
我们的请求经过封装,大概是这种格式
// /api/index.js
import API from '@szyx/axios';
// 请求拦截器
API.interceptors.request.use((config) => {
// xxxx
return config;
});
// 结果拦截器
API.interceptors.response.use(
(response) => {
// 判断状态
if (response.status === '0') {
return response.data;
}
// 否则都是错了
throw new Error(JSON.stringify(response));
},
(error) => {
throw error;
},
);
export default {
getExample: (params) => API.POSTJSON('/xxx/xxx/xxx', params),
};
提取CompositionAPI
tableCompositions.js
import { ref, onMounted } from 'vue';
import API from '@/api/index';
export default function tableCompositon(params, urlName, pageDefault = 1, pageSizeDefault = 10) {
// 表格数据
const tableData = ref([]);
// 总量
const total = ref(0);
// 当前页面,默认1
const page = ref(pageDefault);
// 页面数量,默认10
const pageSize = ref(pageSizeDefault);
const getTableData = () => {
API[urlName]({
...params,
pageNum: page.value,
pageSize: pageSize.value,
}).then((data) => {
tableData.value = data.list;
page.value = data.pageNum;
pageSize.value = data.pageSize;
total.value = data.total;
});
};
// 页面加载请求
onMounted(() => { getTableData(); });
return {
tableData,
total,
page,
pageSize,
getTableData,
};
}
参数解析
-
params
入参是指当前页面除了页码
,分页数量
以外还需要的业务参数 -
urlName
是指请求的方法名,上面可以看到我们的请求封装完以后的导出都是一个个的方法,例如调用getExample
方法,则传递getExample
这个字符串即可。
return解析
-
tableData
页面的表格数据,表格肯定是数组类型的数据 -
total
一共多少条数据 -
page
当前页码 -
pageSize
当前分页数 -
getTableData
获取表格数据的方法
内容解析
应该很简单,都能看懂,定义了一些数据,一个请求方法,还有onMounted
中发起请求,然后return
了这些东西出去
使用
比如像这样的一个页面
省略掉表单弹窗相关的代码
<template>
<div class="flex row align-items-center header">
<div class="flex row align-items-center flex1">
<p class="title">用户名:</p>
<el-input
v-model="nameState.username"
placeholder="用户名称"
class="name-input"
@keyup.enter="getTableData"
/>
</div>
<div class="flex row ">
<el-button type="primary" @click="page.value = 1;
getTableData();">
搜索
</el-button>
<el-button plain @click="nameState.username = '';getTableData();">清空</el-button>
</div>
</div>
<div class="table">
<el-table :data="tableData" border stripe
:header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
>
<el-table-column prop="name" label="用户名" align="center" />
<el-table-column prop="roleName" label="角色名称" align="center" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="primary" v-permission="'100001002'" @click="editUser(scope.row)">
修改
</el-button>
<el-button type="danger" v-permission="'100001003'" @click="deleteUser(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<Pagination
class="pagination"
v-model:total="total"
v-model:page="page"
v-model:limit="pageSize"
@pagination="getTableData"
/>
</template>
<script>
import {
defineComponent, reactive,
} from 'vue';
import Pagination from '@/components/Pagination/index.vue';
import tableCompositions from '@/compositions/tableCompositions';
export default defineComponent({
components: { Pagination },
setup() {
// 用户名称
const nameState = reactive({
username: '',
});
// 获取table数据
const {
tableData, getTableData, total, pageSize, page,
} = tableCompositions(
nameState,
'getUserList',
);
return {
total,
pageSize,
page,
nameState,
tableData,
getTableData,
};
},
});
</script>
大概不到100行的代码,就结束了。
js基本没什么逻辑。只需要定义除page
和pageSize
以外还需要的业务参数
比如我这个页面还需要一个用户名
const nameState = reactive({
username: '',
});
然后将nameState
作为参数传递给tableCompositions
,获取所有的东西
const {
tableData, getTableData, total, pageSize, page,
} = tableCompositions(
nameState,
'getUserList',
);
然后tableData
直接丢给el-table
就行了
pagination
至于pagination
,其实直接用el-pagination
就可以了
我们也只是把一些常用的放进去,包了一层而已
Pagination/index.vue
<template>
<el-pagination
:background="background"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</template>
<script>
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number,
},
page: {
type: Number,
default: 1,
},
limit: {
type: Number,
default: 20,
},
pageSizes: {
type: Array,
default() {
return [5, 10, 20, 30, 50];
},
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper',
},
background: {
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) {
console.log('handleSizeChange', val);
this.$emit('pagination', { page: this.currentPage, limit: val });
},
handleCurrentChange(val) {
console.log('handleCurrentChange', val);
this.$emit('pagination', { page: val, limit: this.pageSize });
},
},
};
</script>
所以上下页的逻辑,往上一绑,就结束了
<Pagination
class="pagination"
v-model:total="total"
v-model:page="page"
v-model:limit="pageSize"
@pagination="getTableData"
/>
react也一样,我这里就不放react代码了,可以自己封装一下