目录
[首页 layout 架子 [element-plus 菜单]](#首页 layout 架子 [element-plus 菜单])
[退出功能 [element-plus 确认框]](#退出功能 [element-plus 确认框])
[文章分类页面 - [element-plus 表格]](#文章分类页面 - [element-plus 表格])
[基本架子 - PageContainer](#基本架子 - PageContainer)
[封装API - 请求获取表格数据](#封装API - 请求获取表格数据)
[el-table 表格动态渲染](#el-table 表格动态渲染)
[el-table 表格 loading 效果](#el-table 表格 loading 效果)
[文章分类添加编辑 [element-plus 弹层]](#文章分类添加编辑 [element-plus 弹层])
[封装弹层组件 ChannelEdit](#封装弹层组件 ChannelEdit)
首页 layout 架子 [element-plus 菜单]
基本架子拆解
架子组件列表:
el-container
-
el-aside 左侧
- el-menu 左侧边栏菜单
-
el-container 右侧
-
el-header 右侧头部
- el-dropdown
-
el-main 右侧主体
- router-view
黑马程序员:小帅鹏<el-dropdown placement="bottom-end"> <el-avatar :src="avatar" /> <el-icon><CaretBottom /></el-icon> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="profile" :icon="User" >基本资料</el-dropdown-item > <el-dropdown-item command="avatar" :icon="Crop" >更换头像</el-dropdown-item > <el-dropdown-item command="password" :icon="EditPen" >重置密码</el-dropdown-item > <el-dropdown-item command="logout" :icon="SwitchButton" >退出登录</el-dropdown-item > </el-dropdown-menu> </template> </el-dropdown> </el-header> <el-main> <router-view></router-view> </el-main> <el-footer>大事件 ©2023 Created by 黑马程序员</el-footer> </el-container> </el-container> </template> <style lang="scss" scoped> .layout-container { height: 100vh; .el-aside { background-color: #232323; &__logo { height: 120px; background: url('@/assets/logo.png') no-repeat center / 120px auto; } .el-menu { border-right: none; } } .el-header { background-color: #fff; display: flex; align-items: center; justify-content: space-between; .el-dropdown__box { display: flex; align-items: center; .el-icon { color: #999; margin-left: 10px; }&:active, &:focus { outline: none; } }
}
.el-footer {
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
}
}
</style>
-
登录访问拦截
需求:只有登录页,可以未授权的时候访问,其他所有页面,都需要先登录再访问
// 登录访问拦截
router.beforeEach((to) => {
const userStore = useUserStore()
if (!userStore.token && to.path !== '/login') return '/login'
})
用户基本信息获取&渲染
-
api/user.js
封装接口export const userGetInfoService = () => request.get('/my/userinfo')
-
stores/modules/user.js 定义数据
const user = ref({})
const getUser = async () => {
const res = await userGetInfoService() // 请求获取数据
user.value = res.data.data
} -
layout/LayoutContainer
页面中调用import { useUserStore } from '@/stores'
const userStore = useUserStore()
onMounted(() => {
userStore.getUser()
}) -
动态渲染
黑马程序员:{{ userStore.user.nickname || userStore.user.username }}<el-avatar :src="userStore.user.user_pic || avatar" />
退出功能 [element-plus 确认框]
-
注册点击事件
<el-dropdown placement="bottom-end" @command="onCommand">
<el-dropdown-menu> <el-dropdown-item command="profile" :icon="User">基本资料</el-dropdown-item> <el-dropdown-item command="avatar" :icon="Crop">更换头像</el-dropdown-item> <el-dropdown-item command="password" :icon="EditPen">重置密码</el-dropdown-item> <el-dropdown-item command="logout" :icon="SwitchButton">退出登录</el-dropdown-item> </el-dropdown-menu> -
添加退出功能
const onCommand = async (command) => {
if (command === 'logout') {
await ElMessageBox.confirm('你确认退出大事件吗?', '温馨提示', {
type: 'warning',
confirmButtonText: '确认',
cancelButtonText: '取消'
})
userStore.removeToken()
userStore.setUser({})
router.push(/login
)
} else {
router.push(/user/${command}
)
}
} -
pinia user.js 模块 提供 setUser 方法
const setUser = (obj) => (user.value = obj)
文章分类页面 - [element-plus 表格]
基本架子 - PageContainer
-
基本结构样式,用到了 el-card 组件
<template> <el-card class="page-container"> <template #header>文章分类</template> ... </el-card> </template> <style lang="scss" scoped> .page-container { min-height: 100%; box-sizing: border-box; .header { display: flex; align-items: center; justify-content: space-between; } } </style><el-button type="primary">添加分类</el-button> -
考虑到多个页面复用,封装成组件
- props 定制标题
- 默认插槽 default 定制内容主体
- 具名插槽 extra 定制头部右侧额外的按钮
{{ title }}</template> <slot></slot> </el-card> </template> <style lang="scss" scoped> .page-container { min-height: 100%; box-sizing: border-box; .header { display: flex; align-items: center; justify-content: space-between; } } </style><slot name="extra"></slot> -
页面中直接使用测试 ( unplugin-vue-components 会自动注册)
-
文章分类测试:
<template> <page-container title="文章分类"> <template #extra> <el-button type="primary"> 添加分类 </el-button> </template>
</template>主体部分 </page-container>
-
文章管理测试:
<template> <page-container title="文章管理"> <template #extra> <el-button type="primary">发布文章</el-button> </template>
</template>主体部分 </page-container>
文章分类渲染
封装API - 请求获取表格数据
-
新建
api/article.js
封装获取频道列表的接口import request from '@/utils/request'
export const artGetChannelsService = () => request.get('/my/cate/list') -
页面中调用接口,获取数据存储
const channelList = ref([])
const getChannelList = async () => {
const res = await artGetChannelsService()
channelList.value = res.data.data
}
el-table 表格动态渲染
<el-table :data="channelList" style="width: 100%">
<el-table-column label="序号" width="100" type="index"> </el-table-column>
<el-table-column label="分类名称" prop="cate_name"></el-table-column>
<el-table-column label="分类别名" prop="cate_alias"></el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button
:icon="Edit"
circle
plain
type="primary"
@click="onEditChannel(row)"
></el-button>
<el-button
:icon="Delete"
circle
plain
type="danger"
@click="onDelChannel(row)"
></el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="没有数据" />
</template>
</el-table>
const onEditChannel = (row) => {
console.log(row)
}
const onDelChannel = (row) => {
console.log(row)
}
el-table 表格 loading 效果
-
定义变量,v-loading绑定
const loading = ref(false)
<el-table v-loading="loading"> -
发送请求前开启,请求结束关闭
const getChannelList = async () => {
loading.value = true
const res = await artGetChannelsService()
channelList.value = res.data.data
loading.value = false
}
文章分类添加编辑 [element-plus 弹层]
点击显示弹层
-
准备弹层
const dialogVisible = ref(false)
<el-dialog v-model="dialogVisible" title="添加弹层" width="30%">我是内容部分<template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary"> 确认 </el-button> </template> </el-dialog> -
点击事件
<template #extra><el-button type="primary" @click="onAddChannel">添加分类</el-button></template>
const onAddChannel = () => {
dialogVisible.value = true
}
封装弹层组件 ChannelEdit
添加 和 编辑,可以共用一个弹层,所以可以将弹层封装成一个组件
组件对外暴露一个方法 open, 基于 open 的参数,初始化表单数据,并判断区分是添加 还是 编辑
- open({ }) => 添加操作,添加表单初始化无数据
- open({ id: xx, ... }) => 编辑操作,编辑表单初始化需回显
具体实现:
-
封装组件
<script setup> import { ref } from 'vue' const dialogVisible = ref(false)article/components/ChannelEdit.vue
const open = async (row) => {
dialogVisible.value = true
console.log(row)
}defineExpose({
<template> <el-dialog v-model="dialogVisible" title="添加弹层" width="30%">
open
})
</script>我是内容部分<template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary"> 确认 </el-button> </template> </el-dialog> </template> -
通过 ref 绑定
const dialog = ref()
<channel-edit ref="dialog"></channel-edit>
-
点击调用方法显示弹窗
const onAddChannel = () => {
dialog.value.open({})
}
const onEditChannel = (row) => {
dialog.value.open(row)
}
准备弹层表单
-
准备数据 和 校验规则
const formModel = ref({
cate_name: '',
cate_alias: ''
})
const rules = {
cate_name: [
{ required: true, message: '请输入分类名称', trigger: 'blur' },
{
pattern: /^\S{1,10}/, message: '分类名必须是1-10位的非空字符', trigger: 'blur' } ], cate_alias: [ { required: true, message: '请输入分类别名', trigger: 'blur' }, { pattern: /^[a-zA-Z0-9]{1,15}/,
message: '分类别名必须是1-15位的字母数字',
trigger: 'blur'
}
]
} -
准备表单
<el-form
<el-form-item label="分类名称" prop="cate_name"> <el-input v-model="formModel.cate_name" minlength="1" maxlength="10" ></el-input> </el-form-item> <el-form-item label="分类别名" prop="cate_alias"> <el-input v-model="formModel.cate_alias" minlength="1" maxlength="15" ></el-input> </el-form-item> </el-form>
:model="formModel"
:rules="rules"
label-width="100px"
style="padding-right: 30px" -
编辑需要回显,表单数据需要初始化
const open = async (row) => {
dialogVisible.value = true
formModel.value = { ...row }
} -
基于传过来的表单数据,进行标题控制,有 id 的是编辑
:title="formModel.id ? '编辑分类' : '添加分类'"
确认提交
-
api/article.js
封装请求 API// 添加文章分类
export const artAddChannelService = (data) => request.post('/my/cate/add', data)
// 编辑文章分类
export const artEditChannelService = (data) =>
request.put('/my/cate/info', data) -
页面中校验,判断,提交请求
<el-form ref="formRef">const formRef = ref()
const onSubmit = async () => {
await formRef.value.validate()
formModel.value.id
? await artEditChannelService(formModel.value)
: await artAddChannelService(formModel.value)
ElMessage({
type: 'success',
message: formModel.value.id ? '编辑成功' : '添加成功'
})
dialogVisible.value = false
} -
通知父组件进行回显
const emit = defineEmits(['success'])
const onSubmit = async () => {
...
emit('success')
} -
父组件监听 success 事件,进行调用回显
<channel-edit ref="dialog" @success="onSuccess"></channel-edit>
const onSuccess = () => {
getChannelList()
}
文章分类删除
-
api/article.js
封装接口 api// 删除文章分类
export const artDelChannelService = (id) =>
request.delete('/my/cate/del', {
params: { id }
}) -
页面中添加确认框,调用接口进行提示
const onDelChannel = async (row) => {
await ElMessageBox.confirm('你确认删除该分类信息吗?', '温馨提示', {
type: 'warning',
confirmButtonText: '确认',
cancelButtonText: '取消'
})
await artDelChannelService(row.id)
ElMessage({ type: 'success', message: '删除成功' })
getChannelList()
}