Vue3大事件项目2

本文介绍了在Vue应用中如何实现登录访问控制,使用Vuex存储用户信息,动态渲染用户信息以及构建可复用的页面组件,包括文章分类和弹层组件的开发与调用。
摘要由CSDN通过智能技术生成

首页 layout 架子

登录访问拦截(router/index.js)

只有登录页,可以未授权的时候访问,其他所有页面,都需要先登录再访问

// 登录访问拦截 => 默认是直接放行的
// 根据返回值决定,是放行还是拦截
// 返回值:
// 1. undefined / true  直接放行
// 2. false 拦回from的地址页面
// 3. 具体路径 或 路径对象  拦截到对应的地址
//    '/login'   { name: 'login' }
router.beforeEach((to) => {
  // 如果没有token, 且访问的是非登录页,拦截到登录,其他情况正常放行
  const useStore =  useUserStore()
  if(!useStore.token && to.path !== '/login') return '/login'
})

获取用户信息

1.封装接口(api/user.js)

//获取用户基本信息接口get
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
    }

    return { token, setToken, removeToken, user, getUser }
  },

3.layout/LayoutContainer页面中调用

import { onMounted } from 'vue'
const userStore = useUserStore()
onMounted(() => {
  userStore.getUser()
})

页面请求成功

4.动态渲染

<div>
  黑马程序员:<strong>{{ userStore.user.nickname || userStore.user.username }}</strong>
</div>

<el-avatar :src="userStore.user.user_pic || avatar" />

退出功能

1.注册点击事件

通过@command监听菜单选择,每个菜单栏里都有一个·command标识,点击某一个选项之后会有一个形参,基于形参做判断,logout就退出,其他的就路由跳转

<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.添加退出功能

import { useUserStore } from '@/stores'
import { useRouter } from 'vue-router'
const userStore = useUserStore()
const router = useRouter()
const handleCommand = async (key) => {
  if (key === 'logout') {
    //退出操作
    await ElMessageBox.confirm('Are you sure', '温馨提示', {
      type: 'warning',
      confirmButtonText: '确认',
      cancelButtonText: '取消'
    })

    //清除本地的数据(token + user信息)
    userStore.removeToken()
    userStore.setUser({})
    router.push('/login')
  } else {
    router.push('/user/${key}')
  }
}

文章分类和文章管理的架子搭建

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>

2.因为多个页面复用,封装成组件

  • props 定制标题

  • 默认插槽 default 定制内容主体

  • 具名插槽 extra 定制头部右侧额外的按钮

  •   使用时直接引入并且更改需要改的内容即可

    <script setup>
    //父传子
    
    //在vue3的setup中,我们使用defineProps来定义父组件传递的props
    //通过使用defineProps,我们可以明确地声明组件所接受的props
    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>
    

    3.页面中直接使用测试 ( unplugin-vue-components 会自动注册)

文章分类测试代码:

<script setup></script>

<template>
  <page-container title="添加分类">
    <template #extra>
      <el-button>测试按钮</el-button>
    </template>

    主体部分,是表格 + del
  </page-container>
</template>

<style lang="scss" scoped></style>

文章管理测试代码:

<script setup></script>

<template>
  <page-container title="文章分类">
    <template #extra>
      <el-button>测试按钮</el-button>
    </template>

    主体部分,是表格
  </page-container>
</template>

<style lang="scss" scoped></style>

文章分类渲染

1.新建 api/article.js 封装获取频道列表的接口get

//获取文章分类表格数据
//封装接口
import request from '@/utils/request'

export const artgetChannelsService = () => request.get('/my/userinfo', {})

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

import { artgetChannelsService } from '../../api/article'
const channelList = ref([])

const getChannelList = async () => {
  const res = await artgetChannelsService()
  channelList.value = res.data.data
  console.log(channelList.value)
}
//一进页面就调用
getChannelList()

el-table 表格动态渲染

定义了channelList,用来接收表格的数据;定义loading变量,用来控制加载动态的实现;getChannelList()用来发请求; onEditChannel()和 onDelChannel()是执行编辑和删除按钮逻辑的函数

<script setup>
import { ref } from 'vue'
import { Edit, Delete } from '@element-plus/icons-vue'
import { artgetChannelsService } from '../../api/article'
const channelList = ref([])
const loading = ref(false)
const onEditChannel = (row, $index) => {
  console.log(row, $index)
}
const onDelChannel = (row, $index) => {
  console.log(row, $index)
}
const getChannelList = async () => {
  //发送请求时开启
  loading.value = true
  const res = await artgetChannelsService()
  channelList.value = res.data.data
  //请求结束后开启
  loading.value = false

  console.log(channelList.value)
}
//一进页面就调用
getChannelList()
</script>
 <el-table v-loading="loading" :data="channelList" style="width: 100%">
      <el-table-column type="index" label="序号" width="100"></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="150">
        <!--自定义按钮 默认插槽-->
        <!--row就是channelList的一项,$index下标-->
        <!--从obj中解构出row 当前行 和 $index 当前行下标-->
        <template #default="{ row, $index }">
          <el-button
            :icon="Edit"
            circle
            plain
            type="primary"
            @click="onEditChannel(row, $index)"
          ></el-button>
          <el-button
            :icon="Delete"
            circle
            plain
            type="danger"
            @click="onDelChannel(row, $index)"
          ></el-button>
        </template>
      </el-table-column>

      <template #empty>
        <el-empty description="无"></el-empty>
      </template>
    </el-table>
  </page-container>
</template>

封装弹层组件 ChannelEdit

添加 和 编辑,可以共用一个弹层,所以可以将弹层封装成一个组件,在article下新建components文件夹,新建 ChannelEdit.vue以封装弹层。

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

  1. open({ }) => 添加操作,添加表单初始化无数据

  2. open({ id: xx, ... }) => 编辑操作,编辑表单

  3. 初始化需回显open调用后,可以打开弹窗

  4.  v-model="dialogVisible": v-model与 dialogVisible绑定。

  5. const dialogVisible = ref(false)  设置dialogVisible变量,默认为false

  6. const open = (row) => {dialogVisible.value = true} 设置open方法,控制弹框出现消失和接收参数

  7. 向外暴露,方便父组件调用方法:

    defineExpose({ open})

<script setup>
import { ref } from 'vue'
const dialogVisible = ref(false)
//组件对外暴露一个方法open,基于open传来的参数,区分是添加还是编辑
//open({}) => 表单没有渲染,说明是添加
//open({id,cate_name,...}) => 表单需要渲染,说明是编辑
//open调用后,可以打开弹窗
const open = (row) => {
  dialogVisible.value = true
  // formModel.value = { ...row }
}

//向外暴露
defineExpose({
  open
})
</script>
<template>
  <el-dialog
    v-model="dialogVisible"
    title="add popup"
    width="500"
    :before-close="handleClose"
  >
    <div>comtent</div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="dialogVisible = false">Cancel</el-button>
        <el-button type="primary" @click="dialogVisible = false">
          Confirm
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

8.在父组件中调用:    <channel-edit ref="dialog"></channel-edit>,通过ref与 dialog变量绑定

 <template #empty>
        <el-empty description="无"></el-empty>
      </template>
    </el-table>
    <channel-edit ref="dialog"></channel-edit>
  </page-container>
</template>

9.点击调用方法显示弹窗(基于传参,判断是添加还是编辑)

   dialog.value.open({}):拿到组件,调用open方法

const onAddChannel = () => {
  dialog.value.open({})
}
const onEditChannel = (row) => {
  dialog.value.open(row)
}

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值