硅谷甄选4(项目主体)

1.路由配置

1.1路由组件的雏形

src\views\home\index.vue(以home组件为例)

安装插件: 

1.2路由配置

1.2.1路由index文件

src\router\index.ts

 //通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router'
import { constantRoute } from './router'
//创建路由器
const router = createRouter({
  //路由模式hash
  history: createWebHashHistory(),
  routes: constantRoute,
  //滚动行为
  scrollBehavior() {
    return {
      left: 0,
      top: 0,
    }
  },
})
export default router

1.2.2路由配置

src\router\router.ts

//对外暴露配置路由(常量路由)
export const constantRoute = [
  {
    //登录路由
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    name: 'login', //命名路由
  },
  {
    //登录成功以后展示数据的路由
    path: '/',
    component: () => import('@/views/home/index.vue'),
    name: 'layout',
  },
  {
    path: '/404',
    component: () => import('@/views/404/index.vue'),
    name: '404',
  },
  {
    //重定向
    path: '/:pathMatch(.*)*',
    redirect: '/404',
    name: 'Any',
  },
]

 

1.3路由出口

src\App.vue

 

2.登录模块

2.1 登录路由静态组件(布局)

src\views\login\index.vue

<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" type="primary" size="default">
              登录
            </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 lang="scss" scoped>
  .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;
      }
      .login_btn {
        width: 100%;
      }
    }
  }
</style>

注意:

  • el-col是24份的,在此左右分为了12份。我们在右边放置我们的结构。
  • :xs="0"是为了响应式。
  • el-form下的element-plus元素都用el-form-item包裹起来。 
  • 通过 row (行)和 col (列)组件,并通过 col 组件的 span 属性我们就可以自由地组合布局。
  • row 行提供 gutter 属性来指定列之间的间距,其默认值为0。
  • 通过制定 col 组件的 offset 属性可以指定分栏偏移的栏数。
  • 参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs、sm、md、lg 和 xl。

2.2 登陆业务实现

2.2.1 登录按钮绑定回调

 

回调应该做的事情 

const login =  () => {
  //点击登录按钮以后干什么
  //通知仓库发起请求
  //请求成功->路由跳转
  //请求失败->弹出登陆失败信息

2.2.2 仓库store初始化
  1. 大仓库(笔记只写一次)

安装pinia:pnpm i pinia@2.0.34

src\store\index.ts

//创建用户相关的小仓库
import { defineStore } from 'pinia'
//创建用户小仓库
const useUserStore = defineStore('User', {
  //小仓库存储数据地方
  state: () => {},
  //处理异步|逻辑地方
  actions: {},
  getters: {},
})
//对外暴露小仓库
export default useUserStore

 

2.2.3 按钮回调

//登录按钮的回调
const login = async () => {
  //按钮加载效果
  loading.value = true
  //点击登录按钮以后干什么
  //通知仓库发起请求
  //请求成功->路由跳转
  //请求失败->弹出登陆失败信息
  try {
    //也可以书写.then语法
    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,
    })
  }
}

2.2.4 用户仓库 

//创建用户相关的小仓库
import {defineStore} from "pinia";
// 引入接口
import {reqLogin} from "../../api/user";
//引入数据类型
import type {loginForm} from '@/api/user/type'
//创建用户小仓库
const useUserStore = defineStore('User', {
小仓库存储数据地方
state: () => {
return {
token: localStorage.getItem('TOKEN')//用户的唯一标识
}
},
//处理异步|逻辑地方
actions: {
async userLogin(data: loginForm) {
//登陆的请求
async
userLogin(data
:
loginForm
)
{
//登陆的请求
const result: any = await reqLogin(data)
if (result.code == 200) {
this.token = result.data.token
localStorage.setItem('TOKEN', result.data.token)
return 'ok'
} else {
return Promise.reject(new Error(result.data.message))
}

}
}
},
getters: {},
})
export default useUserStore

 2.2.5 小结
  1. Element-plus中ElNotification用法(弹窗):

引入:import { ElNotification } from 'element-plus'

使用:

//登录失败的提示信息
    ElNotification({
      type: 'error',
      message: (error as Error).message,
    })

  1. Element-plus中el-buttonloading属性。
  2. pinia使用actions、state的方式和vuex不同:需要引入函数创建实例
  3. $router的使用:也需要引入函数创建实例
  4. 在actions中使用state的token数据:this.token
  5. 类型定义需要注意。
  6. promise的使用和vue2现在看来是一样的

 2.3模板封装登陆业务

2.3.1 result返回类型封装

 src\api\user\type.ts

interface dataType {
  token?: string
  message?: string
}

//登录接口返回的数据类型
export interface loginResponseData {
  code: number
  data: dataType
}

2.3.2 State仓库类型封装 

src\store\modules\types\type.ts

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

2.3.3 本地存储封装

将本地存储的方法封装到一起

 src\utils\token.ts

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

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

2.4 登录时间的判断

  1. 封装函数

 src\utils\time.ts

//封装函数:获取当前时间段
export const getTime = () => {
  let message = ''
  //通过内置构造函数Date
  const hour = new Date().getHours()
  if (hour <= 9) {
    message = '早上'
  } else if (hour <= 14) {
    message = '上午'
  } else if (hour <= 18) {
    message = '下午'
  } else {
    message = '晚上'
  }
  return message
}
 

2.5 表单校验规则

2.5.1 表单校验
  1. 表单绑定项

 

  1. 表单元素绑定项

Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可

 

  1. 使用规则rules

 
//定义表单校验需要的配置对象
const rules = {
  username: [
    //规则对象属性:
    {
      required: true, // required,代表这个字段务必要校验的
      min: 5, //min:文本长度至少多少位
      max: 10, // max:文本长度最多多少位
      message: '长度应为6-10位', // message:错误的提示信息
      trigger: 'change', //trigger:触发校验表单的时机 change->文本发生变化触发校验, blur:失去焦点的时候触发校验规则
    }, 
    
  ],
  password: [
   {
      required: true,
      min: 6,
      max: 10,
      message: '长度应为6-15位',
      trigger: 'change',
    }, 
  ],
}

  1. 校验规则通过后运行

 const login = async () => {
  //保证全部表单项校验通过
  await loginForms.value.validate()
    。。。。。。
}

2.5.2自定义表单校验
  1. 修改使用规则rules

使用自己编写的函数作为规则校验。

//定义表单校验需要的配置对象
const rules = {
  username: [
    //规则对象属性:
    /* {
      required: true, // required,代表这个字段务必要校验的
      min: 5, //min:文本长度至少多少位
      max: 10, // max:文本长度最多多少位
      message: '长度应为6-10位', // message:错误的提示信息
      trigger: 'change', //trigger:触发校验表单的时机 change->文本发生变化触发校验, blur:失去焦点的时候触发校验规则
    }, */
    { trigger: 'change', validator: validatorUserName },
  ],
  password: [
    { trigger: 'change', validator: validatorPassword },
  ],
}

  1. 自定义校验规则函数

//自定义校验规则函数
const validatorUserName = (rule: any, value: any, callback: any) => {
  //rule:校验规则对象
  //value:表单元素文本内容
  //callback:符合条件,callback放行通过,不符合:注入错误提示信息
  if (value.length >= 5) {
    callback()
  } else {
    callback(new Error('账号长度至少5位'))
  }
}

const validatorPassword = (rule: any, value: any, callback: any) => {
  if (value.length >= 6) {
    callback()
  } else {
    callback(new Error('密码长度至少6位'))
  }
}

3. Layout模块(主界面)

3.1 组件的静态页面

3.1.1 组件的静态页面

注意:我们将主界面单独放一个文件夹(顶替原来的home路由组件)。注意修改一下路由配置

 src\layout\index.vue

<template>
  <div class="layout_container">
    <!-- 左侧菜单 -->
    <div class="layout_slider"></div>
    <!-- 顶部导航 -->
    <div class="layout_tabbar"></div>
    <!-- 内容展示区域 -->
    <div class="layout_main">
      <p style="height: 1000000px"></p>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style lang="scss" scoped>
.layout_container {
  width: 100%;
  height: 100vh;
  .layout_slider {
    width: $base-menu-width;
    height: 100vh;
    background: $base-menu-background;
  }
  .layout_tabbar {
    position: fixed;
    width: calc(100% - $base-menu-width);
    height: $base-tabbar-height;
    background: cyan;
    top: 0;
    left: $base-menu-width;
  }
  .layout_main {
    position: absolute;
    width: calc(100% - $base-menu-width);
    height: calc(100vh - $base-tabbar-height);
    background-color: yellowgreen;
    left: $base-menu-width;
    top: $base-tabbar-height;
    padding: 20px;
    overflow: auto;
  }
}
</style>
 

3.1.2定义部分全局变量&滚动条

scss全局变量

src\styles\variable.scss 

//左侧菜单宽度
$base-menu-width :260px;
//左侧菜单背景颜色
$base-menu-background: #001529;

//顶部导航的高度
$base-tabbar-height:50px;

滚动条 

src\styles\index.scss

//滚动条外观设置

::-webkit-scrollbar{
  width: 10px;
}

::-webkit-scrollbar-track{
  background: $base-menu-background;
}

::-webkit-scrollbar-thumb{
  width: 10px;
  background-color: yellowgreen;
  border-radius: 10px;
}

 3.2 Logo子组件的搭建

页面左上角的这部分,我们将它做成子组件,并且封装方便维护以及修改。

3.2.1 Logo子组件

在这里我们引用了封装好的setting

src\layout\logo\index.vue

<template>
  <div class="logo" v-if="setting.logoHidden">
    <img :src="setting.logo" alt="" />
    <p>{{ setting.title }}</p>
  </div>
</template>

<script setup lang="ts">
  //引入设置标题与logo配置文件
  import setting from '@/setting'
</script>

<style lang="scss" scoped>
  .logo {
    width: 100%;
    height: $base-menu-logo-height;
    color: white;
    display: flex;
    align-items: center;
    padding: 20px;
    img {
      width: 40px;
      height: 40px;
    }
    p {
      font-size: $base-logo-title-fontSize;
      margin-left: 10px;
    }
  }
</style>
 

3.2.2 封装setting

为了方便我们以后对logo以及标题的修改。

 //用于项目logo|标题配置
export default {
  title: '硅谷甄选运营平台', //项目的标题
  logo: '/public/logo.png', //项目logo设置
  logoHidden: true, //logo组件是否隐藏
}

3.2.3 使用

在layout组件中引入并使用

3.3 左侧菜单组件

3.3.1静态页面(未封装)

主要使用到了element-plus的menu组件。附带使用了滚动组件

src\layout\index.vue

 <!-- 左侧菜单 -->
<div class="layout_slider">
  <Logo></Logo>
  <!-- 展示菜单 -->
  <!-- 滚动组件 -->
  <el-scrollbar class="scrollbar">
    <!-- 菜单组件 -->
    <el-menu background-color="#001529" text-color="white">
      <el-menu-item index="1">首页</el-menu-item>
      <el-menu-item index="2">数据大屏</el-menu-item>
      <!-- 折叠菜单 -->
      <el-sub-menu index="3">
        <template #title>
          <span>权限管理</span>
        </template>
        <el-menu-item index="3-1">用户管理</el-menu-item>
        <el-menu-item index="3-2">角色管理</el-menu-item>
        <el-menu-item index="3-3">菜单管理</el-menu-item>
      </el-sub-menu>
    </el-menu>
  </el-scrollbar>
</div>

3.3.2 递归组件生成动态菜单

在这一部分,我们要根据路由生成左侧的菜单栏   

  1. 动态菜单子组件:src\layout\menu\index.vue
  2. 根据路由生成左侧的菜单栏
  3. 处理路由--->因为我们要根据路由以及其子路由作为我们菜单的一级|二级标题。因此我们要获取路由信息。{
    {
      //登录路由
      path: '/login',
        component: () => import('@/views/login/index.vue'),
        name: 'login', //命名路由
        meta: {
        	title: '登录', //菜单标题
          hidden: true, //路由的标题在菜单中是否隐藏
          },
          },

           给路由中加入了路由元信息meta:它包含了2个属性:title以及hidden 

    4.仓库引入路由并对路由信息类型声明(vue-router有对应函数)

//引入路由(常量路由)
import { constantRoute } from '@/router/routes'
。。。。。
//小仓库存储数据地方
state: (): UserState => {
  return {
    token: GET_TOKEN(), //用户唯一标识token
    menuRoutes: constantRoute, //仓库存储生成菜单需要数组(路由)
}

   5. 父组件拿到仓库路由信息并传递给子组件

<script setup lang="ts">
。。。。。。
//引入菜单组件
import Menu from './menu/index.vue'
//获取用户相关的小仓库
import useUserStore from '@/store/modules/user'
let userStore = useUserStore()
</script>

   6.子组件prps接收并且处理结构

<template>
  <template v-for="(item, index) in menuList" :key="item.path">
    <!-- 没有子路由 -->
    <template v-if="!item.children">
      <el-menu-item v-if="!item.meta.hidden" :index="item.path">
        <template #title>
          <span>标</span>
          <span>{{ item.meta.title }}</span>
        </template>
      </el-menu-item>
    </template>
    <!-- 有且只有一个子路由 -->
    <template v-if="item.children && item.children.length == 1">
      <el-menu-item
        index="item.children[0].path"
        v-if="!item.children[0].meta.hidden"
      >
        <template #title>
          <span>标</span>
          <span>{{ item.children[0].meta.title }}</span>
        </template>
      </el-menu-item>
    </template>
    <!-- 有子路由且个数大于一个 -->
    <el-sub-menu
      :index="item.path"
      v-if="item.children && item.children.length >= 2"
    >
      <template #title>
        <span>{{ item.meta.title }}</span>
      </template>
      <Menu :menuList="item.children"></Menu>
    </el-sub-menu>
  </template>
</template>

<script setup lang="ts">
//获取父组件传递过来的全部路由数组
defineProps(['menuList'])
</script>
<script lang="ts">
export default {
  name: 'Menu',
}
</script>
<style lang="scss" scoped></style>

注意:

1:因为每一个项我们要判断俩次(是否要隐藏,以及子组件个数),所以在el-menu-item外面又套了一层模板

2:当子路由个数大于等于一个时,并且或许子路由还有后代路由时。这里我们使用了递归组件。递归组件需要命名(另外使用一个script标签,vue2格式)。

3.3.3 菜单图标

  1. 注册图标组件

因为我们要根据路由配置对应的图标,也要为了后续方便更改。因此我们将所有的图标注册为全局组件。(使用之前将分页器以及矢量图注册全局组件的自定义插件)(所有图标全局注册的方法element-plus文档中已给出)

。。。。。。
//引入element-plus提供全部图标组件
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
。。。。。。

//对外暴露插件对象
export default {
  //必须叫做install方法
  //会接收我们的app
 。。。。。。
  //将element-plus提供全部图标注册为全局组件 
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      app.component(key, component)
    }
  },
}

2.给路由元信息添加属性:icon

以laytou和其子组件为例首先在element-puls找到你要使用的图标的名字。将它添加到路由元信息的icon属性上

  {
    //登录成功以后展示数据的路由
    path: '/',
    component: () => import('@/layout/index.vue'),
    name: 'layout',
    meta: {
      title: 'layout',
      hidden: false,
      icon: 'Avatar',
    },
    children: [
      {
        path: '/home',
        component: () => import('@/views/home/index.vue'),
        meta: {
          title: '首页',
          hidden: false,
          icon: 'HomeFilled',
        },
      },
    ],
  },

 3.菜单组件使用

以只有一个子路由的组件为例:

<!-- 有且只有一个子路由 -->
<template v-if="item.children && item.children.length == 1">
  <el-menu-item
    index="item.children[0].path"
    v-if="!item.children[0].meta.hidden"
    >
    <template #title>
      <el-icon>
        <component :is="item.children[0].meta.icon"></component>
      </el-icon>
      <span>{{ item.children[0].meta.title }}</span>
    </template>
  </el-menu-item>
</template>

 

3.3.4 项目全部路由配置
  1. 全部路由配置(以权限管理为例)

 src\router\routes.ts

{
  path: '/acl',
    component: () => import('@/layout/index.vue'),
    name: 'Acl',
    meta: {
    hidden: false,
      title: '权限管理',
      icon: 'Lock',
      },
  children: [
    {
      path: '/acl/user',
      component: () => import('@/views/acl/user/index.vue'),
      name: 'User',
      meta: {
        hidden: false,
        title: '用户管理',
        icon: 'User',
      },
    },
    {
      path: '/acl/role',
      component: () => import('@/views/acl/role/index.vue'),
      name: 'Role',
      meta: {
        hidden: false,
        title: '角色管理',
        icon: 'UserFilled',
      },
    },
    {
      path: '/acl/permission',
      component: () => import('@/views/acl/permission/index.vue'),
      name: 'Permission',
      meta: {
        hidden: false,
        title: '菜单管理',
        icon: 'Monitor',
      },
    },
  ],
    },

 2.添加路由跳转函数

第三种情况我们使用组件递归,所以只需要给前面的2个添加函数

<script setup lang="ts">
。。。。。。
//获取路由器对象
let $router = useRouter()
const goRoute = (vc: any) => {
  //路由跳转
  $router.push(vc.index)
}
</script>

2.layout组件

 

3.3.5 Bug&&总结

在这部分对router-link遇到一些bug,理解也更深了,特意写一个小结总结一下

bug:router-link不生效。

描述:当我点击跳转函数的时候,直接跳转到一个新页面,而不是layout组件展示的部分更新。

思路:首先输出了一下路径,发现路径没有错。其次,因为跳转到新页面,代表layout组件中的router-link不生效,删除router-link,发现没有影响。所以确定了是router-link没有生效。

解决:仔细检查了src\router\routes.ts文件,最后发现一级路由的component关键字写错。导致下面的二级路由没有和以及路由构成父子关系。所以会跳转到APP组件下的router-link

总结:router-link会根据下面的子路由来进行展示。如果发生了路由跳转不对的情况,去仔细检查一下路由关系有没有写对。APP是所有一级路由组件的父组件

3.3.6 动画 && 自动展示
  1. 将router-link封装成单独的文件并且添加一些动画
    <template>
      <!-- 路由组件出口的位置 -->
      <router-view v-slot="{ Component }">
        <transition name="fade">
          <!-- 渲染layout一级路由的子路由 -->
          <component :is="Component" />
        </transition>
      </router-view>
    </template>
    
    <script setup lang="ts"></script>
    
    <style lang="scss" scoped>
      .fade-enter-from {
        opacity: 0;
      }
      .fade-enter-active {
        transition: all 0.3s;
      }
      .fade-enter-to {
        opacity: 1;
      }
    </style>
    
  2. 自动展示

当页面刷新时,菜单会自动收起。我们使用element-plus的default-active 处理。$router.path为当前路由。

src\layout\index.vue

3.4 顶部tabbar组件

3.4.1静态页面

element-plus:breadcrumb el-button el-dropdown

<template>
  <div class="tabbar">
    <div class="tabbar_left">
      <!-- 顶部左侧的图标 -->
      <el-icon style="margin-right: 10px">
        <Expand></Expand>
      </el-icon>
      <!-- 左侧的面包屑 -->
      <el-breadcrumb separator-icon="ArrowRight">
        <el-breadcrumb-item>权限挂历</el-breadcrumb-item>
        <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <div class="tabbar_right">
      <el-button size="small" icon="Refresh" circle></el-button>
      <el-button size="small" icon="FullScreen" circle></el-button>
      <el-button size="small" icon="Setting" circle></el-button>
      <img
        src="../../../public/logo.png"
        style="width: 24px; height: 24px; margin: 0px 10px"
      />
      <!-- 下拉菜单 -->
      <el-dropdown>
        <span class="el-dropdown-link">
          admin
          <el-icon class="el-icon--right">
            <arrow-down />
          </el-icon>
        </span>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item>退出登陆</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style lang="scss" scoped>
.tabbar {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  background-image: linear-gradient(
    to right,
    rgb(236, 229, 229),
    rgb(151, 136, 136),
    rgb(240, 234, 234)
  );
  .tabbar_left {
    display: flex;
    align-items: center;
    margin-left: 20px;
  }
  .tabbar_right {
    display: flex;
    align-items: center;
  }
}
</style>

组件拆分:

<template>
  <!-- 顶部左侧的图标 -->
  <el-icon style="margin-right: 10px">
    <Expand></Expand>
  </el-icon>
  <!-- 左侧的面包屑 -->
  <el-breadcrumb separator-icon="ArrowRight">
    <el-breadcrumb-item>权限挂历</el-breadcrumb-item>
    <el-breadcrumb-item>用户管理</el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup lang="ts"></script>

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

<template>
  <el-button size="small" icon="Refresh" circle></el-button>
  <el-button size="small" icon="FullScreen" circle></el-button>
  <el-button size="small" icon="Setting" circle></el-button>
  <img
    src="../../../../public/logo.png"
    style="width: 24px; height: 24px; margin: 0px 10px"
  />
  <!-- 下拉菜单 -->
  <el-dropdown>
    <span class="el-dropdown-link">
      admin
      <el-icon class="el-icon--right">
        <arrow-down />
      </el-icon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item>退出登陆</el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

<script setup lang="ts"></script>

<style lang="scss" scoped></style>
3.4.2 菜单折叠
  1. 折叠变量

定义一个折叠变量来判断现在的状态是否折叠。因为这个变量同时给breadcrumb组件以及父组件layout使用,因此将这个变量定义在pinia中

//小仓库:layout组件相关配置仓库
import { defineStore } from 'pinia'

let useLayOutSettingStore = defineStore('SettingStore', {
  state: () => {
    return {
      fold: false, //用户控制菜单折叠还是收起的控制
    }
  },
})

export default useLayOutSettingStore

 2.面包屑组件点击图标切换状态

<template>
  <!-- 顶部左侧的图标 -->
  <el-icon style="margin-right: 10px" @click="changeIcon">
    <component :is="LayOutSettingStore.fold ? 'Fold' : 'Expand'"></component>
  </el-icon>
  。。。。。。。
</template>

<script setup lang="ts">
import useLayOutSettingStore from '@/store/modules/setting'
//获取layout配置相关的仓库
let LayOutSettingStore = useLayOutSettingStore()

//点击图标的切换
const changeIcon = () => {
  //图标进行切换
  LayOutSettingStore.fold = !LayOutSettingStore.fold
}
</script>
。。。。。。

 3.layout组件根据fold状态来修改个子组件的样式(以左侧菜单为例)

绑定动态样式修改scss 

 4.左侧菜单使用element-plus折叠collapse属性效果图:

 注意:折叠文字的时候会把图标也折叠起来。在menu组件中吧图标放到template外面就可以。

3.4.3 顶部面包屑动态展示
  1. 引入$route

注意$router和$route是不一样的

<script setup lang="ts">
import { useRoute } from 'vue-router'
//获取路由对象
let $route = useRoute()
//点击图标的切换

</script>

 2.结构展示

注意:使用了$route.matched函数,此函数能得到当前路由的信息

 3.首页修改

访问首页时,因为它是二级路由,会遍历出layout面包屑,处理:删除layout路由的title。再加上一个判断

 4.面包屑点击跳转

注意:将路由中的一级路由权限管理以及商品管理重定向到第一个孩子,这样点击跳转的时候会定向到第一个孩子。

3.4.4 刷新业务的实现
  1. 使用pinia定义一个变量作为标记

 2.点击刷新按钮,修改标记

<script setup lang="ts">
//使用layout的小仓库
import useLayOutSettingStore from '@/store/modules/setting'
let layoutSettingStore = useLayOutSettingStore()
//刷新按钮点击的回调
const updateRefresh = () => {
  layoutSettingStore.refresh = !layoutSettingStore.refresh
}
</script>

 3.main组件检测标记销毁&重加载组件(nextTick

<script setup lang="ts">
import { watch, ref, nextTick } from 'vue'
//使用layout的小仓库
import useLayOutSettingStore from '@/store/modules/setting'
let layOutSettingStore = useLayOutSettingStore()
//控制当前组件是否销毁重建
let flag = ref(true)
//监听仓库内部的数据是否发生变化,如果发生变化,说明用户点击过刷新按钮
watch(
  () => layOutSettingStore.refresh,
  () => {
    //点击刷新按钮:路由组件销毁
    flag.value = false
    nextTick(() => {
      flag.value = true
    })
  },
)
</script>
3.4.5 全屏模式的实现
  1. 给全屏按钮绑定函数

 2.实现全屏效果(利用docment根节点的方法)

//全屏按钮点击的回调
const fullScreen = () => {
  //DOM对象的一个属性:可以用来判断当前是不是全屏的模式【全屏:true,不是全屏:false】
  let full = document.fullscreenElement
  //切换成全屏
  if (!full) {
    //文档根节点的方法requestFullscreen实现全屏
    document.documentElement.requestFullscreen()
  } else {
    //退出全屏
    document.exitFullscreen()
  }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Qt 常用控件】

文章目录 1. Push Button 1. Push Button &#x1f427;给按钮设置图标

链接追踪系列-09.spring cloud项目整合elk显示业务日志

准备工作&#xff1a; 参看本系列之前篇&#xff1a;服务器安装elastic search 本机docker启动的kibana-tencent 使用本机安装的logstash。。。 本微服务实现的logstash配置如下&#xff1a; 使用腾讯云redis 启动本机mysql 启动本机docker 启动nacos,微服务依赖它作为…

防火墙的双机热备实验和通道策略

需求&#xff1a; 12&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 13&#xff0c;办公区上网用户限制流量不超过100M&#xff0c;…

象过河云进销存管理系统,简单、方便、高效!

在当今这个快节奏的商业时代&#xff0c;企业的日常运营管理愈发注重效率和便捷性。基于这样的需求&#xff0c;象过河云进销存管理系统应运而生&#xff0c;它以“简单、方便、高效”为核心价值&#xff0c;为众多企业量身打造了一站式的解决方案。 象过河云进销存管理系统打破…

MDK KEIL程序代码编译成静态库文件及库引用笔记教程

1、为什么要编译成库文件 在商业性的程序代码或软件中&#xff0c;各种静态库、动态库是非常常见的。甚至有许多的开源程序&#xff0c;其开放的源码工程中&#xff0c;也有一些程序代码是并不对外开放的&#xff0c;以一个静态库或动态库和一个头文件及部分说明文件的方式提供…

【Linux系列】TEE 命令:同时输出到终端和文件

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

AmazonS3部署以及nacos配置参数

AmazonS3部署 因为涉及到做的需求的头像的处理&#xff0c;所以需要去找头像的来源&#xff0c;没想到又是我们的老熟人&#xff0c;AmazonS3&#xff0c;巧了已经是第二次用了&#xff0c;上次我是用的别人的工具类去干的&#xff0c;这一次我这边自己编辑具体工具类型。 对应…

谷歌DeepMind被曝抄袭开源成果,论文还中了顶流会议

卡奥斯智能交互引擎是卡奥斯基于海尔近40年工业生产经验积累和卡奥斯7年工业互联网平台建设的最佳实践&#xff0c;基于大语言模型和RAG技术&#xff0c;集合海量工业领域生态资源方优质产品和知识服务&#xff0c;旨在通过智能搜索、连续交互&#xff0c;实时生成个性化的内容…

vue3+ECharts实现可视化中国地图

目录 版本问题解决 中国地图实现 版本问题解决 目前echarts的最新版本为5.5.1 echarts在4.9.0版本以后移除了中国地图&#xff0c;所以如果的你的版本高于4.9.0就需要手动导入中国地图。版本低于或者等于4.9.0则不需要导入。 这里我分享一种导入方法&#xff1a; 1.将项目的…

SQL中的谓词与谓词下推

在 SQL 查询中&#xff0c;谓词&#xff08;Predicate&#xff09;是用来对数据进行过滤的条件。它们决定了数据从数据库表中被选择的条件。理解和正确使用 SQL 谓词对于编写高效查询至关重要。 目录 什么是谓词&#xff1f;一个真实的故事SQL 谓词的代码示例比较谓词逻辑谓词…

Gitee简易使用流程(后期优化)

目录 1.修改用户名 2.文件管理 新建文件/文件夹流程如下&#xff1a; 上传文件流程如下&#xff1a; 以主页界面为起点 1.修改用户名 点解右上角的头像--> 点击“账号设置” 点击左边栏里的“个人资料“ 直接修改用户名即可 2.文件管理 选择一个有修改权限仓库&#…

【RAGFlow】Ubuntu系统下实现源码启动RAGFlow

一、RAGFlow 是什么&#xff1f; RAGFlow 是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程&#xff0c;结合大语言模型&#xff08;LLM&#xff09;针对用…

隧道调频广播信号覆盖系统改造-泄漏电缆隧道全线无盲区调频覆盖解决方法探究

隧道调频广播信号覆盖系统改造-泄漏电缆隧道全线无盲区调频覆盖解决方法探究 由北京海特伟业科技有限公司任洪卓发布于2024年7月15日 随着城市交通的不断发展&#xff0c;隧道作为城市交通的重要组成部分&#xff0c;承担着日益增长的交通压力。为了确保行驶在隧道中的车辆能够…

AV1 编码标准中帧内预测技术概述

AV1 编码标准帧内预测 AV1&#xff08;AOMedia Video 1&#xff09;是一种开源的视频编码格式&#xff0c;旨在提供比现有标准更高的压缩效率和更好的视频质量。在帧内预测方面&#xff0c;AV1相较于其前身VP9和其他编解码标准&#xff0c;如H.264/AVC和H.265/HEVC&#xff0c;…

【分布式系统】CephFS文件系统之MDS接口详解

目录 一.服务端操作 1.在管理节点创建 mds 服务 2.查看各个节点的 mds 服务&#xff08;可选&#xff09; 3.创建存储池&#xff0c;启用 ceph 文件系统 4.查看mds状态&#xff0c;一个up&#xff0c;其余两个待命&#xff0c;目前的工作的是node01上的mds服务 5.创建用户…

做印尼TikTok直播会遇到什么困难?

TikTok直播已成为当下社交娱乐的重要组成部分&#xff0c;越来越多的直播达人在这个平台上崭露头角。特别是海外直播&#xff0c;受到了广大网友的热烈追捧。那么&#xff0c;在进行印尼TikTok直播会遇到哪些困难&#xff1f;这些困难是否可以通过TikTok直播专线来解决呢&#…

DBA 数据库管理 表管理 数据批量处理。表头约束

表管理 建库 库名命名规则&#xff1a;仅可以使用数字、字母、下划线、不能纯数字 不可使用MySQL命令或特殊字符 库名区分字母大小写 加if not exists 命令避免重名报错 create database if not exists gamedb; 建表 drop database if exists gamedb ; 删表…

高频面试题基本总结回顾4(含笔试高频算法整理)

目录 一、基本面试流程回顾 二、基本高频算法题展示 三、基本面试题总结回顾 &#xff08;一&#xff09;Java高频面试题整理 &#xff08;二&#xff09;JVM相关面试问题整理 &#xff08;三&#xff09;MySQL相关面试问题整理 &#xff08;四&#xff09;Redis相关面试…

【通信协议-RTCM】MSM语句(1) - 多信号GNSS观测数据消息格式

注释&#xff1a; RTCM响应消息1020为GLONASS星历信息&#xff0c;暂不介绍&#xff0c;前公司暂未研发RTCM消息类型版本的DR/RTK模块&#xff0c;DR/RTK模块仅NMEA消息类型使用 注释&#xff1a; 公司使用的多信号语句类型为MSM4&MSM7&#xff0c;也应该是运用最广泛的语句…