技术栈:vue3,typescript,naive-ui组件库,axios请求api库。
API层设计图

feedback.ts
ts
import { createDiscreteApi, darkTheme } from 'naive-ui';
const { message, dialog, notification, loadingBar } = createDiscreteApi(
['message', 'dialog', 'notification', 'loadingBar'],
{
configProviderProps: { theme: darkTheme },
messageProviderProps: { keepAliveOnHover: true },
}
);
export { dialog, loadingBar, message, notification };
request.ts
封装http请求模块。axios.create(option)创建axios实例,option.headers.authorization-token用于预防CSRF(跨站请求伪造)攻击。loadingBar组件展示请求接口的进度。http请求拦截响应,如果status为401,表示登录状态过期,需要重新登录。
ts
import axios from 'axios';
import { loadingBar, message } from './feedback';
const http = axios.create({
baseURL: import.meta.env.API_HOST || '',
withCredentials: true,
headers: {
'authorization-token': 'token',
}
});
http.interceptors.request.use(
async (config) => {
loadingBar.start();
return config;
},
(error) => {
loadingBar.error();
return Promise.reject(error);
},
);
http.interceptors.response.use(
async (response) => {
loadingBar.finish();
const { data } = response;
return data;
},
(error) => {
loadingBar.error();
const { response, message: msg } = error;
if (response?.status == 401) {
message.error('登录状态过期, 请重新登录');
router.replace({ path: '/login' });
} else {
if (response?.data) {
const { data } = response;
message.error(data?.msg || data?.message || msg);
}
}
return Promise.reject(error);
},
);
export default http;
通过封装的http模块请求服务端的接口,获取数据。getDataFromApi函数传入一个参数body,body的数据类型为DataBody,返回的数据是promise对象,promise对象resolve和reject的数据类型是<ApiRes<ReturnItem[]>>。
api.ts
ts
import http from './request';
interface DataBody {//自定义数据类型
startTime: string;
endTime?: string;
}
interface ApiResWithoutData {
status: boolean;
msg: string;
code?: number
}
interface ApiRes<T> extends ApiResWithoutData {//继承并新加字段
data: T;
}
interface ReturnItem {
id: number;
name: string;
age: number;
}
export function getDataFromApi(
body: DataBody,
): Promise<ApiRes<ReturnItem[]>> {
const url = `/api/getData`;
return http.post(url, { ...body });
}
封装组合式函数
useTableList。对外提供响应式数据state和设置响应式数据的函数fetchData。 useTableList.ts
ts
import { reactive } from 'vue';
import { getDataFromApi } from './api';
export interface ReturnItem {
id: number;
name: string;
age: number;
}
export function useTableList() {
const state = reactive({
tableData: [] as ReturnItem[],
loading: false,
});
async function fetchData() {
try {
if (state.loading) {
message.info('正在加载数据...');
return;
}
const body: DataRBody = {
startTime: '2024-02-03',
endTime: '2025-06-08',
};
state.loading = true;
const { data} = await getDataFromApi(body);
state.loading = false;
if (!data || !data.length) {
message.warning('定位数据数量统计无数据');
} else
state.tableData = data;
}
} catch (error) {
state.loading = false;
}
}
return {
state,
fetchData,
};
}
在业务中使用组合式函数useTableList。
tableList.vue
vue
<template>
<n-data-table
:columns="columns"
:data="tableData"
:bordered="false"
/>
</template>
<script lang="ts" setup>
import { useTableList } from './compositionFn/useTableList';//使用组合式函数
import { onMounted, ref} from 'vue';
const colums = [
{ title: 'id', key: 'id' },
{ title: '名字', key: 'name' },
{ title: '年龄', key: 'age' },
];
const tableData = ref(tableData);
const { state: tableData, fetchData } = useTableList();
onMounted(() => {
fetchData();
});
</script>
<style lang="scss" scoped>
</style>