前后端实战项目(Spring系列加上vue3)前后端篇(五)(解决TypeError: Cannot read properties of undefined (reading ‘push‘报错问题)

继之前的前后端代码之后,下面来最后完善一下登录以及用户模块的剩下部分

之前是将登录后得到的JWT直接放到前端带啊吗后运行的,这里要想不手动添加,可以使用Pinia状态管理库,(用于跨组件或页面共享状态)

来看看Pinia的的使用方法:

1.安装pinia:

npm install pinia

npm install pinia-persistedstate-plugin(这里再安装一个persist(pinia的持久化插件))

使用Pinia

在main.js中,引入pinia,创建pinia实例,并调用vue应用实例的use方法使用pinia

import { createPinia } from 'pinia'

const pinia = createPinia()
app.use(pinia)

定义Store

在src/stores目录下定义token.js

import { defineStore } from "pinia";
import {ref} from 'vue';

/*
    defineStore参数描述:
        第一个参数:给状态起名,具有唯一性
        第二个参数:函数,可以把定义该状态中拥有的内容

    defineStore返回值描述:
        返回的是一个函数,将来可以调用该函数,得到第二个参数中返回的内容
*/
export const useTokenStore = defineStore('token',()=>{
    //1.定义描述token
    const token = ref('')

    //2.定义修改token的方法
    const setToken = (newToken)=>{
        token.value = newToken
    }

    //3.定义移除token的方法
    const removeToken = ()=>{
        token.value=''
    }
    return {
        token,setToken,removeToken
    }
})

使用Store

//导入token状态
import { useTokenStore } from '@/stores/token.js'

//调用useTokenStore得到状态
const tokenStore = useTokenStore();

//用于登录的事件函数
const login = async () => {
    let result = await loginService(registerData.value)
    //保存token
    tokenStore.setToken(result.data)
    
    ElMessage.success('登录成功!')
    router.push('/')
}

//导入@/stores/token.js
import { useTokenStore } from '../stores/token'


//文章分类列表查询
export const articleCategoryListService = () => {
    //获取token状态
    const tokenStore = useTokenStore()
    //通过请求头Authorization携带token
    return request.get('/category', { headers: { 'Authorization': tokenStore.token } })
}
pinia中使用persist插件

在main.js中
import { createPinia } from 'pinia'
//导入持久化插件
import {createPersistedState} from'pinia-persistedstate-plugin'
const pinia = createPinia()
const persist = createPersistedState()
//pinia使用持久化插件
pinia.use(persis

OK,来试试:

大部分信息都渲染到前端这里了(这里有几个属性没有显示出来,因为前后端属性名有一些区别,这里需要注意一下)

修改一下就OK了

然后开始做修改用户信息接口

    @PostMapping("/user/update")
    @ApiOperation(value ="用户信息更新接口")
    public Result<User> update(@RequestBody @Validated User user) {
        System.out.println("用户信息更新");
        Result<User> result = userService.updateInfo(user);
        return result;
    }
    @Override
    public Result<User> updateInfo(User user) {
        User userDO = userMapper.findByUsername(user.getUserName());
        if(userDO.getUserName()==null){
            return Result.error("用户名信息错误");
        }
        user.setId(userDO.getId());
        boolean isUpdate = userMapper.update(user);
        if(!isUpdate){
            return Result.error("更新失败");
        }

加上动态SQL

<mapper namespace="org.example.cetidenet.dao.UserMapper">
    <update id="update">
        update user
            <set>
                <if test="fullName!=null">
                    fullName=#{fullName},
                </if>
                <if test="email!=null">
                    email=#{email},
                </if>
                <if test="avatar!=null">
                    avatar=#{avatar},
                </if>
                <if test="phone!=null">
                    phone = #{phone},
                </if>
                <if test="address!=null">
                    address = #{address},
                </if>
                <if test="country!=null">
                    country = #{country},
                </if>
                <if test="birthday!=null">
                    birthday= #{birthday},
                </if>
                <if test="gender!=null">
                    gender = #{gender},
                </if>
                <if test="bio!=null">
                    bio = #{bio},
                </if>
            </set>
        where id = #{id}
    </update>
</mapper>

这样,后端的用户更新操作就好了,下面看前端

user.js中定义方法

export const userupdateService = (userNum) =>{
    return request.post('/user/update',userNum)
}

最后在用户中心这里使用就可以了。

//更新用户信息
const userUpdate = async()=>{
  let result = await userupdateService(userNum.value);
  console.log(result);
}
<!-- 编辑个人信息的抽屉 -->
    <a-drawer
      :visible="visible"
      :width="500"
      @ok="handleOk"
      @cancel="handleCancel"
      unmountOnClose
      @click="userUpdate"
    >
      <template #title> 编辑个人信息 </template>
      <div :style="{ marginBottom: '20px' }">
        <div >
          <img :src="userNum.userImg" width="70px" height="70px" class="user-img"/>
      <a-button type="primary" @click="handleNestedClick" style="float: right;margin-top: 20px"
        >更换头像</a-button
      >
        </div>
            <a-divider />
        <div> 昵称:<a-input :style="{width:'320px'}" v-model="userNum.fullName" /></div>
            <a-divider />
        <div> 用户名:<a-input :style="{width:'320px'}" allow-clear v-model="userNum.userName" disabled/></div>
            <a-divider />
        <div> 性别:<a-input :style="{width:'320px'}" v-model="numSex" /></div>
            <a-divider />
        <div> 电话:<a-input :style="{width:'320px'}" v-model="userNum.phone"/></div>
            <a-divider />
        <div> 生日:<a-input :style="{width:'320px'}" v-model="userNum.birthday" /></div>
            <a-divider />
        <div> 城市:<a-input :style="{width:'320px'}" v-model="userNum.country" /></div>
            <a-divider />
        <div> 住址:<a-input :style="{width:'320px'}" v-model="userNum.address" /></div>
            <a-divider />
        <div> CeTide网ID:<a-input :style="{width:'320px'}" v-model="userNum.id" disabled/></div>
            <a-divider />
        <div> 个人简介: <a-textarea v-model="userNum.bio" allow-clear style="height: 100px"/></div>
      </div>

    </a-drawer>
    <a-drawer
      :visible="nestedVisible"
      @ok="handleNestedOk"
      @cancel="handleNestedCancel"
      unmountOnClose
    >

再绑定事件就可以了。

前端添加未登录的统一处理:

补充响应拦截器内容

在后端响应401时,警告未登录并跳转到登录页

import { useRouter } from "vue-router";
const router = useRouter();

instance.interceptors.response.use(
    result => {
        if (result.data.code == 1) {
            return result.data;
        }
        // 如果为0则表示失败
        console.log(result.data.message || '出现错误');
        return Promise.reject('err'); // 异步的状态转化成失败的状态
    },
    err => {
        // 如果响应状态码时401,代表未登录,给出对应的提示并跳转到登录页
        if (err.response.status === 401) {
            alert('请先登录!');
            router.push('/login');
        } else {
            console.log('服务异常');
            return Promise.reject(err); // 异步的状态转化成失败的状态
        }
    }
);

这是按照之前的逻辑进行的一个跳转,但是实际上,这么做却不能实现跳转:

浏览器点击F12,就会发现

  • 拦截器里的router.push('/') 一直报错TypeError: Cannot read properties of undefined (reading 'push') ,这就很难受,搞了半天,才知道是响应拦截器中无法使用Router

那么之前的方法无法实现页面的跳转,那如何实现呢,这里需要改变定义方法:

用import router from '@/router'

import router from '@/router'
//来一个响应拦截器
instance.interceptors.response.use(
    result => {
        if (result.data.code == 1) {
            return result.data;
        }
        // 如果为0则表示失败
        console.log(result.data.message || '出现错误');
        return Promise.reject('err'); // 异步的状态转化成失败的状态
    },
    err => {
        // 如果响应状态码时401,代表未登录,给出对应的提示并跳转到登录页
        if (err.response.status === 401) {
            alert('请先登录!');
            router.push('/login');
        } else {
            console.log('服务异常');
            return Promise.reject(err); // 异步的状态转化成失败的状态
        }
    }
);

注释或删掉之前的定义方法,此处使用import router from '@/router'即可做到(主要是模块加载的顺序问题,使用之前的方式是无效的)

那么到现在,项目的最基础的crud部分算是解决了,这一部分就结束了,后面就不这样搭建了,而是根据模块,配合其他技术来实现(也不能只是crud,hhh)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值