主要是使用vite + element plus 完成
1、第一步 先下载node 下载长期稳定版本
2、查看npm的版本 node下载好就会自带npm
根据npm 版本来使用Vite创建vue3项目
# npm 6.x
npm create vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
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
按照响应命令运行完 会出现让你下载create-vite 按照指示进行即可
3、项目搭建完成 下载vscode插件
vue-language-feature 这个是代码高亮的作用
vue3 snippets 代码提示
4、安装element-plus ui组件库
npm install element-plus --save
在main.js种引入
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
就可以了
5、windi css工具库
6、配置路由
去vue-router官网 下载 4版本以上的
npm install vue-router@4
下载好后根据 官网教程来引入
import {
createRouter,
createWebHashHistory
} from 'vue-router'
import Index from '@/pages/index.vue'
import About from '@/pages/about.vue'
import NotFound from '@/pages/404.vue'
const routes = [{
path:'/',
component:Index
},{
path:'/about',
component:About
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]
const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
export default router
然后 在main.js 使用 app.use(router)完成路由
7、axios请求
去axios官网下载 下载好按照指示引用 这里可以对axios进行一个二次封装 就是请求拦截 和响应拦截
service.interceptors.request.use(function (config) {
// Do something before request is sent
const token = getToken()
if(token) {
config.headers['token'] = token
}
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
service.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data.data;
}, function (error) {
console.log(error.response.data.msg);
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
toast(error.response.data.msg || "请求失败","error")
return Promise.reject(error);
});
这里主要就是请求带token,然后就是响应的时候吧data提取出来
8、路由守卫
这里用到了前置路由守卫和后置路由守卫
前置路由守卫的作用是判断有没有token 不然强制到登录页 后置路由守卫就是一个nprogress 一个加载的动画效果 后置路由守卫关闭这个加载的动画 这个插件需要下载
nprogress
import nprogress from 'nprogress'
// 显示全屏loading
export function showFullLoading(){
nprogress.start()
}
// 隐藏全屏loading
export function hideFullLoading(){
nprogress.done()
}
路由守卫代码
import router from "./routers"
import { getToken } from "@/composables/auth"
import {
toast,
showFullLoading,
hideFullLoading
} from "@/composables/util"
import store from "./store"
// 全局前置守卫
router.beforeEach(async (to,from,next)=>{
// 显示loading
// 显示loading
showFullLoading()
const token = getToken()
// 没有登录,强制跳转回登录页
if(!token && to.path != "/login"){
toast("请先登录","error")
return next({ path:"/login" })
}
// 防止重复登录
if(token && to.path == "/login"){
toast("请勿重复登录","error")
return next({ path:from.path ? from.path : "/" })
}
// 如果用户登录了,自动获取用户信息,并存储在vuex当中
if(token){
await store.dispatch("getinfo")
}
// 设置页面标题
let title = (to.meta.title ? to.meta.title : "") + "-商城后台"
document.title = title
next()
})
// 全局后置守卫
router.afterEach((to, from) => hideFullLoading())
9、layout布局
因为进入页面后 顶部和左侧菜单栏是不动的 所以需要定义这个Layout布局组件 并且每个路由都是他的子组件
去element-plus有个Container 布局容器 整体布局代码如下
# 最外面的el-container是包裹的整体垂直排列 有header就是垂直 没有就是水平排列
<el-container>
<el-header>
头部
</el-header>
<el-container> 这里是水平排列的
<el-aside >
侧边栏
</el-aside>
<el-main>
主要内容 也就是所有组件渲染的地方
</router-view>
</el-main>
</el-container>
</el-container>
这里面有个小知识 flex布局 左右布局可以用
display:flex #父元素
margin-right:auto #左边的子元素元素加这个 如果是右边的就是margin-left
接着是刷新和全屏功能 刷新使用location.reload 全屏用vueuse里面的刷新useFullScreen 前提是下载 ‘@vueuse/core’ 这个包
import { useFullscreen } from '@vueuse/core'
是否全屏 全屏 退出全屏 切换
const { isFullscreen, enter, exit, toggle } = useFullscreen()
10、添加动态路由
定义一个都可以访问的静态路由 一般就是登录页 404 还有就是首页 /
动态路由一般是加在了 首页admin的 children里面 因为admin是layout布局 包括了侧边栏和导航区域 这些都是固定的
具体代码如下
import {
createRouter,
createWebHashHistory
} from 'vue-router'
import Admin from "~/layouts/admin.vue";
import Index from '~/pages/index.vue'
import Login from '~/pages/login.vue'
import NotFound from '~/pages/404.vue'
import GoodList from '~/pages/goods/list.vue'
import CategoryList from '~/pages/category/list.vue'
// 默认路由,所有用户共享
const routes = [
{
path: "/",
name:"admin",
component: Admin,
},
{
path: "/login",
component: Login,
meta: {
title: "登录页"
}
}, {
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
}]
// 动态路由,用于匹配菜单动态添加路由
const asyncRoutes = [{
path:"/",
name:"/",
component:Index,
meta:{
title:"后台首页"
}
},{
path:"/goods/list",
name:"/goods/list",
component:GoodList,
meta:{
title:"商品管理"
}
},{
path:"/category/list",
name:"/category/list",
component:CategoryList,
meta:{
title:"分类列表"
}
}}]
export const router = createRouter({
history: createWebHashHistory(),
routes
})
// 动态添加路由的方法
export function addRoutes(menus){
// 是否有新的路由
let hasNewRoutes = false
const findAndAddRoutesByMenus = (arr) =>{
arr.forEach(e=>{
let item = asyncRoutes.find(o=>o.path == e.frontpath)
if(item && !router.hasRoute(item.path)){
router.addRoute("admin",item)
hasNewRoutes = true
}
if(e.child && e.child.length > 0){
findAndAddRoutesByMenus(e.child)
}
})
}
findAndAddRoutesByMenus(menus)
# 这个变量是为了解决刷新后 页面空白问题
return hasNewRoutes
}
如果只是正常添加动态路由会出现刷新后404的问题,原因是刷新厚vue重新加载,这个时候在加载路由前已经跳转了,所以404,解决办法就是重新再跳转一下我们要去的页面,因此在beforeRouter函数里面hasNewRoutes ? next(to.fullPath) : next() 使用了这句,如果是重新加载路由我们酒 next(to.fullPath) 相当于又加载这个页面一次 这样动态路由已经添加好了,如果添加好后 就next 不然会一直循环的。
因此 添加动态路由的方法里面也要加上判断hasNewRoutes的标识
// 如果用户登录了,自动获取用户信息,并存储在vuex当中
let hasNewRoutes = false
if(token && !hasGetInfo){
let { menus } = await store.dispatch("getinfo")
hasGetInfo = true
// 动态添加路由
hasNewRoutes = addRoutes(menus)
}
// 设置页面标题
let title = (to.meta.title ? to.meta.title : "") + "-帝莎编程商城后台"
document.title = title
hasNewRoutes ? next(to.fullPath) : next()
keep-alive 缓存 和切换页面的动画效果
<template >
<el-container>
<el-header>
<Header></Header>
</el-header>
<el-container>
<el-aside :style="{ width: $store.state.asideWidth }">
<Aside></Aside>
</el-aside>
<el-main>
<Taglist></Taglist>
<router-view v-slot="{ Component }">
<transition name="fade">
<keep-alive :max="10">
<component :is="Component"></component>
</keep-alive>
</transition>
</router-view>
</el-main>
</el-container>
</el-container>
</template>
<script setup >
import Aside from '@/layout/components/Aside.vue'
import Header from '@/layout/components/Header.vue'
import Taglist from '@/layout/components/tagList.vue'
</script>
<style>
.el-aside {
transition: al 0.2s;
}
.fade-enter-from {
opacity: 0;
}
.fade-enter-to {
opacity: 1;
}
.fade-leave-from {
opacity: 1;
}
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.3s;
}
.fade-enter-active {
transition-delay: 0.3s;
}</style>
需要注意 渲染的页面必须是一个根节点div 如下
<template>
<div>
分类
</div>
</template>
11、gsap 数字滚动
npm i gsap
<template>
{{ d.num.toFixed(0) }}
</template>
<script setup>
import { reactive,watch } from "vue"
import gsap from "gsap"
const props = defineProps({
value:{
type:Number,
default:0
}
})
const d = reactive({
num:0
})
function AnimateToValue(){
gsap.to(d,{
duration:0.5,
num:props.value
})
}
AnimateToValue()
watch(()=>props.value,()=>AnimateToValue())
</script>
封装这个组件就可以了
12、echarts图表窗口自适应大小
vueuse 里面的 useResizeObserve 在使用myChart.resize() 重新自己设定大小