实现微信发红包和领红包功能

242 阅读3分钟
image.png

我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

大家好,我是小七月,今天我为大家带来了微信收发红包的实现。

实现的功能如下:

  • 输入金额,红包祝福语以及红包个数,点击发送则在发送按钮下面绘制出一个红包。
  • 点击红包,完成按钮的动画,三秒后跳转到红包领取记录。
  • 红包领取记录里面记录红包总数以及领取的金额记录。
  • 当红包领完后再点击红包,则只显示红包领取记录。

废话不多说,我们先上完整代码链接。 首先我们先来实现发红包的功能,我们使用一个数组存储已有的红包,红包应该有以下几个属性:

  • title 红包标题
  • receiveRecord 红包领取记录
  • balance 红包剩余金额(维护这个值更有利于随机领红包的金额计算)
  • amount 红包总金额
  • unit 红包个数
  • id 以时间戳为值,作为key 为了更形象的展示红包的样式,我为数组建立了一个默认值
 packetList: [
    {
      title: '中秋节快乐',
      receiveRecord: [],
      balance: 100, // 还剩多少没有领
      amount: 100, // 红包总金额
      id: new Date().getTime(),
      unit: 3
    }
  ],

实现该功能的代码比较简单,直接罗列出代码

<template>
  <div class="page-wrapper">
    <div style="position: absolute">
      <!-- 表单,发送红包 -->
      <div class="form-item">
        <label>标题:</label>
        <input v-model="form.title" placeholder="请输入标题" />
      </div>
      <div class="form-item">
        <label>金额:</label>
        <input v-model.number="form.amount" type="number" placeholder="请输入金额" />
      </div>
      <div class="form-item">
        <label>个数:</label>
        <input v-model.number="form.unit" type="number" placeholder="请输入个数" />
      </div>
      <button @click="send">发送</button>
      <!-- 红包列表 -->
      <div class="packet-wrapper">
        <div v-for="item in packetList" :key="item.id" class="packet" @click="handleReceive(item)">
          <div class="packet-top">
            <div class="red-icon">
              <i class="red-icon-coin"></i>
              <div class="red-border"></div>
            </div>
            <div>
              {{ item.title }}
              <!-- -->
              <div style="font-size: 12px" v-if="item.receiveRecord.length === Number(item.unit)">已领完</div>
            </div>
          </div>
          <div class="packet-bottom">红包</div>
        </div>
      </div>
    </div>
</template>
<script>
export default {
  name: 'RedPacket',
  components: {},
  props: {},
  data() {
    return {
      form: {
        amount: undefined,
        unit: undefined,
        title: undefined
      },
      packetList: [
        {
          title: '中秋节快乐',
          receiveRecord: [],
          balance: 100, // 还剩多少没有领
          amount: 100, // 红包总金额
          id: new Date().getTime(),
          unit: 3
        }
      ],
    };
  },
  methods: {
    send() {
      this.packetList.push({
        ...this.form,
        receiveRecord: [],
        balance: this.form.amount,
        id: new Date().getTime()
      });
    }, 
  }
};
</script>

下面我们来实现领红包的功能,在做这个功能时,我们需要考虑下面几点:

  • 红包是否领完,若是领完,则应该跳转到红包领取记录页。
    • 比较红包总个数和红包领取记录的长度,若是相等,那么红包已经领完
  • 如何实现随机领取红包,并且不能在红包领完前就把金额领完了,每次领红包最少可领0.01元。
    • 随机领取的金额应该是[0.01,剩余金额-除当前领的还剩的红包数×0.01)(Math.random() * (balance - (unit - receivedCount + 1) * 0.01) + 0.01).toFixed(2)
  • 红包领取的总金额一定与发送时填写的金额相等。
    • 当领到最后一个红包时,直接将剩余的红包金额给出去。

实现方法如下

<template>
  <div class="page-wrapper">
      <!-- 红包列表 -->
      <div class="packet-wrapper">
        <div v-for="item in packetList" :key="item.id" class="packet" @click="handleReceive(item)">
          <div class="packet-top">
            <div class="red-icon">
              <i class="red-icon-coin"></i>
              <div class="red-border"></div>
            </div>
            <div>
              {{ item.title }}
              <!-- -->
              <div style="font-size: 12px" v-if="item.receiveRecord.length === Number(item.unit)">已领完</div>
            </div>
          </div>
          <div class="packet-bottom">红包</div>
        </div>
      </div>
    </div>

    <!-- 领取红包或查看红包记录 -->
    <div v-if="isReceive" class="layer-wrapper">
      <div class="red-icon receive-page">
        <i
          v-if="!showRecord"
          class="red-icon-coin receive-coin"
          @click="handleClick"
          :class="{ 'add-animation': showAnimation }"
        ></i>
        <div v-else class="record-wrapper">
          <div class="packet-info">
            <div>{{ currentPacket.title }}</div>
            <div style="margin-top: 10%">{{ currentPacket.receiveRecord.length }}/{{ currentPacket.unit }}</div>
          </div>
          <div class="record" v-for="record in currentPacket.receiveRecord" :key="record.time">
            <div class="record-left">
              <div>{{ record.name }}</div>
              <div>{{ record.time }}</div>
            </div>
            <div class="record-right">{{ record.money }}元</div>
          </div>
        </div>
        <div class="red-border" :class="{ 'record-border': showRecord }"></div>
        <div @click="handleClose" class="close-icon">×</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'RedPacket',
  components: {},
  props: {},
  data() {
    return {
      form: {
        amount: undefined,
        unit: undefined,
        title: undefined
      },
      packetList: [
        {
          title: '中秋节快乐',
          receiveRecord: [],
          balance: 100, // 还剩多少没有领
          amount: 100, // 红包总金额
          id: new Date().getTime(),
          unit: 3
        }
      ],
      isReceive: false,
      showRecord: false,
      showAnimation: false,
      currentPacket: {}
    };
  },
  computed: {},
  mounted() {},
  methods: {
    send() {
      this.packetList.push({
        ...this.form,
        receiveRecord: [],
        balance: this.form.amount,
        id: new Date().getTime()
      });
    },
    // 点击红包
    handleReceive(packet) {
      this.currentPacket = packet;
      this.isReceive = true;
      if (packet.receiveRecord.length === Number(packet.unit)) {
        this.showRecord = true;
      }
    },
    // 开红包
    handleClick() {
      this.showAnimation = true;
      setTimeout(() => {
        this.calcMoney();
        this.showRecord = true;
        this.showAnimation = false;
      }, 3000);
    },
    calcMoney() {
      let packet = this.currentPacket;
      const { receiveRecord, balance, unit } = packet;
      // 当还剩最后一个名额没有领取时,由最后一个人领完
      let receivedCount = receiveRecord.length;
      let recordItem = { name: `用户${receivedCount}`, time: this.formatDateTime() };
      if (receivedCount + 1 === Number(unit)) {
        recordItem.money = balance;
      } else {
        // 最少可以领取0.01元
        recordItem.money = (Math.random() * (balance - (unit - receivedCount + 1) * 0.01) + 0.01).toFixed(2);
      }
      packet.balance = (packet.balance - recordItem.money).toFixed(2);
      receiveRecord.push(recordItem);
    },
    handleClose() {
      this.isReceive = false;
      this.showRecord = false;
      this.showAnimation = false;
    },
    formatDateTime(date = new Date()) {
      var y = date.getFullYear();
      var m = date.getMonth() + 1;
      m = m < 10 ? '0' + m : m;
      var d = date.getDate();
      d = d < 10 ? '0' + d : d;
      var h = date.getHours();
      h = h < 10 ? '0' + h : h;
      var minute = date.getMinutes();
      minute = minute < 10 ? '0' + minute : minute;
      var second = date.getSeconds();
      second = second < 10 ? '0' + second : second;
      return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second;
    }
  }
};
</script>

最后说一下领取记录的数据结构

{
    name: `用户${receivedCount}`,// 领取人
    time: this.formatDateTime(),// 领取时间
    money:Math.random(),// 领取金额
}
 

总结:在使用v-model绑定红包的金额和个数时,一定要加上.number修饰符,否则最后form中存放的将是string类型,后面计算金额时就会不准确。js是一个弱类型的,每次涉及到数字类型的计算和等值判断时总是容易出bug。