vue3大事件管理系统 === 首页 layout 文章分类页面 -

news/2024/9/25 15:17:34/

目录

首页 layout 架子 [element-plus 菜单]

基本架子拆解

登录访问拦截

用户基本信息获取&渲染

退出功能 [element-plus 确认框]

文章分类页面 - [element-plus 表格]

基本架子 - PageContainer

文章分类渲染

封装API - 请求获取表格数据

el-table 表格动态渲染

el-table 表格 loading 效果

文章分类添加编辑 [element-plus 弹层]

点击显示弹层

封装弹层组件 ChannelEdit

准备弹层表单

确认提交

文章分类删除


首页 layout 架子 [element-plus 菜单]

基本架子拆解

架子组件列表:

el-container

  • el-aside 左侧

    • el-menu 左侧边栏菜单
  • el-container 右侧

    • el-header 右侧头部
      • el-dropdown
    • el-main 右侧主体
      • router-view
<script setup>
import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom
} from '@element-plus/icons-vue'
import avatar from '@/assets/default.png'
</script><template><el-container class="layout-container"><el-aside width="200px"><div class="el-aside__logo"></div><el-menuactive-text-color="#ffd04b"background-color="#232323":default-active="$route.path"text-color="#fff"router><el-menu-item index="/article/channel"><el-icon><Management /></el-icon><span>文章分类</span></el-menu-item><el-menu-item index="/article/manage"><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu index="/user"><template #title><el-icon><UserFilled /></el-icon><span>个人中心</span></template><el-menu-item index="/user/profile"><el-icon><User /></el-icon><span>基本资料</span></el-menu-item><el-menu-item index="/user/avatar"><el-icon><Crop /></el-icon><span>更换头像</span></el-menu-item><el-menu-item index="/user/password"><el-icon><EditPen /></el-icon><span>重置密码</span></el-menu-item></el-sub-menu></el-menu></el-aside><el-container><el-header><div>黑马程序员:<strong>小帅鹏</strong></div><el-dropdown placement="bottom-end"><span class="el-dropdown__box"><el-avatar :src="avatar" /><el-icon><CaretBottom /></el-icon></span><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'
})

用户基本信息获取&渲染

  1. api/user.js封装接口
export const userGetInfoService = () => request.get('/my/userinfo')
  1. stores/modules/user.js 定义数据
const user = ref({})
const getUser = async () => {const res = await userGetInfoService() // 请求获取数据user.value = res.data.data
}
  1. layout/LayoutContainer页面中调用
import { useUserStore } from '@/stores'
const userStore = useUserStore()
onMounted(() => {userStore.getUser()
})
  1. 动态渲染
<div>黑马程序员:<strong>{{ userStore.user.nickname || userStore.user.username }}</strong>
</div><el-avatar :src="userStore.user.user_pic || avatar" />

退出功能 [element-plus 确认框]

  1. 注册点击事件
<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>
  1. 添加退出功能
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}`)}
}
  1. pinia user.js 模块 提供 setUser 方法
const setUser = (obj) => (user.value = obj)

文章分类页面 - [element-plus 表格]

基本架子 - PageContainer

  1. 基本结构样式,用到了 el-card 组件
<template><el-card class="page-container"><template #header><div class="header"><span>文章分类</span><div class="extra"><el-button type="primary">添加分类</el-button></div></div></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>
  1. 考虑到多个页面复用,封装成组件
    • props 定制标题
    • 默认插槽 default 定制内容主体
    • 具名插槽 extra 定制头部右侧额外的按钮
<script setup>
defineProps({title: {required: true,type: String}
})
</script><template><el-card class="page-container"><template #header><div class="header"><span>{{ title }}</span><div class="extra"><slot name="extra"></slot></div></div></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>
  1. 页面中直接使用测试 ( unplugin-vue-components 会自动注册)
  • 文章分类测试:
<template><page-container title="文章分类"><template #extra><el-button type="primary"> 添加分类 </el-button></template>主体部分</page-container>
</template>
  • 文章管理测试:
<template><page-container title="文章管理"><template #extra><el-button type="primary">发布文章</el-button></template>主体部分</page-container>
</template>

文章分类渲染

封装API - 请求获取表格数据
  1. 新建 api/article.js 封装获取频道列表的接口
import request from '@/utils/request'
export const artGetChannelsService = () => request.get('/my/cate/list')
  1. 页面中调用接口,获取数据存储
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"circleplaintype="primary"@click="onEditChannel(row)"></el-button><el-button:icon="Delete"circleplaintype="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 效果
  1. 定义变量,v-loading绑定
const loading = ref(false)<el-table v-loading="loading">
  1. 发送请求前开启,请求结束关闭
const getChannelList = async () => {loading.value = trueconst res = await artGetChannelsService()channelList.value = res.data.dataloading.value = false
}

文章分类添加编辑 [element-plus 弹层]

点击显示弹层
  1. 准备弹层
const dialogVisible = ref(false)<el-dialog v-model="dialogVisible" title="添加弹层" width="30%"><div>我是内容部分</div><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary"> 确认 </el-button></span></template>
</el-dialog>
  1. 点击事件
<template #extra><el-button type="primary" @click="onAddChannel">添加分类</el-button></template>const onAddChannel = () => {dialogVisible.value = true
}
封装弹层组件 ChannelEdit

添加 和 编辑,可以共用一个弹层,所以可以将弹层封装成一个组件

组件对外暴露一个方法 open, 基于 open 的参数,初始化表单数据,并判断区分是添加 还是 编辑

  1. open({ }) => 添加操作,添加表单初始化无数据
  2. open({ id: xx, … }) => 编辑操作,编辑表单初始化需回显

具体实现:

  1. 封装组件 article/components/ChannelEdit.vue
<script setup>
import { ref } from 'vue'
const dialogVisible = ref(false)const open = async (row) => {dialogVisible.value = trueconsole.log(row)
}defineExpose({open
})
</script><template><el-dialog v-model="dialogVisible" title="添加弹层" width="30%"><div>我是内容部分</div><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary"> 确认 </el-button></span></template></el-dialog>
</template>
  1. 通过 ref 绑定
const dialog = ref()<!-- 弹窗 -->
<channel-edit ref="dialog"></channel-edit>
  1. 点击调用方法显示弹窗
const onAddChannel = () => {dialog.value.open({})
}
const onEditChannel = (row) => {dialog.value.open(row)
}
准备弹层表单
  1. 准备数据 和 校验规则
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'}]
}
  1. 准备表单
<el-form:model="formModel":rules="rules"label-width="100px"style="padding-right: 30px"
><el-form-item label="分类名称" prop="cate_name"><el-inputv-model="formModel.cate_name"minlength="1"maxlength="10"></el-input></el-form-item><el-form-item label="分类别名" prop="cate_alias"><el-inputv-model="formModel.cate_alias"minlength="1"maxlength="15"></el-input></el-form-item>
</el-form>
  1. 编辑需要回显,表单数据需要初始化
const open = async (row) => {dialogVisible.value = trueformModel.value = { ...row }
}
  1. 基于传过来的表单数据,进行标题控制,有 id 的是编辑
:title="formModel.id ? '编辑分类' : '添加分类'"
确认提交
  1. api/article.js 封装请求 API
// 添加文章分类
export const artAddChannelService = (data) => request.post('/my/cate/add', data)
// 编辑文章分类
export const artEditChannelService = (data) =>request.put('/my/cate/info', data)
  1. 页面中校验,判断,提交请求
<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
}
  1. 通知父组件进行回显
const emit = defineEmits(['success'])const onSubmit = async () => {...emit('success')
}
  1. 父组件监听 success 事件,进行调用回显
<channel-edit ref="dialog" @success="onSuccess"></channel-edit>const onSuccess = () => {getChannelList()
}

文章分类删除

  1. api/article.js封装接口 api
// 删除文章分类
export const artDelChannelService = (id) =>request.delete('/my/cate/del', {params: { id }})
  1. 页面中添加确认框,调用接口进行提示
const onDelChannel = async (row) => {await ElMessageBox.confirm('你确认删除该分类信息吗?', '温馨提示', {type: 'warning',confirmButtonText: '确认',cancelButtonText: '取消'})await artDelChannelService(row.id)ElMessage({ type: 'success', message: '删除成功' })getChannelList()
}


http://www.ppmy.cn/news/1499833.html

相关文章

Android11 framework 禁止三方应用通过广播开机自启动-独立方案

之前的文章Android11 framework 禁止三方应用开机自启动记录了我调试Android11应用自启动限制的全过程&#xff0c;但是之前的方案感觉还能再研究&#xff0c;所以有了这一篇文章。 这一篇文章主要探讨Android11上&#xff0c;以广播来进行自启动的应用的限制&#xff0c;极个别…

探索Linux-1-虚拟机远程登陆XShell6远程传输文件Xftp6

Linux是什么&#xff1f; Linux是一个开源的操作系统内核&#xff0c;由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布。它基于Unix操作系统&#xff0c;但提供了更多的自由和灵活性。Linux内核是操作系统的核心部分&#xff0c;负责管理系统资源、处理…

C++ 鼠标轨迹API【神诺科技SDK】

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;使得神诺科技 能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.…

Redis+Lua脚本+AOP+反射+自定义注解,打造我司内部基础架构限流组件

定义注解 Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) Documented public interface RedisLimitAnnotation {/*** 资源的key,唯一* 作用&#xff1a;不同的接口&#xff0c;不同的流量控制*/String key() default "";/*** 最多的访问限制次数…

计算机网络基础:1.上网设备与流程、OSI七层模型、TCP/IP五层模型

你正在经营一家繁忙的餐厅&#xff0c;顾客们点餐并期待着美味的食物。我们可以将网络的各个层次和设备比作餐厅的不同部分。 一、上网设备 网卡&#xff1a;就像是餐厅的点餐系统&#xff0c;顾客通过它来下单&#xff0c;而厨房通过它来接收订单。上网设备必须有网卡&#x…

基于 HTML+ECharts 实现智慧工地数据可视化大屏(含源码)

构建智慧工地数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 智慧工地已成为建筑行业的新趋势。通过实时监控和数据分析&#xff0c;智慧工地可以提高施工效率、降低安全风险。本文将详细介绍如何利用 HTML 和 ECharts 实现一个功能强大的智慧工地数据可视化大屏。 源…

学习记录--Bert、Albert、RoBerta

目录 Bert 1&#xff1a;输入 2&#xff1a;Bert结构 3&#xff1a;模型预训练 Albert 1&#xff1a;SOP任务 2&#xff1a;embedding因式分解 3&#xff1a;参数共享 RoBerta 参考&#xff1a; BERT原理和结构详解_bert结构-CSDN博客 [LLM] 自然语言处理 --- ALBER…

跨境电商独立站:Shopify/Wordpress/店匠选哪个?

在面对不断增加的平台运营压力时&#xff0c;不少跨境电商的商家逐渐将注意力转向建立自己的独立站。据《中国跨境出口电商发展报告&#xff08;2022&#xff09;》所示&#xff0c;中国拥有的独立站数量在2022年已接近20万个&#xff0c;这表明独立站已成为卖家拓展海外市场的…