Kapselung von Tischhaken in Vue3
Vorwort
Bei der Entwicklung des Verwaltungs-Backends werden Sie auf jeden Fall auf viele Seiten zum Hinzufügen, Löschen, Ändern und Abfragen stoßen, und die meisten Logiken dieser Seiten sind gleich, z. B. grundlegende Funktionen wie das Abrufen von Listendaten, Paging usw Filterfunktionen. Der Unterschied besteht in den dargestellten Datenelementen. Es gibt auch einige Aktionsschaltflächen.
Wenn zu Beginn nur 1 oder 2 Seiten vorhanden sind, kopieren die meisten Entwickler möglicherweise direkt den vorherigen Seitencode. Mit fortschreitendem Projekt kann die Anzahl ähnlicher Seiten immer größer werden, was direkt dazu führt, dass die Codekopplung im Projekt immer höher wird und höher.
Dies ist auch einer der Hauptgründe, warum einige wiederverwendbare Funktionen oder Komponenten aus dem Projekt extrahiert werden sollten.
Im Folgenden fassen wir eine useList
allgemeine Listenseite zusammen, die für die meisten Hinzufügungen, Löschungen, Änderungen und Suchvorgänge geeignet ist und es Ihnen ermöglicht, Aufgaben schneller und effizienter zu erledigen und pünktlich mit der Arbeit fertig zu werden~
vorausgesetzte Kenntnisse
- Vue
- Vue Composition API
Verkapselung
Wir müssen einige allgemeine Parameter und Funktionen extrahieren und sie in einem universellen Paket zusammenfassen hook
. Es wird einfacher und bequemer sein, dieselben Funktionen später auf anderen Seiten wiederzuverwenden.
Definieren Sie wesentliche Paging-Daten für Listenseiten
export default function useList() {
// 加载态
const loading = ref(false);
// 当前页
const curPage = ref(1);
// 总数量
const total = ref(0);
// 分页大小
const pageSize = ref(10);
}
So erhalten Sie Listendaten
Wenn Sie darüber nachdenken, lassen Sie useList
die Funktion einen Parameter akzeptieren listRequestFn
, der die Daten in der Liste anfordert.
Definieren Sie eine list
Variable zum Speichern des von der Netzwerkanforderung zurückgegebenen Dateninhalts. Da der Listendatentyp intern nicht direkt bestimmt werden kann, wird der Listendatentyp extern über Generika bereitgestellt.
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
const list = ref<ItemType[]>([]);
}
useList
Erstellen Sie eine Funktion , um loadData
die Funktion „Daten abrufen“ aufzurufen, die einen Parameter empfängt, um die angegebene Anzahl von Datenseiten abzurufen (optional, standardmäßig ist curPage
der Wert).
- Umsetzungsprozess
- Ladestatus festlegen
- Rufen Sie die von außen übergebene Funktion auf und weisen Sie die erhaltenen Daten der
list
Summe zutotal
- Ladezustand schließen
Hier wird die Async/Await-Syntax verwendet. Wenn ein Anforderungsfehler oder ein Dekonstruktionsfehler vorliegt, wird der Catch-Code-Block verwendet und dann wird der Ladezustand geschlossen.
Hierbei ist zu beachten, dass die Anzahl und Art der von der übergebenen listRequestFn-Funktion empfangenen Parameter entsprechend der tatsächlichen Situation angepasst werden sollte.
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;
}
};
}
Vergessen Sie nicht, dass es immer noch um den Seitenwechsel geht
Verwenden Sie watch
die Funktion, um Daten zu überwachen. Wenn sich der Wert von ändert, rufen Sie die Funktion auf curPage
, um neue Daten abzurufen.pageSize
loadData
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
// 监听分页数据改变
watch([curPage, pageSize], () => {
loadData(curPage.value);
});
}
Die Erfassung grundlegender Listendaten ist jetzt implementiert
Implementieren Sie Datenfilter
In einer riesigen Datenliste ist die Datenfilterung eine wesentliche Funktion
Normalerweise würde ich die Filterfelder in einem definieren ref
und sie einfach in die Anfragefunktion werfen, wenn ich eine Anfrage stelle ref
.
In der Funktion useList empfängt der zweite Parameter ein filterOption
Objekt, das dem Filterfeld in der Liste entspricht.
Passen Sie die Funktion an und übergeben Sie das Objekt loadData
in der AnforderungsfunktionfilterOption
Beachten Sie, dass die Anzahl und Art der von der eingehenden listRequestFn-Funktion empfangenen Parameter entsprechend der tatsächlichen Situation angepasst werden sollte.
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;
}
};
}
Beachten Sie, dass der Parametertyp filterOption hier ein Ref-Typ sein muss, da sonst die Reaktionsfähigkeit verloren geht und nicht ordnungsgemäß funktioniert.
Filterfelder löschen
Auf der Seite gibt es eine Schaltfläche zum Zurücksetzen, um die Filterbedingungen zu löschen. Diese wiederholte Aktion kann durch die Reset-Funktion verarbeitet werden.
Setzen Sie alle Werte auf die Verwendung von Reflect undefined
und fordern Sie die Daten dann erneut an.
Was ist Reflect? Schauen Sie sich diesen Artikel „Mapping-Objekte reflektieren“ an
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();
};
}
Exportfunktion
Zusätzlich zum Anzeigen von Daten müssen einige Schnittstellen auch über die Funktion zum Exportieren von Daten verfügen (z. B. das Exportieren von CSV- und Excel-Dateien), und wir haben auch die Exportfunktion darin geschrieben useList
.
Normalerweise besteht die Exportfunktion darin, den vom Backend bereitgestellten Export aufzurufen, Api
um eine Datei-Download-Adresse zu erhalten. loadData
Sie ähnelt der Funktion. Sie ruft die Funktion von außen ab exportRequestFn
und ruft sie auf.Api
Fügen Sie innerhalb der Funktion eine neue exportFile
Funktion hinzu, um sie aufzurufen.
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");
}
};
}
Beachten Sie, dass die Anzahl und Art der von der eingehenden exportRequestFn-Funktion empfangenen Parameter entsprechend der tatsächlichen Situation angepasst werden sollten.
Optimierung
useList
Jetzt wurden alle Anforderungen der Seite erfüllt, mit den Funktionen Daten abrufen, Daten filtern, Daten exportieren und Paging.
Es gibt auch einige Details. Die Codeausschnitte in allen oben genannten Codes try..catch
wurden catch
in keiner Weise verarbeitet, console.log
sie sind einfach einfach.
Haken bereitstellen
Fügen Sie useList
einen Optionsobjektparameter hinzu, der verwendet wird, um die angegebene Hook-Funktion auszuführen und den Nachrichteninhalt auszugeben, wenn die Funktion erfolgreich ist oder fehlschlägt.
Definieren Sie den Optionstyp
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
Standardwert festlegen
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
) {
// ...
}
Für den Fall, dass kein Hook übergeben wird, wird empfohlen, die Standardmeldungsanzeige bei Fehler festzulegen.
Optimierung loadData
, exportFile
Funktion
Kapselungsnachrichtenmethode basierend auf Elementui
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-Funktion
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-Funktion
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-Nutzung
<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 style="width: 100%">
<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>
/>
„