Vue3+Element Plus后台项目框架

 

Vue3+Element Plus后台项目框架

 

本篇文档在Vue3+Element Plus前端项目模板基础上,增加路由、菜单导航、登录、导航等基础功能

 

处理布局

整体布局

 

src/components/layout/AppLayout.vue

 

<template>
<div class="common-layout">
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-container>
<el-header>Header</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</div>
</template>

<style scoped></style>

 

 

src/views/HomeView.vue

 

<script setup lang="ts">
import AppLayout from '@/components/layout/AppLayout.vue'
</script>
<template>
<AppLayout />
</template>

 

 

效果

处理布局侧边栏

 

src/stores/layout/isCollapse.ts

 

export const isCollapse = ref(false)

 

 

src/components/layout/AppAside.vue

 

<script setup lang="ts">
import { isCollapse } from '@/stores/layout/isCollapse'
import { useRoute } from 'vue-router'
const route = useRoute()
const activeIndex = computed(() => {
  const index = route.matched?.[2]?.path
  console.log('matched index', index)
  return index
})
</script>

<template>
<el-aside>
<!-- 1. router: 开启菜单路由 点击后路由到el-menu-item index地址 unique-opened:保证最多只有一个菜单是打开状态  default-active: 选中的子菜单 不能是sub-menu -->
<el-menu router unique-opened :collapse="isCollapse" :default-active="activeIndex">
<a class="logo" href="/">
<img src="@/assets/favicon.ico" fit="cover" />
<h1 v-show="!isCollapse">管理后台</h1>
</a>
<el-sub-menu index="/1">
<template #title>
<el-icon><IEpLocation /></el-icon>
<span>菜单栏1</span>
</template>
<el-menu-item index="/1/table">子菜单1</el-menu-item>
<el-menu-item index="/1/tree">子菜单2</el-menu-item>
</el-sub-menu>

<el-sub-menu index="/2">
<template #title>
<el-icon><IEpLocation /></el-icon>
<span>菜单栏2</span>
</template>
<el-menu-item index="/2/1">子菜单1</el-menu-item>
<el-menu-item index="/2/2">子菜单2</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
</template>

<style scoped>
.el-aside {
  #dedfe0;
  height: 100vh;
  width: auto;
  /* flex布局对其中的menu生效,如果不是flex,menu撑不满 */
  display: flex;
}
.el-menu {
  #dedfe0;

  /* 3. 菜单栏右侧1px的边界去掉 */
  border-right: none;
  width: 200px;
  &.el-menu--collapse {
    width: 60px;
  }
}

.el-menu-item {
  #dedfe0;
}

.logo {
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 2. 去掉 a 链接的下划线 */
  text-decoration: none;
  color: black;
  & img {
    width: 20px;
    height: 20px;
  }
}
</style>

 

 

src/components/layout/AppLayout.vue

 

<script setup lang="ts">
import AppAside from '@/components/layout/AppAside.vue'
</script>
<template>
  <div class="common-layout">
    <el-container>
        <AppAside />
      <el-container>
        <el-header>Header</el-header>
        <el-main>Main</el-main>
      </el-container>
    </el-container>
  </div>
</template>

<style scoped></style>

 

 

处理布局Header

src/components/layout/AppAside.vue

 

<script setup lang="ts">
import { isCollapse } from '@/stores/layout/isCollapse'
</script>
<template>
<el-header>
<!-- 展开 折叠的icon -->
<el-icon class="collapse-icon">
<IEpFold v-show="!isCollapse" @click="isCollapse = !isCollapse" />
<IEpExpand v-show="isCollapse" @click="isCollapse = !isCollapse" />
</el-icon>

<!-- 面包屑导航 -->
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in $route.matched" :key="index">{{
        item.meta.title
      }}</el-breadcrumb-item>
</el-breadcrumb>

<!-- 下拉框 -->
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar :size="30" src="https://i.imgur.com/yXOvdOSs.jpg" />
<el-icon class="el-icon--right">
<IEpArrow-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>
</el-header>
</template>

<style scoped>
.el-header {
  display: flex;
  align-items: center;
  #e9e9eb;
}

.collapse-icon {
  /* icon 在header中 受整体高度的约束 */
  margin-right: 15px;
}

.el-dropdown {
  /* 该元素推到其容器的最右边的效果。这通常用于实现将元素右对齐的布局 */
  margin-left: auto;
}

.el-dropdown-link {
  display: flex;
  align-items: center;
}
</style>

 

处理布局Main区域

src/components/layout/AppLayout.vue

 

<script setup lang="ts">
import AppAside from '@/components/layout/AppAside.vue'
import AppHeader from '@/components/layout/AppHeader.vue'
</script>
<template>
  <div>
    <el-container>
      <AppAside />
      <el-container class="header-and-main">
        <AppHeader />
        <el-main>
          <!-- 打洞slot 避免页面太大撑爆了,加滚动条 -->
          <el-scrollbar>
            <RouterView />
          </el-scrollbar>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<style scoped>
.header-and-main {
  /* header和main在一个contain中 纵向排布 */
  flex-direction: column;
  height: 100vh;
}
.el-main {
  #f4f4f5;
}
</style>

 

 

基本功能性页面

表格

src/views/TableView.vue

 

<script setup lang="ts">
import { isDialogFormVisible } from '@/stores/table/isDialogFormVisible'
import CreaeteTableDialog from '@/components/table/CreateTableDialog.vue'

const tableData = [
{
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
},
{
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
},
{
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
},
{
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
}
]
</script>

<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>表格名称</span>
<el-button @click="isDialogFormVisible = true" class="button" type="primary" size="large"
>创建</el-button
>
</div>
</template>
<!-- 创建弹窗 -->
<CreaeteTableDialog />
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
<el-table-column>
<el-button type="primary" size="small">
<el-icon><IEpEdit /></el-icon>
</el-button>
<el-button type="danger" size="small">
<el-icon><IEpDelete /></el-icon>
</el-button>
</el-table-column>
</el-table>
</el-card>
</template>

<style scoped>
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.text {
  font-size: 14px;
}

.item {
  margin-bottom: 18px;
}

.box-card {
  width: auto;
/* height: 100vh; */
}
</style>

 

 

添加路由

 

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

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
{
      path: '/',
      name: 'index',
component: () => import('../views/IndexView.vue'),
      children: [
{
          path: '',
          name: 'home',
component: () => import('../views/HomeView.vue'),
          meta: { title: '首页' }
},
{
          path: '/1',
          name: 'table',
          meta: { title: '菜单栏1' },
          redirect: '/1/table',
          children: [
{
              path: '/1/table',
              name: 'table',
component: () => import('../views/TableView.vue'),
              meta: { title: '子菜单1' }
}
]
}
]
}
]
})

export default router

 

 

侧边栏路由调整

 

<template>
  <el-aside>
    <!-- 1. router: 开启菜单路由 点击后路由到el-menu-item index地址; unique-opened:保证最多只有一个菜单是打开状态; default-active: 选中的子菜单el-menu-item 不能是sub-menu -->
    <el-menu router unique-opened :collapse="isCollapse" :default-active="activeIndex">
      <a class="logo" href="/">
        <img src="@/assets/favicon.ico" fit="cover" />
        <h1 v-show="!isCollapse">管理后台</h1>
      </a>
      <el-sub-menu index="/1">
        <template #title>
          <el-icon><IEpLocation /></el-icon>
          <span>菜单栏1</span>
        </template>
        <el-menu-item index="/1/table">子菜单1</el-menu-item>
        <el-menu-item index="/1/tree">子菜单2</el-menu-item>
      </el-sub-menu>
    </el-menu>
  </el-aside>
</template>

 

 

弹窗

注意弹窗是作为组件嵌入到TableView中

src/components/table/CreateTableDialog.vue

 

<script setup lang="ts">
import { isDialogFormVisible } from '@/stores/table/isDialogFormVisible'
const form = reactive({
  name: '',
  region: '',
  date1: '',
  date2: '',
  delivery: false,
  type: [],
  resource: '',
  desc: ''
})
const formLabelWidth = '140px'
</script>

<template>
<el-dialog v-model="isDialogFormVisible" title="Shipping address">
<el-form :model="form">
<el-form-item label="Promotion name" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="Zones" :label-width="formLabelWidth">
<el-select v-model="form.region" placeholder="Please select a zone">
<el-option label="Zone No.1" value="shanghai" />
<el-option label="Zone No.2" value="beijing" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="isDialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="isDialogFormVisible = false"> Confirm </el-button>
</span>
</template>
</el-dialog>
</template>

<style scoped></style>

 

 

树状图

router/index.ts和src/components/AppAside.vue文件的路由改动点省略

src/views/TreeView.vue

 

<template>
<el-tree :data="data" :props="DefaultTreeProps" @node-click="handleNodeClick" />
</template>

<script lang="ts" setup>
import { type Tree, DefaultTreeProps } from '@/types/tree'

const handleNodeClick = (data: Tree) => {
  console.log(data)
}

const data: Tree[] = [
{
    label: 'Level one 1',
    children: [
{
        label: 'Level two 1-1',
        children: [
{
            label: 'Level three 1-1-1'
}
]
}
]
},
{
    label: 'Level one 2',
    children: [
{
        label: 'Level two 2-1',
        children: [
{
            label: 'Level three 2-1-1'
}
]
},
{
        label: 'Level two 2-2',
        children: [
{
            label: 'Level three 2-2-1'
}
]
}
]
},
{
    label: 'Level one 3',
    children: [
{
        label: 'Level two 3-1',
        children: [
{
            label: 'Level three 3-1-1'
}
]
},
{
        label: 'Level two 3-2',
        children: [
{
            label: 'Level three 3-2-1'
}
]
}
]
}
]
</script>

 

 

NotFound页面

src/views/NotFound.vue

 

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

<template>
<el-result title="404" sub-title="Sorry, request error">
<template #icon>
<el-image
        src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
/>
</template>
<template #extra>
<!-- route是当前的path,router是带历史的访问路径stack -->
<el-button @click="$router.push({ name: 'home' })" type="primary">Back</el-button>
</template>
</el-result>
</template>

<style scoped></style>

 

src/router/index.ts

 

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

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
{
      path: '/',
      name: 'index',
component: () => import('../views/IndexView.vue'),
      children: [
// 如果不在/的children中,布局就丢失了,只有notfound页面
{
          path: '/:pathMatch(.*)*',
          name: 'NotFound',
          meta: { title: 'NotFound' },
component: () => import('../views/NotFound.vue')
}
]
}
]
})

export default router

 

效果

posted @ 2024-01-23 15:21  烈酒清茶  阅读(43)  评论(0)    收藏  举报