这里给大家分享我在网上总结出来的一些知识 ,希望对大家有所帮助
前言
在开发管理后台过程中 ,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的 ,如获取列表数据 ,分页 ,筛选功能这些基本功能 。而不同的是呈现出来的数据项 。还有一些操作按钮。
如何获取列表数据
思考一番 ,让useList函数接收一个listRequestFn参数 ,用于请求列表中的数据 。
定义一个list变量 ,用于存放网络请求回来的数据内容 ,由于在内部无法直接确定列表数据类型 ,通过泛型的方式让外部提供列表数据类型 。
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
const list = ref<ItemType[]>([]);
}
在useList中创建一个loadData函数 ,用于调用获取数据函数,该函数接收一个参数用于获取指定页数的数据(可选 ,默认为curPage的值) 。
执行流程
设置加载状态
调用外部传入的函数 ,将获取到的数据赋值到list和total中
关闭加载态
这里使用了 async/await 语法,假设请求出错 、解构出错情况会走 catch 代码块 ,再关闭加载态
这里需要注意 ,传入的 listRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
// 数据
const list = ref<ItemType[]>([]);
// 过滤数据
// 获取列表数据
const loadData = async (page = curPage.value) => {
// 设置加载中
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page);
list.value = data;
total.value = count;
} catch (error) {
console.log("请求出错了", "error");
} finally {
// 关闭加载中
loading.value = false;
}
};
}
别忘了,还有切换分页要处理
使用watch函数监听数据 ,当curPage ,pageSize的值发生改变时调用loadData函数获取新的数据 。
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
// 监听分页数据改变
watch([curPage, pageSize], () => {
loadData(curPage.value);
});
}
现在实现了基本的列表数据获取
实现数据筛选器
在庞大的数据列表中 ,数据筛选是必不可少的功能
通常 ,我会将筛选条件字段定义在一个ref中 ,在请求时将ref丢到请求函数即可 。
在 useList 函数中 ,第二个参数接收一个filterOption对象 ,对应列表中的筛选条件字段 。
调整一下loadData函数 ,在请求函数中传入filterOption对象即可
注意 ,传入的 listRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
const loadData = async (page = curPage.value) => {
// 设置加载中
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page, filterOption.value);
list.value = data;
total.value = count;
} catch (error) {
console.log("请求出错了", "error");
} finally {
// 关闭加载中
loading.value = false;
}
};
}
注意,这里 filterOption 参数类型需要的是 ref 类型 ,否则会丢失响应式 无法正常工作
清空筛选器字段
在页面中 ,有一个重置的按钮,用于清空筛选条件 。这个重复的动作可以交给 reset 函数处理 。
通过使用 Reflect 将所有值设定为undefined ,再重新请求一次数据 。
什么是 Reflect?看看这一篇文章Reflect 映射对象
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
const reset = () => {
if (!filterOption.value) return;
const keys = Reflect.ownKeys(filterOption.value);
filterOption.value = {} as FilterOption;
keys.forEach((key) => {
Reflect.set(filterOption.value!, key, undefined);
});
loadData();
};
}
导出功能
除了对数据的查看 ,有些界面还需要有导出数据功能(例如导出 csv,excel 文件) ,我们也把导出功能写到useList里
通常 ,导出功能是调用后端提供的导出Api获取一个文件下载地址 ,和loadData函数类似 ,从外部获取exportRequestFn函数来调用Api
在函数中 ,新增一个exportFile函数调用它。
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(
listRequestFn: Function,
filterOption: Ref<Object>,
exportRequestFn?: Function
) {
// 忽略其他代码
const exportFile = async () => {
if (!exportRequestFn) {
throw new Error("当前没有提供exportRequestFn函数");
}
if (typeof exportRequestFn !== "function") {
throw new Error("exportRequestFn必须是一个函数");
}
try {
const {
data: { link },
} = await exportRequestFn(filterOption.value);
window.open(link);
} catch (error) {
console.log("导出失败", "error");
}
};
}
注意 ,传入的 exportRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
优化
现在 ,整个useList已经满足了页面上的需求了 ,拥有了获取数据 ,筛选数据,导出数据 ,分页功能
还有一些细节方面 ,在上面所有代码中的try..catch中的catch代码片段并没有做任何的处理,只是简单的console.log一下
提供钩子
在useList新增一个 Options 对象参数 ,用于函数成功 、失败时执行指定钩子函数与输出消息内容 。
定义 Options 类型
export interface MessageType {
GET_DATA_IF_FAILED?: string;
GET_DATA_IF_SUCCEED?: string;
EXPORT_DATA_IF_FAILED?: string;
EXPORT_DATA_IF_SUCCEED?: string;
}
export interface OptionsType {
requestError?: () => void;
requestSuccess?: () => void;
message: MessageType;
}
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(
listRequestFn: Function,
filterOption: Ref<Object>,
exportRequestFn?: Function,
options? :OptionsType
) {
// ...
}
设置Options默认值
const DEFAULT_MESSAGE = {
GET_DATA_IF_FAILED: "获取列表数据失败",
EXPORT_DATA_IF_FAILED: "导出数据失败",
};
const DEFAULT_OPTIONS: OptionsType = {
message: DEFAULT_MESSAGE,
};
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(
listRequestFn: Function,
filterOption: Ref<Object>,
exportRequestFn?: Function,
options = DEFAULT_OPTIONS
) {
// ...
}
在没有传递钩子的情况霞 ,推荐设置默认的失败时信息显示
优化loadData,exportFile函数
基于 elementui 封装 message 方法
import { ElMessage, MessageOptions } from "element-plus";
export function message(message: string, option?: MessageOptions) {
ElMessage({ message, ...option });
}
export function warningMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "warning" });
}
export function errorMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "error" });
}
export function infoMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "info" });
}
loadData 函数
const loadData = async (page = curPage.value) => {
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page, filterOption.value);
list.value = data;
total.value = count;
// 执行成功钩子
options?.message?.GET_DATA_IF_SUCCEED &&
message(options.message.GET_DATA_IF_SUCCEED);
options?.requestSuccess?.();
} catch (error) {
options?.message?.GET_DATA_IF_FAILED &&
errorMessage(options.message.GET_DATA_IF_FAILED);
// 执行失败钩子
options?.requestError?.();
} finally {
loading.value = false;
}
};
exportFile 函数
const exportFile = async () => {
if (!exportRequestFn) {
throw new Error("当前没有提供exportRequestFn函数");
}
if (typeof exportRequestFn !== "function") {
throw new Error("exportRequestFn必须是一个函数");
}
try {
const {
data: { link },
} = await exportRequestFn(filterOption.value);
window.open(link);
// 显示信息
options?.message?.EXPORT_DATA_IF_SUCCEED &&
message(options.message.EXPORT_DATA_IF_SUCCEED);
// 执行成功钩子
options?.exportSuccess?.();
} catch (error) {
// 显示信息
options?.message?.EXPORT_DATA_IF_FAILED &&
errorMessage(options.message.EXPORT_DATA_IF_FAILED);
// 执行失败钩子
options?.exportError?.();
}
};
useList 使用方法
<template>
<el-collapse class="mb-6">
<el-collapse-item title="筛选条件" name="1">
<el-form label-position="left" label-width="90px" :model="filterOption">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="用户名">
<el-input
v-model="filterOption.name"
placeholder="筛选指定签名名称"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="注册时间">
<el-date-picker
v-model="filterOption.timeRange"
type="daterange"
unlink-panels
range-separator="到"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-row class="flex mt-4">
<el-button type="primary" @click="filter">筛选</el-button>
<el-button type="primary" @click="reset">重置</el-button>
</el-row>
</el-col>
</el-row>
</el-form>
</el-collapse-item>
</el-collapse>
<el-table v-loading="loading" :data="list" border>
<el-table-column label="用户名" min-width="110px">
<template #default="scope">
{{ scope.row.name }}
</template>
</el-table-column>
<el-table-column label="手机号码" min-width="130px">
<template #default="scope">
{{ scope.row.mobile || "未绑定手机号码" }}
</template>
</el-table-column>
<el-table-column label="邮箱地址" min-width="130px">
<template #default="scope">
{{ scope.row.email || "未绑定邮箱地址" }}
</template>
</el-table-column>
<el-table-column prop="createAt" label="注册时间" min-width="220px" />
<el-table-column width="200px" fixed="right" label="操作">
<template #default="scope">
<el-button type="primary" link @click="detail(scope.row)"
>详情</el-button
>
</template>
</el-table-column>
</el-table>
<div v-if="total > 0" class="flex justify-end mt-4">
<el-pagination
v-model:current-page="curPage"
v-model:page-size="pageSize"
background
layout="sizes, prev, pager, next"
:total="total"
:page-sizes="[10, 30, 50]"
/>
</div>
</template>
<script setup lang="ts">
import { UserInfoApi } from "@/network/api/User";
import useList from "@/lib/hooks/useList/index";
const filterOption = ref<UserInfoApi.FilterOptionType>({});
const {
list,
loading,
reset,
filter,
curPage,
pageSize,
reload,
total,
loadData,
} = useList<UserInfoApi.UserInfo[], UserInfoApi.FilterOptionType>(
UserInfoApi.list,
filterOption
);
</script>
本文useList的完整代码在github.com/QC2168/snip…
本文转载于:
https://juejin.cn/post/7172889961446768670
如果对您有所帮助 ,欢迎您点个关注 ,我会定时更新技术文档 ,大家一起讨论学习 ,一起进步 。
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布。任何个人或组织 ,在未征得本站同意时 ,禁止复制、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。