实现
- 适用于后台管理系统,其中使用了
elementUI的分页组件与table组件
import { cloneDeep, debounce } from "lodash";
import Vue from "vue";
function objectValueAllEmpty(obj = {}) {
return Object.values(obj).every(
(it) => it === "" || it === null || it === undefined
);
}
export function listFilterMixin({
query = {}, // 查询条件
page = { current: 1, size: 10 }, // 单独管理分页
body = {}, // 请求体
api = () => Promise.resolve({ rows: [], total: 0 }), // 请求接口
immediate = true, // 是否立即执行
isScroll = true, // 是否滚动加载
unique = "id", // 唯一标识
}) {
return {
data() {
return {
filterQuery: cloneDeep(query),
filterBody: cloneDeep(body),
renderList: [],
total: -1,
loading: false,
hasMore: true,
uniqueIds: new Set([]),
initPage: {
current: isScroll ? 1 : page.current,
size: isScroll ? 10 : page.size,
},
wholeQuery: {},
};
},
created() {
this.wholeQuery = Vue.observable({
...this.initPage,
...this.filterQuery,
});
immediate && this.initData();
},
computed: {
filter() {
return {
query: { ...this.filterQuery },
body: { ...this.filterBody },
};
},
allQuery() {
return {
...this.wholeQuery,
...this.filter.query,
...this.initPage,
};
},
},
methods: {
setUniqueData(list) {
list.forEach((itm) => {
if (!this.uniqueIds.has(itm[unique])) {
this.uniqueIds.add(itm[unique]);
this.renderList.push(itm);
}
});
},
async fetchData() {
this.loading = true;
try {
const qr = this.allQuery;
const bd = this.filter.body;
console.log("查询全部参数-----", qr, bd);
const { rows, total: _total } = await api(qr, bd);
this.total = _total;
if (rows.length) {
this.setUniqueData(rows);
}
} catch (error) {
console.warn(error);
} finally {
this.loading = false;
}
},
initData() {
this.hasMore = true;
this.initPage.current = 1;
this.renderList = [];
this.uniqueIds = new Set([]);
this.fetchData();
console.log("执行了初始化查找--------------");
},
loadMoreData() {
console.log("列表数据----", this.renderList, this.total);
if (this.renderList.length < this.total) {
this.initPage.current++;
this.fetchData();
} else {
this.hasMore = false;
}
},
pageChange() {
this.renderList = [];
this.uniqueIds = new Set([]);
this.fetchData();
},
resetData() {
const needInit =
objectValueAllEmpty(this.filter.query) &&
objectValueAllEmpty(this.filter.body);
this.hasMore = true;
this.initPage.current = 1;
Object.assign(this.filterQuery, query);
Object.assign(this.filterBody, body);
this.renderList = [];
this.uniqueIds = new Set([]);
needInit && this.initData();
},
},
watch: {
filter: {
handler: debounce(function () {
this.initData();
}, 300),
},
},
};
}
使用
<template>
<div>
<FilterBar
:options="options"
v-model="searchData"
@on-search="handleSearch"
@on-reset="handleReset"
/>
<ListTable
:tableConfig="tableConfig"
:operations="operations"
:tableData="tableData"
v-model="initPage"
:total="100"
/>
</div>
</template>
<script>
import FilterBar from "@/components/FilterBar/index.vue";
import ListTable from "@/components/ListTable/index.vue";
import { listFilterMixin } from "@/mixins/list-filter-mixin.js";
const listFilter = listFilterMixin({
page: { current: 1, size: 10 },
body: {
createTime: "",
},
isScroll: false,
});
export default {
name: "BusniessCapability",
mixins: [listFilter],
components: {
FilterBar,
ListTable,
},
data() {
return {
searchData: {
createTime: "",
},
options: [
{
type: "date",
label: "创建时间",
key: "createTime",
placeholder: "请选择创建时间",
},
],
tableConfig: [
{
prop: "workOrderType",
label: "业务能力支撑类型",
},
{
prop: "workOrderType",
label: "创建时间",
},
],
operations: [
{
label: "修改",
color: "#5bacff",
prop: "edit",
icon: "el-icon-edit",
fun: (row) => {
console.log("修改", this, row);
},
},
{
label: "详情",
color: "#5bacff",
prop: "detail",
icon: "el-icon-search",
fun: (row) => {
console.log("详情", this, row);
},
},
{
label: "删除",
color: "#f56c6d",
prop: "delete",
icon: "el-icon-delete",
fun: (row) => {
console.log("删除", this, row);
},
},
],
tableData: Array.from({ length: 100 }).map((_, idx) => ({
workOrderType: "工单标题1",
workOrderType: "问题描述1",
})),
};
},
methods: {
// 查询
handleSearch(args) {
Object.assign(this.filterBody, args);
console.log("查询参数-----", args);
},
// 重置
handleReset() {
this.resetData();
},
},
watch: {
initPage(nv) {
console.log("页码变化了", this.initPage);
Object.assign(this.wholeQuery, nv);
this.pageChange();
},
},
};
</script>
<style scoped lang="scss"></style>
自定义组件
FilterBar是一个永远筛选的区域,包含select、input、time-picker,可以自行增加
<template>
<div class="m-3">
<el-form
:model="searchData"
ref="queryForm"
size="small"
:inline="true"
label-width="68px"
>
<el-form-item
v-for="item in options"
:key="item.key"
:label="item.label"
:prop="item.key"
>
<el-input
v-if="item.type === 'input'"
v-model="searchData[item.key]"
:placeholder="item.placeholder"
clearable
v-bind="item.attributes"
/>
<el-select
v-if="item.type === 'select'"
v-model="searchData[item.key]"
:placeholder="item.placeholder"
clearable
v-bind="item.attributes"
>
<el-option
v-for="dict in item.options"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-date-picker
v-if="item.type === 'date'"
value-format="yyyy-MM-dd"
v-model="searchData[item.key]"
:placeholder="item.placeholder"
clearable
v-bind="item.attributes"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "FilterBar",
model: {
prop: "searchData",
},
props: {
searchData: Object,
options: {
type: Array,
default: () => [],
},
},
methods: {
handleQuery() {
this.$emit("on-search", this.searchData);
},
resetQuery() {
this.$refs.queryForm.resetFields();
this.$emit("on-reset");
},
},
data() {
return {};
},
};
</script>
<style scoped lang="scss"></style>
ListTable是一个展示数据的表格组件,包含了一个分页器
- 表格通过配置实现,也可以通过插槽自定义某个表格内容的显示
<template>
<div class="m-3">
<el-row>
<el-col :span="24">
<el-table
:data="tableData"
:max-height="itemHeight * visibleLine"
style="width: 100%"
stripe
:header-row-style="{ backgroundColor: '#ddd' }"
:header-cell-style="{
backgroundColor: '#ddd',
fontWeight: '900',
color: '#000',
}"
>
<el-table-column type="index" label="排序" />
<el-table-column
v-for="item in tableConfig"
:prop="item.prop"
:label="item.label"
:width="item.width"
class-name="content"
>
<template slot-scope="scope">
<!-- 定制内容 -->
<slot
v-if="item.isCustom"
:name="item.prop"
v-bind:scope="scope.row"
/>
<span v-else>{{ scope.row[item.prop] }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" class-name="actions">
<template slot-scope="scope">
<span class="flex items-center gap-2">
<span v-for="(btn, idx) in operations" :key="idx">
<el-popconfirm
v-if="btn.prop === 'delete'"
title="确定删除吗?"
@confirm="btn.fun(scope.row)"
>
<el-button
slot="reference"
:icon="btn.icon"
type="text"
size="small"
:style="{ color: btn.color }"
>{{ btn.label }}</el-button
>
</el-popconfirm>
<el-button
v-else
@click="btn.fun(scope.row)"
:icon="btn.icon"
type="text"
size="small"
:style="{ color: btn.color }"
>{{ btn.label }}</el-button
>
</span>
</span>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row>
<div class="pagination-container">
<el-pagination
background
:current-page="page.current"
:page-size="page.size"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[5, 10, 20, 30, 50]"
:pager-count="pageCounter"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-row>
</div>
</template>
<script>
export default {
name: "ListTable",
model: {
prop: "page",
event: "update:page",
},
props: {
tableData: {
type: Array,
default: () => [],
},
tableConfig: {
type: Array,
default: () => [],
},
operations: {
type: Array,
default: () => [],
},
total: {
type: Number,
default: 0,
},
visibleLine: {
type: Number,
default: 13,
},
page: {
type: Object,
default: () => ({ current: 1, size: 10 }),
},
},
computed: {
computedPage() {
return this.page;
},
},
data() {
return {
itemHeight: 0,
pageCounter: document.clientWidth < 992 ? 5 : 7,
};
},
methods: {
handleSizeChange(size) {
this.$emit("update:page", { current: 1, size });
},
handleCurrentChange(current) {
this.$emit("update:page", {
current,
size: this.computedPage.size,
});
},
initItemHeight() {
const oneVhHeight = Math.floor(window.innerHeight * 0.01);
this.itemHeight = oneVhHeight * 6;
console.log("表格项的高度-----", this.itemHeight);
},
},
created() {
this.initItemHeight();
},
};
</script>
<style scoped lang="scss">
.content {
.cell {
span {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
}
.pagination-container {
padding: 32px 16px;
}
</style>