Vue3+Vite+Pinia+Naive后台管理系统搭建之七:utils 工具构建

news2024/11/23 15:16:01

前言

如果对 vue3 的语法不熟悉的,可以移步Vue3.0 基础入门,快速入门。

1. cookie 保存工具

1.1 安装依赖
yarn add js-cookie
// or
npm install js-cookie

 1.2 编写 src/utils/cookie.js
// src/utils/cookie.js
import Cookies from "js-cookie";

const TokenKey = "User-Token";
const ExpiresInKey = "User-Expires-In";

export function getToken() {
  return Cookies.get(TokenKey);
}

export function setToken(token) {
  return Cookies.set(TokenKey, token);
}

export function removeToken() {
  return Cookies.remove(TokenKey);
}

2. 构建 src/utils/common.js 共用方法

// src/utils/common.js
/**
 * 参数处理
 * @param {*} params  参数
 */
export function tansParams(params) {
  let result = "";
  for (const propName of Object.keys(params)) {
    const value = params[propName];
    var part = encodeURIComponent(propName) + "=";
    if (value !== null && value !== "" && typeof value !== "undefined") {
      if (typeof value === "object") {
        for (const key of Object.keys(value)) {
          if (
            value[key] !== null &&
            value[key] !== "" &&
            typeof value[key] !== "undefined"
          ) {
            let params = propName + "[" + key + "]";
            var subPart = encodeURIComponent(params) + "=";
            result += subPart + encodeURIComponent(value[key]) + "&";
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&";
      }
    }
  }
  return result;
}

// 日期格式化
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null;
  }
  const format = pattern || "{y}-{m}-{d} {h}:{i}:{s}";
  let date;
  if (typeof time === "object") {
    date = time;
  } else {
    if (typeof time === "string" && /^[0-9]+$/.test(time)) {
      time = parseInt(time);
    } else if (typeof time === "string") {
      time = time
        .replace(new RegExp(/-/gm), "/")
        .replace("T", " ")
        .replace(new RegExp(/\.[\d]{3}/gm), "");
    }
    if (typeof time === "number" && time.toString().length === 10) {
      time = time * 1000;
    }
    date = new Date(time);
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  };
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key];
    if (key === "a") {
      return ["日", "一", "二", "三", "四", "五", "六"][value];
    }
    if (result.length > 0 && value < 10) {
      value = "0" + value;
    }
    return value || 0;
  });
  return time_str;
}

// 添加日期范围
export function addDateRange(params, dateRange, propName) {
  let search = params;
  search.params =
    typeof search.params === "object" &&
    search.params !== null &&
    !Array.isArray(search.params)
      ? search.params
      : {};
  dateRange = Array.isArray(dateRange) ? dateRange : [];
  if (typeof propName === "undefined") {
    search.params["beginTime"] = dateRange[0];
    search.params["endTime"] = dateRange[1];
  } else {
    search.params["begin" + propName] = dateRange[0];
    search.params["end" + propName] = dateRange[1];
  }
  return search;
}

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 */
export function handleTree(data, id, parentId, children) {
  let config = {
    id: id || "id",
    parentId: parentId || "parentId",
    childrenList: children || "children",
  };

  var childrenListMap = {};
  var nodeIds = {};
  var tree = [];

  for (let d of data) {
    let parentId = d[config.parentId];
    if (childrenListMap[parentId] == null) {
      childrenListMap[parentId] = [];
    }
    nodeIds[d[config.id]] = d;
    childrenListMap[parentId].push(d);
  }

  for (let d of data) {
    let parentId = d[config.parentId];
    if (nodeIds[parentId] == null) {
      tree.push(d);
    }
  }

  function adaptToChildrenList(o) {
    if (childrenListMap[o[config.id]] !== null) {
      o[config.childrenList] = childrenListMap[o[config.id]];
    }
    if (o[config.childrenList]) {
      for (let c of o[config.childrenList]) {
        adaptToChildrenList(c);
      }
    }
  }

  for (let t of tree) {
    adaptToChildrenList(t);
  }

  return tree;
}

3. 构建 src/utils/erorCode.js 错误码转换

//  src/utils/erorCode.js
export default {
  '401': '认证失败,无法访问系统资源',
  '403': '当前操作没有权限',
  '404': '访问资源不存在',
  'default': '系统未知错误,请反馈给管理员'
}

4. 构建 src/utils/jsencrypt.js 数据加密

4.1 安装依赖
yarn add jsencrypt
// or
npm install jsencrypt

 4.2 编写 jsencrypt.js
// src/utils/jsencrypt.js
import JSEncrypt from "jsencrypt/bin/jsencrypt.min";

const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
  'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='

const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
  '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
  'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
  'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
  'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
  'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
  'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
  'UP8iWi1Qw0Y='

  // 加密
export function encrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPublicKey(publicKey) // 设置公钥
  return encryptor.encrypt(txt) // 对数据进行加密
}

// 解密
export function decrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPrivateKey(privateKey) // 设置私钥
  return encryptor.decrypt(txt) // 对数据进行解密
}

5. 构建 src/utils/validate.js 数据验证

// src/utils/validate.js
/**
 * 判断是否外部网址
 * @param {string} path
 * @returns {Boolean}
 */
export function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path);
}

/**
 * 有效用户名
 * @param {string} str
 * @returns {Boolean}
 */
export function validUsername(str) {
  const valid_map = ["admin", "editor"];
  return valid_map.indexOf(str.trim()) >= 0;
}

// 判断是否电话
export function isPhone(str) {
  return /^1[0-9]{10}$/.test(str);
}

/**
 * 是否email
 * @param {string} email
 * @returns {Boolean}
 */
export function isEmail(email) {
  const reg =
    /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return reg.test(email);
}

/**
 * 判断是否网址
 * @param {string} url
 * @returns {Boolean}
 */
export function isURL(url) {
  const reg =
    /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
  return reg.test(url);
}

/**
 * 有效小写字母
 * @param {string} str
 * @returns {Boolean}
 */
export function validLowerCase(str) {
  const reg = /^[a-z]+$/;
  return reg.test(str);
}

/**
 * 有效大写字母
 * @param {string} str
 * @returns {Boolean}
 */
export function validUpperCase(str) {
  const reg = /^[A-Z]+$/;
  return reg.test(str);
}

/**
 * 是否字母
 * @param {string} str
 * @returns {Boolean}
 */
export function isAlphabets(str) {
  const reg = /^[A-Za-z]+$/;
  return reg.test(str);
}

/**
 * 是否字符串
 * @param {string} str
 * @returns {Boolean}
 */
export function isString(str) {
  if (typeof str === "string" || str instanceof String) {
    return true;
  }
  return false;
}

/**
 * 是否数组
 * @param {Array} arg
 * @returns {Boolean}
 */
export function isArray(arg) {
  if (typeof Array.isArray === "undefined") {
    return Object.prototype.toString.call(arg) === "[object Array]";
  }
  return Array.isArray(arg);
}

 6. 完善 src/api/http.js 接口逻辑

// src/api/http.js
import axios from "axios";
import errorCode from "@/utils/errorCode";
import { getToken } from "@/utils/cookie.js";
import { tansParams } from "@/utils/common.js";
import { useUserStore } from "@/store/user.js";

// 请求和响应的消息主体用什么方式编码
axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 10000,
});

// 请求拦截器
service.interceptors.request.use((config) => {
  // 是否需要 token
  const isToken = (config.headers || {}).isToken === true;
  if (getToken() && !isToken) {
    // 让请求携带token
    config.headers["Authorization"] = "Bearer " + getToken();
  }
  // get请求映射params参数
  if (config.method === "get" && config.params) {
    let url = config.url + "?" + tansParams(config.params);
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
  }
  return config;
});

// 响应拦截器
service.interceptors.response.use((res) => {
  let userStore = useUserStore();
  // 未设置返回码默认200
  let code = res.data.code || 200;
  // 获取错误信息
  let msg = errorCode[code] || res.data.msg || errorCode["default"];
  // 二进制数据直接返回
  if (
    res.request.responseType === "blob" ||
    res.request.responseType === "arraybuffer"
  ) {
    return res.data;
  }
  if (code === 401) {
    window.$msg.info("登录状态已过期,请重新登录");
    userStore.logout().then(() => {
      window.location.href = "/home";
    });
  }
  if (code !== 200) {
    window.$msg.error(msg);
  }
  return res.data;
});

export default service;

 7. 完善 src/api/login.js 登录逻辑接口

// src/api/login.js
import api from "./http.js";

// 登录
export function login(data) {
  return api({
    url: `/auth/login`,
    headers: {
      isToken: false,
    },
    method: "post",
    data,
  });
}

// 退出
export function logout() {
  return api({
    url: `/auth/logout`,
    method: "delete",
  });
}

// 获取用户详细信息
export function getInfo() {
  return api({
    url: `/system/user/getInfo`,
    method: "get",
  });
}

8. 完善 src/store/user.js 状态共享逻辑

// src/store/user.js
import { defineStore } from "pinia";
import { login, logout, getInfo } from "@/api/login.js";
import { getToken, setToken, removeToken } from "@/utils/cookie.js";

export const useUserStore = defineStore({
  id: "userStore",
  state: () => {
    return {
      token: getToken() || "",
      user: {},
    };
  },
  getters: {
    ageAfter(state) {
      return state.user.age + 10;
    },
  },
  actions: {
    // 登录接口
    async login(data) {
      return new Promise((resolve, reject) => {
        login(data)
          .then((res) => {
            if (res.code !== 200) {
              reject(res);
            }

            const { data } = res;
            // 保存token
            setToken(data.access_token);
            this.token = data.access_token;
            resolve(data);
          })
          .catch((err) => {
            reject(err);
          });
      });
    },
    // 退出接口
    async logout() {
      return new Promise((resolve, reject) => {
        logout()
          .then((res) => {
            if (res.code !== 200) {
              reject(res);
            }

            this.token = "";
            removeToken();
            resolve();
          })
          .catch((err) => {
            reject(err);
          });
      });
    },
    // 获取用户信息
    async getInfo() {
      return new Promise((resolve, reject) => {
        getInfo()
          .then(({ data }) => {
            if (res.code !== 200) {
              reject(res);
            }

            this.user = data;
            resolve(data);
          })
          .catch((err) => {
            reject(err);
          });
      });
    },
  },
});

9. 完善 src/router/index.js 路由跳转逻辑

9.1 安装依赖
// 路由跳转顶部进度条
yarn add nprogress
// or
npm install nprogress

 9.2 编写 src/router/index.js
// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
import baseRouters from "./baseRouter.js";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/cookie.js";
const whiteList = ["/", "/login"];

const routes = [...baseRouters];
const _createRouter = () =>
  createRouter({
    history: createWebHistory(),
    routes,
    scrollBehavior() {
      return {
        el: "#app",
        top: 0,
        behavior: "smooth",
      };
    },
  });

export function resetRouter() {
  const newRouter = _createRouter();
  router.matcher = newRouter.matcher;
}

const router = _createRouter();

// 路由监听
router.beforeEach((to, from, next) => {
  NProgress.start();
  if (!!getToken()) {
    // 有token,跳转登录时重定向首页
    if (to.path === "/login") {
      next("");
      NProgress.done();
    }
    next();
    NProgress.done();
  } else {
    // 判断路由是否在白名单,是直接跳转
    if (whiteList.indexOf(to.path) !== -1) {
      next();
      // 未登录页面跳转,直接跳转到登录页
    } else {
      next(`/login?redirect=${to.fullPath}`);
    }
    NProgress.done();
  }
});

export default router;

10. 完善 src/pages/login.vue 登录逻辑

<script setup>
import { reactive } from "vue";
import router from "@/router/index.js";
import { NButton, NInput } from "naive-ui";
import { useUserStore } from "@/store/user.js";

// 用户状态管理
let userStore = useUserStore();

let loginForm = reactive({
  username: "yqcoder",
  password: "6666",
});
let handleLogin = () => {
  userStore.login(loginForm).then(() => {
    router.push({ name: "home" });
  });
};
</script>

<template>
  <div class="login">
    <n-input v-model:value="loginForm.username"></n-input>
    <n-input v-model:value="loginForm.password"></n-input>
    <n-button type="primary" size="small" @click="handleLogin">登录</n-button>
  </div>
</template>

<style lang="scss" scoped></style>

 

综上

utils 工具构建完成。接口请求逻辑,页面跳转逻辑,状态共享逻辑处理完成。

下一章:后台管理系统登录页构建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/745961.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统_计算机系统

目录 1. 操作系统的基本概念 1.1 操作系统的特征 1.1.1 并发&#xff08;Concurrence&#xff09; 1.1.2 共享&#xff08;Sharing&#xff09; 1.1.3 虚拟&#xff08;Virtual&#xff09; 1.1.4 异步&#xff08;Asynchronism&#xff09; 1.2 操作系统的目标和功能 …

FIR滤波器与IIR滤波器的区别与特点

目录 FIR滤波器与IIR滤波器的区别与特点 FIR滤波器定义&#xff1a; 特点&#xff1a; IIR滤波器定义&#xff1a; 特点&#xff1a; 区别&#xff1a; IIR滤波器有以下几个特点&#xff1a; IIR与FIR数字滤波器的比较&#xff1a; 1、从性能上比较 2、从结…

Firewalld防火墙 图形和字符

目录 字符界面 一、防火墙介绍 二、防火墙的基本应用 将防火墙接口划分到区域中 区域添加访问规则 图形界面 字符界面 安装图形化防火墙管理工具 [rootbogon ~]# yum -y install firewall-config 一、防火墙介绍 1、netfilter和防火墙管理工具 1&#xff09;netfilter …

Unity Obfuscator 过滤指定目录下的所有类

视频 Unity Obfuscator 过滤指定目录下的所有class 源码 替换调 OptionsManager 脚本文件就可以了 using System.Collections.Generic; using UnityEngine; using UnityEditor;

Tomcat查看源码

比如需要从请求域拿数据 点击右上角的Choose Sources 找到对应源码的位置 源码 下载Tomcat源码 http://tomcat.apache.org 下载指定版本Tomcat https://archive.apache.org/dist/tomcat/ 下载下来解压即可

spring boot MySQL操作

极简spring boot MySQL测试 默认: spring boot环境已经搭好,可以跑最基本的hello world 有MySQL环境有部分测试数据表,并且有MySQL语法基础 配置 application.yml 如下配置,根据自己的数据库信息与个人需求配置 server: tomcat: uri-encoding: UTF-8 threads: …

为什么TCP是面向字节流协议

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 笔者在TCP 机制一文中有说到 TCP 是面向字节流的&#xff0c;这篇博客给大家介绍一下&#xff1a;为什么 TCP 是面向字节流协议的。 首先说一下 UDP &#xff0c;是一…

prompt:有需求就有价值,prompt案例

prompt&#xff1a;有需求就有价值 此文章来自于小七姐 首先来看需求&#xff1a; 客户需要生成1000条俏皮灵动&#xff0c;趣味盎然&#xff0c;比喻精妙的和美食有关的短句子&#xff0c;要求文风优美&#xff0c;句子让人充满食欲。 客户使用这些句子的场景比较奇妙&…

eval() trim() dispaly:contents等属性的理解

1.eval(除去前后的双引号) 2.字符串.trim() 除去前后的空格 3.display:contents(提升到父级元素的子集元素)

How to Make Your Writing Stand Out From AI 如何让你的写作从人工智能中脱颖而出

Thanks to amazing tools such as ChatGPT, there is now a flood of well-written, functional, and useful writing to compete with if you’re a human writer. This means your writing needs to differentiate itself from this new breed of content. Here’s how to do…

OSError [Errno 22] Invalid argument(已解决)

最近跑别人的项目遇到一个这样的问题 OSError: [Errno 22] Invalid argument xxxxxxxxxxxxxxxxxx一开始以为是没有用管理员的权限运行&#xff0c;导致创建不了日志文件 后来发现是和windows的命名规则冲突了&#xff08;以下来源官网地址&#xff09; 命名约定 以下基本规…

希尔排序法解析

希尔排序法解析 什么是希尔排序法 希尔排序法&#xff08;Shell Sort&#xff09;&#xff0c;也称为缩小增量排序&#xff0c;是一种改进的插入排序算法。它通过将待排序的元素按照一定的间隔分组&#xff0c;对每个分组进行插入排序&#xff0c;逐渐减小间隔直至为1&#x…

单个电源模块不带电感的直流压降仿真

单个电源模块不带电感的直流压降仿真 前面讲过POWER DC如何对单个电源模块带电感的直流压降仿真,下面介绍如何对单个电源模块不带电感的直流压降仿真,以下图为例

启动优化中的一些黑科技,了解一下~

1前言 启动速度优化是 android 开发中的常见需求&#xff0c;除了一些常规的手段之外&#xff0c;也有一些黑科技手段&#xff0c;我们来看一下这些黑科技手段是否有效&#xff0c;以及如何实现 本文主要是对Android 性能优化小册相关内容的学习实践&#xff0c;加入了自己的…

腾讯云服务器CVM实例族区别如何选择?

腾讯云服务器CVM有多种实例族&#xff0c;如标准型S6、标准型S5、SA3实例、高IO型、内存、计算型及GPU型实例等&#xff0c;如何选择云服务器CVM实例规格呢&#xff1f;腾讯云服务器网建议根据实际使用场景选择云服务器CVM规格&#xff0c;例如Web网站应用可以选择标准型S5或S6…

linux 读取文件,并输出含空格的每一行

[devusercdp-node12 ~]$ cat test.sh 1234 aaaa 2345 bbbb 3456 cccc 需求 输出三行 分别是 aaaa bbbb cccc 咋一看很简单&#xff0c;实则里面有很多小问题。 rescat test.sh 这个时候思考下 echo $res和 echo "$res"有啥区别 快速的写出for循环 结果 for i in …

Web3 开发指南:使用 NFTScan NFT API 构建一个 NFT 链上追踪器

对于大多数 Web3 团队来说&#xff0c;构建一个完整的链上 NFT 数据追踪系统是一项具有挑战性的任务&#xff0c;构建一个 NFT 链上追踪器更是如此。涉及到处理区块链上的智能合约和交易数据&#xff0c;并将其与外部数据源进行整合和分析工作量是十分巨大的&#xff1a; 区块链…

图像分类——ResNet

目录 残差块ResNet模型手写数字识别 残差块 左图残差块实现如下 import tensorflow as tf from tensorflow.keras import layers,activations#残差块 class Residul(tf.keras.Model):def __init__(self,num_channels,use_1x1convFalse,strides1):super(Residul,self).__init_…

2023年最值得关注的APP开发工具

随着越来越多的开发人员转向移动应用程序开发&#xff0c;行业和企业都在寻找最好的工具来帮助他们。随着市场的变化&#xff0c;客户越来越注重质量和开发效率。 无论您是要创建一个全新的 APP&#xff0c;还是要寻找一种可快速部署的工具来满足您的需求&#xff0c;这篇文章…

RT1176 将代码放到RAM上运行

为了实现IAP固件升级&#xff0c;需要擦写Flash。如果代码在Nor Flash上运行&#xff0c;同时擦写该Flash&#xff0c;代码就会乱了无法正常执行。所以我们要先将代码放到RAM上运行。 但又不能像官方SDK Demo那样将代码直接烧录到RAM上或SDRAM上&#xff0c;否则掉电后代码丢失…