前言:根据我上篇所实现的左边菜单栏之后,需要登录成功跳转home页面。主要分为三步。
就是个接口类方便页面和另一个ts文件数据传递,其实也可以不用
export interface LoginResponse {
userInfo: UserInfo
token: string
}
export interface UserInfo {
user: string | null
}
这个ts就是结合pinia用于处理页面传过来要处理缓存的数据。
import { defineStore } from 'pinia'
import type {LoginResponse, UserInfo} from '@/api/userTypes'
import { useStorage } from '@vueuse/core'
export const useUserStore = defineStore('user', () => {
const userInfoRef = useStorage('USER_INFO', {} as UserInfo, sessionStorage)
const tokenRef = useStorage('TOKEN', '', sessionStorage)
function setUserInfo(userInfo: UserInfo) {
userInfoRef.value = userInfo
console.log('userInfoRef====>',userInfoRef);
}
function setToken(token: string) {
tokenRef.value = token
}
function toLogin(param: LoginResponse) {
setToken(param.token)
setUserInfo(param.userInfo)
}
function toLogout() {
setUserInfo({} as UserInfo);
setToken("");
window.location.href = '/login'
}
function getUsername() {
return userInfoRef.value.user
}
function getToken() {
return tokenRef.value
}
return { toLogin, toLogout, getToken, getUsername }
})
import type {App} from 'vue'
import {createPinia} from 'pinia'
const store = createPinia()
export function useStore(app: App<Element>): void {
app.use(store)
}
export {store}
我这边处理是登录接口请求成功之后,再调用toLogin()方法。其实可以结合接口请求一起写,我这个是借鉴Vue + Ts 项目(七)—— 登录页面及登录校验,实现方式很多。我先结合自己项目实现功能。
import { useUserStore } from "@/stores/modules/user";
import { useRouter, useRoute } from "vue-router";
import { LoginResponse } from "@/api/userTypes";
// 引入这三
//再在setup ()下第一行,切记一定要前排。不然router和route为空。
setup (){
const userStore = useUserStore();
const router = useRouter();
const route = useRoute();
const LoginResponseModel: Ref<LoginResponse> = ref({
userInfo: {user:''},
token: "",
});
// 再在登录回调成功的地方加上
LoginResponseModel.userInfo ={
user:res.user// 根据你自己的数据结构来
}
LoginResponseModel.token = res.token
userStore.toLogin(LoginResponseModel);
// 这里LoginResponseModel,会报红。但是不影响流程。我后期看看怎么去红。哈哈哈哈哈,我还是太废了。
const redirect = route.query.redirect as string
if (redirect) {
router.replace(redirect)
} else {
router.replace('/combination/dashboard')
}
不前排会报错Cannot read properties of undefined (reading 'replace')
,
也不要试图用useRouter()直接.replace会报错inject() can only be used inside setup() or functional components.
等一些问题
我感觉当时脑子突然不够用,发现我有点迷,今天好点,需要改动
// userTypes.ts文件
export interface UserInfo {
user: string | null
picture: string | null
}
export interface LoginResponse {
userInfo: UserInfo
token: string | null
}
// user.ts文件
function setToken(token: string|null) {
tokenRef.value = token
}
function toLogin(userInfo: UserInfo , token: string|null) {
setToken(token)
setUserInfo(userInfo)
}
// vue调用页面
import type { UserInfo } from "@/api/userTypes";
const userInfo : Ref<UserInfo> = ref({
user: null,
picture: null
})
userInfo.value.user = username
userInfo.value.picture = picture ? picture : null
userStore.toLogin(userInfo.value, getToken);
const redirect = route.query.redirect as string
//后面跟之前一样
主要是beforeEach方法。
import {createWebHistory, createRouter} from "vue-router";
import type {App} from 'vue'
// 获取所有路由
import routes from './routes'
import { useUserStore } from "@/stores/modules/user";
const router = createRouter({
routes,
// 这里使用历史记录模式
history: createWebHistory()
})
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
const isAuthenticated = userStore.getToken() && userStore.getToken() !== "";
console.log('====isAuthenticated==',isAuthenticated);
// isAuthenticated用于判断没有登录缓存,手动属于的路径,不会跳转成功。
if (isAuthenticated) {
next();
} else {
if (to.name === "login") {
next();
} else {
next({ name: "login", query: { redirect: to.fullPath } });
}
}
});
export const useRouter = (app: App<Element>): void => {
app.use(router)
}
对了,别忘了配置login页面的路由src\router\modules\login.ts,把默认路径也是设置为登录页面
import type { RouteRecord } from '@/router/type'
import BasicLayout from "@/layouts/BasicLayout.vue";
import { CalendarTools } from '@vicons/carbon'
const loginRoutes: RouteRecord[] = [
{
path: "/",
name:'login',
meta:{
hidden: true
},
children: [
{
path: "/login",
name: "login",
component: () => import("@/views/login/login.vue"),
},
],
},
]
export default loginRoutes
到目前为止,登录成功后已经可以跳转了。先这样
第三步先欠着。下周我再继续学习。毕竟周五了。我心已经起飞了。
今天2023.12.18.周一,天气小雨夹雪。我们继续。
根据我目前项目不需要做过多处理。只需要把token值放headers里
src\stores\modules\apiEncapsulation.ts
import { useUserStore } from "@/stores/modules/user";
import { useRouter } from "vue-router";
import { createDiscreteApi} from "naive-ui"
import { apiToken} from "../../api/apiToken";
import type { DataResult } from "../../api/DataResult ";
const router = useRouter();
const {message} = createDiscreteApi(["message"])
// 接口请求:token过期或未登录,跳转到登录页面
export async function apiTokenRequest(url: string, method:string){
const userStore = useUserStore();
const isAuthenticated = userStore.getToken() && userStore.getToken() !== "";
if(isAuthenticated){
try {// DataResult返回值接口类,可以在userTypes.ts创建再引入使用
// apiToken是 封装的请求方法,引入使用
await apiToken<DataResult>(url,userStore.getToken(),method )
.then(function(res){
if(res.code===0){
return res
}else if(res.code===2){
router.replace('/')
message.error(res.errorMsg)
}else{
message.error(res.errorMsg)
}
})
} catch (error) {
message.error('Request exception!')
console.log('error==>',error);
}
}else{
router.replace('/')
message.warning('未登录,或登录过期')
}
}
apiToken方法参考(这里用到的fetch请求)
export function apiToken<T>(url: string, token: string, method:string): Promise<T>{
return fetch(url,{method: method, headers:{'Authorization' : "Bearer " + token}})
.then(response => {
if (!response.ok) {
throw new Error(response.statusText)
}
return response.json() as Promise<T>
})
}
Authorization头的主要用作http协议的认证。
Authorization的作用是当客户端访问受口令保护时,服务器端会发送401状态码和WWW-Authenticate响应头,要求客户机使用Authorization来应答。
config.headers['Authorization'] = 'Bearer ' + getToken()
注意:Bearer 后面需要加一个空格