我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!
大家好,我是小七月,今天我为大家带来了微信收发红包的实现。
实现的功能如下:
- 输入金额,红包祝福语以及红包个数,点击发送则在发送按钮下面绘制出一个红包。
- 点击红包,完成按钮的动画,三秒后跳转到红包领取记录。
- 红包领取记录里面记录红包总数以及领取的金额记录。
- 当红包领完后再点击红包,则只显示红包领取记录。
废话不多说,我们先上完整代码链接。 首先我们先来实现发红包的功能,我们使用一个数组存储已有的红包,红包应该有以下几个属性:
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)
- 随机领取的金额应该是[0.01,剩余金额-除当前领的还剩的红包数×0.01)
- 红包领取的总金额一定与发送时填写的金额相等。
- 当领到最后一个红包时,直接将剩余的红包金额给出去。
实现方法如下
<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。