El-Plus的面包屑导航组件
HTML
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">homepage</el-breadcrumb-item>
<el-breadcrumb-item>
<a href="/">promotion management</a>
</el-breadcrumb-item>
<el-breadcrumb-item>promotion list</el-breadcrumb-item>
<el-breadcrumb-item>promotion detail</el-breadcrumb-item>
</el-breadcrumb>
</template>
面包屑动态渲染
数据结构
JavaScript
// 面包屑列表
// 首页 / 学生管理 / 学员管理
const list = ref([
{
title: '首页',
path: '/dashboard/console'
},
{
title: '学生管理',
path: '/student/classAdmin'
},
{
title: '学员管理',
path: '/student/studentAdmin'
}
])
动态渲染
HTML
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item
v-for="item in list"
:to="{ path: item.path }"
>
{{item.title}}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
监听路由变化
JavaScript
import { watch,ref } from 'vue';
import { useRoute } from 'vue-router'
const route = useRoute()
//监听路由变化
watch(route, (to) => {
console.log(to) //路由记录
}, {
immediate: true
})
需求分析
/** 分类讨论: 进入首页 /dashboard/console to.matched[0].path = '/dashboard/console' 进入只有一级菜单的路由 /notice/index breadcrumbList:['公告管理'] 进入有二级菜单的路由 /echarts/bar breadcrumbList: ['图表管理','柱状图] */
代码实现
JavaScript
//监听路由变化
watch(route, (to) => {
//每次切换路由时,清空以前的列表,只保留首页
list.value = [{
title: '首页',
path: '/dashboard/console'
}]
// 如果path= '/dashboard/console',则说明当前是首页,不需要处理
if (to.fullPath === '/dashboard/console') return;
//只有一级菜单,则只添加一次
if (to.matched[0].children.length === 1) {
list.value.push({
title: to.meta.title,
path: to.fullPath
})
return;
} else {
//有二级菜单
to.matched.forEach(item => {
let path;
// /首页/学生管理/班级管理 (学生管理下的第一个菜单 )
// /首页/学生管理/学员管理
// 如上,单击学生管理,应该跳转至班级管理,所以在学生管理的路由配置中添加
// meta {
// home: '/student/studentAdmin'
// }
if (item.meta.home){
path = item.meta.home
}else{
path = item.fullPath
}
list.value.push({
title: item.meta.title,
path
})
})
}
}, {
immediate: true
})
实现国际化代码
HTML
<script lang="ts" setup>
import { computed, ref } from 'vue'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import en from 'element-plus/dist/locale/en.mjs'
const language = ref('zh-cn')
const locale = computed(() => (language.value === 'zh-cn' ? zhCn : en))
const toggle = () => {
language.value = language.value === 'zh-cn' ? 'en' : 'zh-cn'
}
</script>
<template>
<el-config-provider :locale="locale">
<el-date-picker type="date" placeholder="Pick a date" :default-value="new Date(2010, 9, 1)" />
</el-config-provider>
</template>
日期组件国际化【添加中英文切换功能】
**国际化之前
![[360X360.PNG]]
**国际化之后
![[360X360 (1).PNG]]
添加中英文切换功能
图片上传
创建上传图片的组件
在upload/index.vue
HTML
<script setup>
</script>
<template>
<div class="upload">
图片上传
</div>
</template>
<style lang="scss" scoped>
</style>
添加路由
JavaScript
// 文件上传-一级路由
{
path: '/upload',
name: 'upload',
component: () => import('@/pages/layout/index.vue'),
children:[{
// /moreLang/index
path: 'index',
name: 'uploadimg',
component: () => import('@/pages/upload/index.vue'),
}]
},
配置图片上传模板
HTML
<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
const imageUrl = ref('')
// 通过header头传递token
const headers = ref({
"Authorization": "Bearer " + sessionStorage.getItem('token')
});
// 上传成功之后的处理方法
const handleAvatarSuccess = (response, uploadFile) => {
imageUrl.value = URL.createObjectURL(uploadFile.raw)
}
// 上传之前的格式验证:是否为图片,大小是否在2MB
const beforeAvatarUpload = (rawFile) => {
if (rawFile.type !== 'image/jpeg') {
ElMessage.error('Avatar picture must be JPG format!')
return false
} else if (rawFile.size / 1024 / 1024 > 2) {
ElMessage.error('Avatar picture size can not exceed 2MB!')
return false
}
return true
}
</script>
<template>
<!--
action:后端的路径地址
show-file-list:是否显示上传成功的文件列表
on-success:当文件上传成功之后
before-upload:文件上传之前
headers:添加headers值,里面主要用来添加token
-->
<el-upload class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:headers="headers">
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
</template>
<style scoped>
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
<style>
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
</style>
注意:如果想要自定义上传方式,可以使用下面方式
HTML
<el-upload class="avatar-uploader" action="#" :show-file-list="true" :http-request="changeHeader"
:on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :headers="headers">
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
<script>
let changeHeader = async (params) => {
let fileObject = params.file
let pic = new FormData()
pic.append('files', fileObject);
let url = '/api/uploadImg'
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios({
method: 'post',
url: url,
data: pic,
config: config
}).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
</script>
# Tab标签页
```HTML
<script setup>
/**
* 分类讨论:
* 1. 首次进入后台管理主页 ['首页']
* 2. 切换路由,先判断是否已存在,
* 如果不存在则在tab标签上添加当前路由的标题 ['首页','公告管理','图片上传'] , 在标题上还要绑定路由,
* 如果已经存在,则只需要获取路由对应的索引,处理高亮
* 3. 控制高亮
* 4. 删除: 当前标签从tabList中删除掉,
*/
import { watch, ref,Ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const route = useRoute()
const router = useRouter()
interface IRoute{
fullPath:String,
name?:String | any,
path:String | any,
title?:String
}
// tabList = [{},{},{}]
const tabList:Ref = ref([])
const activeIndex = ref(0)
//判断是否重复
const isRepeat = (to:IRoute) => {
return tabList.value.some((item:IRoute) => item.path === to.fullPath)
}
watch(route, (to) => {
console.log(to)
//先判断是否重复
if (!isRepeat(to)) {
//不重复
tabList.value.push({
title: to.meta.title,
path: to.fullPath
})
activeIndex.value = tabList.value.length - 1
} else {
// 重复
activeIndex.value = tabList.value.findIndex((item:IRoute)=> item.path === to.fullPath)
}
}, {
immediate: true
})
//跳转
const jump = (item:IRoute) => {
router.push(item.path)
}
//关闭
const handleClose = (index) => {
//删除前面的
if (index < activeIndex.value) {
tabList.value.splice(index, 1)
activeIndex.value = activeIndex.value - 1
return;
}
if (index === activeIndex.value && index !== tabList.value.length - 1) {
tabList.value.splice(index, 1)
router.push(tabList.value[activeIndex.value].path)
return;
}
//判断当前删除的是最后一个,而且是高亮的
if (index === tabList.value.length - 1 && index === activeIndex.value) {
//是最后一个
tabList.value.splice(index, 1)
activeIndex.value = tabList.value.length - 1
router.push(tabList.value[activeIndex.value].path)
return;
}
//删后面的
if (index > activeIndex.value) {
tabList.value.splice(index, 1)
}
}
</script>
<template>
<div class="tabs">
<!-- <el-tag type="danger" closable>Tag 3</el-tag>
<el-tag type="info" closable>Tag 3</el-tag> -->
<el-tag v-for="(item, index) in tabList" :type="activeIndex === index ? 'danger' : 'info'" closable
@click="jump(item)" @close="handleClose(index)">{{ item.title }}</el-tag>
</div>
</template>
<style lang="scss" scoped></style>