三、发起登录请求

1.在view/login.vue中导入login

import { login } from '@/api/login'

出现报错1:Vue3 webpack 报错:options has an unknown property ‘hotOnly’. These
properties are valid
则直接将vue.config.js中的hotOnly: false,改为hot: “only”,

出现报错2: - options has an unknown property ‘prependData’. These properties are valid:
object { implementation?, api?, sassOptions?, additionalData?, sourceMap?, webpackImporter?, warnRuleAsWarning? }
将vue.config.js的css下的prependData改为additionalData: `

将点击函数部分改为

const formRef = ref(null)
const handleLogin = () => {
  formRef.value.validate(async (valid) => {
    if (valid) {
      // store.dispatch('app/login', form.value)
      await login(form.value)
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

在这里插入图片描述
2.切换密码的隐藏

<el-input v-model="form.password" type="password" />
<svg-icon icon="eye"></svg-icon>

将输入框改为password类型,并加入图标
写切换输入框类型的函数changeType

const passwordTYpe=ref('password')
const changeType=()=>{
  if(passwordTYpe.value==='password'){
    passwordTYpe.value='text'
  }
  else{
    passwordTYpe.value='password'
  }
}

再将type改为双向绑定 变为:type 并为图标进行修改,点击睁开,再点闭上 且控制type类型

<el-input v-model="form.password" :type="passwordType" />
<svg-icon :icon="passwordType === 'password' ? 'eye' : 'eye-open'" @click="changeType"></svg-icon>

关于ref的说明:JavaScript的DOM操作可以改变网页内容、结构和样式innerText 、innerHTML;操作常见元素属性:src、href、title、alt等;操作表单元素属性:type、value、disabled等;操作元素样式属性:element.style、className
1.原生js获取DOM元素:document.getElementById等等
2.jquery:KaTeX parse error: Expected 'EOF', got '#' at position 3: ("#̲id")jQuery仅仅是库(…refs.ref名.进行使用
注意事项:
1. ref用于原生标签就是获取dom;ref用于组件标签就是获取组件实例对象
2.在setup函数中,可以使用ref函数,用于创建一个响应式数据,当数据发生改变时,Vue会自动更新UI 例如:使用ref函数定义一个变量count
3.在setup中定义的变量或方法,都必须通过return {xxx,xxx}暴露出去,外界才能使用
4.在 js 中使用 ref 的值必须使用 .value 获取;在 Vue 的模板中使用 ref 的值不需要通过 value 获取 (Vue 会通过自动给 ref 的值加上 .value)

3.使用响应式拦截器
首先先将用户名与密码写死

const form = ref({
  username: 'admin',
  password: 'admin'
})

然后添加const res = await login(form.value) console.log(res)
整体为:

const handleLogin = () => {
  formRef.value.validate(async (valid) => {
    if (valid) {
      
      const res = await login(form.value)
      console.log(res)
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

此时再进行登录,即使用你上面所定义的用户名与密码
在这里插入图片描述

为了进一步简化数据操作(因为我们想要的数据在data中的data)
在request.js中添加
到element-plus官网查找消息提示官网

service.interceptors.response.use(
  (response) => {//如果有回应值
	// 从返回的数据中取数据
    const { data, meta } = response.data
    if (meta.status === 200 || meta.status === 201) {
      return data
    } else {
      ElMessage.error(meta.msg)
      return Promise.reject(new Error(meta.msg))
    }
  },
  (error) => {//如果有报错
    error.response && ElMessage.error(error.response.data)
    return Promise.reject(new Error(error.response.data))
  }
)

注释:response的拦截器service.interceptors.response.use()中2个参数,分别是成功的回调函数、失败的回调函数

四、将token存在vuex中

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

1.找到store 删掉其他仅留下modules

import { createStore } from 'vuex'

export default createStore({
  
  modules: {
  }
})

在store下创建文件夹modules(store是Vuex的一个功能),再在其下创建app.js
可以从这了解vuex基本使用
2.为了方便进行登录操作,可以将登录操作放在app.js中进行
因此先在login/index.js中将登录操作注释
在这里插入图片描述
将import { login } from '@/api/login’注释并复制到app.js中

import { login as loginApi} from '@/api/login'
import router from '@/router'
import { reject, resolve } from 'core-js/fn/promise'
export default{
    namespaced:true,
    state:()=>({
        token:localStorage.getItem('token') || ''
    }),
    mutations:{
        setToken(state,token){
            state.token=token
            localStorage.setItem('token',token)
        }
    },
    actions:{
        login({commit},userInfo){
            return new Promise((resolve,reject)=>{
                // 登录成功
                loginApi(userInfo).then(res=>{
                    commit('setToken',res.token)
                    router.replace('/')
                    resolve()

                }).catch(err=>{
                    reject(err)
                })
            })
        }
    }
}

并在store/index.js中注册

import { createStore } from 'vuex'
import app from '@/store/modules/app'
export default createStore({
  
  modules: {
    app
  }
})

在login/index.vue中:

import { useStore } from 'vuex'
const store = useStore()
const formRef = ref(null)
const handleLogin = () => {
  formRef.value.validate(async (valid) => {
    if (valid) {
      // store.dispatch('app/login', form.value)
      // const res = await login(form.value)
      // console.log(res)
      // 添加下面这句
      store.dispatch('app/login', form.value)
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

报错: Can’t resolve ‘core-js/fn/promise’ in
‘D:\vue3项目实战\vue_3\src\store\modules’ 则删除app.js中自动生成的 import { reject,
resolve } from ‘core-js/fn/promise’

五、配置请求拦截器:

在request.js中:

service.interceptors.request.use(config=>{
  config.headers.Authorization=localStorage.getItem('token')
  return config
},err=>{
  return Promise.reject(new Error(err))
})

六、配置路由守卫

1.首先在router下新建文件permission.js

import router from './index'
import store from '@/store'
router.beforeEach((to,from,next)=>{
    
})

2.再去store下创建getters.js。从state中取token值

export default{
    token:state=>state.app.token
}

3.在store/index.js中进行配置 添加getters

import { createStore } from 'vuex'
import app from '@/store/modules/app'
import getters from '@/store/getters'
export default createStore({
  
  modules: {
    app
  },
  getters
})

4.再回到permission.js中

import router from './index'
import store from '@/store'

//定义白名单,即不需要登录也能去的
const whiteList=['/login']
router.beforeEach((to,from,next)=>{
    // 判断是否取到token
    if(store.getters.token){
        //如果要去页面是login
        if(to.path==='/login'){
            //让他去首页,因为当前在登录页
            next('/')
        }else{//如果不是首页,则让他去
            next()
        }
    }else{//判断是否要去的路径在白名单中,如果在则不管他,如果不在则去登录页
        if(whiteList.includes(to.path)){
            next()
        }else{
            next('/login')
        }
    }
})

5.在main.js中

import '@/router/permission'

6.去页面将token删除掉,则自动跳转至登录页面

七、实现layout布局

1.在src下新建目录layout,再创建index.vue(输入vue自动化格式)
2.去element官网
3.在index.vue中

<template>
  <el-container class="app-wrapper">
    <el-aside width="200px" class="sidebar-container">Aside</el-aside>
    <el-container class="container">
      <el-header>Header</el-header>
      <el-main>Main</el-main>
    </el-container>
  </el-container>
</template>

<script setup></script>

<style lang="scss" scoped>
.app-container {
  position: relative;
  width: 100%;
  height: 100%;
}
.container {
  width: calc(100% - $sideBarWidth);
  height: 100%;

  position: fixed;
  top: 0;
  right: 0;
  z-index: 9;
  transition: all 0.28s;
  &.hidderContainer {
    width: calc(100% - $hideSideBarWidth);
  }
}
::v-deep .el-header {
  padding: 0;
}
</style>

在这里插入图片描述
4.实现menus菜单

在layout下创建Menu文件夹,再创建index.vue
到element-plus找到menu菜单

<template>
  <el-menu
    active-text-color="#ffd04b"
    background-color="#545c64"
    class="el-menu-vertical-demo"
    default-active="2"
    text-color="#fff"
    @open="handleOpen"
    @close="handleClose"
  >
    <el-sub-menu index="1">
      <template #title>
        <el-icon><location /></el-icon>
        <span>Navigator One</span>
      </template>
      <el-menu-item-group title="Group One">
        <el-menu-item index="1-1">item one</el-menu-item>
        <el-menu-item index="1-2">item two</el-menu-item>
      </el-menu-item-group>
      <el-menu-item-group title="Group Two">
        <el-menu-item index="1-3">item three</el-menu-item>
      </el-menu-item-group>
      <el-sub-menu index="1-4">
        <template #title>item four</template>
        <el-menu-item index="1-4-1">item one</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="2">
      <el-icon><icon-menu /></el-icon>
      <span>Navigator Two</span>
    </el-menu-item>
    <el-menu-item index="3" disabled>
      <el-icon><document /></el-icon>
      <span>Navigator Three</span>
    </el-menu-item>
    <el-menu-item index="4">
      <el-icon><setting /></el-icon>
      <span>Navigator Four</span>
    </el-menu-item>
  </el-menu>
</template>

<script setup></script>

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

在这里插入图片描述
再到外层的index.vue:// 1.添加menu 2.

<template>
  <el-container class="app-wrapper">
    <el-aside width="200px" class="sidebar-container">
      <Menu />
    </el-aside>
    <el-container class="container">
      <el-header>Header</el-header>
      <el-main>Main</el-main>
    </el-container>
  </el-container>
</template>

<script setup>
// 1.添加menu 2.<Menu />
import Menu from './Menu'
</script>

5.实现菜单的发起请求
在api下创建menu.js:

import request from './request'
export const menuList=()=>{
    return request({
        url:'./menus'
    })
}

6.在menu下的index.vue:

<template>
  <el-menu
    class="el-menu-vertical-demo"
    default-active="2"
    text-color="#fff"
    router
  >
    <el-sub-menu :index="item.id" v-for="item in menusList" :key="item.id">
      <template #title>
        <el-icon><location /></el-icon>
        <span>{{ item.authName }}</span>
      </template>
      <el-menu-item
        :index="'/' + it.path"
        v-for="it in item.children"
        :key="it.id"
        >{{ it.authName }}</el-menu-item
      >
    </el-sub-menu>
  </el-menu>
</template>

<script setup>
import { menuList } from '@/api/menu'
import { ref } from 'vue'
//import variables from '@/styles/variables.scss'

const menusList = ref([])
const initMenusList = async () => {
  menusList.value = await menuList()
}
initMenusList()
</script>

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

7在views下创建多个页面
在这里插入图片描述
8.配置路由

 import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [
  {
    path: '/login',
    name: 'Login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/login')
  },
  {
    path: '/',
    name: '/',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../layout'),
    redirect: '/users',
    children: [
      {
        path: 'users',
        name: 'users',
        component: () => import('@/views/users/index.vue')
      },
      {
        path: 'categories',
        name: 'categories',
        component: () => import('@/views/categories/index.vue')
      },
      {
        path: 'goods',
        name: 'goods',
        component: () => import('@/views/goods/index.vue')
      },
      {
        path: 'orders',
        name: 'orders',
        component: () => import('@/views/orders/index.vue')
      },
      {
        path: 'params',
        name: 'params',
        component: () => import('@/views/params/index.vue')
      },
      {
        path: 'reports',
        name: 'reports',
        component: () => import('@/views/reports/index.vue')
      },
      {
        path: 'rights',
        name: 'rights',
        component: () => import('@/views/rights/index.vue')
      },
      {
        path: 'roles',
        name: 'roles',
        component: () => import('@/views/roles/index.vue')
      }
    ]
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

9.配置跳转页面嵌套路由






Header






10.在menu/index中添加unique-opened

<template>
  <el-menu
    active-text-color="#ffd04b"
    background-color="#545c64"
    class="el-menu-vertical-demo"
    default-active="2"
    text-color="#fff"
    @open="handleOpen"
    @close="handleClose"
    router
    unique-opened

可参考menu属性方法根据你想要的属性方法自己添加
11.可配置default-active 定义当前页面(刷新也不会跳到其他页面)

default-active="defaultActive"

再定义一个click事件(传当前的path值)

<el-menu-item
        :index="'/' + it.path"
        v-for="it in item.children"
        :key="it.id"
        @click="savePath(it.path)"
        >{{ it.authName }}</el-menu-item
      >

接下来对页面有两种做法:
第一种:写死一个页面的做法

const defaultActive = ref('/users')

第二种:动态页面

const savePath = (path) => {
  sessionStorage.setItem('path', '/${path}')
}
const defaultActive = ref(sessionStorage.getItem('path') || '/users')

over
12.导入图标
1)首先在main.js中以iconName作为组件名

import * as ELIcons from '@element-plus/icons-vue'
const app = createApp(App)
for(const iconName in ELIcons){
    app.component(iconName,ELIcons[iconName])
}

2)在menu下的index.vue中:

//一级图标
const iconlist = ref(['user', 'settings', 'shop', 'tickets', 'pie-chart'])
//二级图标
const icon = ref('menu')
<el-sub-menu
      :index="item.id"
      v-for="(item, index) in menusList"
      :key="item.id"
    >
      <template #title>
        <el-icon><component :is="iconlist[index]"></component></el-icon>
        <span>{{ item.authName }}</span>
      </template>
      <el-menu-item
        :index="'/' + it.path"
        v-for="it in item.children"
        :key="it.id"
        @click="savePath(it.path)"
      >
        <template #title>
          <el-icon><component :is="icon"></component></el-icon>
          <span>{{ it.authName }}</span>
        </template>
      </el-menu-item>

具体的修改是: 用is绑定下面的iconlist
绑定icon图标

Logo

前往低代码交流专区

更多推荐