主要是使用vite + element plus 完成
根据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 按照指示进行即可
vue-language-feature 这个是代码高亮的作用
vue3 snippets 代码提示
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')
就可以了
去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)完成路由
去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提取出来
这里用到了前置路由守卫和后置路由守卫
前置路由守卫的作用是判断有没有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())
因为进入页面后 顶部和左侧菜单栏是不动的 所以需要定义这个Layout布局组件 并且每个路由都是他的子组件
去element-plus有个Container 布局容器 整体布局代码如下
# 最外面的el-container是包裹的整体垂直排列 有header就是垂直 没有就是水平排列
头部
这里是水平排列的
侧边栏
主要内容 也就是所有组件渲染的地方
这里面有个小知识 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()
定义一个都可以访问的静态路由 一般就是登录页 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
}
路由跳转这里也要改一下 用这个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 缓存 和切换页面的动画效果
需要注意 渲染的页面必须是一个根节点div 如下
分类
npm i gsap
{{ d.num.toFixed(0) }}
封装这个组件就可以了
vueuse 里面的 useResizeObserve 在使用myChart.resize() 重新自己设定大小