欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微信朋友圈-高仿

程序员文章站 2022-05-13 08:15:34
...

1.项目介绍

使用Vue进行开发,运用到了vuex、页面结构使用了weui框架,实现的效果跟温馨朋友圈基本相似,发表朋友圈、私信聊天、下拉刷新等等

2.项目代码

	 2.1登录注册		

微信朋友圈-高仿
手机号用正则表达式进行了手机合法校验,然后进行配对验证码,验证码每六十秒才能点击一次。

2.2 发表朋友圈功能

微信朋友圈-高仿
图片上传使用了weui里面的上传图片插件
把上传的js写到mouted函数中,发表前进行预验证,如果没有发表心情就提示。

  mounted () {
    /**
     * 上传图片之前,确保服务端程序根目录下存储 public/upload 目录,否则会报错
     */
    const self = this
    weui.uploader('#uploader', {
      url: service.baseURL + '/post/uploadimg',
      auto: true,
      type: 'file', // 将图片文件上传,而不是转换为 base64再上传编码
      fileVal: 'image',
      compress: {
        width: 1600,
        height: 1600,
        quality: 0.8
      },
      onBeforeQueued: function (files) {
        // `this` 是轮询到的文件, `files` 是所有文件

        if (['image/jpg', 'image/jpeg', 'image/png', 'image/gif'].indexOf(this.type) < 0) {
          weui.alert('请上传符合条件的图片')
          return false // 阻止文件添加
        }
        if (this.size > 10 * 1024 * 1024) {
          weui.alert('请上传不超过10M的图片')
          return false
        }
        // 控制不能多选超过5张图片
        if (files.length > self.totalUploadCount) { // 防止一下子选择过多文件
          weui.alert('最多只能上传' + self.totalUploadCount + '张图片,请重新选择')
          return false
        }
        // 控制如果当前已经上传了5张,那么就不能再选择图片上传
        if (self.uploadCount + 1 > self.totalUploadCount) {
          weui.alert('最多只能上传' + self.totalUploadCount + '张图片')
          return false
        }

        self.uploadCount++

        // return true; // 阻止默认行为,不插入预览图的框架
      },
      onBeforeSend: function (data, headers) {
        const token = document.cookie.split('=')[1]
        // console.log(token)
        // console.log(this)
        // console.log(data)
        // console.log(headers)
        headers['wec-access-token'] = token

        // return false; // 阻止文件上传
      },
      onProgress: function (procent) {
        // console.log(this, procent)
        // return true; // 阻止默认行为,不使用默认的进度显示
      },
      onSuccess: function (ret) {
        ret.data.id = this.id
        self.picList.push(ret.data)

        // return true; // 阻止默认行为,不使用默认的成功态
      }
    })
  }
2.3朋友圈

微信朋友圈-高仿
发表成功跳转到朋友圈列表展示,一次只能展示5条信息,上拉到底部进行更新,每次更新5条数据,
下拉刷新更新最新的朋友圈。

微信朋友圈-高仿
功能代码

  mounted () {
    this.$bus.$on('dataLoadReady', () => {
    //   // 为动画设置定时器
      if (this.$refs.circleIconInner) {
        setTimeout(() => {
        // 取消圆球的旋转
          this.$refs.circleIconInner.classList.remove('circle-rotate')
          // 让圆球的位置归位
          this.$refs.circleIcon.style.transition = 'all 500ms'
          this.$refs.circleIcon.style.transform = 'translate(0,-30px) rotate(0deg)'
          this.pullRefresh.isPull = false
        }, 800)
      }
    })
  },
  methods: {
    touchstart (e) {
      // 记录触摸的起始纵坐标
      this.pullRefresh.dragStart = e.targetTouches[0].clientY
    },
    touchmove (e) {
      console.log('2222')
      const target = e.targetTouches[0]
      // 记录(手指现在位置-初始位置)/屏幕高度 计算得来的数值
      this.pullRefresh.percentage = (this.pullRefresh.dragStart - 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.dragStart = null
        }
      } else {
        // 如果没有在页面顶部执行拖动事件,则不执行下拉刷新逻辑
        this.pullRefresh.dragStart = null
      }
    },
    // 手指松开屏幕后,圆球归位,加载最新数据
    touchend (e) {
      if (!this.pullRefresh.isPull) {
        return
      }
      console.log(1111)
      // console.log(Math.abs(this.pullRefresh.percentage) > this.pullRefresh.dragEnd)
      if (Math.abs(this.pullRefresh.percentage) > this.pullRefresh.dragEnd) {
        // 为小圆球引用动画
        this.$refs.circleIconInner.classList.add('circle-rotate')
        // 通知使用此组件的组件加载最新数据
        this.$emit('onRefresh')
      } else {
        // 如果用户松开手指时,下拉的距离没有达到临界值,就自动收回
        this.$refs.circleIcon.style.transition = 'all 500ms'
        this.$refs.circleIcon.style.transform = 'translate(0,0)  rotate(0deg)'
      }
      // 重置dragstart
      this.pullRefresh.dragStart = null
      this.pullRefresh.percentage = null
    }
  }

上拉更新功能代码

  mounted () {
    // 为 window 对象添加一个滚动事件
    window.addEventListener('scroll', () => {
      // 获取clientHeight
      const clientHeight = document.documentElement.clientHeight
      // 获取scrollHeight
      const scrollHeight = document.body.scrollHeight
      // 获取scrollTop,注意兼容问题
      const scrollTop = document.documentElement.scrollTop || document.body.scrollTop

      // 通知headerbar组件可以根据当前的scrollTop的值
      this.$bus.$emit('scroll', scrollTop)
      if ((clientHeight + scrollTop) >= (scrollHeight - 50)) {
        if (this.$store.state.flag) {
          if (!this.readyToLoad) {
            return false
          }
          // 通知父组件加载数据
          this.$emit('loadData')
        }
      }
    })
  }
}
2.4 切换背景图片

微信朋友圈-高仿
样式是使用weui进行编写的,上传代码跟发表里的上传代码是一样的。

2.5 点赞评论
点赞就是调用需要的接口进行验证是否点赞,如果点赞就在那条朋友圈下面显示点赞人,取消点赞随之消失。

微信朋友圈-高仿
点击评论弹出对话框,输入完成隐藏。
微信朋友圈-高仿
微信朋友圈-高仿

功能代码

 // 展示点赞和评论面板
    showPanel (e) {
      this.showOpera = !this.showOpera
    },
    // 点赞和取消点赞
    operaLike () {
      if (this.data.isLike) {
        // 取消点赞
        this.removeLike()
      } else {
        // 点赞
        this.addLike()
      }
    },
    // 取消点赞
    async removeLike () {
      const res = await service.post('likecomment/removelike', {
        postId: this.data._id
      })
      if (res.data.code === 0) {
        // 通知vuex的store更新朋友圈的列表的数据
        this.$store.dispatch('removeLike', {
          pid: this.data._id,
          user: this.$store.state.currentUser
        })
      }
    },
    // 点赞
    async addLike () {
      const res = await service.post('likecomment/addlike', {
        postId: this.data._id
      })
      if (res.data.code === 0) {
        // 通知vuex的store更新朋友圈的列表的数据
        this.$store.dispatch('addLike', {
          pid: this.data._id,
          user: this.$store.state.currentUser
        })
      }
    },
    // 点击评论触发的事件
    addComment (e) {
      // 获取当前点击的坐标
      this.data.pageY = e.pageY
      this.data.clientY = e.clientY
      this.$bus.$emit('showInput', this.data)
    },
    goPersonPage (userId) {
      // console.log(userId)
      // console.log()
      if (userId === this.$store.state.currentUser._id) {
        return this.$router.push('mypage')
      }
      this.$router.push({
        path: 'personPage',
        query: {
          id: userId
        }
      })
    }
2.6 查看用户信息

微信朋友圈-高仿
点击用户头像可以查看用户信息,可以进行与用户的私信

3.个人信息功能

	点击自己的个人信息查看自己的信息,点击每一条信息都可进行编辑信息。

微信朋友圈-高仿
代码都是一些简单的查改

4.私信功能

 4.1.私信

点击发消息就可与用户进行聊天,运用vue-socket.io和socket.io-client插件,实现了聊天的实时通讯。
微信朋友圈-高仿
代码

<template>
    <div class="container">
        <navHeader :title="topName"></navHeader>
        <!-- 聊天界面 -->
        <div class="chat-view" ref="chatView" @touchstart="touchstart">
            <chatItem v-for="(item) in dataList" :data="item" :key="item._id"/>
        </div>
        <!-- 输入框 -->
        <div :class="bottomClass">
            <inputbar ref="inputBar"
            @uploaded="uploaded"
            @showMorePanel="showMorePanel"
            :option="option"
            @publish="publish"></inputbar>
        </div>
    </div>
</template>
<script>
import navHeader from '@/components/navHeader'
import inputbar from '@/components/inputbar'
import chatItem from '@/components/chatItem'
import service from '@/utils/service'
export default {
  components: {
    navHeader,
    inputbar,
    chatItem
  },
  data () {
    return {
      bottomClass: 'bottom-view',
      topName: this.$route.query.name,
      toUserId: this.$route.query.id, // 聊天对象的id
      dataList: [], // 存储的聊天信息,
      option: { noPlus: false } // 设置输入框组件是否显示更多按钮和更多面板:noPlus=true:不显示,noPlus=false:显示
    }
  },
  // 页面加载时,首先当前用户登录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) {
      console.log(obj.fromUser._id, this.toUserId)
      if (obj.fromUser._id === this.toUserId) {
        this.addMessage({
          content: obj.content,
          fromUser: obj.fromUser,
          mine: false // 对方发送的消息
        })
      }
    },
    /**
     * 服务器掉之后,客户端会重新连接,连接成功后会触发下面的事件
     * 我们就在这个事件中,重新登录
     */
    reconnect (obj) {
      if (this.$store.state.currentUser && this.$store.state.currentUser._id) {
      // 登录socket
        this.$socket.emit('login', this.$store.state.currentUser)
      }
    }
  },
  methods: {
    touchstart () {
      this.bottomClass = this.bottomClass.replace('show', '')
    },
    // 获取聊天记录
    async fetchData () {
      const res = await service.get('message/getchathistory', {
        toUser: this.toUserId
      })
      if (res.data.code === 0) {
        this.dataList = (res.data.data)
      }
    },
    // 发表文字内容
    async publish (data) {
      // 将当前登录用户发表的消息存储到数据库中
      const res = await service.post('message/addmsg', {
        content: { type: 'str', value: data.value },
        toUser: this.toUserId
      })
      // 如果发表成功,将当前消息,添加到dataList中

      this.addMessage({
        content: { type: 'str', value: data.value },
        fromUser: this.$store.state.currentUser,
        mine: true
      })
      if (res.data.code !== 0) {
        weui.toast('消息发送失败')
      }
    },
    // 向dataList中加入最新的消息(可能时自己发的,也可能是最新收到的)
    addMessage (message) {
      console.log(message)
      this.dataList.push(message)
      // 设置页面滚动到底部
      setTimeout(() => {
        this.$nextTick(() => {
          this.$refs.chatView.scrollTop = this.$refs.chatView.scrollHeight
        })
      }, 500)
    },
    // 展示更多面板
    showMorePanel () {
      if (this.bottomClass.indexOf('show') > -1) {
        this.bottomClass = this.bottomClass.replace('show', '')
      } else {
        this.bottomClass += ' show'
      }
    },
    // 图片上传成功之后,将图片路径作为一条消息进行保存
    async uploaded (data) {
      // console.log(data)
      // 将当前登录用户发表的消息存储到数据库中
      const res = await service.post('message/addmsg', {
        content: { type: 'pic', value: data.data },
        toUser: this.toUserId
      })
      // 如果发表成功,将当前消息,添加到dataList中

      this.addMessage({
        content: { type: 'pic', value: data.data },
        fromUser: this.$store.state.currentUser,
        mine: true
      })
      if (res.data.code !== 0) {
        weui.toast('消息发送失败')
      }
    }
  }

}
</script>
4.2 查看私信

微信朋友圈-高仿
查看与你聊天人的信息。

<template>
    <div class="container">
        <navHeader title="消息列表"></navHeader>
        <!-- 搜索框 -->
        <div class="weui-search-bar" id="searchBar" :class="searchBarClass">
            <form class="weui-search-bar__form">
                <div class="weui-search-bar__box">
                    <i class="weui-icon-search"></i>
                    <input v-model="keyword" type="search" class="weui-search-bar__input" id="searchInput" placeholder="搜索" @input="searchChat($event)" required/>
                    <a href="javascript:"  @click="clearSearch" class="weui-icon-clear" id="searchClear"></a>
                </div>
                <label class="weui-search-bar__label" id="searchText">
                    <i class="weui-icon-search"></i>
                    <span>搜索</span>
                </label>
            </form>
            <a href="javascript:" class="weui-search-bar__cancel-btn" id="searchCancel">取消</a>
        </div>
        <!-- 消息列表展示 -->
        <div class="content-list">
            <div class="weui-loadmore" v-show="loading">
                <i class="weui-loading"></i>
                <span class="weui-loadmore__tips">正在加载</span>
            </div>
            <div class="weui-loadmore weui-loadmore_line weui-loadmore_dot" v-show="!loading&&dataList.length===0">
                <span class="weui-loadmore__tips"></span>
            </div>
            <!-- 消息列表展示 -->
            <div @click="goChat(item)"  class="item" v-for="item in dataList" :key="item.id">
                <!-- 用户头像 -->
                <!-- 用户头像 -->
                <img :src="item.user.avatar" class="avatar">
                <div class="right-content scale-1px">
                <p class="nickname one-line">{{item.user.nickname}}</p>
                <p v-if="item.msg.content && item.msg.content.type==='str'" class="text one-line">{{item.msg.content.value}}</p>
                <p v-if="item.msg.content && item.msg.content.type==='pic'" class="text one-line">[图片]</p>
                </div>
                <div class="time">{{item.msg.create|ceshi}}</div>
            </div>
        </div>
    </div>
</template>
<script>
import navHeader from '@/components/navHeader'
import service from '@/utils/service'
import moment from 'moment'
import Vue from 'vue'
// import formatTime from '@/utils/formatTime'
Vue.filter('ceshi', (e) => {
  var now = moment().locale('zh-cn').format('YYYY-MM-DD HH:mm:ss')
  return moment(now).diff(moment(e), 'minutes') + '分钟前'
})
export default {
  components: {
    navHeader
  },
  mounted () {
    // 没有这行代码,文本框无法输入内容
    weui.searchBar('#searchBar')
  },
  data () {
    return {
      searchBarClass: '',
      loading: true,
      dataList: [] // 存放消息列表的数组
    }
  },
  computed: {
    keyword: {
      get: function () {
        return this.$store.state.keyword
      },
      set: function (newvalue) {
        this.$store.dispatch('setkeyword', newvalue)
      }
    }
  },
  created () {
    if (this.keyword) {
      this.searchBarClass = 'weui-search-bar_focusing'
    } else {
      this.searchBarClass = ''
    }

    this.fetchData()
  },
  methods: {
    async fetchData () {
      this.loading = true
      const res = await service.get('message/getchatlist', {
        keyword: this.keyword
      })

      if (res.data.code === 0) {
        this.loading = false
        this.dataList = res.data.data
        // console.log(this.dataList)
      }
    },
    searchChat () {
      this.fetchData()
    },
    clearSearch () {
      this.keyword = ''
      this.fetchData()
    },
    goChat (item) {
      this.$router.push({
        path: 'chat',
        query: {
          id: item.user._id,
          name: item.user.nickname
        }
      })
    }
  }
}
</script>