admin管理员组

文章数量:1530884

1 模板的路由配置

搭建后台管理系统项目路由,本项目需要配置四个路由
1.建立views文件夹,放置路由界面

<template>
    <div>
      <h1>我是一级路由404</h1>
      
    </div>
  </template>
  
  <script setup lang="ts">
  
  </script>
  
  <style scoped>
  
  </style>

2.建立router文件夹,配置路由

// 通过vue-router插件实现模板路由的配置
import { createRouter, createWebHashHistory } from 'vue-router';
import { constantRoute } from './routes';

// 创建路由器
let router = createRouter({
    // 路由模式hash
    // 这种模式会在 URL 中使用 # 来管理路由
    history: createWebHashHistory(),
    routes: constantRoute,
    // 滚动行为
    scrollBehavior() {
        return {
            left: 0,
            top: 0
        }
    }

});

export default router;
// 对外暴露配置路由(常量路由)
export const constantRoute = [
    {
        // 登录路由
        path: '/login',
        component: () => import('@/views/login/index.vue'),
        name: 'login'//命名路由
    },
    {
        // 登录成功展示数据的路由
        path: '/',
        component: () => import('@/views/home/index.vue'),
        name: 'layout'

    },
    {
        // 404
        path: '/404',
        component: () => import('@/views/404/index.vue'),
        name: '404'

    },
    {
        path: '/:pathMatch(.*)*',
        redirect: '/404',
        name: 'Any'

    }
]

3.在入口文件引入注册,

4.测试一下:

<template>
  <div>
<router-view></router-view>
  </div>
</template>

<script setup lang="ts">

</script>

<style scoped>

</style>

2.登录路由静态搭建

1.根据element-plus官网进行搭建
2.login/index.vue
vh 是视口高度单位(Viewport Height),其中 1vh 等于视口高度的1%。
:xs屏幕宽度小于768时

<template>
  <div class="login_container">
    <el-row>
      <el-col :span="12" :xs="0"></el-col>
      <el-col :span="12" :xs="24">
        <el-form class="login_form">
          <h1>Hello</h1>
          <h2>欢迎来到硅谷甄选</h2>
          <el-form-item >
            <el-input :prefix-icon="User" v-model="loginForm.username"></el-input>
          </el-form-item>
          <el-form-item>
            <el-input  type="password" :prefix-icon="Lock" v-model="loginForm.password" show-password></el-input>
          </el-form-item>
          <el-form-item>
            <el-button class="login_btn">登录</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>


  </div>
</template>

<script setup lang="ts">
import{User,Lock} from '@element-plus/icons-vue';
import{reactive} from 'vue';
// 收集账号与密码的数据
let loginForm = reactive({username:'admin',password:'111111'})


</script>

<style scoped lang="scss">
.login_container {
  width: 100%;
  height: 100vh;
  background: url('@/assets/images/background.jpg') no-repeat;
  background-size: cover;

  .login_form {
    position: relative; //相对定位
    width: 80%;
    top: 30vh;
    background: url('@/assets/images/login_form.png')no-repeat;
    background-size: cover;
    padding: 40px;
    h1{
      color: white;
      font-size: 40px;
    }
    h2{
      color: white;
      font-size: 20px;
      margin: 20px 0px;//上下20,左右0
    }
    .login_btn{
      width:100%;
    }

  }
}
</style>

3.收集账号密码
在开发者模式中使用vuedivtools插件,好查看是是否能手机数据:
在插件应用商城搜索该插件,并且选中开发者模式即可

3.模板封装登录业务

登录流程:发请求–输入账号密码–登录成功进入首页,登录失败给提示
如果用户登录成功,会返回用户信息token,根据token向服务器发请求,要这个人相应的数组
把token存储到pinia仓库中

3.1 配置仓库pinia

pnpm i pinia

在src/store文件夹下存放仓库文件
在入口文件maints引入仓库
有root显示表明有大仓库了

3.2用户仓库

建立src/store/modules 存放用户小仓库

// 创建用户相关的小仓库
import { defineStore } from 'pinia';
// 引入接口
import { reqLogin } from '@/api/user';
// 引入数据类型
import type { loginForm } from '@/api/user/type';
// 创建用户小仓库
let useUserStore = defineStore('User', {
    // 小仓库存储数据的地方
    state: () => {
        return {
            token: localStorage.getItem("TOKEN"),
        }
    },
    // 异步|逻辑的地方
    actions: {
        // 用户登录的方法
        async userLogin(data: loginForm) {
            // 登录请求
            let result:any = await reqLogin(data);
            console.log(result);
            // 登录请求:成功200--token
            // 登录请求:失败201--登录失败的错误信息
            if (result.code == 200) {
                // pinia仓库存储一下token
                // 由于pinia|vuex存储数据其实利用js对象
                this.token = result.data.token;
                // 本地存储持久化存储一份
                localStorage.setItem("TOKEN", result.data.token);
                // 能保证async函数能返回一个成功的promise
                return 'ok';

            } else {
                return Promise.reject(new Error(result.data.message));

            }

        }

    },
    // 
    getters: {

    }
});
// 对外暴露获取小仓库的方法
export default useUserStore;

TIPS:1.异步编程,async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果,async返回值是promise对象
详细异步编程解说:https://juejin/post/6844903776780828685
2.let 关键字,用于声明变量,仅在{}内有效,且let 声明的变量必须在声明之后才能被访问。与var不同,var 声明的变量具有函数作用域或被提升至全局作用域。

3.3 持久化存储token

后面要根据token找服务器要信息,所以要保证token持久化
现在的token一刷新就没有了
采用本地存储存储token,当然也可以用cookie封装

组件路由要知道自己登录情况是成功还是失败,如果成功,会有消息提示登录成功
async会返回promise对象,pending(等待),fulfilled(成功),rejected(失败)
src/views/login/index.ts

<template>
  <div class="login_container">
    <el-row>
      <el-col :span="12" :xs="0"></el-col>
      <el-col :span="12" :xs="24">
        <el-form class="login_form">
          <h1>Hello</h1>
          <h2>欢迎来到硅谷甄选</h2>
          <el-form-item>
            <el-input :prefix-icon="User" v-model="loginForm.username"></el-input>
          </el-form-item>
          <el-form-item>
            <el-input type="password" :prefix-icon="Lock" v-model="loginForm.password" show-password></el-input>
          </el-form-item>
          <el-form-item>
            <el-button :loading="loading" type="primary" class="login_btn" @click="login">登录</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>


  </div>
</template>

<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue';
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElNotification } from 'element-plus';
// 引入用户相关的小仓库
import useUserStore from '@/store/modules/user';
let useStore = useUserStore();
// 获取路由器
let $router = useRouter();
// 定义变量控制按钮加载效果
let loading = ref(false);
// 收集账号与密码的数据
let loginForm = reactive({ username: 'admin', password: '111111' })
// 登录按钮的回调
// 加载效果:开始加载
const login = async () => {
  loading.value = true;
  // 点击登录按钮以后干什么??
  // 通知仓库发登录请求
  // 请求成功--首页展示数据的地方
  // 请求失败--弹出登录失败信息
  try {
    // 可以采用.then语法,我这里使用try catch语法
    // 保证登录成功
    // 请求失败,加载效果消失
    
    await useStore.userLogin(loginForm);
    // 编程式导航跳转到展示数据的首页
    $router.push('/');
    ElNotification({
      type: 'success',
      message: '登录成功'
    });
    loading.value = false;
  } catch (error) {
    loading.value = false;
    // 登录失败的提示信息
    ElNotification({
      type: 'error',
      // 断言
      message: (error as Error).message

    });


  }



}


</script>

<style scoped lang="scss">
.login_container {
  width: 100%;
  height: 100vh;
  background: url('@/assets/images/background.jpg') no-repeat;
  background-size: cover;

  .login_form {
    position: relative; //相对定位
    width: 80%;
    top: 30vh;
    background: url('@/assets/images/login_form.png')no-repeat;
    background-size: cover;
    padding: 40px;

    h1 {
      color: white;
      font-size: 40px;
    }

    h2 {
      color: white;
      font-size: 20px;
      margin: 20px 0px; //上下20,左右0
    }

    .login_btn {
      width: 100%;
    }

  }
}
</style>

TIPS:1.变量名前面的 符号通常是一个习惯用法,用来表示某些特别的、全局可用的或重要的对象, 符号通常是一个习惯用法,用来表示某些特别的、全局可用的或重要的对象, 符号通常是一个习惯用法,用来表示某些特别的、全局可用的或重要的对象,router 是一个变量名,这里使用 $ 是为了明确指出这个变量持有一个对Vue路由器实例的引用。
2.采用try(成功干嘛)catch(不成功干嘛)结构
3.类型断言:有两种断言方法1.as断言, message: (error as Error).message告诉ts编译器error是Error对象,目的是为了能够安全地访问 Error 对象的 message 属性。如果没有这个断言,如果 error 的类型不确定(undefined),编译器可能会警告或错误,表示不确定 error 是否有 message 属性。2.<> 语法let strLength: number = (<string>someValue).length;


el消息提示组件elnotification

elbutton按钮 loading属性(加载现象)
TIPS:
1.ref是一个函数,用来创建一个响应式的引用对象,这个对象包含一个可以被Vue跟踪其变化的值。先给loading为ref(false)关闭状态,点击按钮把他变为true状态
2.可以点击开发者模式 network调节网速查看效果

3.4 用户仓库数据ts类型的定义

1.state函数返回的数据类型
上次state()返回对象类型直接定义为any
在modules新建文件types/type.ts存放定义类型文件

// 定义小仓库数据state类型
export interface UserState{
    token:string|null;
}

将state()返回对象类型改为state:UserState()

2.定义登录接口返回的数据类型

 let result: loginResponseData = await reqLogin(data);
interface dataType {
    token?: string,
    message?:string
}
// 登录接口返回数据类型
export interface loginResponseData {
    code: number,
    data: dataType
}

3.封装本地存储存数据和读数据的方法
将上述方法封装在utils/token.ts文件里面

// 封装本地存储数据和读取数据的方法
export const SET_TOKEN = (token: string) => {
    localStorage.setItem("TOKEN", token);
}


export const GET_TOKEN = () => {
    return localStorage.getItem('TOKEN');
}

在user.ts引入调用即可

 state: (): UserState => {
        return {
            token: GET_TOKEN(),
        }
    },
    // 异步|逻辑的地方
    actions: {
        // 用户登录的方法
        async userLogin(data: loginForm) {
            // 登录请求
            let result: loginResponseData = await reqLogin(data);
            console.log(result);
            // 登录请求:成功200--token
            // 登录请求:失败201--登录失败的错误信息
            if (result.code == 200) {
                // pinia仓库存储一下token
                // 由于pinia|vuex存储数据其实利用js对象
                this.token = (result.data.token as string);
                // 本地存储持久化存储一份
                SET_TOKEN((result.data.token as string));
                // 能保证async函数能返回一个成功的promise
                return 'ok';

3.5登录时间的判断与封装

使用内置函数Date获取当前时间
新建utils/time.ts文件

// 封装一个函数,获取一个结果:当前是早上|上午|下午|晚上
export const getTime = () => {
    let message = '';
    // 通过内置构造函数Date
    let hours = new Date().getHours();
    if (hours <= 9) {
        message = '早上';
    } else if (hours <= 12) {
        message = '上午';
    } else if (hours <= 18) {
        message = '下午';
    } else {
        message = '晚上';
    }
    return message;
}

在login/index.vue改动

ElNotification({
      type: 'success',
      message: '欢迎回来',
      title: `HI,${getTime()}好`
    });

TIPS:
1.~ ~为模板字符串,使用反引号标记的字符串,模板字符串可以包含差值表达式${表达式},表达式的结果会被转换为字符串,并插入到模板字符串的相应位置。

4.登录模块表单校验

利用element-plus 里面form表单所提供的组件
1.给表单定义model属性,告诉表单我们数据收集到哪里
2.给表单添加规则rules

<el-form class="login_form" :model="loginForm" :rules="rules" ref="loginForms">
// 定义表单规则需要的配置对象

const rules = {
  username: [
    // trigger:触发校验时机,一般有两个,一个是blur(失去焦点)一个是change(当文本发生变化)
    // required: true代表这个字段必填
    // message:错误提示信息
    // { required: true, message: '用户名不能为空', trigger: "blur" },
    { required: true, min: 6, max: 10, message: '账号长度至少为6位', trigger: "change" }
  ],
  password: [
    { required: true, min: 6, max: 15, message: '密码长度至少为6位', trigger: "change" }
  ]


}

查看校验规则有没有生效


3.每个el-form-item对象添加prop属性

<el-form-item prop="username">

TIPS:vue3的ref是一个指令,接收的是一个变量而不是字符串
4.保证全部的表单项校验通过再发送请求
validate返回的是一个promise对象

 await loginForms.value.validate();

4.1自定义校验表单

上述规则只能约束长度大小,不能做复杂的约束
使用自定义校验规则
1.写规则

const rules = {
  username: [
    // trigger:触发校验时机,一般有两个,一个是blur(失去焦点)一个是change(当文本发生变化)
    // required: true代表这个字段必填
    // message:错误提示信息
    // { required: true, message: '用户名不能为空', trigger: "blur" },
    // { required: true, min: 6, max: 10, message: '账号长度至少为6位', trigger: "change" }
    { trigger: 'change', validator: validatorUserName }
  ],
  password: [
    // { required: true, min: 6, max: 15, message: '密码长度至少为6位', trigger: "change" }
    { trigger: 'change', validator: validatorPassword }
  ]

const validatorPassword = (rule: any, value: any, callback: any) => {
  // rule:即为校验规则对象
  // value:即为表单元素的文本内容
  // callback:函数,如果符合条件,callback放行即可
  // 如果不符合条件,callback方法注入错误提示信息
  // /^\d{5,10}$/.test(value)
  if (value.length >= 6) {
    callback();
  } else {
    callback(new Error('密码长度至少为6位'));
  }
  };
  // 自定义校验规则需要的函数
const validatorUserName = (rule: any, value: any, callback: any) => {
  // rule:即为校验规则对象
  // value:即为表单元素的文本内容
  // callback:函数,如果符合条件,callback放行即可
  // 如果不符合条件,callback方法注入错误提示信息
  // /^\d{5,10}$/.test(value)
  if (value.length >= 5) {
    callback();
  } else {
    callback(new Error('账号长度至少为5位'));
  }
  console.log(callback);


};

TIPS:关于正则表达式https://blog.csdn/saber04/article/details/100042797
边界字符 ^ $:
^ : 表示匹配行首的文本(从谁开始)
$ : 表示匹配行尾的文本(以谁结束)
test用于检测字符串是否符合规则,返回true/false
/^\d{5,10}$/这个是5到10位的数字

5 一级路由组件静态搭建

该界面分三部分:左边导航区(展示菜单)顶部导航区 内容展示区(二级路由组件)
新建scr/layout/index.vue文件

<template>
    <div class="layout_container">
        <!-- 左侧菜单 -->
        <div class="layout_slider">123</div>
        <!-- 顶部导航 -->
        <div class="layout_tabbar">456</div>
        <!-- 内容展示区 -->
        <div class="layout_main">
            <p style="height: 10000px;background-color: aqua;">我是一个段落</p>
        </div>

    </div>
</template>

<script setup lang="ts">

</script>

<style scoped lang="scss">


.layout_container {
    width: 100%;
    height: 100vh;
   

    .layout_slider {
        // 宽度写成全局变量可以随时改
        width: $base-menu-width;
        height: 100vh;
        background-color: $base-menu-background;

    }


    .layout_tabbar {
        position: fixed;
        width: calc(100% - $base-menu-width);
        height: $base-tabbar-height;
        background-color: blue;
        top: 0px;
        left: $base-menu-width;
    }

    .layout_main {
        position: absolute;
        width: calc(100% - $base-menu-width);
        height: calc(100vh - $base-tabbar-height);
        background-color: white;
        left:$base-menu-width;
        top:$base-tabbar-height;
        padding:20px;
        overflow: auto;
    }
}
</style>

将一些变量写成全局变量存放在styles/variable.scss文件夹中

// 左侧的菜单宽度
$base-menu-width:260px;
// 左侧菜单背景颜色
$base-menu-background:#001529;
// 顶部导航的高度
$base-tabbar-height:50px;

滚动条外观放置在styles/index.scss文件夹中

// 滚动条外观设置
::-webkit-scrollbar{
    width: 10px;
}
::-webkit-scrollbar-track{
    background-color: $base-menu-background;
}
::-webkit-scrollbar-thumb{
    width:10px;
    background-color: yellowgreen;
    border-radius: 10px;
}


TIPS:$符号用于引用变量,可以重复利用数据

本文标签: 硅谷项目vue