微信朋友圈-高仿

  • Post author:
  • Post category:其他




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>



版权声明:本文为ABSCYU原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。