Vue series (3) - teach you how to build a basic template for vue3 management background

Table of contents

I. Introduction:

2. Website page analysis

3. Development steps

(1), install element

(2), install and use the svg plug-in

(3) Write the framework code of the main interface

 (4) Writing the menu bar

 (5), new page and routing

(6), custom page label bar

first step:

Step two:

(7) Modify the package menu bar

(8) Add bread crumbs

4. End


I. Introduction:

First, the screenshot of the finished project template:

There are many open source vue management background templates. If you need to quickly develop a background management, you can search for "vue management background template". Students who only want the code can directly click on the source code . Through this chapter blog you can learn:

(1), the use of element UI component library.

(2) How to customize components.

(3) Skilled use of routing.

(4) Improvement of Vue project development ideas.

(5)、……

This project is developed on the basis of the integration project in the previous chapter, please click the link below to read first. (If you read this chapter without barriers, please ignore it)

Vue series (2) - vue3 basic project (integration framework) construction 5. At the end, open the editor to create a new project, fill in the project name, click Create, and then wait for the project to load. My Hbuilder X version is 3.4.14. The newly created project directory is in the form of the vue project loading page, single-page rendering, all content display is rendered on the index.html page, and App.vue is inside the index.html The outermost component container contains global js code css styles. The rendering of all pages is carried out in the App.vue container. File main.js: entry js file, reference of all global files https://blog.csdn.net/xxfen_/article/details/125327388?spm=1001.2014.3001.5501

2. Website page analysis

The website consists of a login page, a main interface, and a content page.

The overall module of the main interface is composed of:

(1) Navigation bar;

(2), the navigation menu bar on the left side;

(3), page tab bar;

(4), content column (display page).

composition.

The click switch of the menu bar only changes the content of the content bar, which leads to:

The login page and the main interface are first-level routes, and the content page is a nested route under the main interface.

3. Development steps

(1), install element

Official website:  A Vue 3 UI framework | Element Plus .

First install the domestic npm mirror, so that the download speed of the resource package will be faster

npm install cnpm -g --registry=https://registry.npmmirror.com

Then, install element

npm install element-plus --save

 Import the project and add the following code to the main.js file

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)

Test whether the introduction is successful, and add the button component to home.vue

<el-button type="primary">Primary</el-button>

 

 Run the project: npm run dev

The running effect is as follows, indicating that the import is successful:

(2), install and use the svg plug-in

  •  Install:

npm i vite-plugin-svg-icons -D

  •  Create a new storage svg directory under src:

  • Create a new component in the components directory: SvgIcon.vue
<template>
  <svg aria-hidden="true">
    <use :xlink:href="symbolId" />
  </svg>
</template>

<script>
  import { defineComponent, computed } from 'vue';

  export default defineComponent({
    name: 'SvgIcon',
    props: {
      prefix: {
        type: String,
        default: 'icon',
      },
      name: {
        type: String,
        required: true,
      },
    },
    setup(props) {
      const symbolId = computed(() => `#${props.prefix}-${props.name}`);
      return { symbolId };
    },
  });
</script>
  •  Configure in vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
  	vue(),
	createSvgIconsPlugin({
	        // 指定需要缓存的图标文件夹
	        iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
	        // 指定symbolId格式
	        symbolId: '[name]',
	      })
  ]
})
  • Introduced in main.js, add code:

//Import Svg image plug-in, you can display Svg images on the page
import 'virtual:svg-icons-register'
import SvgIcon from "./components/SvgIcon.vue";
app.component('SvgIcon',SvgIcon)

  •  At this point, an error occurs when running the project: no fast-glob found. You need to install fast-glob.

npm i fast-glob

  •  test use

Open Icon Icon | Element Plus

 Click the icon to copy the svg content

Create a new svg file in the newly created svg directory, name format: icon-"icon name", paste the content and save

 In the page use:

<SvgIcon name="aim" class="icon-svg" />

(3) Write the framework code of the main interface

  • First build an overall framework, home.vue code
<template v-slot:default>
	<div :class="['content',isCollapse?'menu--fold':'menu--unfold']">
		<!-- 侧边菜单栏 -->
		<div class="menuLeft">
			<div class="menu-nav-header">
				<span>{
   
   {isCollapse?'控制台':'管理控制台'}}</span>
			</div>
             <!--todo 菜单栏组件 -->
		</div>
		<!-- 右边内容 -->
		<div class="content-main">
			<div class="navTop horizontalView">
				<div class="nav_tools horizontalView">
					<SvgIcon :name="isCollapse?'expand':'fold'" class="icon-svg" @click="isCollapse=!isCollapse" />
				</div>
			</div>
			<!-- todo 内容组件 -->
		</div>
	</div>
</template>

<script>
export default {
		components: {

		},
		data: function() {
			return {
				isCollapse: false
				
			}
		}
}
</script>

<style>
	@import url('../assets/css/home.css');
</style>

  •  write css style

 

 Common ones are placed in base.css, and page-specific ones are placed in home.css

  • base.css code:
.content {
	width: 100%;
	height: 100%;
	font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}

/* 水平布局 居中*/
.horizontalView {
	position: relative;
	flex-direction: row;
	display: flex;
	align-items: center;
}

/* 垂直布局居中 */
.verticalView {
	position: relative;
	flex-direction: column;
	display: flex;
	align-items: center;
}

/* 居中 */
.center {
	position: absolute;
	top: 50%;
	left: 50%;
	font-size: 28px;
	transform: translate(-50%, -50%);
}

.w100 {
	width: 100%;
}

.h100 {
	height: 100%;
}
.icon-svg {
	width: 1.4rem;
	height: 1.4rem;
	fill: currentColor;
	overflow: hidden;
}
  • home.css code:
/* -------侧边栏 折叠 */
.menu--fold .menuLeft {
	width: 64px;
}

.menu--fold .content-main {
	margin-left: 64px;
}

/* --------------------- */

/* ---------侧边栏 展开 */
.menu--unfold .menuLeft {
	width: 230px;
}

.menu--unfold .content-main {
	margin-left: 230px;
}

/* ------------- */

.navTop {
	position: relative;
	width: 100%;
	height: 50px;
	z-index: 100;
	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
	box-sizing: border-box;
	background: white;
}

.nav_tools {
	height: 100%;
}

.nav_tools .icon-svg {
	margin-left: 10px;
	color: #5b5b5b;
}

.menuLeft {
	position: fixed;
	top: 0;
	left: 0;
	bottom: 0;
	z-index: 1020;
	overflow: hidden;
	background-color: #263238;
}

.content-main {
	position: relative;
	background: #f1f4f5;
	height: 100%;

}
.menu-nav-header {
	color: white;
	height: 50px;
	line-height: 50px;
	text-align: center;
	font-size: 20px;
	font-weight: bold;
	/* background-color: #9fbea7; */
	background-color: #566f7e;
}

/* 动画 */
.nav_tools,
.menuLeft,
.content-main {
	transition: inline-block 0.3s, left 0.3s, width 0.3s, margin-left 0.3s, font-size 0.3s;
}





  •  base.css is placed in app.vue

@import url("./assets/css/base.css");

  • Take a look at the page effect:

  • Click the Collapse button above

 (4) Writing the menu bar

Please go to understand the component usage documentation first: Menu menu | Element Plus

Copy the example code to customize the content properties and styles, close the folding animation provided by the component, and customize the animation style

  • Add and modify the menu component code in home.vue
<!--todo 菜单栏组件 -->
			<el-menu active-text-color="#fff" background-color="#263238" class="el-menu-vertical-demo"
				:collapse-transition="false" default-active="2" text-color="#96a4ab " @open="handleOpen"
				@close="handleClose" :collapse="isCollapse">
				<el-menu-item index="1">
					<SvgIcon name="home" class="icon-svg" />
					<span slot="">&nbsp;&nbsp;首页</span>
				</el-menu-item>
				<el-sub-menu index="2">
					<template #title>
						<SvgIcon name="img" class="icon-svg" />
						<span>&nbsp;&nbsp;图片管理</span>
					</template>
					<el-menu-item index="1-1">
						<SvgIcon name="img" class="icon-svg" />
						<span>&nbsp;&nbsp;图片1</span>
					</el-menu-item>
					<el-menu-item index="1-2">
						<SvgIcon name="img" class="icon-svg" />
						<span>&nbsp;&nbsp;图片2</span>
					</el-menu-item>
					<el-sub-menu index="1-4">
						<template #title>
							<SvgIcon name="img" class="icon-svg" />
							<span>&nbsp;&nbsp;图片3</span>
						</template>
						<el-menu-item index="1-4-1">
							<SvgIcon name="img" class="icon-svg" />
							<span>&nbsp;&nbsp;图片三级子菜单</span>
						</el-menu-item>
					</el-sub-menu>
				</el-sub-menu>
				<el-sub-menu index="3">
					<template #title>
						<SvgIcon name="collection" class="icon-svg" />
						<span>&nbsp;&nbsp;收藏管理</span>
					</template>
					<el-menu-item index="3">
						<SvgIcon name="collection" class="icon-svg" />
						<span class="icon-text">&nbsp;&nbsp;收藏</span>
					</el-menu-item>
				</el-sub-menu>

				<el-menu-item index="4">
					<SvgIcon name="about" class="icon-svg" />
					<span>&nbsp;&nbsp;设置</span>
				</el-menu-item>

			</el-menu>
  • Add modified style code to home.css
/* 修改菜单栏样式样式 */

.menuLeft .el-menu {
	border-right: none;
}

.el-menu-vertical-demo:not(.el-menu--collapse) {
	border-right: none;
	width: 230px;
}

.el-menu .icon-text {
	margin-left: 10px;
}

  • Page effect:

 The writing of the menu bar is not finished yet. The above method of writing is that every time a menu is added, modified or deleted, it is a bit cumbersome to find the location on the page and then modify it. It is even more troublesome to edit and modify when there are too many page codes or menu items. Therefore, we will optimize the code later, encapsulate the menu into a menu data set, and then display it in a for loop on the page.

 (5), new page and routing

  •  New page:

index.vue, img1.vue, collect.vue, set.vue. And add the page identification text in the page.

  • Configure routing:

The index.js code in the router directory:

// import Vue from 'vue'   //引入Vue
import {
	createRouter,
	createWebHashHistory
} from 'vue-router' //引入vue-router
// Vue.use(Router)  //Vue全局使用Router

import home from '../views/home.vue'
import login from '../views/login.vue'
import index from '../views/index.vue'
import collect from '../views/collect.vue'
import set from '../views/set.vue'
import img1 from '../views/img1.vue'

const routes = [{
		path: '',
		redirect: "home"
	}, {
		path: '/',
		redirect: "home"
	},
	{
		path: '/login',
		name: 'login',
		component: login,
		meta: {
			title: '登录'
		}
	},
	{
		path: '/home',
		name: 'home',
		component: home,
        /* 子路由 */
		children: [{
				path: '/',
				redirect: "index"
			},{
				path: '',
				redirect: "index"
			}, {
				path: '/index',
				name: 'index',
				component: index,
				meta: {
					title: '首页',
				}
			},
			{
				path: '/collect',
				name: 'collect',
				component: collect,
				meta: {
					title: '收藏',
					isTab: true
				}
			},
			{
				path: '/img1',
				name: 'img1',
				component: img1,
				meta: {
					title: '图片1',
					isTab: true
				}
			},
			{
				path: '/set',
				name: 'set',
				component: set,
				meta: {
					title: '设置',
					isTab: true
				}
			}
		]
	}
];

// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
/* router.beforeEach((to, from, next) => {
	if (to.path === '/login') {
		next();
	} else {
		let token = localStorage.getItem('Authorization');

		if (token === null || token === '') {
			next('/login');
		} else {
			next();
		}
	}
}); */
const router = createRouter({
	history: createWebHashHistory(),
	routes
})
export default router;
  •  Add a routing component to home.vue to test the routing jump:

<router-view />

  •  Add the jump routing code to the menu item

Add the click redirect routing code to the "Home" menu item: @click="$router.push({ name: 'index' })"

Similarly, add corresponding codes to other menu items.

  •  Test effect

Click "Settings"

 Click "Favorites"

 ok, routing configuration is successful!

(6), custom page label bar

Two steps:

(1) Monitor route switching, store the name (or path) collection of the redirected route, and store the current route name.

(2) Use the el-tabs tab page component Tabs tab | Element Plus to customize the style and write content components.

first step:

  • Monitor route changes, watch and data are at the same layer:
watch: {
			$route: {
				handler(to, from) {
					if (to.path != from.path) {
						// 处理路由
						this.routeHandle(to);
					}
				}
			}
		},
  • Forget it, just put all the js code directly:
<script>

	export default {
		components: {

		},
		data: function() {
			return {
				isCollapse: false,
				mainTabs: [],
				mainTabsActiveName: '',
				menuActiveName: '',
				menus: []
			}
		},
		created() {
			let that = this;
			that.routeHandle(that.$route);
		},
		// 监听路由变化
		watch: {
			$route: {
				handler(to, from) {
					if (to.path != from.path) {
						// 处理路由
						this.routeHandle(to);
					}
				}
			}
		},
		methods: {
			resetDocumentClientHeight: function() {
				this.documentClientHeight = document.documentElement['clientHeight'];
				window.onresize = () => {
					this.documentClientHeight = document.documentElement['clientHeight'];
					this.loadSiteContentViewHeight();
				};
			},
			loadSiteContentViewHeight: function() {
				let height = this.documentClientHeight - 52; //减去导航栏高度50 
				console.log(this.$route.meta.isTab)
				if (this.$route.meta.isTab) {
					height -= 70; //减去tab栏高度40,减去上下边距30
					/* this.siteContentViewHeight = {
						'min-height': height + 'px'
					}; */
					this.siteContentViewHeight = height;
				} else {
					height -= 30;
					//给内容区设置高度
					this.siteContentViewHeight = height;
				}

			},
			routeHandle: function(route) {
				//每次切换页面,重新计算页面高度和内容区高度
				this.resetDocumentClientHeight();
				this.loadSiteContentViewHeight();
				if (route.meta.isTab) {
					// tab选中, 不存在先添加
					var tab = this.mainTabs.filter(item => item.name === route.name)[0];
					if (!tab) {
						if (route.meta.isDynamic) {
							route = this.dynamicMenuRoutes.filter(item => item.name === route.name)[0];
							if (!route) {
								return console.error('未能找到可用标签页!');
							}
						}
						tab = {
							menuId: route.meta.menuId || route.name,
							name: route.name,
							title: route.meta.title,
							iframeUrl: route.meta.iframeUrl || '',
							params: route.params,
							query: route.query
						};
						this.mainTabs = this.mainTabs.concat(tab);
					}
					this.menuActiveName = tab.menuId + '';
					this.mainTabsActiveName = tab.name;
				}
			},
			mounted: function() {
				let that = this;
				that.resetDocumentClientHeight();
				that.loadSiteContentViewHeight();
			}
		}
	}
</script>

Step two:

  • Write components
<!-- todo 内容组件 -->
			<el-tabs v-if="$route.meta.isTab" v-model="mainTabsActiveName" :closable="true"
				@tab-click="selectedTabHandle" @tab-remove="removeTabHandle">
				
				<el-scrollbar ref="scroll" :height="siteContentViewHeight+32+'px'" @scroll="scroll">
					<el-tab-pane v-for="item in mainTabs" :label="item.title" :name="item.name">
						<el-card :style="'min-height:'+siteContentViewHeight + 'px'">
			
							<router-view v-if="item.name === mainTabsActiveName" />
			
						</el-card>
					</el-tab-pane>
				</el-scrollbar>
			</el-tabs>
			<div v-else>
				<el-scrollbar ref="scroll" :height="siteContentViewHeight+32+'px'" @scroll="scroll">
					<!-- 主入口标签页 e -->
					<el-card :style="'min-height:'+ siteContentViewHeight + 'px'">
						<router-view />
					</el-card>
				</el-scrollbar>
			</div>
  • Modify the style:

/* 修改标签栏样式 */
.content-main .el-tabs .el-tabs__header {
	z-index: 90;
	padding: 0 55px 0 15px;
	box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 0 6px 0 rgba(0, 0, 0, 0.04);
	background-color: #fff;
}

.content-main .el-tabs .el-tabs__nav-wrap::after {
	width: 0px;
}

.content-main .el-scrollbar .el-card {
	margin: 15px 15px;
	
}
.content-main .el-tabs .el-tabs__header{
	margin: unset;
}
.content-main .el-tabs .el-tab-pane{
}
  •  Add function method in methods
selectedTabHandle: function(tab, e) {
				tab = this.mainTabs.filter(item => item.name === tab.paneName);
				if (tab.length >= 1) {
					this.$router.push({
						name: tab[0].name,
						query: tab[0].query,
						params: tab[0].params
					});
				}
			},
			removeTabHandle: function(tabName) {
				this.mainTabs = this.mainTabs.filter(item => item.name !== tabName);
				if (this.mainTabs.length >= 1) {
					// 当前选中tab被删除
					if (tabName === this.mainTabsActiveName) {
						var tab = this.mainTabs[this.mainTabs.length - 1];
						this.$router.push({
								name: tab.name,
								query: tab.query,
								params: tab.params
							},
							() => {
								this.mainTabsActiveName = this.$route.name;
							}
						);
					}
				} else {
					this.menuActiveName = '';
					this.$router.push({
						name: 'Home'
					});
				}
			},
  • Effect:

(7) Modify the package menu bar

  •  Create a new file under the router file

  •  menu.js file code:
var mu = {
	longTitle: '管理控制台',
	littleTitle: '控制台',
	items: [{
			iconName: 'home',
			name: '首页',
			routerName: 'index',
			disabled: false
		}, {
			iconName: 'img',
			name: '图片管理',
			submenu: [{
				iconName: 'img',
				name: '图片一',
				routerName: 'img1',
				disabled: false
			}, {
				iconName: 'img',
				name: '图片二',
				routerName: 'img2',
				disabled: false
			}, {
				iconName: 'img',
				name: '图片三管理',
				submenu: [{
					iconName: 'img',
					name: '图片三',
					routerName: 'img1',
					disabled: true
				}]

			}]
		},
		{
			iconName: 'collection',
			name: '收藏管理',
			submenu: [{
				iconName: 'collection',
				name: '收藏',
				routerName: 'collect',
				disabled: false
			}]
		},
		{
			iconName: 'about',
			name: '设置',
			routerName: 'set',
			disabled: false
		}

	]
}
export default mu;
  • Rewrite the menu component:
<div class="menu-nav-header">
				<span>{
   
   {isCollapse?littleTitle:longTitle}}</span>
			</div>
			<el-menu active-text-color="#fff" background-color="#263238" class="el-menu-vertical-demo"
				:collapse-transition="false"  text-color="#96a4ab " @open="handleOpen"
				@close="handleClose" :collapse="isCollapse">
				<template v-for="(item,index) in menus">
					
					<el-menu-item v-if="!item.submenu" :index="index" @click="$router.push({ name: item.routerName })" :disabled="item.disabled">
						<SvgIcon :name="item.iconName" class="icon-svg" />
						<span slot="">&nbsp;&nbsp;{
   
   {item.name}}</span>
					</el-menu-item>
					<el-sub-menu v-else :index="index">
						<template #title>
							<SvgIcon :name="item.iconName" class="icon-svg" />
							<span slot="">&nbsp;&nbsp;{
   
   {item.name}}</span>
						</template>
						<template v-for="(submenuItem,submenuIndex) in item.submenu">
						
							<el-menu-item v-if="!submenuItem.submenu" :index="index+'-'+submenuIndex" :disabled="submenuItem.disabled"
								@click="$router.push({ name: submenuItem.routerName })">
								<SvgIcon :name="submenuItem.iconName" class="icon-svg" />
								<span slot="">&nbsp;&nbsp;{
   
   {submenuItem.name}}</span>
							</el-menu-item>
							<el-sub-menu v-else :index="index+'-'+submenuIndex">
								<template #title>
									<SvgIcon :name="submenuItem.iconName" class="icon-svg" />
									<span slot="">&nbsp;&nbsp;{
   
   {submenuItem.name}}</span>
								</template>
								<el-menu-item v-for="(item3,index3) in submenuItem.submenu" :index="index" :disabled="item3.disabled"
									@click="$router.push({ name: item3.routerName })">
									<SvgIcon :name="item3.iconName" class="icon-svg" />
									<span slot="">&nbsp;&nbsp;{
   
   {item3.name}}</span>
								</el-menu-item>

							</el-sub-menu>
						</template>
					</el-sub-menu>
				</template>
			</el-menu>

Only three levels of submenus are nested. If there are more submenus, multiple for loops need to be nested in the component.

  • The script code first imports the menu

import mu from '../router/menu/menu.js';

  •  Called in created

 In this way, to modify the menu bar, you only need to edit it in menu.js, and you no longer need to modify the page code.

(8) Add bread crumbs

  • Add the breadcrumb component to the page:
<el-breadcrumb separator="/">
					<el-breadcrumb-item v-if="!breadcrumbList.size && breadcrumbList[0]!='首页'" :to="{ name: 'index' }">
						首页
					</el-breadcrumb-item>
					<el-breadcrumb-item v-for="it in breadcrumbList">{
   
   {it}}</el-breadcrumb-item>
				</el-breadcrumb>
  • In created, process the menu bar collection into the following format:
{
"首页":["首页"],
"图片一":["图片管理","图片一"],
......
}
  • code:
    //菜单项层级处理,做一个面包屑集合保存
            var mus=that.menus
            for (let i1 of mus) {
                if (i1.submenu) {
                    for (let i2 of i1.submenu) {
                        if (i2.submenu) {
                            for (let i3 of i2.submenu) {
                                if (!i3.submenu) {
                                    that.breadcrumbObj[i3.name] = [i1.name, i2.name, i3.name];
                                }
                            }
                        } else {
                            that.breadcrumbObj[i2.name] = [i1.name, i2.name];
                            console.log(i2.name)
                        }
                    }
                } else {
                    that.breadcrumbObj[i1.name] = [i1.name];
                    console.log(i1.name)
                }
            }

Assign a value when the route changes, and add it to the watch:

this.breadcrumbList = this.breadcrumbObj[to.meta.title]

Note: The name in the route must be consistent with the name in the menu.

4. End

At this point, a simple management background template is completed. The source code is not appreciated, and the code words are not easy. Now that you have seen this, please like it before leaving.

Guess you like

Origin blog.csdn.net/xxfen_/article/details/125410412