文章目录
前言
这篇文章将用之前写的增删改查接口渲染到前端页面,这里借助elementui完成页面,效果如下
一、安装elementUI
文档:https://element-plus.gitee.io/zh-CN/guide/installation.html#%E7%8E%AF%E5%A2%83%E6%94%AF%E6%8C%81
# 选择一个你喜欢的包管理器
# NPM
$ npm install element-plus --save
# Yarn
$ yarn add element-plus
# pnpm
$ pnpm install element-plus
二、搭建路由及静态页面
1. 路由搭建
2. 静态页面
我这里直接上代码了,在Table.vue中直接粘贴即可
<template>
<el-button type="primary" plain @click="dialogFormVisible = true"
>新增</el-button
>
<!-- 表格 -->
<el-table :data="tableData">
<el-table-column prop="name" label="Name" />
<el-table-column prop="age" label="Age" />
<el-table-column fixed="right" label="Operations" width="120">
<template #default>
<el-button type="text" size="small" @click="handleClick"
>Delete</el-button
>
<el-button type="text" size="small">Edit</el-button>
</template>
</el-table-column>
</el-table>
<!-- 弹窗 -->
<el-dialog v-model="dialogFormVisible" title="Shipping address">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="Age" :label-width="formLabelWidth">
<el-input v-model="form.age" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogFormVisible = false"
>Confirm</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";
import { getList } from "@/components/axiosTable";
const dialogTableVisible = ref(false);
const dialogFormVisible = ref(false);
const formLabelWidth = "140px";
const form = reactive({
name: "",
age: "",
});
const tableData = ref([
{
name:'TOM',
agr:10
}, {
name:'TOM1',
agr:11
}
]);
</script>
<style lang="less">
</style>
效果是这样的
弹窗效果
三、封装axios请求
1. 首先安装axios
$ yarn add axios
or
$ npm i --save axios
2. 请求封装
- Request类封装
//axios/request.js
import axios from "axios";
import type {
RequestConfig,
RequestInterceptors,
CancelRequestSource,
} from "./type";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
class Request {
// axios的实例
instance: AxiosInstance;
// 拦截器对象
interceptorsObj?: RequestInterceptors;
/**
* 存放取消方法的集合
* 在创建请求后将取消请求方法 push 到该集合中
* 封装一个方法,可以取消请求,传入 url: string|string[]
* 在请求之前判断同一URL是否存在,如果存在就取消请求
*/
cancelRequestSourceList?: CancelRequestSource[];
/**
* 存放所有请求URL的集合
* 请求之前需要将url push到该集合中
* 请求完毕后将url从集合中删除
* 添加在发送请求之前完成,删除在响应之后删除
*/
requestUrlList?: string[];
constructor(config: RequestConfig) {
// 数据初始化
this.requestUrlList = [];
this.cancelRequestSourceList = [];
this.instance = axios.create(config);
this.interceptorsObj = config.interceptors;
// 拦截器 拦截器的执行顺序为实例请求→类请求→实例响应→类响应
this.instance.interceptors.request.use(
(res: AxiosRequestConfig) => {
console.log("全局请求拦截器");
return res;
},
(err: any) => err
);
// 实例拦截器
this.instance.interceptors.request.use(
this.interceptorsObj?.requestInterceptors,
this.interceptorsObj?.requestInterceptorsCatch
);
this.instance.interceptors.response.use(
this.interceptorsObj?.responseInterceptors,
this.interceptorsObj?.responseInterceptorsCatch
);
// 全局响应拦截器最后执行
this.instance.interceptors.response.use(
// 因为接口的数据都在res.data下,直接返回res.data即可
(res: AxiosResponse) => {
console.log("全局响应拦截器");
return res.data;
},
(err: any) => err
);
}
// 接口拦截
request<T>(config: RequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
//单个请求的拦截器
if (config.interceptors?.requestInterceptors) {
config = config.interceptors.requestInterceptors(config);
}
const url = config.url;
// url存在保存取消请求方法和当前请求url
if (url) {
this.requestUrlList?.push(url);
config.cancelToken = new axios.CancelToken((c) => {
this.cancelRequestSourceList?.push({
[url]: c,
});
});
}
this.instance
.request<any, T>(config)
.then((res) => {
//单个相应拦截器
if (config.interceptors?.responseInterceptors) {
res = config.interceptors.responseInterceptors<T>(res);
}
resolve(res);
})
.catch((err: any) => {
reject(err);
})
.finally(() => {
url && this.delUrl(url);
});
});
}
/**
* @description: 获取指定 url 在 cancelRequestSourceList 中的索引
* @param {string} url
* @returns {number} 索引位置
*/
private getSourceIndex(url: string): number {
return this.cancelRequestSourceList?.findIndex(
(item: CancelRequestSource) => {
return Object.keys(item)[0] === url;
}
) as number;
}
/**
* @description: 删除 requestUrlList 和 cancelRequestSourceList
* @param {string} url
* @returns {*}
*/
private delUrl(url: string) {
const urlIndex = this.requestUrlList?.findIndex((u) => u === url);
const sourceIndex = this.getSourceIndex(url);
// 删除url和cancel方法
urlIndex !== -1 && this.requestUrlList?.splice(urlIndex as number, 1);
sourceIndex !== -1 &&
this.cancelRequestSourceList?.splice(sourceIndex as number, 1);
}
/**
* 取消全部请求
*/
cancelAllRequest() {
this.cancelRequestSourceList?.forEach((source) => {
const key = Object.keys(source)[0];
source[key]();
});
}
/** 取消请求 */
cancelRequest(url: string | string[]) {
if (typeof url === "string") {
// 取消单个请求
const sourceIndex = this.getSourceIndex(url);
sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][url]();
} else {
// 存在多个需要取消请求的地址
url.forEach((u) => {
const sourceIndex = this.getSourceIndex(u);
sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][u]();
});
}
}
}
export default Request;
- type类型/接口定义文件
//axios/type.ts
// *实例拦截器 为保证封装的灵活性(因为拦截后传参可能不一样)
import type { AxiosRequestConfig, AxiosResponse } from "axios";
//基础拦截器
export interface RequestInterceptors {
// 请求拦截
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig;
requestInterceptorsCatch?: (err: any) => any;
// 响应拦截
responseInterceptors?: <T = AxiosResponse>(config: T) => T;
responseInterceptorsCatch?: (err: any) => any;
}
//自定义传入的参数 因为axios提供的AxiosRequestConfig不允许传入拦截器
export interface RequestConfig extends AxiosRequestConfig {
interceptors?: RequestInterceptors;
}
//取消请求方法的接口定义
export interface CancelRequestSource {
[index: string]: () => void;
}
- index.ts
//axios/index.ts
import Request from "./request";
import type { RequestConfig } from "./type";
interface MyRequestConfig<T> extends RequestConfig {
data?: T;
}
interface MyResponse<T> {
code: number;
message: string;
data?: T;
}
// 封装请求方法
const request = new Request({
// baseURL: "import.meta.url",
baseURL: "http://localhost:3000/",
timeout: 1000 * 60 * 5,
interceptors: {
// 请求拦截器
requestInterceptors: (config) => {
console.log("实例请求拦截器");
return config;
},
// 响应拦截器
responseInterceptors: (result) => {
console.log("实例响应拦截器");
return result;
},
},
});
/**
* *发送网络请求
* @description: 函数的描述
* @interface D 请求参数的interface
* @interface T 响应结构的intercept
* @param {MyRequestConfig} config 不管是GET还是POST请求都使用data
* @returns {Promise}
*/
const myRequest = <D, T = any>(config: MyRequestConfig<D>) => {
const { method = "GET" } = config;
if (method === "get" || method === "GET") {
config.params = config.data;
}
return request.request<MyResponse<T>>(config);
};
export default myRequest;
// 取消请求
export const cancelRequest = (url: string | string[]) => {
return request.cancelRequest(url);
};
// 取消全部请求
export const cancelAllRequest = () => {
return request.cancelAllRequest();
};
四、页面掉接口实现增删改查
1. 先在创建一个文件用于存放接口的
当前了名字自定义,取的时候路径别错了就行
代码
import request from "@/components/axios/index";
interface Req {
name: string;
age: string;
id: string;
}
// 查询
export const getList = (data: Partial<Req>) => {
return request<any, any>({
url: "users/list",
method: "get",
data,
});
};
// 增加
export const addPeople = (data: Pick<Req, 'name' | 'age'>) => {
return request<any, any>({
url: "users/add",
method: "post",
data,
});
};
// 修改
export const updatePeople = (data: Req) => {
return request<any, any>({
url: "users/update",
method: "post",
data,
});
};
//删除
export const delPeople = (id: string) => {
return request<any, any>({
url: "users/del",
method: "get",
data: { id },
});
};
- url 为之前写的接口的名称
- method为请求方法
- data为请求参数
一定要确保后端服务启动了,不然一定会调取失败
2. 页面实现增删改查
我将代码需要注意的地方都打上了注释,感觉这块就是调取上面文件里的方法,这块我就直接贴代码,不细说了,有问题欢迎评论区留言哈
- Table.vue最终代码
<template>
<el-button type="primary" plain @click="addClick">新增</el-button>
<!-- 表格 -->
<el-table :data="tableData">
<el-table-column prop="name" label="Name" />
<el-table-column prop="age" label="Age" />
<el-table-column fixed="right" label="Operations" width="120">
<template #default="scope">
<el-button type="text" size="small" @click="delClick(scope)"
>Delete</el-button
>
<el-button type="text" size="small" @click="editClick(scope)"
>Edit</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 弹窗 -->
<el-dialog v-model="dialogFormVisible" title="Shipping address">
<el-form :model="form">
<el-form-item label="Name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="Age" :label-width="formLabelWidth">
<el-input v-model="form.age" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="handleSubmit">Confirm</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";
import {
getList,
updatePeople,
addPeople,
delPeople,
} from "@/components/axiosTable";
const dialogFormVisible = ref(false);
const formLabelWidth = "140px";
const tableData = ref();
let form = reactive({
id: null,
name: "",
age: "",
});
/**
* 重置form
*/
function resetForm() {
form.id = null;
form.name = "";
form.age = "";
}
/** 打开弹窗 添加 */
function addClick() {
dialogFormVisible.value = true;
// 一定要记得把form重置,不然会有上次修改赋值的数据
resetForm();
}
/**
* 弹窗保存
*/
async function handleSubmit() {
dialogFormVisible.value = false;
// 修改
if (form.id) {
await updatePeople(form);
} else {
//新增
let { id, ...data } = form;
await addPeople(data);
}
let { data } = await getList();
tableData.value = data;
}
// 删除
async function delClick({ row }) {
await delPeople(row._id);
let { data } = await getList();
tableData.value = data;
}
/** 打开弹窗 修改 */
function editClick({ row }) {
form.id = row._id;
form.name = row.name;
form.age = row.age;
dialogFormVisible.value = true;
}
onMounted(async () => {
let { data } = await getList();
tableData.value = data;
});
</script>
<style lang="less"></style>
最后
ok,效果已经实现了,可以自己点点试试,我把项目传在了gitee上:https://gitee.com/zdp970719/v3demo,也可以直接拷我的项目