文章目录
视频介绍 威辛=>web8931拿所有的内容
项目介绍
项目目录
创建项目
vite官网地址
https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project
创建vue3+vite的项目
要求:
Vite 需要 Node.js 版本 18+
创建命令:
# npm 7+, 需要额外加 --:
npm create vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app --template vue
下载依赖
# npm:
npm i
# yarn
yarn
启动项目
yarn dev
删减不需要的东西
在main.js中,把默认引入的样式去掉
import './style.css'
删除components下的HelloWorld.vue
删除app.vue中的代码改成以下这样
<script setup>
</script>
<template>
</template>
<style scoped>
</style>
下载必备的依赖
yarn add less
yarn add vue-router
yarn add element-plus
yarn add @element-plus/icons-vue
配置@别名
在vite.config.js下
export default defineConfig({
plugins: [vue()],
//这个resolve是添加的别名
resolve:{
alias:[
{
find: "@", replacement: "/src"
}
]
}
})
引入重置样式文件和图片资源
把课件中的less文件夹和images文件夹,都放到src下的assets中
在main.js中引入
import '@/assets/less/index.less'
#路由的创建
1.在src下创建router文件夹,在其中创建index.js文件
//引入两个方法,第一个创建路由器对象,第二个是开启hash模式的方法
import {
createRouter, createWebHashHistory } from 'vue-router'
//路由规则
const routes = [
{
path: '/',
name: 'main',
component: () => import('@/views/Main.vue')
}
]
const router = createRouter({
//history设置路由模式
history: createWebHashHistory(),
routes
})
//把路由器暴露出去
export default router
2.在src下创建views文件夹,并在其中创建Main.vue(组件需要默认的代码,不然会报错)
<template>
<div>
我是main组件
</div>
</template>
<script setup>
</script>
<style lang="less" scoped >
</style>
3.在main.js中 使用路由,这里我们把createApp(App)用一个变量接收
import router from './router'
const app =createApp(App)
app.use(router).mount('#app')
4.在app.vue组件中放置路由出口
<template>
<router-view />
</template>
//这个是app的样式,设置全屏展示,防止滚动条的出现
//注意,style上不要使用scoped
<style>
#app{
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
#整体布局的实现
引入element-plus和@element-plus/icons-vue
文档:
element-plus:https://element-plus.org/zh-CN/guide/quickstart.html#%E5%AE%8C%E6%95%B4%E5%BC%95%E5%85%A5
@element-plus/icons-vue:https://element-plus.org/zh-CN/component/icon.html#%E6%B3%A8%E5%86%8C%E6%89%80%E6%9C%89%E5%9B%BE%E6%A0%87
//这里ElementPlus我们使用完整引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)
//注册@element-plus/icons-vue图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
1.main组件的实现
<template>
<div class="common-layout">
<el-container class="lay-container">
//这个是自定义的菜单组件
<common-aside />
<el-container>
<el-header>
//这个是自定义的头部组件,需要用el-header包裹
<common-header />
</el-header>
<el-main class="right-main">
main
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
//这三个组件后面定义,我们统一暴露出来
import {CommonAside,CommonHeader} from "@/components"
</script>
<style lang="less" scoped>
.common-layout,.lay-container {
height: 100%;
}
.el-header{
background-color: #333;
}
</style>
2.创建菜单和头部组件
在components下创建CommonAside.vue和CommonHeader.vue组件。还有index.js文件
在index.js中,把这两个组件统一暴露出去
export {
default as CommonAside} from "./CommonAside.vue"
export {
default as CommonHeader} from "./CommonHeader.vue"
菜单组件的实现
在components下的CommonAside.vue中
html
<template>
<el-aside width="180px">
<el-menu
background-color="#545c64"
text-color="#fff"
:collapse-transition="false"
:collapse="false"
>
<h3 >后台管理</h3>
//这个是没有子选项的菜单,注意这个index必须是item.path,后面有用
<el-menu-item
:index="item.path"
v-for="item in noChildren"
:key="item.path"
@click="clickMenu(item)"
>
<component class="icons" :is="item.icon"></component>
<span>{
{ item.label }}</span>
</el-menu-item>
//这个是有子选项的菜单,注意这个index必须是item.path,后面有用
<el-sub-menu
v-for="item in hasChildren"
:key="item.path"
:index="item.path"
>
<template #title>
<component class="icons" :is="item.icon"></component>
<span>{
{ item.label }}</span>
</template>
<el-menu-item-group>
<el-menu-item
:index="subItem.path"
v-for="(subItem, subIndex) in item.children"
:key="subIndex"
@click="clickMenu(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>
js
<script setup>
import {
ref,computed} from 'vue'
import {
useRouter } from 'vue-router';
const router=useRouter()
const list =ref([
{
path: '/home',
name: 'home',
label: '首页',
icon: 'house',
url: 'Home'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
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=(item)=>{
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-color: #545c64;
}
</style>
编写header页面
在components下的CommonHeader.vue中
html
<template>
<div class="header">
<div class="l-content">
<!-- 这个点击事件是控制菜单组件的收缩的-->
<el-button size="small" @click="handleCollapse">
<!-- 图标的展示,这里我们使用动态组件来展示图标-->
<component class="icons" is="menu"></component>
</el-button>
<!-- 面包屑,separator是分隔符-->
<el-breadcrumb separator="/" class="bread">
<!-- 首页是一定存在的所以直接写死 -->
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<!-- if判断一定要加-->
<el-breadcrumb-item v-if="current" :to="current.path" >{
{
current.label
}}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!--右侧用户头像-->
<div class="r-content">
<el-dropdown>
<span>
<!--我们定义一个URl对象地址,这里是传入图片的名称-->
<img :src="getImageUrl('user')" class="user" />
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click="handleLoginOut">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
js
<script setup>
import {
computed} from 'vue'
import {
useRouter} from 'vue-router'
const router =useRouter()
const getImageUrl = (user) => {
return new URL(`../assets/images/${
user}.png`, import.meta.url).href;
};
const current = computed(() => {
//这个是当前选中菜单的面包屑,但是我们需要和菜单联动,先写成null
return null
})
const handleCollapse=()=>{
}
const handleLoginOut=()=>{
router.push("/login")
}
</script>
样式
<style lang="less" scoped>
.header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 100%;
background-color: #333;
}
.icons{
width: 20px;
height: 20px;
}
.r-content {
.user {
width: 40px;
height: 40px;
border-radius: 50%;
}
}
.l-content {
display: flex;
align-items: center;
.el-button {
margin-right: 20px;
}
}
:deep(.bread span) {
color: #fff !important;
cursor: pointer !important;
}
</style>
使用pinia
pinia官网:https://pinia.vuejs.org/zh/core-concepts/
下载
yarn add pinia
在main.js中使用
import {
createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
在src下创建stores文件夹,在其中创建index.js
import {
defineStore } from 'pinia'
import {
ref } from 'vue'
//初始化state数据,这里我们使用一个函数来返回
function initState(){
return {
}
}
//第一个参数要求是一个独一无二的名字
//第二个参数可接受两类值:Setup 函数或 Option 对象。
export const useAllDataStore = defineStore('allData', (a) => {
//在 Setup Store 中:
//ref() 就是 state 属性
//computed() 就是 getters
//function() 就是 actions
const state=ref(initState())
//需要把所有定义的state,getters,actions返回出去
return {
state
}
})
菜单组件和头部组件联动
components下的CommonAside.vue
html中
//这个width也改成动态的
<el-aside :width="width">
//在el-menu上有一个配置collapse,表示菜单是否收缩,这里我们在pinia中存放这个变量
<el-menu :collapse="isCollapse">
//这个标题,我们使用v-show来动态展示,!isCollapse表示不收缩,isCollapse表示收缩
<h3 v-show="!isCollapse">后台管理</h3>
<h3 v-show="isCollapse">后台</h3>
</el-menu>
</el-aside>
js中
//引入定义的pinia
import {
useAllDataStore } from '@/stores'
//这个store和vuex的差不多
const store=useAllDataStore()
//从pinia中取出isCollapse
const isCollapse =computed(() => store.state.isCollapse)
//width也是通过判断isCollapse动态决定
const width =computed(() => store.state.isCollapse? "64px" : "180px")
const clickMenu=(item)=>{
//这个selectMenu 相当于vuex的action方法,传入item,用于头部的面包屑
store.selectMenu(item)
router.push(item.path)
}
在pinia中定义属性和方法
//改变初始化state的值
function initState(){
return {
isCollapse: false,
currentMenu:null,
}
}
export const useAllDataStore = defineStore('allData', (a) => {
//新添加的方法
function selectMenu(val){
//如果是点击的首页,那就不需要设置currentMenu
if (val.name == 'home') {
state.value.currentMenu = null
}else {
//如果点击的不是首页,那就把这个设置为第二个面包屑
state.value.currentMenu = val
}
}
//返回出去
return {
selectMenu
}
})
components下的CommonHeader.vue
js
import {
useAllDataStore } from '@/stores'
const store = useAllDataStore()
const current = computed(() => {
//返回pinia中的当前点击的菜单
return store.state.currentMenu
})
const handleCollapse=()=>{
//在点击图标的时候,我们把isCollapse进行一个取反
//pinia中可以直接修改state的值,省去了vuex中的Mutation的步骤
store.state.isCollapse=!store.state.isCollapse
}
首页
1添加路由
打开路由文件
const routes = [
{
path: '/',
name: 'main',
component: () => import('@/views/Main.vue'),
//添加重定向
redirect: '/home',
//添加子路由
children:[
{
path: 'home',
name: 'home',
component: () => import('@/views/Home.vue')
}
]
}
]
2.在views下创建Home.vue
3.在Main.vue中放置路由出口
<el-main class="right-main">
//在el-main中放置
<router-view />
</el-main>
4.编写Home.vue
html,左侧的用户卡片和table表格
<template>
<el-row class="home" :gutter="20">
<el-col :span="8" style="margin-top: 20px">
<el-card shadow="hover">
<div class="user">
<img :src="getImageUrl('user')" class="user" />
<div class="user-info">
<p>Admin</p>
<p>超级管理员</p>
</div>
</div>
<div class="login-info">
<p>上次登录时间:<span>2022-7-11</span></p>
<p>上次登录的地点:<span>北京</span></p>
</div>
</el-card>
<el-card shadow="hover" class="table" >
<el-table :data="tableData">
<el-table-column
v-for="(val, key) in tableLabel"
:key="key"
:prop="key"
:label="val"
>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
</template>
js
<script setup>
import {
ref} from 'vue'
const getImageUrl = (user) => {
return new URL(`../assets/images/${
user}.png`, import.meta.url).href;
}
//这个tableData是假数据,等会我们使用axios请求mock数据
const tableData = ref([
{
name: "Java",
todayBuy: 100,
monthBuy: 200,
totalBuy: 300,
},
{
name: "Python",
todayBuy: 100,
monthBuy: 200,
totalBuy: 300,
}
])
const tableLabel = ref({
name: "课程",
todayBuy: "今日购买",
monthBuy: "本月购买",
totalBuy: "总购买",
})
</script>
样式
<style lang="less" scoped >
.home {
height: 100%;
overflow: hidden;
.user {
display: flex;
align-items: center;
border-bottom: 1px solid #ccc;
margin-bottom: 20px;
img {
width: 150px;
height: 150px;
border-radius: 50%;
margin-right: 40px;
}
}
.login-info {
p {
line-height: 30px;
font-size: 14px;
color: #999;
span {
color: #666;
margin-left: 60px;
}
}
}
.table{
margin-top: 20px;
}
.num {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.el-card {
width: 32%;
margin-bottom: 20px;
}
.icons {
width: 80px;
height: 80px;
font-size: 30px;
text-align: center;
line-height: 80px;
color: #fff;
}
.detail {
margin-left: 15px;
display: flex;
flex-direction: column;
justify-content: center;
.num {
font-size: 30px;
margin-bottom: 10px;
}
.txt {
font-size: 14px;
text-align: center;
color: #999;
}
}
}
.top-echart{
height: 280px
}
.graph {
margin-top: 20px;
display: flex;
justify-content: space-between;
.el-card {
width: 48%;
height: 260px;
}
}
}
</style>
封装axios
axios官网:https://www.axios-http.cn/docs/intro
下载
yarn add axios
1.配置环境地址
在src下创建config文件夹,在其中创建index.js
/**
* 环境配置文件
* 一般在企业级项目里面有三个环境
* 开发环境
* 测试环境
* 线上环境
*/
// 当前的环境
const env = import.meta.env.MODE || 'prod'
const EnvConfig = {
development: {
</