Vue大事件练习(保姆级教程)06

171 阅读1分钟

08-个人中心

1.1-基本资料

1656498914916.png

(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,
        }
        

1656498874870.png

(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 属性,其值为设置为需校验的字段名

1656499559973.png

(5)完成修改基本资料

1656500203941.png

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-更换头像

1656505061786.png

本案例技术点有以下几个

  • 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字符串

1656508602867.png

(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>

\