从页面到后端 - 完整记录 Vue 2 一个功能模块的编写流程

47 阅读3分钟

我正在参加「掘金·启航计划」

一、说明

本次记录的是Go Vue Blog 项目中,独立完成一个“用户密码重置”模块的编写流程。包括

  1. 后端 - 对应router 添加
  2. 后端 - 对应API设计
  3. 后端 - 对应Model编写
  4. 前端 - 页面组件展示
  5. 前端 - 相关数据结构、校验规则定义。其中数据模型需要新定义,而校验规则可以复用。
  6. 前端 - 相关JS事件编写

二、需求分析

“重置密码”功能模块全流程.png

三、编写流程

大体流程:先写后端,接口测试通过,再写前端。

1.添加router

//  routers/router.go
auth := r.Group("api/v1")
auth.Use(middleware.JwtToken())
{
...
auth.PUT("user/resetpw/:id", v1.ResetPass)  
...
}

根据传入的id确定修改哪条数据库记录的密码。

2.编写API层的HandlerFunc

// ResetPass 重置用户密码  调用Model层函数并反馈JSON
func ResetPass(c *gin.Context) {
   id, _ := strconv.Atoi(c.Param("id"))
   var user model.User

   c.ShouldBindJSON(&user)

   code := model.ResetPass(id, &user)

   c.JSON(http.StatusOK, gin.H{
      "status":  code,
      "message": errmsg.GetErrMsg(code),
   })
}

3. 编写Model层函数,操作数据库并返回错误码

#原生SQL
UPDATE user SET password = newPassword
WHERE ID = id
// ResetPass 重置用户密码
func ResetPass(id int, u *User) int {
   EncryptedPassword := ScryptPw(u.Password)//将原始密码再次加密
   err := db.Model(&u).Where("id = ?", id).Update("password", EncryptedPassword).Error
   if err != nil {
      return errmsg.ERROR_PASSWORD_WRONG
   }
   return errmsg.SUCCESS
}

以上接口已经被API工具测试通过,开始前端页面编写

4. <template>中设置“重置密码”按钮

按钮放在表单的这个位置(下图),
需要用slot / slot-scoped 在父(UserList.vue)组件和子(a-table)组件间传递数据: image.png

<!--UserList.vue-->

      <a-table :columns="columns">
        <!--↓↓↓范围内是父组件 => 子组件的数据,父组件应该用slot表明数据要传入子组件的哪个部分;而子组件<a-table>早已用columns[]所定义的key指定具名插槽↓↓↓------------------------------------------------->
        
        <!--1.【组件传值:父→子】“管理员”/“订阅者” 这两个数据是属于 父组件<a-table> 的,想传给子组件其中一列<Col>显示。此处使用具名插槽 slot 和子组件<Col>的 scopedSlots 所声明的具名插槽值一致 -->
        <!--2.【组件传值:子→父】当前行的role值来自子组件<Col>,想传给 父组件<a-table>(用于验证)。使用slot-scoped 在父组件<a-table>调用时产生一个接收数据的接口 -->
        <span slot="role" slot-scope="role">{{role==1?'管理员':'订阅者'</span>

        <!--1.【组件传值:父→子】父组件:<a-table>;子组件:<Col>。在两处约定具名插槽 "action" ,将<template>内的 3 个按钮元素传到“操作”一列-->
        <!--2.【组件传值:子→父】子组件<Row> 返回给父组件 <a-table> 的返回数据,父组件 <a-table>用自定义的 data 变量名接收-->


        <template slot="action"  slot-scope="data">
          <div>
            <a-button @click="editUser(data)">编辑</a-button>
            <a-button @click="deleteConfirm(data.ID)">删除</a-button>
            <a-button @click="resetPassword(data)">重置密码</a-button>
          </div>
        </template>
      </a-table>

下列代码中,columns用于表格定义列信息。
使用action具名插槽,父组件<a-table>将<template>内的数据传入<Col>子组件中去

<!--Table 通过绑定 :columns 来定义每行数据-->
const columns = [
  ...
  {
    title: "操作",
    width: "30%",
    align: "center",
    key: "action",
    scopedSlots: { customRender: "action" },
  },
];

5.相关数据结构和校验规则

    //隐藏/浮现重置密码弹框
    resetPassVisible: false, 

    //重置用户密码弹框绑定的数据对象
      resetPass: {
        id: 0,
        username: "",
        password: "",
        checkPass: "",
      },
    //重置密码弹框表单验证
      resetPassRules: {
        password: [{ validator: validatePass, trigger: "change" }],
        checkPass: [{ validator: validateResetPass2, trigger: "change" }],
      },

6.相关JS事件编写

    // 重置密码按钮
    resetPassword(data) {
      1.将 data 导入v-model数据模型
      不需要传来旧密码
      2.打开弹框
    },
    handleResetPassOK() {
        1.密码安全校验(配合 resetPassRules 使用)
        2.获取数据向后端发送 PUT 请求
        3.消息提示和清理工作
        ...
      });
    },
    handleResetPassCancel() {
      this.resetPassVisible = false;
      this.$refs.resetPassRef.resetFields();
      this.$message.info("已取消密码重置");
    },

Bug分析

1.后端绑定JSON 失败。

传到后端的数据类型必须和Model 定义保持一致。User类的Password

Username string `gorm:"type:varchar(20);not null" json:"username" validate:"required,min=4,max=12" label:"用户名"`

而传入的JSON 一开始写成了一个 Number 类型:

{
    "password":222222
}

这就是为什么在API 层对应函数试图绑定JSON时没绑定到值。 把密码的数据类型改为正确的String类型即可。

{
    "password":"222222"
}