搭建页面架构
一. 初始化ElementPlus
参照:https://element-plus.gitee.io/zh-CN/guide/quickstart.html#全局配置
- 安装:
npm install element-plus --save
- 引入
- 完整引入。字面意思将全部组件加载到项目里,那么在将来打包时,也会全部打包组件到结果中,不推荐。
- 按需引入。引入需要的组件,打包的时候压力比较小。
- 但是,如果引入的组件较多,推荐完整引入,不然按需引入还得一个一个组件注册和配置,比较麻烦。
- 此项目里,需要大量组件,于是选择完整引入。
在plugins
里创建一个element-plus.ts
专门配置elementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { App } from 'vue'
export default {
// 将来注册组件时,自动调用install函数,把vue实例传进来
install (app: App) {
app.use(ElementPlus)
}
}
然后导入到main.ts
import elementPlus from './plugins/element-plus'
createApp(App)
.use(router)
...
.use(elementPlus)
这就完成了elementPlus的注册
验证一下
<!--home.vue-->
<el-button>测试按钮</el-button>
呈现出来了!
全局配置
可以针对elementPlus组件进行全局配置,比如我想让所有插入的element Plus的组件都显示很小的尺寸size,并且让他们在页面最外层zIndex
只需要在完整引入的element-plus.ts
里注册的时候加上设置
app.use(ElementPlus,{size:'small', zIndex:3000})
而如果针对某个组件,想修改尺寸
<!--home.vue-->
<el-button size="mini"></el-button>
国际化:语言
组件内部显示的语言默认为英语,比如当我们插入日期组件,那么日期都是英语的Jan, Feb, March等等这些。
ConfigProvider
可以针对局部生效,也能针对全局生效。
<el-config-provider :locale="locale">
<app />
</el-config-provider>
被包裹的app就生效。
全局生效:
找到根组件,App.vue,包裹住路由的渲染出口,即可全局生效。
<el-config-provider :locale="locale">
<router-view />
</el-config-provider>
<script lang="ts" setup>
// 将locale加载进来,也就是语言。
import locale from 'element-plus/lib/locale/lang/zh-cn'
</script>
这就完事了!
总结
- 安装,通过npm
- 引入elementPlus,我选择了全局引入,创建了element-plus.ts来存放,然后导入,并且注册app.use(elementPlus)
- 将组件的语言设置为中文
二. Layout布局和导航菜单
通过ElementPlus的Container布局容器
快速搭建布局
而我们恰好需要以下布局:
即可直接用。
Layout组件
新建src / layout / AppLayout.vue
文件,然后将element提供的代码直接导入。
<template>
<div class="common-layout">
<el-container>
<el-aside width="200px">
<AppMenu />
</el-aside>
<el-container>
<el-header>Header</el-header>
<el-main>
<!-- 子路由出口 -->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script lang="ts" setup>
import AppMenu from './components/AppMenu.vue'
</script>
<style lang="scss" scoped>
.el-header,.el-footer {
background-color: #B3C0D1;
color:#333;
}
.el-aside {
background-color: #304156;
color: #333;
}
.el-main {
background-color: #E9EEF3;
color:#333;
}
.el-container {
height: 100vh;
}
</style>
然后将AppLayout导出。
路由配置
编辑src / router / index.ts
文件
在路由里,将首页改成我们的AppLayout,然后子路由是home/index.vue,这样就被套在里面了。
const routes: RouteRecordRaw[] = [
{
path: '/',
component: AppLayout,
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import('../views/home/index.vue')
}
]
},
....
样式优化
编辑src / styles / common.scss
文件:
* {
margin: 0;
padding: 0;
}
完成第一步:
页面展示
第二步:
三. NavMenu菜单导航:
找到menu,提供的一个模板:
侧边栏组件
新建src / layout / components / AppMenu
文件:
<el-menu
active-text-color="#ffd04b"
background-color="#304156"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
>
<el-sub-menu index="1">
<template #title>
<el-icon><location /></el-icon>
<span>Navigator One</span>
</template>
<el-menu-item-group title="Group One">
<el-menu-item index="1-1">item one</el-menu-item>
<el-menu-item index="1-2">item two</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group Two">
<el-menu-item index="1-3">item three</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="1-4">
<template #title>item four</template>
<el-menu-item index="1-4-1">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="2">
<el-icon><icon-menu /></el-icon>
<span>Navigator Two</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<el-icon><document /></el-icon>
<span>Navigator Three</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><setting /></el-icon>
<span>Navigator Four</span>
</el-menu-item>
</el-menu>
</template>
<script lang="ts" setup>
import {
Document,
Menu as IconMenu,
Location,
Setting
} from '@element-plus/icons-vue'
</script>
注意:ElementPlus的icon只能按需手动引入
并在AppLayout.vue
里导入, 并且将菜单放去左上角的Aside位置
<el-aside width="200px">
<AppMenu />
</el-aside>
<script>
import AppMenu from './components/AppMenu.vue'
</script>
完成第二步
最后我们的成果是需要完成如下:
因此还需要
- product商品
- attr 规则管理
- classify 分类
- list 列表
- reply 评论
- order订单相关的:
- list订单列表
- offine收银订单
- media商品图片资源
- permisson权限相关的
- admin 管理员
- role 角色
- rule 规则
路由目录
为了方便管理组件,我们设置每个组件为一个新的路由ts文件
比如:product管理商品列表,商品规格价格等等。
副路由的设置方法:
比如我们点击菜单,会下拉,菜单就是副路由,点击后只会拉下菜单。
component
设置为RouterView
, 而下拉的路由写在children
里
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/product',
component: RouterView,
meta: {
title: '商品'
},
children: [
{
path: 'product_list',
name: 'product-list',
component: () => import('@/views/product/list/index.vue'),
meta: { // 自定义路由元数据
title: '商品列表'
}
},
{
path: 'add_product',
name: 'product-add',
component: () => import('@/views/product/add/index.vue'),
meta: {
title: '添加商品'
}
},
{
path: 'product_attr',
name: 'product-attr',
component: () => import('@/views/product/attr/index.vue'),
meta: {
title: '商品规格'
}
},
{
path: 'product_classify',
name: 'product-classify',
component: () => import('@/views/product/classify/index.vue'),
meta: {
title: '商品分类'
}
},
{
path: 'product_reply',
name: 'product-reply',
component: () => import('@/views/product/reply/index.vue'),
meta: {
title: '商品评论'
}
}
]
}
export default routes
完成后,将product导入到主路由里
...
import productRouter from './modules/product'
const routes: RouteRecordRaw[] = [
{
path: '/',
component: AppLayout,
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import('../views/home/index.vue')
},
productRouter
]
},
{
path: '/login',
...
并且分别填写product每个子路由匹配的组件,比如product_list
对应的src/views/product/list/index.vue
<template>
<h1>商品列表</h1>
</template>
<script lang="ts" setup>
</script>
<style lang="scss" scoped></style>
页面样式
项目思路:
分为两个大路由,布局组件和登录组件。
布局的主路由是布局组件,子组件分别是首页,商品:商品列表啥的。设置
登录组件。
四. 切换侧边栏展开收起
也就是实现点击后展开菜单栏,再次点击后收起。
源码里,主要实现的方式是通过:collapse="isCollapse"
的布尔值来控制收起或者展开。
const isCollapse = ref(true)
思路:
将isCollapse的值注入项目的容器store,默认为false,默认展开状态,这样全局都能看到他了。
然后在AppHeader里写一个按钮组件来控制isCollapse。
最后将这个按钮组件导入目录里就好了。
1. 全局变量vuex和store
编辑src / store / index.tsx
...
export interface State {
count:number,
//主要变化在这!!!!加入了isCollapse
isCollapse: boolean
}
...
export const key: InjectionKey<Store<State>> = Symbol('store')
// 创建一个新的 store 实例
export const store = createStore<State>({
state () {
return {
count: 0,
// 默认为false
isCollapse: false
}
mutations: {
// 当触发后,第一个变量等于第二个
setIsCollapse (state, payload) {
state.isCollapse = payload
}
}
2. 侧边组件
编辑@ / layout / components / AppMenu / index.vue
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<el-menu
active-text-color="#ffd04b"
background-color="#304156"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
:collapse="$store.state.isCollapse"
router
>
<el-menu-item index="/">
<el-icon><home-filled /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="1">
<template #title>
<el-icon><goods-filled /></el-icon>
<span>商品</span>
</template>
<el-menu-item index="/product/product_list">
<span>商品列表</span>
</el-menu-item>
<el-menu-item index="/product/product_classify">
<span>商品分类</span>
</el-menu-item>
<el-menu-item index="/product/product_attr">
<span>商品规格</span>
</el-menu-item>
<el-menu-item index="/product/product_reply">
<span>商品评论</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon><ShoppingCart /></el-icon>
<span>订单</span>
</template>
<el-menu-item index="/order/order_list">
订单列表
</el-menu-item>
<el-menu-item index="/order/offine">
收银订单
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2-2">
<template #title>
<el-icon><Promotion /></el-icon>
<span>媒体</span>
</template>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<el-icon><Setting /></el-icon>
<span>权限</span>
</template>
<el-menu-item index="/permission/permission_role">
角色
</el-menu-item>
<el-menu-item index="/permission/admin">
管理员
</el-menu-item>
<el-menu-item index="/permission/rule">
权限规则
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script lang="ts" setup>
import {
HomeFilled,
GoodsFilled,
ShoppingCart,
Setting,
Promotion
} from '@element-plus/icons-vue'
// import { ref } from 'vue'
// const isCollapse = ref(true)
</script>
<style lang="scss" scoped>
.el-menu {
border-right: none;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
</style>
3. 头部组件
新建src / layout / AppHeader / ToggleSidebar.vue
<template>
<el-icon
@click="$store.commit('setIsCollapse', false)"
v-show="$store.state.isCollapse"
style="cursor: pointer;"
>
<expand />
</el-icon>
<el-icon
@click="$store.commit('setIsCollapse', true)"
v-show="!$store.state.isCollapse"
style="cursor: pointer;"
>
<fold />
</el-icon>
<h1>header1</h1>
</template>
<script lang="ts" setup>
import { Fold, Expand } from '@element-plus/icons-vue'
</script>
<style lang="scss" scoped></style>
4. Layout布局
编辑src / layout / AppLayout.vue
文件
<template>
<div class="common-layout">
<el-container>
<el-aside>
<AppMenu />
</el-aside>
<el-container>
<el-header>
<AppHeader />
</el-header>
<el-main>
<!-- 子路由出口 -->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script lang="ts" setup>
import AppMenu from './AppMenu/index.vue'
import AppHeader from './AppHeader/ToggleSidebar.vue'
</script>
<style lang="scss" scoped>
.el-header{
background-color: #b3c0d1;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
.el-aside {
width: auto;
background-color: #304156;
color: #333;
}
.el-main {
background-color: #e9eef3;
color: #333;
}
.el-container {
height: 100vh;
}
</style>
5. 样式展示
五. 面包屑导航
也就是每个页面展示当前位置: 首页 > 商品 > 商品价格
如何才能实现当前标题的展示?
思路:
在路由里给每个路由和子路由增加meta属性,并且将title写好。
在AppHeader
组件上,导入路由,获取当前的路由值,并且过滤,获取含有title的路由。
1. 面包屑导航组件
新建src / layout / AppHeader / Breadcrumb.vue
文件:
思路:
导入路由。
然后获取路由useRouter()
,通过router.currentRoute.value.matched
拿到当前的所有路由,比如当前在商品列表,那么将获得layout => 商品 => 商品列表 三个路由。
将获取的路由过滤一下,获取有title的路由,routes = router.currentRoute.value.matched.filter(item => item.meta.title)
然后通过v-for
在模板里输出。
<template>
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item
v-for="item in routes"
:key="item.path"
>
{{ item.meta.title }}
<i class="el-icon-edit" />
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { ArrowRight } from '@element-plus/icons-vue'
const router = useRouter()
console.log(router.currentRoute.value.matched)
const routes = computed(() => {
return router.currentRoute.value.matched.filter(item => item.meta.title)
})
</script>
<style lang="scss" scoped></style>
2. 自定义路由元数据
也就是meta
编辑src / router / modules / product.tsx
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: 'product',
component: RouterView,
meta: {
title: '商品'
},
children: [
{
path: 'product_list',
name: 'product-list',
component: () => import('@/views/product/list/index.vue'),
meta: { // 自定义路由元数据
title: '商品列表'
}
},
{
path: 'product_attr',
name: 'product-attr',
component: () => import('@/views/product/attr/index.vue'),
meta: {
title: '商品规格'
}
},
{
path: 'product_classify',
name: 'product-classify',
component: () => import('@/views/product/classify/index.vue'),
meta: {
title: '商品分类'
}
},
{
path: 'product_reply',
name: 'product-reply',
component: () => import('@/views/product/reply/index.vue'),
meta: {
title: '商品评论'
}
}
]
}
export default routes
以及首页,src / router / index.tsx
...
import orderRoutes from './modules/order'
const routes: RouteRecordRaw[] = [
{
path: '/',
component: AppLayout,
children: [
{
path: '', // 默认子路由
meta: {
title: '首页'
},
...
3. 样式展示
4. meta的类型补充
而当我导入路由时,我发现meta
属性的类型是未知的,能不能给他定义类型呢?
于是,我翻Vue Router的官方网站,惊喜地找到了,在路由元信息,可以通过扩展RoueMeta
来做到。(后来不知道什么原因,官方文档删除了TS的这一块)
import 'vue-router'
// 声明一个模块,vue-router, 给他的RoureMeta做类型补充声明
declare module 'vue-router' {
// eslint-disable-next-line no-unused-vars
interface RouteMeta {
// 是可选的
isAdmin?: boolean
// 每个路由都必须声明
requiresAuth: boolean
}
}
那么项目里则不用必须声明
import 'vue-router'
// 声明一个模块,vue-router, 给他的RoureMeta做类型补充声明
declare module 'vue-router' {
// eslint-disable-next-line no-unused-vars
interface RouteMeta {
title: string
}
}
这不就完成了!!
六. 全屏
参照MDN的H5全屏API
https://developer.mozilla.org/zh-CN/docs/Web/API/Fullscreen_API
1. 全屏组件
新建src / AppHeader / FullScreen.vue
文件
<template>
<el-icon
@click="toggleFullScreen"
style="cursor: pointer;"
>
<full-screen />
</el-icon>
</template>
<script lang="ts" setup>
import { FullScreen } from '@element-plus/icons-vue'
const toggleFullScreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen()
} else {
if (document.exitFullscreen) {
document.exitFullscreen()
}
}
}
</script>
<style lang="scss" scoped></style>
2. AppHeader汇总
编辑src / AppHeader / index.vue
<template>
<el-space>
<ToggleSidebar />
<BreadcrumbVue />
</el-space>
<el-space>
<FullScreen />
<h1>header1</h1>
</el-space>
</template>
<script lang="ts" setup>
import ToggleSidebar from './ToggleSidebar.vue'
import BreadcrumbVue from './Breadcrumb.vue'
import FullScreen from './FullScreen.vue'
</script>
<style lang="scss" scoped></style>
3. 样式展示
七. 页面加载进度条
参考github的NProgress
在页面还没加载完成的时候出现进度条,是不是更好玩,也会提高用户体验。
基本使用
NProgress.start();
NProgress.done();
1. 进度条组件
# . 第一步安装
npm install --save nprogress
# .ts 类型补充
npm i --save-dev @types/nprogress
2. 路由配置
使用路由拦截器,设置每个路由导航前开启和导航完成以后完成加载。
- 全局前置守卫
router.beforeEach(()=>{})
.所有页面的导航都会经过这 - 全局后置守卫
router.afterEach(()=>{})
编辑src / router / index.ts
文件
import nProgress from 'nprogress'
const router = createRouter({
...
})
router.beforeEach(() => {
nProgress.start() // 开始加载进度条nProgress
})
router.afterEach(() => {
nProgress.done() // 结束进度条
})