vue3+antd搭建登录页面——vue3初体验——基础积累

news2024/11/25 6:30:22

最近在跟着大神学习vue3,学习过程中遇到各种问题,简直跟几年前学习vue2时一样的不知所措。
认识vite_vue3 初始化项目到打包:http://t.csdn.cn/B3bwC
在这里插入图片描述
为了方便,我是直接在stepin-template项目的基础上操作的,省略了上面的几个步骤。在使用过程中,还是会遇到各种问题:

效果图如下:
在这里插入图片描述
在这里插入图片描述
下面汇总一下遇到的问题,有不对的地方请指正。感谢!!!

1.添加环境变量——.env .env.developmet .env.test

1.1 vue2中的环境变量

vue2中编写项目时,有三种环境,开发环境(.env.development)+测试环境(.env.test)+生产环境(.env)

1.1.1 .env.xx文件中变量的命名方式

.env.development文件为例:

NODE_ENV = development
VUE_APP_API_BASE_URL = http://xxxx:5572
VUE_APP_API_BASE_LOGIN = http://xxxx:5572
VUE_APP_API_HREF_URL = http://xxxx:8894

1.1.2 package.json中的启动方式

对应的在package.json文件中也有相应的触发方式:

"scripts": {
  "serve": "vue-cli-service serve",
  "test": "vue-cli-service serve --mode test",
  "build": "vue-cli-service build",
  "build:test": "vue-cli-service build --mode test"
},

1.1.3 环境变量的使用——process.env.xxx

1.2 vue3中的环境变量

相对应的vue3中也有同样的环境变量:开发环境(.env.development)+测试环境(.env.test)+生产环境(.env)

区别如下:

1.2.1 .env.xx文件中变量的命名方式

.env.development文件为例:

NODE_ENV=development
VITE_BASE_URL=https://xxxx:5572
VITE_BASE_LOGIN=https://xxxx:5572
VITE_BASE_INFO=https://xxxx:5572

定义变量必须以VITE开头,因为我是用的vite,如果你用vue3不用vite就太亏了,vite的运行速度简直是秒开。

1.2.2 package.json中的启动方式

对应的在package.json文件中也有相应的触发方式:

"scripts": {
  "dev": "vite",
  "build": "vue-tsc --noEmit && vite build",
  "build:test": "vue-tsc --noEmit && vite build --mode test",
},

1.2.3 环境变量的使用——import.meta.env.xxx

2.自行封装request.ts文件

stepin-template项目上是有http的封装好的请求文件的,但是我不太懂,因此有些问题不知道从何下手,所以自行封装一个request.ts文件,内容如下:

import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
import router from "../router";
import Cookie from 'js-cookie'
import {message} from 'ant-design-vue';
const xsrfHeaderName = 'Authorization'
// 进度条
import nprogress from "nprogress";
import "nprogress/nprogress.css";
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_BASE_URL, // URL地址
  timeout: 120 * 1000, // 超时时间
});
// 添加请求拦截器
service.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    // 在发送请求之前做些什么
    nprogress.start();
    const { url, xsrfCookieName, headers } = config;
    if (
      headers.Authorization &&
      xsrfCookieName &&
      !Cookie.get(xsrfCookieName)
    ) {
      message.warning('认证 token 已过期,请重新登录');
    }
    if (!headers.__tenant) {
      config.headers['Authorization'] = Cookie.get(xsrfHeaderName);
    } else {
      delete config.headers.Authorization;
    }
    return Promise.resolve(config);
  },
  (error: any) => {
    // 处理请求错误
    return Promise.reject(error);
  },
);

// 添加响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse<any>) => {
    // 对响应数据做点什么
    const res = response.data;
    console.log('response:', res);
    if (res.status == 401) {
      localStorage.removeItem("xsrfHeaderName");
      router.replace({ path: "/login" });
      nprogress.done();
      return Promise.reject(res);
    }else{
      nprogress.done();
      return Promise.resolve(res);
    }
  },
  (error: any) => {
    // 处理响应错误
    return Promise.reject(error);
  },
);
export default service;

其他页面的使用:

2.1引入request.ts文件

import request from './request'; 注意不要在文件尾部添加.ts,否则会报错。

2.2 以login登录接口为例,代码应当如下:

import qs from 'querystring';
export async function login(username, password, tenant) {
  const params = {
    username: username,
    password: password,
  };
  return request({
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    method: 'post',
    url: apiObj.LOGIN,//这个是登录的接口地址,改成自己项目的即可
    data: qs.stringify(params)
})
}

注意上面代码中,关于修改headers请求的content-type的方式,这个经常会出现在formData格式的数据中,默认的基本上都是application/json
由于我这边的登录接口是post的请求方式,而入参是formData的形式,因此我通过qs进行了对象参数的转化。也就是上面代码中的qs.stringify(params)

3.添加setAuthorization checkAuthorization removeAuthorization

function setAuthorization(auth){
  Cookie.set(xsrfHeaderName, 'Bearer ' + auth.token, {
    expires: auth.expireAt * 1000,
  });
}
function removeAuthorization(){
  Cookie.remove(xsrfHeaderName);
}
function checkAuthorization(){
  if (Cookie.get(xsrfHeaderName)) {
    const token = Cookie.get(xsrfHeaderName);
    if (token ) {
        return true
    }
 }
}
export default {
  setAuthorization,
  removeAuthorization,
  checkAuthorization,
};

页面上的使用:

import http from '@/store/http';
http.setAuthorization({
  token: xxxx,
  expireAt: new Date(new Date().getTime() + xxxx),
});
http.removeAuthorization();
http.checkAuthorization()

4.修改accout.tsvuex文件——用于存储用户信息和权限等

直接上代码:

import { defineStore } from 'pinia';
import http from './http';
import { Response } from '@/types';
import { useAuthStore } from '@/plugins';
import { applicationConfiguration } from '@/utils/user';
import { GetUserInfo } from '@/services/storehouse/common';
export interface Profile {
  account: Account;
  permissions: string[];
  role: string;
}
export interface Account {
  username: string;
  avatar: string;
  gender: number;
}
export function handlePermissions(obj) {
  let permissions = [];
  if (!obj) {
    return permissions;
  }
  permissions = Object.keys(obj).map((x) => {
    return {
      id: x,
      operation: [],
    };
  });
  return permissions;
}
export type TokenResult = {
  token: string;
  expires: number;
};
export const useAccountStore = defineStore('account', {
  state() {
    return {
      account: {} as Account,
      permissions: [] as string[],
      role: '',
      logged: true,
    };
  },
  actions: {
    async logout() {
      return new Promise<boolean>((resolve) => {
        localStorage.removeItem('stepin-menu');
        http.removeAuthorization();
        this.logged = false;
        resolve(true);
      });
    },
    async profile(callback) {
      applicationConfiguration()
        .then((res) => {
          const data = res.data;
          data.currentUser.tenantName = data.currentTenant.name;
          let permissions = handlePermissions(data.auth.grantedPolicies);
          const { setAuthorities } = useAuthStore();
          this.permissions = permissions;
          this.role = data.currentUser.roles;
          GetUserInfo().then((res) => {
            if (res.code == 1) {
              this.account = {
                ...data.currentUser,
                headPhoto: res.data?.extraProperties?.HeadPhoto,
              };
              setAuthorities(permissions);
              callback && callback('success');
            }
          });
        })
        .catch(() => {
          callback && callback('error');
        });
    },
    setLogged(logged: boolean) {
      this.logged = logged;
    },
  },
});

页面上的使用:

import { message } from 'ant-design-vue';
import { useRouter } from 'vue-router';
setup(){
	const { profile } = useAccountStore();
	const router = useRouter();
	profile((res) => {
	  if (res == 'success') {
	    message.success('登录成功', 3);
	    router.push('/test');//页面跳转到测试页面
	  }
	}).finally(() => {
	//
	});
}

5.编写登录页面

5.1 template代码如下:

<template>
  <div class="common-layout">
    <div class="login_box">
      <div class="login_left">
        <img src="../../assets/loginlogo.png" alt="" />
      </div>
      <div class="login_right">
        <div class="top">
          <div class="header">
            <span class="title">后台管理系统</span>
          </div>
        </div>
        <div class="login">
          <a-form @finish="onSubmit" :model="form">
            <a-alert
              type="error"
              :closable="true"
              v-show="error"
              :message="error"
              showIcon
              style="margin-bottom: 24px"
            />
            <a-form-item :rules="[{ required: true, message: '请输入账户名' }]" name="username">
              <a-input autocomplete="autocomplete" size="large" placeholder="账户名" v-model:value="form.username">
                <template #prefix><user-outlined /></template>
              </a-input>
            </a-form-item>
            <a-form-item :rules="[{ required: true, message: '请输入密码' }]" name="password">
              <a-input
                size="large"
                placeholder="密码"
                autocomplete="autocomplete"
                type="password"
                v-model:value="form.password"
              >
                <template #prefix><lock-outlined /></template>
              </a-input>
            </a-form-item>
            <a-form-item>
              <a-button
                :loading="loading"
                style="width: 100%; margin-top: 24px"
                size="large"
                htmlType="submit"
                type="primary"
                class="login_btn"
                >登录</a-button
              >
            </a-form-item>
          </a-form>
        </div>
      </div>
    </div>
  </div>
</template>

5.2 script代码如下:

<script lang="ts">
  import { defineComponent, getCurrentInstance, reactive, ref } from 'vue';
  import { useAccountStore } from '@/store';
  import { login } from '@/utils/user';
  import http from '@/store/http';
  import { message } from 'ant-design-vue';
  import { useRouter } from 'vue-router';
  export interface LoginFormProps {
    username: string;
    password: string;
  }
  export default defineComponent({
    setup() {
      const loading = ref(false);
      const error = ref('');
      const form = reactive({
        username: undefined,
        password: undefined,
        tenant: undefined,
      });
      const onSubmit = function () {
        http.removeAuthorization();
        loading.value = true;
        login(form.username, form.password, form.tenant)
          .then(afterLogin)
          .finally(() => {
            loading.value = false;
          });
      };
      const router = useRouter();
      const { profile } = useAccountStore();
      function afterLogin(res) {
        const loginRes = res;
        if (loginRes) {
          http.setAuthorization({
            token: loginRes.access_token,
            expireAt: new Date(new Date().getTime() + loginRes.expires_in),
          });

          if (http.checkAuthorization()) {
            profile((res) => {
              if (res == 'success') {
                message.success('登录成功', 3);
                router.push('/test');
              }
            }).finally(() => {
              loading.value = false;
            });
          }
        } else {
          error.value = loginRes.message;
        }
      }
      return {
        onSubmit,
        error,
        form,
        loading,
      };
    },
  });
</script>

完成!!!多多积累,多多收获!

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

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

相关文章

Unity VisualScripting(Bolt)自定义Node(带详细注释)

效果&#xff1a;获取一个物体的全部子物体和孙物体等从属物体 //引用一些东西&#xff0c;这样才能用某些API using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine;//类名和Node名一样 public class GetTreeChildr…

为什么要学框架?什么是Spring?

为什么要学框架&#xff1f;什么是Spring&#xff1f; 一、为什么要学框架&#xff1f; 学习框架相当于从 “小作坊” 到 “工厂” 的升级&#xff0c;小作坊什么都要自己做&#xff0c;工厂是组件式装配&#xff0c;特点就是高效。框架更加易用、简单且高效。 框架的优点展…

51单片机--DS18B20温度感应器

文章目录 DS18B20的介绍内部结构框图DS18B20存储器单总线的介绍硬件结构 单总线的时序结构温度存储格式DS18B20操作流程在LCD屏上显示温度实例 DS18B20的介绍 DS18B20是一种常用的数字温度传感器&#xff1b; 下面介绍它的特点和功能&#xff1a; 封装和引脚定义&#xff1a;…

win11我们无法创建新的分区也找不到现有的分区

U盘重装系统的时候 提示&#xff1a;win11我们无法创建新的分区也找不到现有的分区 ShiftF10 &#xff0c;调出 命令提示符&#xff1b; diskpart list disk select disk 盘编号 clean convert gpt 参考&#xff1a;怎么解决我们无法创建新的分区也找不到现有的分区问题&#x…

数据库索引解析(面试重点)

一.索引的基本含义 1.索引类似于一本书的目录&#xff0c;可以加快查询的进度 2.是以列为维度来设置的&#xff08;为一列来添加索引&#xff09; 二.索引的优劣 1.优势&#xff1a;提高查询的速度 2.劣势&#xff1a;占据额外的硬盘空间&#xff08;因为索引的相关数据要存储到…

vue3中将后端传来的json文件转成excel下载到本地

前言&#xff1a; 在浏览器中将JSON格式数据以excel文件的形式下载。该组件是基于[this thread] (https://stackoverflow.com/questions/17142427/javascript-to-export-html-table-to-excel)提出的解决方案。支持Vue3.2.25及以上版本使用 重要提示! Microsoft Excel中的额外提…

亚马逊云科技:云服务是支持数字创新的关键生产力

面对日益复杂的外部环境以及各种不期而至的“黑天鹅”事件&#xff0c;广大企业迫切地需要更加快速、更加高效、更加弹性、性价比更高的解决方案&#xff0c;通过持续不断的数字创新&#xff0c;来应对数字化转型中遭遇的各种挑战。 而在这个过程中&#xff0c;云服务正在发挥至…

opencv常用API记录(python版)

文章目录 1. cv2.minAreaRect2. cv2.boxPoints()3. cv2.drawContours()4. cv2.GaussianBlur5. cv2.Laplacian 1. cv2.minAreaRect 函数cv2.minAreaRect()是OpenCV中用于计算最小外接矩形的函数。它可以根据给定的轮廓点集&#xff0c;计算出一个包围该轮廓的最小外接矩形。 以…

kafka查看消息两种方式(命令行和软件)

1、命令行方式 ①找到kafka安装文件夹 ②执行命令 #指定offset为指定时间作为消息起始位置 kafka-consumer-groups.sh \ --bootstrap-server 20.2.246.116:9092 \ --group group_1 \ --topic lanxin_qiao \ --reset-offsets \ --to-datetime 2023-07-19T01:00:00.000 \ -exe…

【C++进阶之路】适配器、反向迭代器、仿函数

文章目录 前言一、适配器①模拟实现栈②模拟实现对列 二、反向迭代器三、仿函数总结 前言 我们先来笼统的介绍一下今天的三个内容。 适配器——简单的理解就是复用&#xff0c;用已经实现的轮子&#xff0c;来继续实现某种功能。 反向迭代器——原理很简单&#xff0c;就是对…

Openlayers实战:setCenter,setZoom设置跳转

Openlayers开发的项目中,经常会重新设定一个zoom,也会重新跳转到一个中心点。 所用的方法就是setZoom和setCenter。在Openlayers实战中,我们做一个简单的设置,来很好的认识一下这个常用的方法。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是大剑师兰特…

Vue3组合式API+TypeScript写法入门

文章目录 前言1.reactive2.ref3.props4.computed5.emit6.watch总结 前言 参考Vue3官网. 本篇以组合式API为例, 但不包含setup语法糖式写法. 原本打算结合class-component, Vue3不推荐就不用了: OverView|Vue Class Component. 而且是不再推荐基于类的组件写法, 推荐单文件组件…

电脑硬盘指的是什么?电脑硬盘长什么样子呢

在很早之前就听说过电脑里面有硬盘&#xff0c;但是不知道电脑硬盘是什么样子&#xff0c;本章文章结合硬盘的接口类型&#xff0c;以及应用技术&#xff0c;说说与硬盘样式有关的知识 一。机械硬盘 如果从硬盘的应用技术来区分硬盘&#xff0c;一般分为两种&#xff0c;早些年…

Leetcode周赛 | 2023-7-23

2023-7-23 题1体会我的代码 题2我的代码 题3体会我的代码 题1 体会 01背包啊。01背包啊&#xff01;怎么能一直往回溯上想&#xff01;还是对动态规划太不熟悉了&#xff01;这不就是01背包吗&#xff1f;还要别人提示才知道。 我的代码 class Solution:def numberOfWays(se…

设计模式再探——状态模式

目录 一、背景介绍二、思路&方案三、过程1.状态模式简介2.状态模式的类图3.状态模式代码4.状态模式还可以优化的地方5.状态模式的项目实战&#xff0c;优化后 四、总结五、升华 一、背景介绍 最近产品中有这样的业务需求&#xff0c;不同时间(这里不是活动的执行时间&…

Spring 统一登录验证、数据格式返回、异常处理的实现

文章目录 spring统一功能实现前言1. 统一用户登录权限验证1.1 传统实现方式1.2 Spring AOP用户统一登录验证1.2.1 Spring 拦截器 2. 统一数据格式返回3. 统一异常处理 spring统一功能实现 前言 在上一篇博客我们介绍了Spring AOP以及简单使用了Spring AOP&#xff0c;这篇博客…

力扣 -- 122. 买卖股票的最佳时机 II

一、题目&#xff1a; 题目链接&#xff1a;122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 二、解题步骤 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码&#xff1a; clas…

【数据可视化】基于Python和Echarts的中国经济发展与人口变化可视化大屏

1.题目要求 本次课程设计要求使用Python和ECharts实现数据可视化大屏。要求每个人的数据集不同&#xff0c;用ECharts制作Dashboard&#xff08;总共至少4图&#xff09;&#xff0c;要求输入查询项&#xff08;地点和时间&#xff09;可查询数据&#xff0c;查询的数据的地理…

工业的相机与镜头(简单选型)

面阵相机&#xff0c;需要多大的分辨率&#xff1f;多少帧数&#xff1f; 前提条件&#xff1a; 1.被检测的物体大小 2.要求检测的精度是多少 3.物体是否在运动过程中进行检测&#xff0c;速度是多少 线阵相机选择(分辨率、扫描行数) 行频&#xff1a;每秒扫描多少行&#xf…

【GitOps系列】使用Kustomize和Helm定义应用配置

文章目录 使用 Kustomize 定义应用改造示例应用1.创建基准和多环境目录2.环境差异分析3.为 Base 目录创建通用 Manifest4.为开发环境目录创建差异 Manifest5.为预发布环境创建差异 Manifest6.为生产环境创建差异 Manifest 部署 Kustomize 应用部署到开发环境部署到生产环境 使用…