🎨 use alova / add nprogress

This commit is contained in:
landaiqing
2024-08-08 23:45:51 +08:00
parent 562071cdc5
commit c13613ec07
18 changed files with 533 additions and 252 deletions

11
src/api/user/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import {service} from "@/utils/alova/service.ts";
export const getUserInfo = () => {
return service.Get('/api/auth/user/List', {
meta: {
ignoreToken: true
},
});
};

View File

@@ -6,7 +6,10 @@
},
}"
>
<router-view>
<router-view v-slot="{ Component, route }">
<transition name="animation" mode="out-in">
<component :is="Component" :key="route.path"/>
</transition>
</router-view>
</AConfigProvider>
</template>

View File

@@ -1,15 +1,15 @@
import {createApp} from 'vue'
import App from './App.vue'
import '@/assets/styles/scroll-bar.scss'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import {createApp} from 'vue';
import App from './App.vue';
import '@/assets/styles/scroll-bar.scss';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
//Pinia
import {createPinia, Pinia} from 'pinia'
import {createPinia, Pinia} from 'pinia';
import router from "@/router/router.ts";
const pinia: Pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const pinia: Pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
createApp(App)
.use(router)
.use(pinia)
.mount('#app')
.mount('#app');

View File

@@ -1,10 +1,11 @@
export default [
{
path: '/',
name: 'login',
component: () => import('@/views/Login/LoginPage.vue'),
meta: {
title: '登录页'
}
}
{
path: '/',
name: 'login',
component: () => import('@/views/Login/LoginPage.vue'),
meta: {
requiresAuth: false,
title: '登录页'
}
}
];

View File

@@ -4,6 +4,7 @@ export default [
name: 'test',
component: () => import('@/views/TestTheme.vue'),
meta: {
requiresAuth: true,
title: '测试'
}
}

View File

@@ -1,7 +1,11 @@
import {createRouter, createWebHistory, RouteRecordRaw} from 'vue-router';
/* eslint-disable */
// @ts-nocheck
import {createRouter, createWebHistory, Router, RouteRecordRaw} from 'vue-router';
import login from './modules/login';
import test from "@/router/modules/test.ts";
import useStore from "@/store";
import {message} from "ant-design-vue";
import {close, start} from '@/utils/nprogress/nprogress.ts';
const routes: Array<RouteRecordRaw> = [
@@ -10,8 +14,33 @@ const routes: Array<RouteRecordRaw> = [
];
const router = createRouter({
const router: Router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach((to, from, next) => {
start();
if (to.meta.requiresAuth) {
const user = useStore().user;
const token: string | undefined = user.getUser()?.token;
const userId: string | undefined = user.getUser()?.userId;
if (token !== undefined && userId !== undefined) {
next();
} else {
message.warn('请先登录').then();
next({
path: '/',
query: {redirect: to.fullPath}
});
}
} else {
next();
}
});
router.afterEach(() => {
// 关闭进度条
close();
});
export default router;

View File

@@ -43,7 +43,7 @@ export const useThemeStore = defineStore(
{
persist: {
key: 'theme',
paths: ['themeName','darkMode',"darkModeComp","themeConfig"],
paths: ['themeName','darkMode'],
storage: handleLocalforage,
serializer: {
deserialize: parse,

View File

@@ -0,0 +1,17 @@
import {handleLocalforage} from "@/utils/localforage";
export const localforageStorageAdapter = {
set(key: string, value: any) {
handleLocalforage.setItem(key, value);
},
get(key: string) {
const data: string = handleLocalforage.getItem(key);
return data ? JSON.parse(data) : data;
},
remove(key: any) {
handleLocalforage.removeItem(key);
},
clear() {
handleLocalforage.clear().then();
}
};

153
src/utils/alova/service.ts Normal file
View File

@@ -0,0 +1,153 @@
import {createAlova} from 'alova';
import VueHook from 'alova/vue';
import useStore from "@/store";
import {axiosRequestAdapter} from "@alova/adapter-axios";
import {AxiosError, AxiosResponse} from "axios";
import {message} from "ant-design-vue";
import {localforageStorageAdapter} from "@/utils/alova/adapter/localforageStorageAdapter.ts";
export const service = createAlova({
timeout: 5000,
baseURL: import.meta.env.VITE_APP_BASE_API,
statesHook: VueHook,
// 请求适配器
requestAdapter: axiosRequestAdapter(),
l2Cache: localforageStorageAdapter,
cacheFor: {
GET: {
mode: 'restore',
expire: 60 * 10 * 1000
},
},
cacheLogger: import.meta.env.VITE_NODE_ENV === 'development',
// 设置全局的请求拦截器
beforeRequest(method) {
if (!method.meta?.ignoreToken) {
const user = useStore().user;
method.config.headers.token = user.getUser()?.token;
}
},
// 响应拦截器
responded: {
onSuccess: async (response: AxiosResponse) => {
if (response.data instanceof Blob) {
return response;
} else {
return response.data;
}
},
onError(error: AxiosError) {
const {response} = error;
if (response) {
handleCode(response.status);
}
if (!window.navigator.onLine) {
message.error("网络连接失败").then();
return Promise.reject(error);
}
}
}
});
function handleCode(code: number): void {
switch (code) {
case 400:
message
.open({
content: "请求错误(400)",
type: "error",
})
.then();
break;
case 401:
message
.open({
content: "未授权,请重新登录(401)",
type: "error",
})
.then();
break;
case 403:
message
.open({
content: "拒绝访问(403)",
type: "error",
})
.then();
break;
case 404:
message
.open({
content: "请求出错(404)",
type: "error",
})
.then();
break;
case 408:
message
.open({
content: "请求超时(408)",
type: "error",
})
.then();
break;
case 500:
message
.open({
content: "服务器错误(500)",
type: "error",
})
.then();
break;
case 501:
message
.open({
content: "服务未实现(501)",
type: "error",
})
.then();
break;
case 502:
message
.open({
content: "网络错误(502)",
type: "error",
})
.then();
break;
case 503:
message
.open({
content: "服务不可用(503)",
type: "error",
})
.then();
break;
case 504:
message
.open({
content: "网络超时(504)",
type: "error",
})
.then();
break;
case 505:
message
.open({
content: "HTTP版本不受支持(505)",
type: "error",
})
.then();
break;
default:
message
.open({
content: `连接出错(${code})!`,
type: "error",
})
.then();
break;
}
}

View File

@@ -1,163 +1,164 @@
/** @format */
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { message } from "ant-design-vue";
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
import axios, {AxiosInstance, AxiosRequestConfig} from "axios";
import {message} from "ant-design-vue";
import useStore from "@/store";
class Request {
private instance: AxiosInstance | undefined;
private instance: AxiosInstance | undefined;
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config);
// 全局请求拦截
this.instance.interceptors.request.use(
(config) => {
const token: string | null = getStorageFromKey("token");
if (token) {
config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
},
);
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config);
// 全局请求拦截
this.instance.interceptors.request.use(
(config) => {
const user = useStore().user;
const token: string | undefined = user.getUser()?.token;
if (token) {
config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 全局响应拦截
this.instance.interceptors.response.use(
(response) => {
if (response.data instanceof Blob) {
return response;
} else {
return response.data;
}
},
(error) => {
const { response } = error;
if (response) {
this.handleCode(response.status);
}
if (!window.navigator.onLine) {
message.error("网络连接失败");
return Promise.reject(error);
}
},
);
}
// 全局响应拦截
this.instance.interceptors.response.use(
(response) => {
if (response.data instanceof Blob) {
return response;
} else {
return response.data;
}
},
(error) => {
const {response} = error;
if (response) {
this.handleCode(response.status);
}
if (!window.navigator.onLine) {
message.error("网络连接失败");
return Promise.reject(error);
}
},
);
}
handleCode(code: number): void {
switch (code) {
case 400:
message
.open({
content: "请求错误(400)",
type: "error",
})
.then();
break;
case 401:
message
.open({
content: "未授权,请重新登录(401)",
type: "error",
})
.then();
break;
case 403:
message
.open({
content: "拒绝访问(403)",
type: "error",
})
.then();
break;
case 404:
message
.open({
content: "请求出错(404)",
type: "error",
})
.then();
break;
case 408:
message
.open({
content: "请求超时(408)",
type: "error",
})
.then();
break;
case 500:
message
.open({
content: "服务器错误(500)",
type: "error",
})
.then();
break;
case 501:
message
.open({
content: "服务未实现(501)",
type: "error",
})
.then();
break;
case 502:
message
.open({
content: "网络错误(502)",
type: "error",
})
.then();
break;
case 503:
message
.open({
content: "服务不可用(503)",
type: "error",
})
.then();
break;
case 504:
message
.open({
content: "网络超时(504)",
type: "error",
})
.then();
break;
case 505:
message
.open({
content: "HTTP版本不受支持(505)",
type: "error",
})
.then();
break;
default:
message
.open({
content: `连接出错(${code})!`,
type: "error",
})
.then();
break;
}
}
handleCode(code: number): void {
switch (code) {
case 400:
message
.open({
content: "请求错误(400)",
type: "error",
})
.then();
break;
case 401:
message
.open({
content: "未授权,请重新登录(401)",
type: "error",
})
.then();
break;
case 403:
message
.open({
content: "拒绝访问(403)",
type: "error",
})
.then();
break;
case 404:
message
.open({
content: "请求出错(404)",
type: "error",
})
.then();
break;
case 408:
message
.open({
content: "请求超时(408)",
type: "error",
})
.then();
break;
case 500:
message
.open({
content: "服务器错误(500)",
type: "error",
})
.then();
break;
case 501:
message
.open({
content: "服务未实现(501)",
type: "error",
})
.then();
break;
case 502:
message
.open({
content: "网络错误(502)",
type: "error",
})
.then();
break;
case 503:
message
.open({
content: "服务不可用(503)",
type: "error",
})
.then();
break;
case 504:
message
.open({
content: "网络超时(504)",
type: "error",
})
.then();
break;
case 505:
message
.open({
content: "HTTP版本不受支持(505)",
type: "error",
})
.then();
break;
default:
message
.open({
content: `连接出错(${code})!`,
type: "error",
})
.then();
break;
}
}
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.instance
?.request<T, T>(config)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.instance
?.request<T, T>(config)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
}
export default Request;

View File

@@ -5,87 +5,86 @@
*/
interface Options {
/** key前缀 */
prefix?: string;
/** key前缀 */
prefix?: string;
}
/** 默认配置 */
const defaultOptions: Options = {
prefix: "",
prefix: "",
};
class CookieStorage {
private prefix: string;
private prefix: string;
constructor(options: Options = defaultOptions) {
const { prefix } = options;
this.prefix = prefix ?? "";
}
constructor(options: Options = defaultOptions) {
const {prefix} = options;
this.prefix = prefix ?? "";
}
/**
* @description: 设置cookie
* @param {string} key 键
* @param {any} value 值
* @param {number} expires 过期时间s毫秒不传默认2年有效
* @Date: 2023-05-17 18:10:34
* @Author: mulingyuer
*/
public setItem(key: string, value: string | number, expires?: number): void {
const timestamp = Date.now();
if (typeof expires === "number") {
expires = timestamp + expires;
} else {
expires = timestamp + 2 * 365 * 24 * 60 * 60 * 1000;
}
document.cookie = `${this.prefix}${key}=${value};expires=${new Date(expires).toUTCString()}`;
}
/**
* @description: 设置cookie
* @param {string} key 键
* @param {any} value 值
* @param {number} expires 过期时间s毫秒不传默认2年有效
* @Date: 2023-05-17 18:10:34
* @Author: mulingyuer
*/
public setItem(key: string, value: string | number, expires?: number): void {
const timestamp = Date.now();
if (typeof expires === "number") {
expires = timestamp + expires;
} else {
expires = timestamp + 2 * 365 * 24 * 60 * 60 * 1000;
}
document.cookie = `${this.prefix}${key}=${value};expires=${new Date(expires).toUTCString()}`;
}
/**
* @description: 获取cookie
* @param {string} key 键
* @Date: 2023-05-17 18:31:50
* @Author: mulingyuer
*/
public getItem(key: string): string | undefined {
const cookies = document.cookie.split("; ");
let val: string | undefined = undefined;
cookies.find((item) => {
const [k, v] = item.split("=");
if (k === `${this.prefix}${key}`) {
val = v;
return true;
}
return false;
});
/**
* @description: 获取cookie
* @param {string} key 键
* @Date: 2023-05-17 18:31:50
* @Author: mulingyuer
*/
public getItem(key: string): string | undefined {
const cookies = document.cookie.split("; ");
let val: string | undefined = undefined;
cookies.find((item) => {
const [k, v] = item.split("=");
if (k === `${this.prefix}${key}`) {
val = v;
return true;
}
return false;
});
return val;
}
return val;
}
/**
* @description: 删除指定key的cookie
* @param {string} key 键
* @Date: 2023-05-17 18:32:56
* @Author: mulingyuer
*/
public removeItem(key: string): void {
this.setItem(key, "", -1);
}
/**
* @description: 删除指定key的cookie
* @param {string} key 键
* @Date: 2023-05-17 18:32:56
* @Author: mulingyuer
*/
public removeItem(key: string): void {
this.setItem(key, "", -1);
}
/**
* @description: 清空所有cookie
* @Date: 2023-05-17 23:13:04
* @Author: mulingyuer
*/
public clear(): void {
const cookies = document.cookie.split("; ");
cookies.forEach((item) => {
const [k] = item.split("=");
this.removeItem(k);
});
}
/**
* @description: 清空所有cookie
* @Date: 2023-05-17 23:13:04
* @Author: mulingyuer
*/
public clear(): void {
const cookies = document.cookie.split("; ");
cookies.forEach((item) => {
const [k] = item.split("=");
this.removeItem(k);
});
}
}
const cookieStorage = new CookieStorage();
export default cookieStorage;
export { CookieStorage };
export {cookieStorage};

View File

@@ -41,7 +41,7 @@ export const decrypt = (data: string | unknown) => {
export const handleLocalforage = {
config: async (options?: LocalForageOptions) => localforage.config(options || {}),
setItem: (key: string, value: string) => {
setItem: (key: string, value: any) => {
localforage.setItem(key, encrypt(value));
},
getItem: (key: string) => {

View File

@@ -0,0 +1,22 @@
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
//全局进度条的配置
NProgress.configure({
easing: 'ease', // 动画方式
speed: 300, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3, // 更改启动时使用的最小百分比
parent: 'body' //指定进度条的父容器
});
// 打开进度条
export const start = () => {
NProgress.start();
};
// 关闭进度条
export const close = () => {
NProgress.done();
};

View File

@@ -1,15 +1,20 @@
<template>
<AButton> 测试页面</AButton>
<AButton> alvoa</AButton>
</template>
<script setup lang="ts">
import useStore from "@/store";
const store = useStore().user;
store.setUser({
token: "test",
userId: "1",
});
console.log(store.getUser()?.userId);
// import {getUserInfo} from "@/api/user";
// import {useRequest} from "alova/client";
//
// const {loading, data, send} = useRequest(getUserInfo, {
// immediate: true,
// throttle: 3000,
// });
// const handleClick = () => {
// send();
// console.log(loading.value);
// console.log(data.value);
//
// };
</script>
<style src="./index.scss" scoped>
</style>