1. 初始化项目
npm create vite@latest my-vue-app -- --template vue
2. 安装依赖
npm install less less-loader -S
npm install vue-router -S
npm install pinia -D
npm install element-plus -S
npm install @element-plus/icons-vue -S
3.配置@根标记
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve:{
alias:[
{
find:"@",replacement:"/src"
}
]
}
})
4.常用的reset.less
D:\vueProject\vue3\my-vue-app\src\assets\less\index.less
D:\vueProject\vue3\my-vue-app\src\assets\less\reset.less
引入
import { createApp } from 'vue'
import App from './App.vue'
import "@/assets/less/index.less"
createApp(App).mount('#app')
5. 配置路由
route/index.js
import {createRouter,createWebHashHistory} from 'vue-router';
// import Main from '@/views/Main.vue'
// 制定路由规则
const routes=[
{
path:'/',
name:'main',
component:()=>import('@/views/Main.vue'),
redirect:'/home',
children:[
{
path:'/home',
name:'home',
component:()=>import('@/views/Home.vue'),
},
{
path:'/user',
name:'user',
component:()=>import('@/views/User.vue'),
},
{
path:'/mall',
name:'mall',
component:()=>import('@/views/Mall.vue'),
},
]
}
]
const router=createRouter({
history:createWebHashHistory(),
routes
})
export default router;
同时修改main.js ,app.vue
import { createApp } from 'vue';
import App from './App.vue';
import "@/assets/less/index.less";
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import router from './router';
import { createPinia } from 'pinia';
const pinin = createPinia();
const app = createApp(App);
app.use(router);
app.use(pinin);
app.use(ElementPlus);
// https://element-plus.sxtxhy.com/
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
<script setup>
</script>
<template>
<router-view></router-view>
</template>
<style scoped>
#app{
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
6.配置pinia
stores/index.js
import {defineStore} from 'pinia';
import {ref} from 'vue';
function initState(){
return{
isCollapse:false,
tags:[
{
path:'/home',
name:'home',
label:'首页',
icon:''
}
],
currentMenu:null
}
}
export const useAllDataStore = defineStore('allData', () => {
const state = ref(initState())
function selectMenu(val){
if(val.name === 'home'){
state.value.currentMenu=null;
}else{
state.value.currentMenu=val;
let index= state.value.tags.findIndex(item=>
item.name === val.name)
index === -1 ? state.value.tags.push(val):'';
}
}
function undateTags(tag){
let index= state.value.tags.findIndex(item=>item.name === tag.name)
state.value.tags.splice(index,1)
}
return { state,selectMenu,undateTags }
})
7.配置组件
CommonAside.vue
<template>
<el-aside :width="width">
<el-menu
background-color="#545c64"
text-color="#fff"
:collapse="isCollapse"
>
<h3 v-show="!isCollapse">管理平台</h3>
<h3 v-show="isCollapse">平台</h3>
<el-menu-item v-for="item in noChildren" :index="item.path" :key="item.path"
@click="handleMenu(item)"
>
<component class="icons" :is="item.icon"></component>
<span>{{ item.label }}</span>
</el-menu-item>
<el-sub-menu v-for="item in hasChildren" :index="item.path" :key="item.path" >
<template #title>
<component class="icons" :is="item.icon"></component>
<span>{{ item.label }}</span>
</template>
<el-menu-item-group>
<el-menu-item v-for="(subItem, subIndex) in item.children" :index="subItem.path" :key="subItem.path" @click="handleMenu(subItem)">
<component class="icons" :is="subItem.icon"></component>
<span>{{ subItem.label }}</span>
</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</el-aside>
</template>
<script setup>
import { ref, computed } from 'vue';
import {useAllDataStore} from '@/stores';
import { useRouter,useRoute } from 'vue-router';
const store = useAllDataStore()
const isCollapse=computed(()=>store.state.isCollapse);
const width = computed(()=>store.state.isCollapse ?'64px':'180px');
const router = useRouter();
const route = useRoute();
const handleMenu=(item)=>{
router.push(item.path)
store.selectMenu(item)
}
const list = ref([
{
path: '/home',
name: 'home',
label: '首页',
icon: 'house',
url: 'Home'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-paly',
url: 'Mall'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'User'
},
// {
// path: 'other',
// label: '其他',
// icon: 'location',
// children: [
// {
// path: '/page1',
// name: 'page1',
// label: '页面1',
// icon: 'setting',
// url: 'Page1'
// }, {
// path: '/page2',
// name: 'page2',
// label: '页面2',
// icon: 'setting',
// url: 'Page2'
// },
// ]
// }
])
const noChildren = computed(() => list.value.filter(item => !item.children))
const hasChildren = computed(() => list.value.filter(item => item.children))
const clickMenu = () => {
router.push(item.path)
}
</script>
<style lang="less" scoped>
.icons{
width:18px;
height:18px;
margin-right:5px;
}
.el-menu{
border-right: none;
h3{
line-height: 48px;
color:#fff;
text-align: center;
}
}
.el-aside{
height: 100%;
background: #545c64;
}
</style>
CommonHeader.vue
<template>
<div class="header">
<div class="l-content">
<el-button size="small" @click="handleCollapse">
<component class="icons" is="menu" ></component>
</el-button>
<el-breadcrumb separator="/" class="bread">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item v-if="current" :to="current.path">{{ current.label }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="r-content">
<el-dropdown>
<span class="el-dropdown-link">
<img :src="getImageUrl('user')" class="user">
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import {useAllDataStore} from '@/stores';
const getImageUrl = (user) => new URL(`../assets/images/${user}.png`, import.meta.url).href;
const store = useAllDataStore()
const handleCollapse=()=>{
store.state.isCollapse=!store.state.isCollapse
}
const current = computed(()=>store.state.currentMenu)
console.log('xxxxxxxxxxx current:',current)
</script>
<style lang="less" scoped>
.header{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 100%;
background: #333;
}
.icons{
width: 20px;
height: 20px;
}
.l-content{
display: flex;
align-items: center;
.el-button{
margin-right: 20px;
}
}
.r-content{
.user{
width: 40px;
height: 40px;
border-radius: 50%;
}
}
:deep(.bread span){
color:#fff !important;
cursor: pointer !important;
}
</style>
CommonTab.vue
<template>
<div class="tags">
<el-tag v-for="(tag, index) in tags"
:key="tag.name"
:closable="tag.name !== 'home'"
:effect="route.name === tag.name ? 'dark' : 'plain'"
@click="handleMenu(tag)"
@close="handleClose(tag,index)"
>
{{ tag.label }}
</el-tag>
</div>
</template>
<script setup>
import { ref, computed, transformVNodeArgs } from 'vue';
import { useAllDataStore } from '@/stores';
import { useRouter, useRoute } from 'vue-router';
const store = useAllDataStore()
const tags = computed(() => store.state.tags)
const router = useRouter()
const route = useRoute()
const handleMenu=(tag)=>{
router.push(tag.name)
store.selectMenu(tag)
}
const handleClose=(tag,index)=>{
store.undateTags(tag)
// 如果点击的关闭的tag不是当前页面
if(tag.name !== route.name)
return
if(index === store.state.tags.length){
store.selectMenu(tags.value[index-1])
router.push(tags.value[index-1].name)
}else{
store.selectMenu(tags.value[index])
router.push(tags.value[index].name)
}
}
</script>
<style lang="less" scoped>
.tags {
margin: 20px 0 0 20px;
}
.el-tag{
margin-right: 10px;
}
</style>
8. 配置页面
views/Home.vue
<template>
home
</template>
<script>
export default {
}
</script>
<style>
</style>
views/Main.vue
<template>
<div class="common-layout">
<el-container class="lay-container">
<!-- 左侧组件 -->
<common-aside />
<el-container>
<el-header class="el-header">
<!-- header组件 -->
<common-header />
</el-header>
<common-tab></common-tab>
<el-main class="right-main">
<!-- 路由出口 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import CommonAside from'@/components/CommonAside.vue';
import CommonHeader from'@/components/CommonHeader.vue';
import CommonTab from'@/components/CommonTab.vue';
</script>
<style lang="less" scoped>
.common-layout,
.lay-container {
height: 100%;
}
.el-header{
background: #333;
}
</style>
views/Mall.vue
<template>
mall
</template>
<script>
export default {
}
</script>
<style>
</style>
views/User.vue
<template>
user
</template>
<script>
export default {
}
</script>
<style>
</style>
APP.vue
<script setup>
</script>
<template>
<router-view></router-view>
</template>
<style scoped>
#app{
width: 100%;
height: 100%;
overflow: hidden;
}
</style>