校园综合平台-微信小程序版

1,352 阅读6分钟

我正在参与掘金创作者训练营第5期,点击了解活动详情

前言

        学习了一段时间的微信小程序,开始都是做零零散散的小项目,暑假借着晚上有的一点时间,决定自己写一个校园综合平台,包含二手市场、表白墙、音乐、文章、动态等一些功能,一方面可以锻炼自己的编程能力,一方面也是检验自己学习的成果。最近时间有点充足,分享一下。O(∩_∩)O~

微信小程序实现功能

  • 二手市场
  • 表白墙
  • 发布动态
  • 动态悬浮球
  • 独立swiper组件(超简洁、好看)
  • 程序版本记录
  • 文章查询
  • 天气查询
  • 音乐
  • 云开发
  • 多记录获取
  • 动态更换背景
  • 校园导航

展示图

IMG_1526.PNG

IMG_1527.PNG

IMG_1528.PNG

IMG_1529.PNG

一、二手市场

实现功能

  1. 发布二手消息(含图片、文字)
  2. 详情页买方可以联系卖方
  3. 用户评论功能
  4. 发布信息的浏览量、点赞数、评论数
  5. 用户评论功能

模块代码 1、信息发布功能 参考海轰的往期文章--微信小程序仿QQ动态发布

2、买方联系卖方 电话联系

call: function(e) {
    console.log(e)
    wx.makePhoneCall({
      phoneNumber: this.data.phone,
    })
  },

微信联系(QQ同理,其实就是复制用户提供的微信号、QQ号)

to_weixin: function(e) {
    console.log(e.currentTarget.dataset.id)
    wx.setClipboardData({
      data: e.currentTarget.dataset.id,
      icon: "none",
      success: res => {
        wx.showToast({
          title: '已复制微信号ヽ(✿゚▽゚)ノ',
          duration: 1000,
          icon: "none",
        })
      }
    })
  }

3、点赞功能(云函数实现,每个用户都需要对数据进行操作,所以选用云函数封装一下)

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()
const db = cloud.database();
const _ = db.command;
// 云函数入口函数
exports.main = async (event, context) => {
  console.log(event.name)
  try {
    return await db.collection(event.name).doc(event._id).update({
      data: {
        fabulous_num: _.inc(1)
      }
    })
  } catch (e) {
    console.error(e)
  }
}

4、评论功能

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()
const db = cloud.database();

// 云函数入口函数
exports.main = async (event, context) => {
  try {
    return await db.collection("information").doc(event._id).update({
      data: {
        comment: event.comment
      }
    })
  } catch (e) {
    console.error(e)
  }
}

二、动态悬浮球

wxml

<image
 src="/images/fabu.png"
 bindtouchmove="buttonMove" catchtap="to_publish" bindtouchstart="buttonStart" bindtouchend="buttonEnd" style="top:{{buttonTop}}px;left:{{buttonLeft}}px;width:112rpx;height:92rpx;position:fixed;;line-height:50px;font-size:25pt;color:#fff;z-index:5;"></image>
</view>

js

buttonStart: function (e) {
    startPoint = e.touches[0]
  },
  buttonMove: function (e) {
    var endPoint = e.touches[e.touches.length - 1]
    var translateX = endPoint.clientX - startPoint.clientX
    var translateY = endPoint.clientY - startPoint.clientY
    startPoint = endPoint
    var buttonTop = this.data.buttonTop + translateY
    var buttonLeft = this.data.buttonLeft + translateX
    //判断是移动否超出屏幕
    if (buttonLeft + 50 >= this.data.windowWidth) {
      buttonLeft = this.data.windowWidth - 50;
    }
    if (buttonLeft <= 0) {
      buttonLeft = 0;
    }
    if (buttonTop <= 0) {
      buttonTop = 0
    }
    if (buttonTop + 50 >= this.data.windowHeight) {
      buttonTop = this.data.windowHeight - 50;
    }
    this.setData({
      buttonTop: buttonTop,
      buttonLeft: buttonLeft
    })
  },
  buttonEnd: function (e) {

  },

三、独立swiper组件

效果图

在这里插入图片描述

组件源码

文件示意图 在这里插入图片描述 js

// components/theSwiper.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    imgUrls: Array,
  },

  /**
   * 组件的初始数据
   */
  data: {
    currentIndex: 0
  },
  /**
   * 组件的方法列表
   */
  methods: {
    swiperChange(e) {
      this.setData({
        currentIndex: e.detail.current
      });
    }
  }
});
/*

<view class="dots-box own-class">
  <view class="dots {{currentIndex == index ? 'bg-333' : ''}}" wx: for="{{ imgUrls }}" wx:key="{{ index }}"></view>
</view >
*/

json

{
  "component": true,
  "usingComponents": {}
}

wxml

<swiper indicator-dots="false" 
        autoplay="{{true}}" 
        interval="5000" 
        indicator-dots="{{false}}" 
        indicator-color='#8a8a8a' 
        indicator-active-color='#333' 
        circular="true" 
        class="swiper-block" 
        bindchange="swiperChange" 
        previous-margin="100rpx" 
        next-margin="100rpx" 
        current="{{0}}">
  <block wx:for="{{imgUrls}}" wx:index="{{index}}" wx:key="{{index}}">
    <swiper-item class="swiper-item ">
      <image mode="aspectFill" src="{{item}}" class="slide-image {{currentIndex == index ? 'active' : 'common'}}" />
    </swiper-item>
  </block>
</swiper>


wxss

page{
  background-color: #fff;
}
.swiper-block {
  background: #fff;
  height: 500rpx;
  width: 100%;
}

.swiper-item{
  display: flex;
  flex-direction: column;
  justify-content: start;
  align-items: flex-start;
  overflow: unset;
  width: 550rpx;
  height: 450rpx;
  padding-top: 70rpx;
  padding-bottom: 20rpx;
  box-sizing: border-box;
}

.slide-image{
  height: 300rpx;
  width: 450rpx;
  border-radius: 10rpx;
  margin: 0rpx 50rpx ;
  z-index: 1;
  box-shadow: 10rpx 5px 40rpx rgba(0, 0, 0,0.5);
}
.active{
  transform: scale(1.3);
  transition: all .5s ease-in 0s;
  z-index: 20;
  opacity: 1;
}
.common{
  transform: scale(1);
  transition: all .5s ease-in 0s;
  z-index: 0;
  opacity: 0.4;
}

.dots-box{
  display: flex;
  justify-content: center;
  align-items: center;
}

.dots{
  width: 30rpx;
  height: 6rpx;
  margin: 0 4rpx;
  background-color: #aaa;
  margin-top: -80rpx;
}
.bg-333{
  background-color: #333;
}

如何使用?

使用界面的 wxml 添加

<custom-swiper imgUrls="{{carouselImgUrls}}" /> 

json 这里组件地址写自己放组件的地址就行

{
  "usingComponents": {
    "custom-swiper": "../../components/customSwiper/customSwiper"
  },
}

js中的data添加数据:

  carouselImgUrls:[
      "https://wx1.sinaimg.cn/mw690/006cV2kkly1g90322akslj30on1hcjvf.jpg",
      "https://wx2.sinaimg.cn/mw690/006cV2kkly1g9032310y9j30on1hcdkw.jpg",
      "https://wx3.sinaimg.cn/mw690/006cV2kkly1g90323z18oj30on1hc77z.jpg",
      "https://wx1.sinaimg.cn/mw690/006cV2kkly1g90324d2mrj30on1hcwic.jpg",
      "https://wx3.sinaimg.cn/mw690/006cV2kkly1g903258itpj30on1hctby.jpg"
    ],

四、音乐

这里我使用的GitHub上一个音乐UI为模板,按照自己想法,改成了一个属于自己的音乐播放器。 wxml

  <view class="player" v-show="playlist.length>0">
    <view class="normal-player" wx:if="fullScreen">
      <view class="background">
      </view>
      <view class="top">
        <view class="title">{{song.music_name || '暂无正在播放歌曲'}}</view>
        <view class="subtitle">{{song.singer_name}}</view>
      </view>
      <swiper class="middle" style="height: 700rpx" bindchange="changeDot">
        <swiper-item class="middle-l" style="overflow: visible">
          <view class="cd-wrapper" ref="cdWrapper">
            <view class="cd {{cdCls}}">
              <image src="{{song.singer_img}}" alt="" class="image"/>
            </view>
          </view>
          <view class="currentLyricWrapper">{{currentText}}</view>
        </swiper-item>
        <swiper-item class="middle-r">
          <scroll-view class="lyric-wrapper" scroll-y scroll-into-view="line{{toLineNum}}" scroll-with-animation>
            <view v-if="currentLyric">
              <view ref="lyricLine"
                    id="line{{index}}"
                    class="text {{currentLineNum == index ? 'current': '' }}"
                    wx:for="{{currentLyric.lines}}">{{item.txt}}
              </view>
            </view>
            <view wx:if="{{!currentLyric}}">
              <view class="text current">暂无歌词</view>
            </view>
          </scroll-view>
        </swiper-item>
      </swiper>
      <view class="dots-wrapper">
        <view class="dots {{currentDot==index?'current':''}}" wx:for="{{dotsArray}}"></view>
      </view>
      <view class="bottom">
        <view class="progress-wrapper">
          <text class="time time-l">{{currentTime}}</text>
          <view class="progress-bar-wrapper">
            <progress-bar percent="{{percent}}"></progress-bar>
          </view>
          <text class="time time-r">{{duration}}</text>
        </view>
        <view class="operators">
          <view class="icon i-left">
            <i bindtap="changeMod"
               class="{{playMod==1? 'icon-sequence':''}}{{playMod==2? ' icon-random':''}}{{playMod==3?' icon-loop':''}}"></i>
          </view>
          <view class="icon i-left">
            <i class="icon-prev" bindtap="prev"></i>
          </view>
          <view class="icon i-center" bindtap="togglePlayings">
            <i class="{{playIcon}}" ></i>
          </view>
          <view class="icon i-right">
            <i class="icon-next" bindtap="next"></i>
          </view>
          <view class="icon i-right" bindtap="openList">
            <i class="icon-playlist"></i>
          </view>
        </view>
      </view>
    </view>
    <view class="content-wrapper {{translateCls}}">
      <view class="close-list"  bindtap="close"></view>
      <view class="play-content">
        <view class="plyer-list-title">播放队列({{songs.length}}首)</view>
        <scroll-view class="playlist-wrapper" scroll-y scroll-into-view="list{{currentIndex}}">
          <view class="item {{index==currentIndex ? 'playing':''}}" wx:for="{{songs}}" id="{{index}}"
                data-index="{{songs.music_path}}" bindtap="playthis" wx:key="{{index}}">
            <view class="name" style='text-white'>{{item.music_name}}</view>
            <view class="play_list__line">-</view>
            <view class="singer">{{item.singer_name}}</view>
            
          </view>
        </scroll-view>
        <view class="close-playlist" bindtap="close">关闭</view>
      </view>
    </view>
  </view>


<view class='cu-load load-modal' wx:if="{{loadModal}}">
  <!-- <view class='cuIcon-emojifill text-orange'></view> -->
  <image src='/images/T1.jpg' class='png' mode='aspectFit'></image>
  <view class='text-red'>加载中...</view>
</view>

wxss

.player .normal-player {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 150;
    background: #222;
}
.player .normal-player .background {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
    background-image: url(https://wx3.sinaimg.cn/mw690/006cV2kkly1g6e0w1wb20j30yi22oacu.jpg);
  background-size: cover;
}
.player .normal-player .top {
    position: relative;
    margin-bottom: 50rpx;
}
.player .normal-player .top .title {
    width: 70%;
    margin: 50rpx auto;
    line-height: 80rpx;
    text-align: center;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    font-size: 36rpx;
    color: #fff;
}
.player .normal-player .top .subtitle {
    line-height: 40rpx;
    text-align: center;
    font-size: 28rpx;
    color: #fff;
}
.player .normal-player .middle {
    position: fixed;
    width: 100%;
    top: 360rpx;
    bottom: 200rpx;
    white-space: nowrap;
    font-size: 0;
}
.player .normal-player .middle .middle-l {
    display: inline-block;
    vertical-align: top;
    position: relative;
    width: 100%;
    height: 0!important;
    padding-top: 80%!important;
}
.player .normal-player .middle .middle-l .cd-wrapper {
    position: absolute;
    left: 15%;
    top: 0;
    width: 70%;
    height: 90%;
}
.player .normal-player .middle .middle-l .cd-wrapper .play {
    -webkit-animation-play-state: running !important;
    animation-play-state: running !important;
}
.player .normal-player .middle .middle-l .cd-wrapper .pause {
    -webkit-animation-play-state: paused!important;
    animation-play-state: paused!important;
}
.player .normal-player .middle .middle-l .cd-wrapper .cd {
  width: 100%;
  height: 100%;
  border: 10rpx solid rgba(255,255,255,0.1);
  border-radius: 50%;
    -webkit-animation: rotate 20s linear infinite;
    animation: rotate 20s linear infinite;
}
.player .normal-player .middle .middle-l .cd-wrapper .cd .image {
    position: absolute;
    left: 0rpx;
    top: 0rpx;
    width: 100%;
    height: 100%;
    border-radius: 50%;
}
.player .normal-player .bottom {
    position: absolute;
    bottom: 50rpx;
    width: 100%;
}
.player .normal-player .bottom .progress-wrapper {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: 80%;
    margin: 0rpx auto;
    padding: 20rpx 0;
}
.player .normal-player .bottom .progress-wrapper .time.time-l {
    text-align: left;
}
.player .normal-player .bottom .progress-wrapper .time.time-r {
    text-align: right;
}
.player .normal-player .bottom .progress-wrapper .time {
  color: #fff;
  font-size: 24rpx;
  -webkit-box-flex: 0;
  -ms-flex: 0 0 60rpx;
  flex: 0 0 60rpx;
  line-height: 60rpx;
  width: 60rpx;
}
.player .normal-player .bottom .operators {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    height: 200rpx;
}
.player .normal-player .bottom .operators .icon {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    color: #ffcd32;
}
.player .normal-player .bottom .operators .i-left {
    text-align: right;
}
.player .normal-player .bottom .operators .icon i {
    font-size: 80rpx;
}
.player .normal-player .bottom .operators .i-center {
    padding: 0 40rpx;
    text-align: center;
}
.player .normal-player .bottom .operators .i-right {
    text-align: left;
}
.player .normal-player .top .back .icon-back {
    display: block;
    padding: 18rpx;
    font-size: 44rpx;
    color: #ffcd32;
    -webkit-transform: rotate(-90deg);
    transform: rotate(-90deg);
}
.content-wrapper {
    position: fixed;
    top: 100%;
    height: 100%;
    width: 100%;
    z-index: 150;
    transition: all 0.5s;
}
.close-list {
    position: absolute;
    top: 0;
    height: 100%;
    width: 100%;
}
.playlist-wrapper {
    height: 660rpx;
    width: 100%;
    padding:15rpx 30rpx;
    box-sizing: border-box;
}
.playlist-wrapper .item {
    display: flex;
    position: relative;
    height: 90rpx;
    line-height: 90rpx;
    margin-left: 30rpx;
    padding-right: 30rpx;
    border-bottom: 1rpx dashed rgba(255,255,255,.3);
}
.playlist-wrapper .playing ,.playlist-wrapper .playing .singer, .playlist-wrapper .playing .play_list__line{
    color: #ff9900!important;
}

.playlist-wrapper .item .name {
    max-width:350rpx;
    overflow:hidden;
    white-space:nowrap;
    text-overflow:ellipsis;
    font-size: 14px;
    color: white
}
.playlist-wrapper .item .play_list__line {
    display: block;
    margin: 0 5px;
    color: rgba(255,255,255,.5);
}
.playlist-wrapper .item .singer {
    max-width:200rpx;
    overflow:hidden;
    white-space:nowrap;
    text-overflow:ellipsis;
    font-size: 12px;
    color: rgba(255,255,255,.5);
}
.playlist-wrapper .item .playing-img {
    width: 24rpx;
    height: 24rpx;
    position: absolute;
    top: 32rpx;
    right: 0;
}
.play-content {
    position: absolute;
    /*//bottom: -860rpx;*/
    bottom: 0;
    /*transform: translateY(860rpx);*/
    left: 0;
    right: 0;
    height: 860rpx;
    width: 100%;
    background: rgba(0,0,0,.9);
    z-index: 200;
    transition: all 0.5s;
}
.uptranslate {
    transform: translateY(-100%)!important;
}
.downtranslate {
    transform: translateY(100%)!important;
}
.close-playlist {
    height: 100rpx;
    width: 100%;
    text-align: center;
    line-height: 100rpx;
    border-top: 1px solid rgba(255,255,255,.3);
    font-size: 16px;
}
.plyer-list-title{
    height: 100rpx;
    width: 100%;
    color: white;
    text-align: center;
    line-height: 100rpx;
    border-bottom: 1px solid rgba(255,255,255,.3);
    font-size: 16px;
}
.player .normal-player .bottom .progress-wrapper .progress-bar-wrapper {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
}
@keyframes rotate{
    0% {
        transform: rotate(0)
    }
    100% {
        transform: rotate(360deg)
    }
}

.middle-r {
    display: inline-block;
    vertical-align: top;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.lyric-wrapper{
    width: 80%;
    margin: 0 auto;
    overflow: hidden;
    text-align: center;
    height: 100%;
}
.text{
    line-height: 32px;
    color: rgba(255, 255, 255, 0.5);
    font-size: 14px;
}
.current {
    color: #ffcd32;
}
.currentLyricWrapper {
    height:70rpx;
    font-size:12px;
    position:absolute;
    bottom:-80rpx;
    line-height: 70rpx;
    text-align: center;
    width: 100%;
    color: #ffcd32;
}
.dots-wrapper {
    position: absolute;
    bottom: 210rpx;
    height: 20rpx;
    line-height: 20rpx;
    text-align: center;
    width: 100%;
}
.dots-wrapper .dots {
    width: 20rpx;
    height: 20rpx;
    border-radius: 10rpx;
    background: rgba(255, 255, 255, 0.5);
    display: inline-block;
    margin: 10rpx;
    margin-top: 0;
}
.dots-wrapper .current {
    width: 40rpx;
    background: rgba(255,255,255,.8);
}

注:看着几年前的小作品 还是有点感触的 留个纪念 见证自己的过往