通用vue组件化首页

一、首先先建立文件main.vue,构建主体

1.选择合适的模板element-plus,直接复制

2.编写相应的样式

<template>
  <div class="main">
    <el-container class="main-content">
      <el-aside> aside </el-aside>
      <el-container class="page">
        <el-header class="page-header"> header </el-header>
        <el-main class="page-content"> main </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({})
</script>
<style scoped lang="less">
.main {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.main-content,
.page {
  height: 100%;
}
.page-content {
  height: calc(100%-48px);
}
.page-info {
  background-color: #fff;
  border-radius: 5px;
}
.el-header.el-footer {
  display: inline;
  color: #333;
  text-align: center;
  align-items: center;
}
.el-header {
  height: 48px !important;
}
.el-aside {
  overflow-x: hidden;
  overflow-y: auto;
  line-height: 200px;
  text-align: left;
  cursor: pointer;
  background-color: #001529;
  transition: width 0.3s linear;
  scrollbar-width: none; /* firefox */
  -ms-overflow-style: none; /* IE 10+ */

  &::-webkit-scrollbar {
    display: none;
  }
}
.el-main {
  color: #333;
  text-align: center;
  background-color: #f0f2f5;
}
</style>

二、利用组件化思想,将菜单栏,头部和中间内容分别写成三个文件

1.先建立nav-header和nav-menu文件夹,然后在里面编写主要代码,然后在index,ts里面导出组件

2.然后在main.vue里面对应的aside和header的位置引用上面创建的相应组件

 3.在nav-menu里面写菜单导航

(1)先去找合适的element-plus样式复制

(2)给每个菜单栏一个点击事件,跳转到对应的路由页面

nav-menu.vue

 4.在nav-header.vue设计一个折叠的按钮。可以实现点击折叠菜单栏

(1)先定义一个按钮,并且给按钮一个点击事件,在点击事件里面负责将foldChange事件传给父组件 

(2)然后将点击事件传给引用他的父组件main.vue

 (3)然后父组件里面将接受到的值赋给另一个新变量isCollapse,然后 将这个新的变量值传给子组件nav-menu,同时isCollapse也会响应式改变,从而改变el-aside的宽度

(4)子组件nav-menu在菜单栏el-menu(这个是el-menu的一个属性)接收新变量值,然后实现折叠效果

 

三、nav-header的面包屑和个人信息组件的设计

1.首先是先定义好面包屑和个人信息的位置和相应的样式

<template>
  <div class="nav-header">
    <button @click="handleFoldclick" class="fole-menu">折叠</button>
    <div class="content">
      <div>面包屑</div>
      <div>个人信息</div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue"

// import { Expand } from '@element-plus/icons-vue'
export default defineComponent({
  emits: ["foldChange"],

  setup(props, { emit }) {
    const isFold = ref(false)
    const handleFoldclick = () => {
      isFold.value = !isFold.value
      emit("foldChange", isFold.value)
    }
    return {
      isFold,
      handleFoldclick
    }
  }
})
</script>
<style scoped>
.nav-header {
  display: flex;
  width: 100%;
  text-align: center;
  margin-top: 10px;
}
.fold-menu {
  width: 30px;
  height: 20px;
  cursor: pointer;
  margin: 10px;
}
.content {
  /* 自己内部flex布局,两端 */
  display: flex;
  justify-content: space-between;
  /* 在大的nav-header里面flex */
  flex: 1;
  padding: 0 20px;
}
</style>

 2.可以将个人信息单独抽离出来当一个组件user-info.vue,然后再在nav-header对应的个人信息的位置引用user-info组件

user-info.vue

<template>
  <div class="user-info">
    <el-dropdown>
      <span class="el-dropdown-link">
        Dropdown List
        <el-icon class="el-icon--right">
          <arrow-down />
        </el-icon>
      </span>
      <template #dropdown>
        <el-dropdown-menu>
          <el-dropdown-item>Action 1</el-dropdown-item>
          <el-dropdown-item>Action 2</el-dropdown-item>
          <el-dropdown-item>Action 3</el-dropdown-item>
          <el-dropdown-item disabled>Action 4</el-dropdown-item>
          <el-dropdown-item divided>Action 5</el-dropdown-item>
        </el-dropdown-menu>
      </template>
    </el-dropdown>
  </div>
</template>
<script>
import { defineComponent, computed } from "vue"

export default defineComponent({})
</script>
<style scoped></style>

nav-header.vue

<template>
  <div class="nav-header">
    <button @click="handleFoldclick" class="fole-menu">折叠</button>
    <div class="content">
      <div>面包屑</div>
      <user-info></user-info>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue"
import UserInfo from "./user-info.vue"

export default defineComponent({
  components: {
    UserInfo
  },
  emits: ["foldChange"],

  setup(props, { emit }) {
    const isFold = ref(false)
    const handleFoldclick = () => {
      isFold.value = !isFold.value
      emit("foldChange", isFold.value)
    }
    return {
      isFold,
      handleFoldclick
    }
  }
})
</script>
<style scoped>
.nav-header {
  display: flex;
  width: 100%;
  text-align: center;
  margin-top: 10px;
}
.fold-menu {
  width: 30px;
  height: 20px;
  cursor: pointer;
  margin: 10px;
}
.content {
  /* 自己内部flex布局,两端 */
  display: flex;
  justify-content: space-between;
  /* 在大的nav-header里面flex */
  flex: 1;
  padding: 0 20px;
}
</style>

 3.将登录后的用户名显示在个人信息头像旁边

(1)如果直接把登录后存储在vuex里面的loginInfo直接拿出来赋值给当前user-info里面的变量,虽然可以实现显示用户名,但是会出现一个bug,刷新之后获取不到当前登录的用户信息

(2)如果发生这样的情况,需要将实时将存储在localCache里面的loginInfo拿出来赋值

四、面包屑的设计

1.由于这里的菜单导航不是从接口获取到的,是自定义的,所以这里需要拿到当前的路由的meta里面的title进行匹配显示

2.重新创建一个组件文件breadcrumb.vue,在这个文件里面去使用element-plus里面的面包屑组件

3.这里遍历自定义变量list,然后再 根据当前的路由路经去显示对应的页面

breadcrumb.vue

<template>
  <div class="nav-breadcrumb">
    <el-breadcrumb separator>
      <el-breadcrumb-item v-for="item in list" :key="item.path">
        <router-link :to="{ path: item.path }">
          {{ item.meta.title }}
        </router-link>
      </el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from "vue"
import { IBreadcrumb } from "../types"
import { useRouter } from "vue-router"
export default defineComponent({
  // props: {
  //   breadcrumbs: {
  //     type: Array as PropType<IBreadcrumb[]>,
  //     default: () => []
  //   }
  // }
  setup() {
    const router = useRouter()
    let list = computed(() => {
      return router.currentRoute.value.matched
    })

    return {
      list
    }
  }
})
</script>

nav-menu.vue

这里也解决了当刷新页面时,导航栏和右侧页面不匹配的问题,这里主要是修改index和default-active

<template>
  <div class="nav-menu">
    <div class="logo">
      <img class="img" src="~@/assets/img/logo.svg" alt="logo" />
      <span class="title" v-if="!collapse">Vue3+TS</span>
    </div>
    <div>
      <el-menu
        :default-active="activeIndex"
        class="el-menu-vertical"
        :collapse="collapse"
      >
        <el-menu-item index="/main/system" @click="handleChartClick">
          <el-icon>
            <House />
          </el-icon>
          <span>系统首页</span>
        </el-menu-item>
        <el-sub-menu index="/main/">
          <template #title>
            <el-icon>
              <User />
            </el-icon>
            <span>商品管理</span>
          </template>
          <el-menu-item index="/main/category" @click="handleUserClick">
            <el-icon>
              <Menu />
            </el-icon>
            商品中心
          </el-menu-item>
          <el-menu-item index="/main/goods" @click="handleAdminUserClick">
            <el-icon>
              <Menu />
            </el-icon>
            商品信息
          </el-menu-item>
        </el-sub-menu>
        <el-menu-item index="/main/order" @click="handleQuestionClick">
          <el-icon>
            <Document />
          </el-icon>
          <span>订单管理</span>
        </el-menu-item>
      </el-menu>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from "vue"
// import { useStore } from "@/store"
import { useRouter, useRoute } from "vue-router"
// 使用 document.documentElement.style 实现换肤功能
document.documentElement.style.setProperty(`--el-menu-bg-color`, "transparent")

export default defineComponent({
  props: {
    collapse: {
      type: Boolean,
      default: false
    }
  },
  setup() {
    const router = useRouter()
    const activeIndex = ref("")
    // 因为这次是匹配的子路由 所以不需要做处理
    activeIndex.value = router.currentRoute.value.path

    console.log(activeIndex.value)
    const handleUserClick = () => {
      router.push({ path: "/main/category" })
    }
    const handleAdminUserClick = () => {
      router.push({ path: "/main/goods" })
    }
    const handleQuestionClick = () => {
      router.push({ path: "/main/order" })
    }

    const handleChartClick = () => {
      router.push({ path: "/main/system" })
    }

    return {
      // handleHomeClick,
      handleUserClick,
      handleAdminUserClick,
      handleQuestionClick,
      handleChartClick,
      activeIndex
      // handleQuestionClick2
    }
  }
})
</script>
<style scoped lang="less">
.nav-menu {
  height: 100%;

  .logo {
    display: flex;
    height: 28px;
    padding: 12px 10px 8px 10px;
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
  }
  img {
    height: 100%;
    margin: 0 10px;
  }
  .title {
    font-size: 16px;
    font-weight: 700;
    color: white;
  }
}
// 目录
.el-sub-menu {
  // 二级菜单 ( 默认背景 )
  .el-menu-item {
    padding-left: 50px !important;
    background-color: transparent !important;
    color: #ccc;
  }
}
.el-menu {
  border-right: none;
}

.el-menu-item {
  color: #ccc;
}

.el-sub-menu {
  .el-menu-item {
    padding-left: 50px !important;
    /* background-color: transparent !important; */
    color: #ccc;
  }
}

::v-deep .el-sub-menu__title {
  background-color: transparent !important;
  color: #ccc;
}

// hover 高亮

.el-menu-item:hover {
  color: #20a0ff !important; // 菜单
  background-color: #2c394c !important;
}

.el-menu-item.is-active {
  color: #20a0ff !important;
  background-color: #2c394c !important;
}

.el-menu-vertical:not(.el-menu--collapse) {
  width: 100%;
  height: 100%;
  // height: calc(100% - 48px);
}

a {
  text-decoration: none;
}
</style>

nav-header.vue

<template>
  <div class="nav-header">
    <button @click="handleFoldclick" class="fole-menu">折叠</button>
    <div class="content">
      <hy-breadcrumb></hy-breadcrumb>
      <user-info></user-info>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue"
import UserInfo from "./user-info.vue"
import HyBreadcrumb, { IBreadcrumb } from "@/base-ui/breadcrumb"

export default defineComponent({
  components: {
    UserInfo,
    HyBreadcrumb
  },
  emits: ["foldChange"],

  setup(props, { emit }) {
    const isFold = ref(false)
    const handleFoldclick = () => {
      isFold.value = !isFold.value
      emit("foldChange", isFold.value)
    }

    return {
      isFold,
      handleFoldclick
    }
  }
})
</script>
<style scoped>
.nav-header {
  display: flex;
  width: 100%;
  text-align: center;
  margin-top: 10px;
}
.fold-menu {
  width: 30px;
  height: 20px;
  cursor: pointer;
  margin: 10px;
}
.content {
  /* 自己内部flex布局,两端 */
  display: flex;
  justify-content: space-between;
  /* 在大的nav-header里面flex */
  flex: 1;
  padding: 0 20px;
  align-items: center;
  text-align: center;
}
</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值