- 数据库: mongdb
- 后台开发技术: nodejs
- 前台开发:vue
- 组件库:WeUI 是一套同微信原生视觉体验一致的基础样式库
- 管理状态:vuex
创建项目:vue-cli3.0脚手架
vue create friend
注册/ 登录功能
效果展示:
- 在阿里云注册短信验证码服务
- 用户根据手机号获取到短信验证码进行登录
- 服务端验证手机号和验证码是否符合
- 验证通过后,将登录或注册的用户信息存储起来,方便其他页面使用
- 将用户信息存储到程序本身的内存中(使用vuex)
- 同时在localstorage存储一个备份
// 注册或登录
async singup () {
// 验证手机号
if (!(/^1[3456789]\d{9}$/.test(this.mobile))) {
weui.topTips('请输入合法的手机号', 2000)
return false
}
// 验证验证码
if (!this.code) {
weui.topTips('请输入验证码', 2000)
return false
}
// 发送登录请求
const res = await service.post('users/signup', {
phonenum: this.mobile,
code: this.code
})
if (res.data.code === 0) {
this.$store.dispatch('setUser', res.data.data)
this.$router.go(-1)
}
}
// 重新发送验证码倒计时
countTime () {
// 为this对象(当前vue实例)动态添加一个属性 clearFlag
this.clearFlag = setInterval(() => {
if (this.timeCode === 0) {
this.timeCode = 60
// 清除定时器
clearInterval(this.clearFlag)
return
}
this.timeCode--
}, 1000)
}
好友动态列表
- 获取存储在vuex的用户信息,将用户昵称、头像、背景图展示
- 更换背景图:使用actionSheet组件弹出菜单中选择‘从相册选择’弹出一个选择图片文件,服务器上传成功后更新vuex的数据
- 加载更多:列表默认只展示5条数据,数据加载完毕后手指滑动通过向上刷新组件继续加载更多内容
- 下拉刷新:小圆球跟着下拉移动并旋转,加载最新数据,手指松开小圆球归位
touchstart (e) {
// 触摸的起始纵坐标
this.pullRefresh.dargStart = e.targetTouches[0].clientY
},
touchmove (e) {
const target = e.targetTouches[0]
// 手指移动的距离 = (手指当前的位置-手指初始位置)/屏幕高度
this.pullRefresh.percentage = (this.pullRefresh.dargStart - target.clientY) / window.screen.height
// 获取scrollTop的值,当值为0时,才会开始下拉刷新逻辑
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop === 0) {
if (this.pullRefresh.percentage < 0 && e.cancelable) {
this.pullRefresh.isPull = true
// 禁用浏览器默认行为
e.preventDefault()
// 计算圆球的纵向移动距离
const translateY = -this.pullRefresh.percentage * this.pullRefresh.moveCount
if (Math.abs(this.pullRefresh.percentage) <= this.pullRefresh.dragEnd) {
// 计算圆球的旋转角度
const rotate = translateY / 100 * 360
// 设置圆球的纵向位置
this.$refs.circleIcon.style.transform = `translate(0,${translateY}px) rotate(${rotate}deg)`
}
} else {
// 向上拖动,就不会进入下拉刷新逻辑
this.pullRefresh.dargStart = null
}
} else {
// 如果没有再页面顶部执行拖动事件,则不执行下拉刷新逻辑
this.pullRefresh.dargStart = null
}
},
touchend (e) {
if (!this.pullRefresh.isPull) {
return false
}
if (Math.abs(this.pullRefresh.percentage) > this.pullRefresh.dragEnd) {
this.$emit('onRefresh')
// 设置小圆圈原地旋转
this.$refs.circleIconInner.classList.add('circle-rotate')
} else {
// 如果用户松开手时,下拉距离没有达到临界值,就自动收回
this.$refs.circleIcon.style.transition = 'all 500ms'
this.$refs.circleIcon.style.transform = 'translate(0,0)'
}
this.pullRefresh.dargStart = null
this.pullRefresh.percentage = null
}
发表动态
- 点击动态列表页面右上角进入发表动态页面(用户必须是登录状态)
- 动态内容限制在200字内,上传的图片可以点击预览或者删除
- 点击发表时判断用户是否输入内容
gallery (e) {
const self = this
// 获取li 里面的style属性值
const style = e.target.getAttribute('style')
const url = style.split('"')[1]
var gallery = weui.gallery(url, {
onDelete: function () {
weui.confirm('确定删除该图片?', () => {
const id = e.target.getAttribute('data-id')
const index = self.picList.findIndex(item => {
return item.id === id
})
self.picList.splice(index, 1)
// 删除对应的dom元素
e.target.remove()
self.uploadCount--
})
// if (confirm('确定删除该图片?')) { console.log('删除') }
gallery.hide(function () {
console.log('`gallery` has been hidden')
})
}
})
},
点赞/评论功能
- 点击好友动态右下角的按钮可以进行点赞或评论
- 点赞/取消点赞:请求后台接口实现成功后通知vuex更新列表的数据
- 评论:点击评论按钮显示文本框输入内容后点击发表成功后,更新vuex中的数据
个人中心
- 获取vuex中存储的用户信息,然后展示在页面
- 更新头像:点击头像弹出uploader组件选择图片文件上传成功后通知vuex更新用户信息
- 更新昵称:点击昵称更新昵称页面,输入要更新的昵称点击确定保存同时更新数据库和本地存储的用户信息并并返回到个人信息页面
- 更新个性签名:点击个性签名更新个性签名页面,输入要更新的个性签名点击确定保存同时更新数据库和本地存储用户信息并返回到个人信息页面
- 更新性别:点击性别从下方弹出选择性别的选择框,选择后更新数据库和本地存储用户信息并显示
私信功能
- 点击私信跳转到消息列表页面,展示与好友们聊天的最后一条消息
- 可以通过关键字进行搜索
- 点击与好友的聊天列表可以进入该好友的聊天页面
获取历史聊天记录代码
async fetchData () {
this.loading = true
const res = await service.get('message/getchatlist', {
keyword: this.keyword
})
if (res.data.code === 0) {
this.dataList = res.data.data
this.loading = false
}
},
好友名片
- 在好友动态页面点击好友头像跳转到好友名片页面,跳转时将好友的id传过去
- 根据的好友的id查询好友的所有信息数据,并展示在页面
好友聊天
- 点击发消息可以与好友进行聊天
- 获取与好友的历史聊天记录
- 默认只显示输入文本框,点击加号按钮显示更多面板,可以发送图片
- 运用socket.io-client + vue-socket.io插件实现实时通讯聊天
// 页面加载时,首先当前用户登录socket,获取当前登录用户与当前指定用户的聊天记录
created () {
if (this.$store.state.currentUser && this.$store.state.currentUser._id) {
// 登录socket
this.$socket.emit('login', this.$store.state.currentUser)
}
// 获取历史聊天数据
this.fetchData()
},
sockets: {
// 当服务器端有消息推送过来的时候执行这个方法,推送的数据赋值给参数obj
// 接收消息
recieveMsg: function (obj) {
if (obj.fromUser._id === this.toUserId) {
this.dataList.push({
content: obj.content,
fromUser: obj.fromUser,
mine: false
})
this.goBottom()
}
},
/**
* 服务器掉之后,客户端会重新连接,连接成功后会触发下面的事件
* 我们就在这个事件中,重新登录
*/
reconnect (obj) {
if (this.$store.state.currentUser && this.$store.state.currentUser._id) {
// 登录socket
this.$socket.emit('login', this.$store.state.currentUser)
}
}
}
async publish (data) {
const res = await service.post('message/addmsg', {
content: { type: 'str', value: data.value },
toUser: this.toUserId
})
// 发表成功讲最新消息,添加datalist里面
if (res.data.code !== 0) {
return weui.topTips('消息发送失败')
}
const message = {
content: {
type: 'str',
value: data.value
},
fromUser: this.$store.state.currentUser,
mine: 'true'
}
this.dataList.push(message)
this.goBottom()
}