我正在参加「掘金·启航计划」
一、说明
本次记录的是Go Vue Blog
项目中,独立完成一个“用户密码重置”模块的编写流程。包括
- 后端 - 对应
router
添加 - 后端 - 对应
API
设计 - 后端 - 对应
Model
编写 - 前端 - 页面组件展示
- 前端 - 相关数据结构、校验规则定义。其中数据模型需要新定义,而校验规则可以复用。
- 前端 - 相关
JS
事件编写
二、需求分析
三、编写流程
大体流程:先写后端,接口测试通过,再写前端。
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)组件间传递数据:
<!--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"
}