08-个人中心
1.1-基本资料
(1)页面布局
<template>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>基本资料</span>
</div>
<!-- 表单 -->
</el-card>
</template>
<script>
export default {
name: 'UserInfo'
}
</script>
<style lang="less" scoped>
.el-form {
width: 500px;
}
</style>
(2)el-form搭建表单
<!-- 表单 -->
<el-form label-width="100px">
<el-form-item label="登录名称" >
<el-input disabled></el-input>
</el-form-item>
<el-form-item label="用户昵称" >
<el-input minlength="1" maxlength="10"></el-input>
</el-form-item>
<el-form-item label="用户邮箱" >
<el-input ></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary">提交修改</el-button>
<el-button>重置</el-button>
</el-form-item>
</el-form>
(3)v-model双向绑定表单数据
-
user : this.$store.state.userInfo
-
这种写法是拷贝 vuex中userInfo的地址, 那么一旦修改user的数据, 本质还是修改vuex的数据。(而vuex中的数据是不允许在组件中直接修改的)
本质是拷贝地址
-
-
user : { ...this.$store.state.userInfo }
-
这种写法并没有拷贝vuex中userInfo的地址, 而是单独声明一个空对象, 然后把vuex中的数据给拷贝进去。相当于下面这种写法.
本质是拷贝数据,没有拷贝地址-
user = { username : this.$store.state.userInfo.username, nickname : this.$store.state.userInfo.nickname, email : this.$store.state.userInfo.email, }
-
(4)el-form实现表单校验
-
1.data(){}中定义校验规则
-
// 表单的验证规则对象 userRules: { nickname: [ { required: true, message: '请输入用户昵称', trigger: 'blur' }, { pattern: /^\S{1,10}$/, message: '昵称必须是1-10位的非空字符串', trigger: 'blur' } ], email: [ { required: true, message: '请输入用户邮箱', trigger: 'blur' }, { type: 'email', message: '邮箱格式不正确', trigger: 'blur' } ] } -
2.在模板中配置校验规则
- 给表单设置
rules属性传入验证规则 - 给表单设置
model属性传入表单数据 - 给表单项(Form-Item )设置
prop属性,其值为设置为需校验的字段名
- 给表单设置
(5)完成修改基本资料
methods: {
async doSubmit() {
// 1. 验证表单是否全部校验通过
this.$refs.userFormRef.validate(async valid => {
if( valid ){
// 2. 验证通过,发起请求
const { data: res } = await this.$axios.put("/my/userinfo",this.user)
if( res.code === 0 ){
//弹窗提示
this.$message.success("更新用户信息成功!")
//提交vuex的actions更新用户信息
this.$store.dispatch("initUserInfo")
}else{
this.$message.error("更新用户信息失败!")
}
}
})
}
}
完整代码
<template>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>基本资料</span>
</div>
<!-- 表单 -->
<el-form
label-width="100px"
:model="user"
:rules="userRules"
ref="userFormRef"
>
<el-form-item label="登录名称">
<el-input disabled v-model="user.username"></el-input>
</el-form-item>
<el-form-item label="用户昵称" prop="nickname">
<el-input
minlength="1"
maxlength="10"
v-model="user.nickname"
></el-input>
</el-form-item>
<el-form-item label="用户邮箱" prop="email">
<el-input v-model="user.email"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="doSubmit">提交修改</el-button>
<el-button @click="$refs.userFormRef.resetFields()">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
name: "UserInfo",
data() {
return {
user: { ...this.$store.state.userInfo },
//表单验证规则
userRules: {
nickname: [
{ required: true, message: "请输入用户昵称", trigger: "blur" },
{
pattern: /^\S{1,10}$/,
message: "昵称必须是1-10位的非空字符串",
trigger: "blur"
}
],
email: [
{ required: true, message: "请输入用户邮箱", trigger: "blur" },
{ type: "email", message: "邮箱格式不正确", trigger: "blur" }
]
}
};
},
methods: {
async doSubmit() {
// 1. 验证表单的合法性
this.$refs.userFormRef.validate(async valid => {
if( valid ){
// 2. 验证通过,发起请求
const { data: res } = await this.$axios.put("/my/userinfo",this.user)
if( res.code === 0 ){
//弹窗提示
this.$message.success("更新用户信息成功!")
//提交vuex的actions更新用户信息
this.$store.dispatch("initUserInfo")
}else{
this.$message.error("更新用户信息失败!")
}
}
})
}
}
}
</script>
<style lang="less" scoped>
.el-form {
width: 500px;
}
</style>
1.2-更换头像
本案例技术点有以下几个
-
1.el-card : 卡片组件 element.eleme.cn/#/zh-CN/com…
-
2.服务器接口文档比较特殊,给服务器发送的不是图片二进制数据,而是一个base64字符串。
-
2.1 如果接口文档要求图片上传二进制数据 (之前学习过的)
-
(1)文件预览思路:将图片转成url
URL.createObjectURL(文件对象)
-
(2)文件上传思路:使用FormData
- 这个对象会自动处理文件二进制,并且修改请求头为文件请求头
const fd = new FormData()fd.append('图片接口参数',文件对象)
-
-
2.2 如果接口文档要求图片上传base64字符串
-
(1)文件预览思路:使用 fileReader对象读取文件生成base64字符串
-
(2)文件上传思路:不需要额外处理, 就把文件当做字符串提交即可
- 前提是要把文件转成base64格式字符串
-
-
(1)页面布局
-
自定义上传按钮思路
-
(1)给file表单绑定一个onchange事件,并且默认display为none
-
(2) 添加自定义上传按钮,点击按钮调用file表单的click事件
- 这个是dom原生自带的功能,因此要给file表单绑定一个ref
- 点击按钮就相当于点击了file表单
-
<template>
<!-- 卡片盒子 -->
<el-card class="box-card">
<!-- 1.头部插槽 -->
<div slot="header" class="clearfix">
<span>更换头像</span>
</div>
<!-- 2.内容盒子 -->
<div>
<!-- 图片,用来展示用户选择的头像 -->
<img src="@/assets/images/avatar.jpg" alt="" class="preview" />
<!-- 按钮区域 -->
<div class="btn-box">
<!-- 渲染一个被隐藏的文件选择框,只允许选中图片文件 -->
<input type="file" style="display: none" accept="image/*" ref="iptRef" @change="onFileChange" />
<el-button type="primary" icon="el-icon-plus" @click="$refs.iptRef.click()">选择图片</el-button>
<el-button type="success" icon="el-icon-upload" @click="uploadAvatar">上传头像
</el-button>
</div>
</div>
</el-card>
</template>
<script>
export default {
name: "UserAvatar",
data() {
return {
//用户头像 base64字符串
avatar: ''
}
},
methods: {
//1.头像预览
onFileChange(e) {
},
//2.头像提交
uploadAvatar(){
}
}
};
</script>
<style lang="less" scoped>
.btn-box {
margin-top: 10px;
}
.preview {
object-fit: cover;
width: 350px;
height: 350px;
}
</style>
(2)给data添加avatar绑定图片base64字符串
(3)使用fileReader实现文件预览
-
核心步骤
- 1.创建FileReader对象
- 2.调用readAsDataURL() 开始读取
- 3.通过onload事件获取读取好的base64字符串
//1.头像预览
onFileChange(e) {
//(1)获取选中的文件
const file = e.target.files[0];
if (file) {
//(2) 创建 FileReader 对象
const fr = new FileReader();
//(3)调用 readAsDataURL 函数,读取文件内容
fr.readAsDataURL(file);
//(4)监听 fr 的 onload 事件
fr.onload = e => {
// 通过 e.target.result 获取到读取的结果,值是字符串(base64 格式的字符串)
this.avatar = e.target.result;
};
}
},
(4)更新头像
//2.头像提交
async uploadAvatar() {
// 1. 调接口上传头像
const { data: res } = await this.$axios.patch("/my/update/avatar", {
avatar: this.avatar
})
//2.成功之后,提交actions更新个人信息
if (res.code === 0) {
this.$message.success("更新头像成功!");
this.$store.dispatch("initUserInfo");
}else{
this.$message.error("更新头像失败!")
}
}
(5)完整代码
<template>
<!-- 卡片盒子 -->
<el-card class="box-card">
<!-- 1.头部插槽 -->
<div slot="header" class="clearfix">
<span>更换头像</span>
</div>
<!-- 2.内容盒子 -->
<div>
<!-- 图片,用来展示用户选择的头像 -->
<!-- (1)如果当前用户有头像就展示用户头像 -->
<img :src="avatar" alt v-if="avatar" class="preview" />
<!-- (2)否则就展示默认图片 -->
<img src="@/assets/images/avatar.jpg" alt v-else class="preview" />
<!-- 按钮区域 -->
<div class="btn-box">
<!-- 渲染一个被隐藏的文件选择框,只允许选中图片文件 -->
<input
type="file"
style="display: none"
accept="image/*"
ref="iptRef"
@change="onFileChange"
/>
<el-button
type="primary"
icon="el-icon-plus"
@click="$refs.iptRef.click()"
>选择图片</el-button
>
<el-button
type="success"
icon="el-icon-upload"
:disabled="avatar === ''"
@click="uploadAvatar"
>上传头像</el-button
>
</div>
</div>
</el-card>
</template>
<script>
export default {
name: "UserAvatar",
data() {
return {
//用户头像 base64字符串
avatar: ""
};
},
methods: {
//1.头像预览
onFileChange(e) {
//(1)获取选中的文件
const file = e.target.files[0];
if (file) {
//(2) 创建 FileReader 对象
const fr = new FileReader();
//(3)调用 readAsDataURL 函数,读取文件内容
fr.readAsDataURL(file);
//(4)监听 fr 的 onload 事件
fr.onload = e => {
// 通过 e.target.result 获取到读取的结果,值是字符串(base64 格式的字符串)
this.avatar = e.target.result;
};
}
},
//2.头像提交
async uploadAvatar() {
// 1. 调接口上传头像
const { data: res } = await this.$axios.patch("/my/update/avatar", {
avatar: this.avatar
});
//2.成功之后,提交actions更新个人信息
if (res.code === 0) {
this.$message.success("更新头像成功!");
this.$store.dispatch("initUserInfo");
} else {
this.$message.error("更新头像失败!");
}
}
}
};
</script>
<style lang="less" scoped>
.btn-box {
margin-top: 10px;
}
.preview {
object-fit: cover;
width: 350px;
height: 350px;
}
</style>
\