使用回调函数解决Promise异步问题

问题:使用pure-admin-vue项目进行登录,想要代码执行顺序是:1. 在登录接口调用成功后,进行设置本地存储信息setToken()方法操作, 2. 但是setToken方法中存在获取用户信息的接口getUserInfo(), 3. 等获取成功信息后再路由初始化配置 initRouter()

但是项目中确实执行了登录后,就进行了路由初始化配置,导致第一次登录后页面不进行跳转。第二次执行后才跳转。

总共三个页面:1. src\views\login\index.vue

<script setup lang="ts">
import { useI18n } from "vue-i18n";
import Motion from "./utils/motion";
import { useRouter } from "vue-router";
import { message } from "@/utils/message";
import { loginRules } from "./utils/rule";
import TypeIt from "@/components/ReTypeit";
import { debounce } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
import { useEventListener } from "@vueuse/core";
import type { FormInstance } from "element-plus";
import { $t, transformI18n } from "@/plugins/i18n";
import { operates, thirdParty } from "./utils/enums";
import { useLayout } from "@/layout/hooks/useLayout";
import LoginPhone from "./components/LoginPhone.vue";
import LoginRegist from "./components/LoginRegist.vue";
import LoginUpdate from "./components/LoginUpdate.vue";
import LoginQrCode from "./components/LoginQrCode.vue";
import { useUserStoreHook } from "@/store/modules/user";
import { initRouter, getTopMenu } from "@/router/utils";
import { bg, avatar, illustration } from "./utils/static";
import { ReImageVerify } from "@/components/ReImageVerify";
import { ref, toRaw, reactive, watch, computed } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { useTranslationLang } from "@/layout/hooks/useTranslationLang";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";

import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import globalization from "@/assets/svg/globalization.svg?component";
import Lock from "~icons/ri/lock-fill";
import Check from "~icons/ep/check";
import User from "~icons/ri/user-3-fill";
import Info from "~icons/ri/information-line";
import Keyhole from "~icons/ri/shield-keyhole-line";
import { Encrypt, Decrypt } from "@/utils/aes";
import { getAesKey, getIvKey } from "@/utils/config";

defineOptions({
  name: "Login"
});

const imgCode = ref("");
const loginDay = ref(7);
const router = useRouter();
const loading = ref(false);
const checked = ref(false);
const disabled = ref(false);
const ruleFormRef = ref<FormInstance>();
const currentPage = computed(() => {
  return useUserStoreHook().currentPage;
});

const { t } = useI18n();
const { initStorage } = useLayout();
initStorage();
const { dataTheme, themeMode, dataThemeChange, setLayoutThemeColor } =
  useDataThemeChange();
dataThemeChange(themeMode.value);
const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
const { locale, translationCh, translationEn } = useTranslationLang();

const ruleForm = reactive({
  username: "admin",
  password: "123456",
  verifyCode: ""
});

const onLogin = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate(valid => {
    if (valid) {
      loading.value = true;
      useUserStoreHook()
        .loginByUsername({
          username: ruleForm.username,
          // password: ruleForm.password,
          // password: Encrypt(ruleForm.password, getAesKey(), getIvKey()),
          encryptPassword: Encrypt(
            ruleForm.password,
            "w8ghb7kyuH15ykby",
            "GcxQYCWvHQW58XAD"
          )
        })
        .then(res => {
          // console.log("打印", res);
          if (res.msg == "success" || res.success) {
            console.log("路由初始化");
            // 获取后端路由;
            return initRouter().then(() => {
              disabled.value = true;
              router
                .push(getTopMenu(true).path)
                .then(() => {
                  message(t("login.pureLoginSuccess"), { type: "success" });
                })
                .finally(() => (disabled.value = false));
            });
          } else {
            console.log("路由跳转失败哦");
            message(t("login.pureLoginFail"), { type: "error" });
          }
        })
        .finally(() => (loading.value = false));
    }
  });
};

const immediateDebounce: any = debounce(
  formRef => onLogin(formRef),
  1000,
  true
);

useEventListener(document, "keydown", ({ code }) => {
  if (
    ["Enter", "NumpadEnter"].includes(code) &&
    !disabled.value &&
    !loading.value
  )
    immediateDebounce(ruleFormRef.value);
});

watch(imgCode, value => {
  useUserStoreHook().SET_VERIFYCODE(value);
});
watch(checked, bool => {
  useUserStoreHook().SET_ISREMEMBERED(bool);
});
watch(loginDay, value => {
  useUserStoreHook().SET_LOGINDAY(value);
});
</script>

<template>
  <!-- 登录框页面 -->
  <div class="select-none">
    <img :src="bg" class="wave" />
    <div class="flex-c absolute right-5 top-3">
      <!-- 主题 -->
      <!-- <el-switch
        v-model="dataTheme"
        inline-prompt
        :active-icon="dayIcon"
        :inactive-icon="darkIcon"
        @change="dataThemeChange"
      /> -->
      <!-- 国际化 -->
      <!-- <el-dropdown trigger="click">
        <globalization
          class="hover:text-primary hover:bg-[transparent]! w-[20px] h-[20px] ml-1.5 cursor-pointer outline-hidden duration-300"
        />
        <template #dropdown>
          <el-dropdown-menu class="translation">
            <el-dropdown-item
              :style="getDropdownItemStyle(locale, 'zh')"
              :class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]"
              @click="translationCh"
            >
              <IconifyIconOffline
                v-show="locale === 'zh'"
                class="check-zh"
                :icon="Check"
              />
              简体中文
            </el-dropdown-item>
            <el-dropdown-item
              :style="getDropdownItemStyle(locale, 'en')"
              :class="['dark:text-white!', getDropdownItemClass(locale, 'en')]"
              @click="translationEn"
            >
              <span v-show="locale === 'en'" class="check-en">
                <IconifyIconOffline :icon="Check" />
              </span>
              English
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown> -->
    </div>
    <div class="login-container">
      <div class="img">
        <component :is="toRaw(illustration)" />
      </div>
      <div class="login-box">
        <div class="login-form">
          <!-- 图标矢量图 -->
          <avatar class="avatar" />
          <!-- 标题Title -->
          <Motion>
            <h2 class="outline-hidden">
              <TypeIt
                :options="{ strings: [title], cursor: false, speed: 100 }"
              />
            </h2>
          </Motion>

          <el-form
            v-if="currentPage === 0"
            ref="ruleFormRef"
            :model="ruleForm"
            :rules="loginRules"
            size="large"
          >
            <Motion :delay="100">
              <el-form-item
                :rules="[
                  {
                    required: true,
                    message: transformI18n($t('login.pureUsernameReg')),
                    trigger: 'blur'
                  }
                ]"
                prop="username"
              >
                <el-input
                  v-model="ruleForm.username"
                  clearable
                  :placeholder="t('login.pureUsername')"
                  :prefix-icon="useRenderIcon(User)"
                />
              </el-form-item>
            </Motion>

            <Motion :delay="150">
              <el-form-item prop="password">
                <el-input
                  v-model="ruleForm.password"
                  clearable
                  show-password
                  :placeholder="t('login.purePassword')"
                  :prefix-icon="useRenderIcon(Lock)"
                />
              </el-form-item>
            </Motion>

            <Motion :delay="200">
              <el-form-item prop="verifyCode">
                <el-input
                  v-model="ruleForm.verifyCode"
                  clearable
                  :placeholder="t('login.pureVerifyCode')"
                  :prefix-icon="useRenderIcon(Keyhole)"
                >
                  <template v-slot:append>
                    <ReImageVerify v-model:code="imgCode" />
                  </template>
                </el-input>
              </el-form-item>
            </Motion>

            <Motion :delay="250">
              <el-form-item>
                <div class="w-full h-[20px] flex justify-between items-center">
                  <el-checkbox v-model="checked">
                    <span class="flex">
                      <select
                        v-model="loginDay"
                        :style="{
                          width: loginDay < 10 ? '10px' : '16px',
                          outline: 'none',
                          background: 'none',
                          appearance: 'none',
                          border: 'none'
                        }"
                      >
                        <option value="1">1</option>
                        <option value="7">7</option>
                        <option value="30">30</option>
                      </select>
                      {{ t("login.pureRemember") }}
                      <IconifyIconOffline
                        v-tippy="{
                          content: t('login.pureRememberInfo'),
                          placement: 'top'
                        }"
                        :icon="Info"
                        class="ml-1"
                      />
                    </span>
                  </el-checkbox>
                  <el-button
                    link
                    type="primary"
                    @click="useUserStoreHook().SET_CURRENTPAGE(4)"
                  >
                    {{ t("login.pureForget") }}
                  </el-button>
                </div>
                <el-button
                  class="w-full mt-4!"
                  size="default"
                  type="primary"
                  :loading="loading"
                  :disabled="disabled"
                  @click="onLogin(ruleFormRef)"
                >
                  {{ t("login.pureLogin") }}
                </el-button>
              </el-form-item>
            </Motion>

            <Motion :delay="300">
              <el-form-item>
                <div class="w-full h-[20px] flex justify-between items-center">
                  <el-button
                    v-for="(item, index) in operates"
                    :key="index"
                    class="w-full mt-4!"
                    size="default"
                    @click="useUserStoreHook().SET_CURRENTPAGE(index + 1)"
                  >
                    {{ t(item.title) }}
                  </el-button>
                </div>
              </el-form-item>
            </Motion>
          </el-form>

          <!-- 手机号登录 -->
          <LoginPhone v-if="currentPage === 1" />
          <!-- 二维码登录 -->
          <LoginQrCode v-if="currentPage === 2" />
          <!-- 注册 -->
          <LoginRegist v-if="currentPage === 3" />
          <!-- 忘记密码 -->
          <LoginUpdate v-if="currentPage === 4" />
        </div>
      </div>
    </div>
    <div
      class="w-full flex-c absolute bottom-3 text-sm text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
    >
      Copyright © 2020-present
      <a
        class="hover:text-primary!"
        href="https://github.com/pure-admin"
        target="_blank"
      >
        &nbsp;{{ title }}
      </a>
    </div>
  </div>
</template>

<style scoped>
@import url("@/style/login.css");
</style>

<style lang="scss" scoped>
:deep(.el-input-group__append, .el-input-group__prepend) {
  padding: 0;
}

.translation {
  ::v-deep(.el-dropdown-menu__item) {
    padding: 5px 40px;
  }

  .check-zh {
    position: absolute;
    left: 20px;
  }

  .check-en {
    position: absolute;
    left: 20px;
  }
}
.select-none {
  width: 100%;
  height: 100vh; /* 视窗高度 */
  background-image: radial-gradient(
    circle,
    rgba(81, 126, 168, 0.7) 0%,
    rgba(177, 121, 121, 0.3) 50%,
    transparent 70%
  );
  background-size: cover;
  animation: ripple 5s infinite;
}
@keyframes ripple {
  0% {
    background-position: center;
  }
  100% {
    background-position: 100% 100%; /* 使背景移动以创建动态效果 */
  }
}
</style>

 

关键代码:

const onLogin = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate(valid => {
    if (valid) {
      loading.value = true;
      useUserStoreHook()
        .loginByUsername({
          username: ruleForm.username,
          // password: ruleForm.password,
          encryptPassword: Encrypt(
            ruleForm.password,
            "w8ghb7kyuH15ykby",
            "GcxQYCWvHQW58XAD"
          )
        })
        .then(res => {
          if (res.msg == "success" || res.success) {
            console.log("路由初始化");
            // 获取后端路由---这里未等待loginByUsername里面的异步接口(getUserInfo)执行完成,就直接调用了initRouter(),导致第一次登录不进行跳转
            return initRouter().then(() => {
              disabled.value = true;
              router
                .push(getTopMenu(true).path)
                .then(() => {
                  message(t("login.pureLoginSuccess"), { type: "success" });
                })
                .finally(() => (disabled.value = false));
            });
          } else {
            console.log("路由跳转失败哦");
            message(t("login.pureLoginFail"), { type: "error" });
          }
        })
        .finally(() => (loading.value = false));
    }
  });
};

 

 

第二个页面:src\store\modules\user.ts

import { defineStore } from "pinia";
import {
  type userType,
  store,
  router,
  resetRouter,
  routerArrays,
  storageLocal
} from "../utils";
import {
  type UserResult,
  type RefreshTokenResult,
  getLogin,
  logOutApi,
  refreshTokenApi,
  getUserInfo
} from "@/api/user";
import { useMultiTagsStoreHook } from "./multiTags";
import { type DataInfo, setToken, setUserToken, removeToken, userKey } from "@/utils/auth";

export const useUserStore = defineStore("pure-user", {
  state: (): userType => ({
    // id
    id: storageLocal().getItem<DataInfo<number>>(userKey)?.id ?? "",
    // 头像
    avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
    // 用户名
    username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "",
    // 昵称
    nickname: storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "",
    // 手机号
    mobile: storageLocal().getItem<DataInfo<number>>(userKey)?.mobile ?? "",
    // 角色Id
    roleId: storageLocal().getItem<DataInfo<number>>(userKey)?.roleId ?? "",
    // 角色名称
    roleName: storageLocal().getItem<DataInfo<number>>(userKey)?.roleName ?? "",
    // 页面级别权限
    roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],
    // 按钮级别权限
    permissions:
      storageLocal().getItem<DataInfo<number>>(userKey)?.permissions ?? [],
    // 前端生成的验证码(按实际需求替换)
    verifyCode: "",
    // 判断登录页面显示哪个组件(0:登录(默认)、1:手机登录、2:二维码登录、3:注册、4:忘记密码)
    currentPage: 0,
    // 是否勾选了登录页的免登录
    isRemembered: false,
    // 登录页的免登录存储几天,默认7天
    loginDay: 7
  }),
  actions: {
    /** 存储ID */
    SET_ID(id: any) {
      this.id = id;
    },
    /** 存储头像 */
    SET_AVATAR(avatar: string) {
      this.avatar = avatar;
    },
    /** 存储用户名 */
    SET_USERNAME(username: string) {
      this.username = username;
    },
    /** 存储昵称 */
    SET_NICKNAME(nickname: string) {
      this.nickname = nickname;
    },
    /** 存储手机号 */
    SET_MOBILE(mobile: any) {
      this.mobile = mobile;
    },
    /** 存储角色id */
    SET_ROLEID(roleId: any) {
      this.roleId = roleId;
    },
    /** 存储角色名称 */
    SET_ROLENAME(roleName: any) {
      this.roleName = roleName;
    },
    /** 存储角色 */
    SET_ROLES(roles: Array<string>) {
      this.roles = roles;
    },
    /** 存储按钮级别权限 */
    SET_PERMS(permissions: Array<string>) {
      this.permissions = permissions;
    },
    /** 存储前端生成的验证码 */
    SET_VERIFYCODE(verifyCode: string) {
      this.verifyCode = verifyCode;
    },
    /** 存储登录页面显示哪个组件 */
    SET_CURRENTPAGE(value: number) {
      this.currentPage = value;
    },
    /** 存储是否勾选了登录页的免登录 */
    SET_ISREMEMBERED(bool: boolean) {
      this.isRemembered = bool;
    },
    /** 设置登录页的免登录存储几天 */
    SET_LOGINDAY(value: number) {
      this.loginDay = Number(value);
    },
    /** 登入 */
    async loginByUsername(data) {
      return new Promise<UserResult>((resolve, reject) => {
        getLogin(data)
          .then(data => {
            // console.log('成功登录', data, data.body);
            if (data?.msg) {
              setToken(data.body, () => {
                resolve(data); //通过回调解决 Promise
              });
            } else {
              reject();
            }

          })
          .catch(error => {
            reject(error);
          });
      });
    },
    /** 登出(调用接口) */
    logOut() {
      return new Promise<UserResult>((resolve, reject) => {
        logOutApi().then(data => {
          // console.log('退出登录成功', data);
          this.webLogOut()
        })
          .catch(error => {
            // console.log('退出失败哦', error);
            this.webLogOut()
          });
      });
    },

    /** 前端登出(不调用接口) */
    webLogOut() {
      this.username = "";
      this.roles = [];
      this.permissions = [];
      removeToken();
      useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
      resetRouter();
      router.push("/login");
    },
    /** 刷新`token` */
    async handRefreshToken(data) {
      return new Promise<RefreshTokenResult>((resolve, reject) => {
        refreshTokenApi(data)
          .then(data => {
            if (data) {
              setToken(data.data);
              resolve(data);
            }
          })
          .catch(error => {
            reject(error);
          });
      });
    }
  }
});

export function useUserStoreHook() {
  return useUserStore(store);
}

 

第二个页面-关键代码:

    /** 登入 */
    async loginByUsername(data) {
      return new Promise<UserResult>((resolve, reject) => {
        getLogin(data)
          .then(data => {
            // console.log('成功登录', data, data.body);  
            if (data?.msg) {
         // setToken(data.body) 这里使用promise的回调函数()=>{}进行操作,解决异步问题
              setToken(data.body, () => {
                resolve(data); //这里之前没有写回调函数就会出现首次登录不跳转,通过回调解决 Promise
              });
            } else {
              reject();
            }
          })
          .catch(error => {
            reject(error);
          });
      });
    },

 

第三个页面:src\utils\auth.ts

import Cookies from "js-cookie";
import { useUserStoreHook } from "@/store/modules/user";
import { storageLocal, isString, isIncludeAllChildren } from "@pureadmin/utils";
import { getUserInfo } from "@/api/user";
import { object } from "vue-types";
import { log } from "console";

export interface DataInfo<T> {
  /** token */
  accessToken: string;
  /** `accessToken`的过期时间(时间戳) */
  expires: T;
  /** 用于调用刷新accessToken的接口时所需的token */
  refreshToken: string;
  /** id */
  id: any;
  /** 头像 */
  avatar?: string;
  /** 用户名 */
  username?: string;
  /** 昵称 */
  nickname?: string;
  /** 当前登录用户的角色 */
  roles?: Array<string>;
  /** 当前登录用户的按钮级别权限 */
  permissions?: Array<string>;
}

export const userKey = "user-info";
export const TokenKey = "authorized-token";
/**
 * 通过`multiple-tabs`是否在`cookie`中,判断用户是否已经登录系统,
 * 从而支持多标签页打开已经登录的系统后无需再登录。
 * 浏览器完全关闭后`multiple-tabs`将自动从`cookie`中销毁,
 * 再次打开浏览器需要重新登录系统
 * */
export const multipleTabsKey = "multiple-tabs";

/** 获取`token` */
export function getToken(): DataInfo<number> {
  // 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
  // console.log('获取token', TokenKey, Cookies.get(TokenKey), JSON.parse(Cookies.get(TokenKey)), storageLocal().getItem(userKey));
  return Cookies.get(TokenKey)
    ? JSON.parse(Cookies.get(TokenKey))
    : storageLocal().getItem(userKey);
}

/**
 * @description 设置`token`以及一些必要信息并采用无感刷新`token`方案
 * 无感刷新:后端返回`accessToken`(访问接口使用的`token`)、`refreshToken`(用于调用刷新`accessToken`的接口时所需的`token`,`refreshToken`的过期时间(比如30天)应大于`accessToken`的过期时间(比如2小时))、`expires`(`accessToken`的过期时间)
 * 将`accessToken`、`expires`、`refreshToken`这三条信息放在key值为authorized-token的cookie里(过期自动销毁)
 * 将`avatar`、`username`、`nickname`、`roles`、`permissions`、`refreshToken`、`expires`这七条信息放在key值为`user-info`的localStorage里(利用`multipleTabsKey`当浏览器完全关闭后自动销毁)
 */
export async function setToken(data: DataInfo<Date>, logincb?) {
  // logincb ? 这个是形参
  let expires = 0;
  // console.log('存储信息', data);
  const accessToken = data
  const refreshToken = data
  // const { accessToken, refreshToken } = data;
  // console.log('存储赋值', accessToken, refreshToken);
  const { isRemembered, loginDay } = useUserStoreHook();
  expires = new Date(data.expires).getTime(); // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo<Date>改成DataInfo<number>即可
  const cookieString = JSON.stringify({ accessToken, expires, refreshToken });

  expires > 0
    ? Cookies.set(TokenKey, cookieString, {
      expires: (expires - Date.now()) / 86400000
    })
    : Cookies.set(TokenKey, cookieString);

  Cookies.set(
    multipleTabsKey,
    "true",
    isRemembered
      ? {
        expires: loginDay
      }
      : {}

  );

  await getUserInfo().then(userData => {
    // console.log('用户列表获取成功', userData,);
    if (userData?.msg == 'success') {
      logincb && logincb();
      setUserToken(userData.body, refreshToken, expires,);

    }
  })
    .catch(error => {
      console.log('用户列表获取失败', error);
      reject(error);
    });


}

// 存储用户信息
export function setUserToken(data: DataInfo<Date>, refreshToken: any, expires: any) {
  console.log('返回用户信息', data);
  function setUserKey({ id, avatar, username, nickname, mobile, roleId, roleName, roles, permissions }) {
    useUserStoreHook().SET_ID(id);
    useUserStoreHook().SET_AVATAR(avatar);
    useUserStoreHook().SET_USERNAME(username);
    useUserStoreHook().SET_NICKNAME(nickname);
    useUserStoreHook().SET_MOBILE(mobile);
    useUserStoreHook().SET_ROLEID(roleId);
    useUserStoreHook().SET_ROLENAME(roleName);
    useUserStoreHook().SET_ROLES(roles);
    useUserStoreHook().SET_PERMS(permissions);
    storageLocal().setItem(userKey, {
      refreshToken,
      expires,
      id,
      avatar,
      username,
      nickname,
      mobile,
      roleId,
      roleName,
      roles,
      permissions
    });
  }
  // if (data.username && data.roles) {
  if (data.id && data.roleId) {
    const { username, roles } = data;
    setUserKey({
      id: data?.id ?? "",
      avatar: data?.avatarUrl ?? "",
      username: data?.name ?? "",
      nickname: data?.nickname ?? "",
      mobile: data?.mobile ?? "",
      roleId: data?.roleId ?? "",
      roleName: data?.roleName ?? "",
      roles,
      permissions: data?.permissions ?? []
    });
  } else {
    const id =
      storageLocal().getItem<DataInfo<number>>(userKey)?.id ?? "";
    const avatar =
      storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "";
    const username =
      storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
    const nickname =
      storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "";
    const mobile =
      storageLocal().getItem<DataInfo<number>>(userKey)?.mobile ?? "";
    const roleId =
      storageLocal().getItem<DataInfo<number>>(userKey)?.roleId ?? [];
    const roleName =
      storageLocal().getItem<DataInfo<number>>(userKey)?.roleName ?? [];
    const roles =
      storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
    const permissions =
      storageLocal().getItem<DataInfo<number>>(userKey)?.permissions ?? [];
    setUserKey({
      id,
      avatar,
      username,
      nickname,
      mobile,
      roleId,
      roleName,
      roles,
      permissions
    });
  }
}

/** 删除`token`以及key值为`user-info`的localStorage信息 */
export function removeToken() {
  Cookies.remove(TokenKey);
  Cookies.remove(multipleTabsKey);
  storageLocal().removeItem(userKey);
}

/** 格式化token(jwt格式) */
export const formatToken = (token: string): string => {
  // return "Bearer " + token;
  return token;
};

/** 是否有按钮级别的权限(根据登录接口返回的`permissions`字段进行判断)*/
export const hasPerms = (value: string | Array<string>): boolean => {
  if (!value) return false;
  const allPerms = "*:*:*";
  const { permissions } = useUserStoreHook();
  if (!permissions) return false;
  if (permissions.length === 1 && permissions[0] === allPerms) return true;
  const isAuths = isString(value)
    ? permissions.includes(value)
    : isIncludeAllChildren(value, permissions);
  return isAuths ? true : false;
};

 

第三个页面关键代码:

export async function setToken(data: DataInfo<Date>, logincb?) {
  // logincb ? 这个是形参
  let expires = 0;
  const accessToken = data
  const refreshToken = data
  // console.log('存储赋值', accessToken, refreshToken);
  const { isRemembered, loginDay } = useUserStoreHook();
  expires = new Date(data.expires).getTime(); // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo<Date>改成DataInfo<number>即可
  const cookieString = JSON.stringify({ accessToken, expires, refreshToken });
  expires > 0
    ? Cookies.set(TokenKey, cookieString, {
      expires: (expires - Date.now()) / 86400000
    })
    : Cookies.set(TokenKey, cookieString);
  Cookies.set(
    multipleTabsKey,
    "true",
    isRemembered
      ? {
        expires: loginDay
      }: {}
  );
  await getUserInfo().then(userData => {
    // console.log('用户列表获取成功', userData,);  这里形参logincb()
    if (userData?.msg == 'success') {
      logincb && logincb();
      setUserToken(userData.body, refreshToken, expires,);
    }
  })
    .catch(error => {
      console.log('用户列表获取失败', error);
      reject(error);
    });
}

 

posted @ 2025-12-24 11:45  Evident  阅读(0)  评论(0)    收藏  举报