1. 拼团商品属性设置
拼团商品在普通商品属性的基础上,需要增加拼团标志属性。我们可以在云开发数据库中创建一个专门存储拼团商品的集合,其中包含以下关键字段:
现在微信小程序开发十分火热,实现微信有庞大的使用人群,而且微信小程序不需要客户端实现下载,只需要微信搜索就可以使用,在各种各样的小程序中电商小程序显得尤为突出,而且项目实现起来十分困难。今天小胡同学我自己设计了一个电商拼团的小功能(基于云开发),大家可以学习学习。我这个是仿橙心优选的电商项目
is_group: true/false(标识是否为拼团商品)
group_price: 拼团价
original_price: 原价
group_number: 成团人数要求
group_end_time: 拼团截止时间
joined_users: 已参团用户数组
2. 拼团限时时间设置
拼团活动需要设置倒计时功能,可以通过前端定时器实现。核心代码如下:
// 获取拼团结束时间
const endTime = new Date('2023-12-31 23:59:59').getTime()
setInterval(() => {
const now = new Date().getTime()
const distance = endTime - now
const hours = Math.floor((distance % (1000 60 60 24)) / (1000 60 60))
const seconds = Math.floor((distance % (1000 60)) / 1000)
// 更新页面显示
}, 1000)
3. 拼团人数设置
在管理后台可以设置成团人数,通常建议设置为2-5人。关键实现代码:
wx.cloud.callFunction({
name: 'setGroupNumber',
data: {
productId: 'xxx',
groupNumber: 3
}
})
4. 拼团流程实现
拼团流程主要通过以下步骤实现:
1) 用户点击"我要拼团"按钮
2) 前端调用云函数检查库存和参团情况
/**
* 需要一个目标日期,初始化时,先得出到当前时间还有剩余多少秒
* 1.将秒数换成格式化输出为XX天XX小时XX分钟XX秒 XX
* 2.提供一个时钟,每10ms运行一次,渲染时钟,再总ms数自减10
* 3.剩余的秒次为零时,return,给出tips提示说,已经截止
*/
// 定义一个总毫秒数,以一天为例var total_micro_second = 10 * 1000;//这是10秒倒计时
var total_micro_second = 3600 * 1000*24;//这是一天倒计时
/* 毫秒级秒杀倒计时 */
function countdown(that) {
// 渲染倒计时时钟
that.setData({
clock:dateformat(total_micro_second)//格式化时间
});
if (total_micro_second <= 0) {
that.setData({
clock:"拼团结束"
});
// timeout则跳出递归
return ;
}
//settimeout实现倒计时效果
setTimeout(function(){
// 放在最后--
total_micro_second -= 10;
countdown(that);
}
,10)//注意毫秒的步长受限于系统的时间频率,于是我们精确到0.01s即10ms
}
// 时间格式化输出,如1天天23时时12分分12秒秒12 。每10ms都会调用一次
function dateformat(micro_second) {
// 总秒数
var second = Math.floor(micro_second / 1000);
// 天数
var day = Math.floor(second / 3600/24);
// 总小时
var hr = Math.floor(second / 3600);
// 小时位
var hr2 = hr%24;
// 分钟位
var min = Math.floor((second - hr * 3600) / 60);
// 秒位
var sec = (second - hr * 3600 - min * 60);// equal to => var sec = second % 60;
// 毫秒位,保留2位
var micro_sec = Math.floor((micro_second % 1000) / 10);
return /*day+"天"+*/hr2 + "时" + min + "分" + sec + "秒" ;//+ micro_sec;
}
Page({
data: {
new_product: [],
clock: '',
openid: '',
},
onLoad: function(options) {
countdown(this);
},
3) 云函数更新已参团人数
4) 返回结果给前端展示
<view class="jishi">
<text >限时: {
{
clock}}</text>
</view>
核心云函数代码:
exports.main = async (event, context) => {
const db = cloud.database()
.newest-box .newest-list .jishi{
background: red;
border-radius: 10px;
width: 150px;
margin-left: 10px;
}
const _ = db.command
return await db.collection('group_products').doc(event.productId)
.update({
data: {
joined_users: _.push(event.userInfo),
current_number: _.inc(1)
}
})
const util = require('../util.js');
const db = wx.cloud.database();
Page({
/**
* 页面的初始数据
*/
data: {
name:'',
price:'',
ago_price:'',
classify:'',
detail:'',
stock:'',
isRecommend:'',
fileID:'',
recommendObject:[{
name:'是',checked:false},
{
name:'否',checked:false}],
classifyObject:[{
name:'蔬菜',checked:false},
{
name:'肉禽蛋品',checked:false},
{
name:'海鲜水产',checked:false},
{
name:'粮油调味',checked:false},
{
name:'熟食卤味',checked:false},
{
name:'冰品面点',checked:false},
{
name:'牛奶面包',checked:false},
{
name:'酒水冷饮',checked:false},
{
name:'休闲零食',checked:false},
],
now:'',
array:[]
},
getName(res){
this.setData({
name:res.detail.value
})
},
getClassify(res){
this.setData({
classify:res.detail.value
})
},
getPrice(res){
this.setData({
price:res.detail.value
})
},
getago_price(res){
this.setData({
ago_price:res.detail.value
})
},
getkucun(res){
this.setData({
kucun:res.detail.value
})
},
getDetail(res){
this.setData({
detail:res.detail.value
})
},
getStock(res){
this.setData({
stock:res.detail.value
})
},
isRecommend(res){
this.setData({
isRecommend:res.detail.value
})
},
getPicture(res){

var that = this;
var num = Math.floor(Math.random()*10000);
var time = Date.parse(new Date());
wx.chooseImage({
count: 1,
success(res){
console.log(res);
wx.showLoading({
title: '上传中',
})
wx.cloud.uploadFile({
cloudPath:'shop/' + time + '-' + num,
filePath:res.tempFilePaths[0],
success(res){
console.log("上传成功",res);
that.setData({
fileID:res.fileID
})
wx.hideLoading({
success: (res) => {
},
})
},
fail(res){
console.log("上传失败",res);
}
})
}
})
},
submit(res){
var that = this;
console.log(that.data.name,that.data.classify,that.data.price,that.data.detail,that.data.isRecommend,that.data.fileID);
if(that.data.name == '' || that.data.classify == '' || that.data.price == '' || that.data.ago_price == ''|| that.data.kucun == '' || that.data.detail == '' ||that.data.isRecommend == '' || that.data.fileID == ''){
wx.showToast({
title: '请完整填写信息',
})
}else{
if(that.data.now == '修改'){
wx.cloud.callFunction({
name:'updatepintuan',
data:{
id:that.data.array._id,
name:that.data.name,
fenlei:that.data.classify,
price:that.data.price,
ago_price:that.data.ago_price,
kucun:parseInt(that.data.kucun),
detail:that.data.detail,
isRecommend:that.data.isRecommend,
fileID:that.data.fileID
},
success(res){
console.log("信息修改成功");
wx.redirectTo({
url: '../admin/admin',
success(res){
wx.showToast({
title: '修改成功',
duration:3000
})
}
})
},
fail(res){
console.log("信息修改失败",res);
}
})
}else{
db.collection('pintuan').add({
data:{
detail:that.data.detail,
fenlei:that.data.classify,
img_src:that.data.fileID,
name:that.data.name,
price:that.data.price,
ago_price:that.data.ago_price,
kucun:parseInt(that.data.kucun),
pinglun:[],
isRecommend:that.data.isRecommend
},
success(res){
console.log("上传成功");
wx.showToast({
title: '上传成功',
success(res){
wx.redirectTo({
url: '../admin/admin',
})
}
})
}
})
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this;
var classifyObject = that.data.classifyObject;
var recommendObject = that.data.recommendObject;
console.log(options.data);
if(options.data == undefined){
}else{
var array = JSON.parse(options.data);
console.log(array);
for(var i = 0; i < classifyObject.length; i++){
if(classifyObject[i].name == array.fenlei){
classifyObject[i].checked = true;
}
}
for(var j = 0; j < recommendObject.length; j++){
if(recommendObject[j].name == array.isRecommend){
recommendObject[j].checked = true;
}
}
that.setData({
classifyObject:classifyObject,
recommendObject:recommendObject,
classify:array.fenlei,
isRecommend:array.isRecommend,
now:'修改',
name:array.name,
price:array.price,
detail:array.detail,
ago_price:that.data.ago_price,
kucun:that.data.kucun,
fileID:array.img_src,
array:array
})
}
},
})
}
5. 防止重复拼团
<view class="content">
<view class="body">
<text>拼团商品名称:text>
<input bindinput="getName" value="{
{name}}"/>
view>
<view class="body">
<text>商品分类:text>
<radio-group bindchange="getClassify">
<radio wx:for="{
{classifyObject}}" value="{
{item.name}}" checked="{
{item.checked}}">{
{item.name}}radio>
radio-group>
view>
<view class="body">
<text>原价格:text>
<input bindinput="getago_price" value="{
{ago_price}}" type="number" />
view>
<view class="body">
<text>拼团价格:text>
<input bindinput="getPrice" value="{
{price}}" type="number" />
view>
<view class="body">
<text>拼团人数:text>
<input bindinput="getkucun" value="{
{kucun}}" type="number" />
view>
<view class="body">
<text>描述:text>
<input bindinput="getDetail" value="{
{detail}}"/>
view>
<view class="body">
<text>是否推荐到主页:text>
<radio-group bindchange="isRecommend">
<radio wx:for="{
{recommendObject}}" value="{
{item.name}}" checked="{
{item.checked}}">{
{item.name}}radio>
radio-group>
view>
<view class="image">
<text>图片:text>
<button type="primary" style="width:50%" bindtap="getPicture" wx:if="{
{fileID == ''}}">添加图片button>
<image src="{
{fileID}}" wx:if="{
{fileID !== ''}}" mode="widthFix" style="width:200rpx;height:200rpx;margin:0 auto">image>
view>
<button style="margin-top:30rpx;background-color:#00f;color:#fff;width:70%;margin-bottom:30rpx" bindtap="submit">提交button>
view>
通过创建辅助数据库记录用户拼团状态:
const checkRepeat = async (userId, productId) => {
page {
background-color: #f3f3f3;
}
.content {
background-color: #fff;
width: 90%;
margin: 0 auto;
margin-top: 20rpx;
}
.body {
display: flex;
flex-direction: row;
margin-top: 20rpx;
align-items: center;
}
.body > text {
width: 30%;
padding: 10rpx 20rpx;
margin-left: 42rpx;
}
.body > input,radio-group {
width: 70%;
margin-right: 30rpx;
}
.image {
display: flex;
margin-top: 20rpx;
flex-direction: column;
}
.image > text {
margin-left: 62rpx;
margin-bottom: 30rpx;
}
const res = await db.collection('group_records')
.where({
userId: userId,
productId: productId
})
.get()
return res.data.length > 0
}
6. 拼团成功后购买流程
拼团成功后,用户可以在"我的订单"中看到拼团商品,点击购买时会检查:
1) 是否达到成团人数
2) 是否在有效期内
pintuan: function(e) {
var that = this;
const db = wx.cloud.database();
const _=db.command;
var clock = this.data.clock;
var _id = e.currentTarget.dataset.id;//获取当前商品的_id
var _openid=app.globalData.openid;
console.log(_openid);
console.log(_id);
if (clock==0){
wx.showModal({
title: '',
content: '时间已经截止',
showCancel:false,
})
}
wx.showModal({
title: '',
content: '是否确认拼团?',
mask: true,
success(res) {
if (res.confirm) {
console.log(_id);
db.collection("pintuan").doc(_id).update({
data: {
kucun:_.inc(-1), //拼团名额减一
people:_.inc(0-1),//拼团人数加一
},
success(res) {
wx.showToast({
title: '拼团成功!',
mask:true,
})
}
})
}
}
})
},
3) 用户是否已参团
购买流程与普通订单相似,但价格使用拼团价。
7. 数据库设计补充
建议创建以下几个集合:
group_products: 拼团商品主表
<view class="pintuan" wx:if="{
{item.kucun>0}}"> //当kucun,拼团名额已经小于等于0时,不在显示我要拼团的前端按钮,拼团结束了
<text bindtap="pintuan" data-id="{
{item._id}}" >我要拼团text>
view>
group_records: 拼团记录表
每个集合都需要建立适当的索引以提高查询效率。
8. 前端展示优化
.newest-box {
padding: 0 35rpx;
margin-top: 20rpx;
column-count: 2;
height: 100%;
}
.newest-list {
display: inline-block;
width: 345rpx;
height: 100%;
margin: 0 0 20rpx -5rpx;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
text-align: center; /*图片和文字居中 */
background: rgba(245, 127, 59, 0.829);
}
.newest-box .newest-list:nth-child(2n) {
margin-right: 0;
}
.newest-box .newest-list image {
width: 300rpx;
height: 200rpx;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
margin: 0;
}
.newest-text-child {
opacity: 0.5;
display: flex;
margin-top: 8rpx;
margin-left: 20rpx;
font-size: 20rpx;
color: rgb(10, 10, 10);
}
.ago_price {
text-decoration: line-through;
margin-top: 0px;
opacity: 0.7;
height: 30rpx;
font-size: 28rpx;
}
.newest-text {
margin-top: 0px;
display: flex;
align-items: center;
}
.cruuent_price {
width: 150rpx;
font-size: 30rpx;
}
.newest-box .newest-list .newest-text {
display: flex;
font-size: 30rpx;
color: #e6343a;
padding-top: 30rpx;
}
.newest-text1 {
overflow: hidden;
font-size: 32rpx;
text-overflow: ellipsis;
white-space: nowrap;
}
.pintuan{
color: rgb(240, 240, 240);
background: rgb(233, 196, 30);
width: 100px;
border-radius: 15px;
margin-left: 20%;
}
.newest-box .newest-list .jishi{
background: red;
border-radius: 10px;
width: 150px;
margin-left: 10px;
}
.buy{
color: rgb(240, 240, 240);
background: rgb(250, 3, 3);
width: 100px;
border-radius: 15px;
margin-left: 20%;
margin-top: 2px;
}
.major{
white-space: nowrap; padding:10px ;background-color:#ebf0f34d; margin-top: 10rpx; width: 100%;border-radius: 16rpx;}
.major .box{
text-align: center; width:180rpx; align-self: center; display: inline-block; border-radius: 16rpx;}
.major .pic{
width:100rpx; height: 100rpx;}
.major .pic1{
width:120rpx; height: 110rpx;}
.major .txt{
font-size:26rpx; margin-top:2rpx;}
建议在前端展示:
当前参团人数
剩余时间
参团用户头像列表
拼团进度条
版权声明:本文为 “博览广文网” 原创文章,转载请附上原文出处链接及本声明;
工作时间:8:00-18:00
客服电话
0755-88186625
电子邮件
admin@lanyu.com
扫码二维码
获取最新动态