个人中心页的效果如上所示。我们分析一下应该如何实现这样的布局。首先蓝色背景色的是一个大的div,然后头像和用户信息也是一个div包裹,下面的动态粉丝关注则是需要三个div,这样个人信息框大致布局就完成了。下面的只需要三个input输入框就可以解决了,这样个人中心页布局就完成了。
<template> <div class="user-container"> <!-- 用户基本信息面板 --> <div class="user-card" > <!-- 用户头像、姓名 --> <van-cell > <!-- 使用 title 插槽来自定义标题 --> <template #icon> <img :src="list.photo" alt="" class="avatar"> </template> <template #title> <span class="username">{{list.name}}</span> </template> <template #label> <van-tag color="#fff" text-color="#007bff">申请认证</van-tag> </template> </van-cell> <!-- 动态、关注、粉丝 --> <div class="user-data"> <div class="user-data-item"> <span>{{list.art_count}}</span> <span>动态</span> </div> <div class="user-data-item"> <span>{{list.follow_count}}</span> <span>关注</span> </div> <div class="user-data-item"> <span>{{list.fans_count}}</span> <span>粉丝</span> </div> </div> </div> <!-- 操作面板 --> <van-cell-group class="action-card"> <van-cell icon="edit" title="编辑资料" is-link @click="edit"/> <van-cell icon="chat-o" title="小思同学" is-link /> <van-cell icon="warning-o" title="退出登录" is-link @click="logout"/> </van-cell-group> </div> </template> <script> import { UserAPI } from '@/api' import Vue from 'vue' import { Dialog } from 'vant' import { removeToken } from '@/utils/token' Vue.use(Dialog) export default { async created () { const res1 = await UserAPI() console.log(res1) this.list = res1.data.data console.log(this.list) }, data () { return { list: {} } }, methods: { logout () { Dialog.confirm({ title: '退出登录', message: '退出后本网站不保存你的当前操作,真的要退出吗' }) .then(() => { // on confirm removeToken() this.$router.replace('/login') }) .catch(() => { // on cancel }) }, edit () { this.$router.push('/userEdit') } } } </script> <style scoped lang="less"> .user-container { .user-card { background-color: #007bff; color: white; padding-top: 20px; .van-cell { background: #007bff; color: white; &::after { display: none; } .avatar { width: 60px; height: 60px; background-color: #fff; border-radius: 50%; margin-right: 10px; } .username { font-size: 14px; font-weight: bold; } } } .user-data { display: flex; justify-content: space-evenly; align-items: center; font-size: 14px; padding: 30px 0; .user-data-item { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 33.33%; } } } </style>
页面布局已完成。
<template> <div class="user-edit-container"> <!-- Header 区域 --> <van-nav-bar title="编辑资料" left-arrow @click-left="$router.back()" fixed /> <!-- 用户资料 --> <van-cell-group class="action-card"> <van-cell title="头像" is-link center> <template #default> <van-image round class="avatar" :src="list.photo" @click="Click"/> <input type="file" ref="iptFile" v-show="false" accept="image/*" @change="onFileChange" /> </template> </van-cell> <van-cell title="名称" is-link :value="list.name" @click="nameClick"/> <van-cell title="生日" is-link :value="list.birthday" @click="birthdayClick"/> </van-cell-group> <!-- 修改姓名的组件 组件调用方式--> <van-dialog v-model="show" title="修改用户名" show-cancel-button :before-close="beforeClose"> <input v-haha type="text" v-model="username"/> </van-dialog> <!-- 时间选择器 --> <van-popup round="true" v-model="dateShow" position="bottom" :style="{height: '50%'}"> <van-datetime-picker v-model="currentDate" type="date" title="选择年月日" :min-date="minDate" :max-date="maxDate" @cancel="dateCancel" @confirm ="confirmDate" /> </van-popup> </div> </template> <script> import { Image, Notify, DatetimePicker, Popup } from 'vant' import Vue from 'vue' import { updatePhotoAPI, updateUserInfo, userInfoAPI } from '@/api' import { formatDate } from '../../utils/date' Vue.use(Image) Vue.use(Notify) Vue.use(DatetimePicker) Vue.use(Popup) export default { name: 'UserEdit', data () { return { list: {}, show: false, // 控制对话框的显示与隐藏 username: '', minDate: new Date(1970, 0, 1), maxDate: new Date(2025, 11, 31), currentDate: new Date(2022, 7, 24), dateShow: false } }, async created () { const res = await userInfoAPI() console.log(res) this.list = res.data.data console.log(this.list) }, methods: { // 文件改变事件 async onFileChange (e) { if (e.target.files.length === 0) return // 如果没有数据直接返回 console.log(e.target.files[0]) const theFd = new FormData() theFd.append('photo', e.target.files[0]) const res = await updatePhotoAPI(theFd) console.log(res) this.item.photo = res.data.data.photo }, // 头像点击事件 Click () { this.$refs.iptFile.click() // JS模拟标签事件触发 }, // 名称改变事件 async nameClick () { this.show = true this.username = this.list.name }, // 姓名修改框关闭前的回调 async beforeClose (action, done) { if (action === 'confirm') { // 点击确定 const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1,7}$/ if (reg.test(this.username) === true) { // 通过了校验,关闭弹窗 await updateUserInfo({ name: this.username }) this.list.name = this.username done() } else { Notify({ type: 'warning', message: '用户名只能是1-7位中英文数字组合' }) done(false) } } else { done() // 关闭修改框 } }, // 生日修改事件 async birthdayClick () { this.dateShow = true this.currentDate = new Date(this.list.birthday) }, // 日期选择器取消 dateCancel () { this.dateShow = false }, async confirmDate () { await updateUserInfo({ birthday: formatDate(this.currentDate) }) this.list.birthday = formatDate(this.currentDate) this.dateShow = false } } } </script> <style lang="less" scoped> .user-edit-container { padding-top: 1.2432rem; .avatar { width: 1.3514rem; height: 1.3514rem; } } /deep/.van-dialog__content{ text-align: center; input{ padding:0; outline: none; border:none; text-align: center; } } /deep/.van-dialog{ height:3.6667rem; } </style>
修改用户信息也完成。
发送的接口请求:
// 获取用户个人资料
export const userInfoAPI = () => request({
url: '/v1_0/user/profile'
})
// 获取用户自己信息
export const UserAPI = () => request({
url: '/v1_0/user'
})
// 更改用户头像
export const updatePhotoAPI = (fd) => request({
url: '/v1_0/user/photo',
method: 'PATCH',
data: fd // 外面一会传进来的新表单数据,切记不能写对象,不然axios会解析成json文件
})
// 编辑用户个人资料
export const updateUserInfo = (obj) => {
return request({
url: '/v1_0/user/profile',
method: 'PATCH',
data: obj // data不会忽略值为null的那对键值对,params遇到null值会忽略不携带此键值对给后台obj
})
}
由于小思同学需要用到websocket技术,还没学会所以暂时没实现。