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

目录

[首页 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
      <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">
      <el-menu active-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> 文章分类 </el-menu-item> <el-menu-item index="/article/manage"> <el-icon><Promotion /></el-icon> 文章管理 </el-menu-item> <el-sub-menu index="/user"> <template #title> <el-icon><UserFilled /></el-icon> 个人中心 </template> <el-menu-item index="/user/profile"> <el-icon><User /></el-icon> 基本资料 </el-menu-item> <el-menu-item index="/user/avatar"> <el-icon><Crop /></el-icon> 更换头像 </el-menu-item> <el-menu-item index="/user/password"> <el-icon><EditPen /></el-icon> 重置密码 </el-menu-item> </el-sub-menu> </el-menu> </el-aside> <el-container> <el-header>
      黑马程序员:小帅鹏
      <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'
})

用户基本信息获取&渲染

  1. api/user.js封装接口

    export const userGetInfoService = () => request.get('/my/userinfo')

  2. stores/modules/user.js 定义数据

    const user = ref({})
    const getUser = async () => {
    const res = await userGetInfoService() // 请求获取数据
    user.value = res.data.data
    }

  3. layout/LayoutContainer页面中调用

    import { useUserStore } from '@/stores'
    const userStore = useUserStore()
    onMounted(() => {
    userStore.getUser()
    })

  4. 动态渲染

    黑马程序员:{{ userStore.user.nickname || userStore.user.username }}
    <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>
  2. 添加退出功能

    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})
    }
    }

  3. pinia user.js 模块 提供 setUser 方法

    const setUser = (obj) => (user.value = obj)

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

基本架子 - PageContainer

  1. 基本结构样式,用到了 el-card 组件

    <template> <el-card class="page-container"> <template #header>
    文章分类
    <el-button type="primary">添加分类</el-button>
    </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>
  2. 考虑到多个页面复用,封装成组件

    • props 定制标题
    • 默认插槽 default 定制内容主体
    • 具名插槽 extra 定制头部右侧额外的按钮
    <script setup> defineProps({ title: { required: true, type: String } }) </script> <template> <el-card class="page-container"> <template #header>
    {{ title }}
    <slot name="extra"></slot>
    </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>
  3. 页面中直接使用测试 ( 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')

  2. 页面中调用接口,获取数据存储

    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 效果
  1. 定义变量,v-loading绑定

    const loading = ref(false)

    <el-table v-loading="loading">
  2. 发送请求前开启,请求结束关闭

    const getChannelList = async () => {
    loading.value = true
    const res = await artGetChannelsService()
    channelList.value = res.data.data
    loading.value = false
    }

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

点击显示弹层
  1. 准备弹层

    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>
  2. 点击事件

    <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 = true
    console.log(row)
    }

    defineExpose({
    open
    })
    </script>

    <template> <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>
  2. 通过 ref 绑定

    const dialog = ref()

    <channel-edit ref="dialog"></channel-edit>

  3. 点击调用方法显示弹窗

    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'
    }
    ]
    }

  2. 准备表单

    <el-form
    :model="formModel"
    :rules="rules"
    label-width="100px"
    style="padding-right: 30px"

    <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>
  3. 编辑需要回显,表单数据需要初始化

    const open = async (row) => {
    dialogVisible.value = true
    formModel.value = { ...row }
    }

  4. 基于传过来的表单数据,进行标题控制,有 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)

  2. 页面中校验,判断,提交请求

    <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
    }

  3. 通知父组件进行回显

    const emit = defineEmits(['success'])

    const onSubmit = async () => {
    ...
    emit('success')
    }

  4. 父组件监听 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 }
    })

  2. 页面中添加确认框,调用接口进行提示

    const onDelChannel = async (row) => {
    await ElMessageBox.confirm('你确认删除该分类信息吗?', '温馨提示', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消'
    })
    await artDelChannelService(row.id)
    ElMessage({ type: 'success', message: '删除成功' })
    getChannelList()
    }

相关推荐
Jiaberrr1 小时前
微信小程序中巧妙使用 wx:if 和 catchtouchmove 实现弹窗禁止页面滑动功能
前端·javascript·微信小程序·小程序·uni-app
Jiaberrr1 小时前
如何在微信小程序中实现WebSocket连接
前端·javascript·websocket·微信小程序·小程序
战族狼魂3 小时前
使用vue2+axios+chart.js画折线图 ,出现 RangeError: Maximum call stack size exceeded 错误
前端·javascript·vue.js
周bro5 小时前
vue3使用panolens.js实现全景,带有上一个下一个,全屏功能
开发语言·javascript·ecmascript
晓风伴月6 小时前
Vue:使用v-model绑定的textarea在光标处插入指定文本
vue.js
ZhangTao_zata7 小时前
前端知识点
前端·javascript·css
程序猿大波7 小时前
基于Java、SpringBoot、Vue的加油站管理系统设计
java·vue.js·spring boot
Lee_Yu_Fan7 小时前
vue项目如何在js文件中导入assets文件夹下图片
前端·vue.js
秋沐7 小时前
Vue3流程图插件-Vue Flow
前端·vue.js·流程图
卿卿qing7 小时前
【React Native】路由和导航
javascript·react native·react.js