×
微信咨询
微信二维码

长按图片识别添加或保存到相册

产品服务 帮助文档

课程:来团小程序商城会员价格功能实现

小程序商城会员价格功能实现教程,涵盖后台会员价格结构分析、前端数据结构设计、动态计算会员价格与价格显示优化,帮助开发者区分原价和优惠价,实现会员价划线展示。

来团智慧商业小程序零代码开发平台 多行业适用

来团智慧商业小程序零代码开发平台 多行业适用

来团智慧商业小程序零代码开发平台,多行业适配。无需代码,拖拽式设计,轻松打造订货商城、会员制商城、分销商城及小程序官网。不仅能满足通用需求,还支持定制化,从页面布局到功能模块,随心定制,助您快速搭建专属商业小程序,抢占市场先机。

小程序商城会员价格功能实现教程

课程概览

本教程将详细介绍如何在小程序端实现会员价格功能,包括:

  • 后台新会员价格结构分析
  • 前端数据结构设计
  • 动态计算会员价格
  • 友好的价格显示

第一课:理解需求和现有架构

1.1 后台的新会员价格结构

后台现在支持三种会员价格模式:

字段 说明 可选值
is_enable_grade 是否开启会员价格 0=关闭, 1=开启
is_alone_grade 会员价格设置模式 0=暂无折扣, 1=默认设置, 2=单独设置
grade_price_type 价格类型 10=折扣, 20=金额
alone_grade_price SKU级别的会员价格 JSON格式: {"等级ID": 价格/折扣}

1.2 小程序端需要解决的问题

问题:后台返回的商品价格已经是计算后的会员价,小程序无法区分"原价"和"优惠价"

解决方案:

  1. 在小程序端保存原始价格
  2. 规格切换时重新计算会员价格
  3. 显示时区分原价和优惠价

第二课:数据结构分析

2.1 API返回的数据结构

后台API返回的商品详情包含:

{
  "detail": {
    "goods_id": 1,
    "goods_name": "商品名称",
    "is_enable_grade": 1,
    "is_alone_grade": 2,
    "grade_price_type": 10,
    "alone_grade_equity": {},
    "is_user_grade": true,
    "user_grade": {
      "grade_id": 10001,
      "name": "黄金会员"
    },
    "goods_sku": {
      "spec_sku_id": "1_2_3",
      "goods_price": 80,
      "line_price": 100
    },
    "goods_multi_spec": {
      "spec_list": [
        {
          "spec_sku_id": "1_2_3",
          "form": {
            "goods_price": 80,
            "alone_grade_price": {
              "10001": "80",
              "10002": "70"
            }
          }
        }
      ]
    }
  }
}

关键发现:API返回的价格已经是会员优惠价,我们需要保存原始价格用于显示。


第三课:前端数据准备

3.1 修改 Page.data

在 index.js 的 data 中添加两个新字段:

data: {
  // ... 其他字段
  
  goods_price: 0,           // 商品价格(显示价格,可能已优惠)
  original_price: 0,        // 原始价格(未优惠)- 新增
  userGradeId: 0,           // 用户会员等级ID - 新增
  
  // ... 其他字段
}

设计说明:

字段 作用
original_price 保存用户看到的"划线价/原价",用于判断是否享受优惠,切换规格时作为计算基准
userGradeId 记录当前用户的会员等级,切换规格时根据等级查找对应的会员价格

第四课:初始化数据保存原始价格

4.1 修改 _initGoodsDetailData 方法

/**
 * 初始化商品详情数据
 */
_initGoodsDetailData(data) {
  let _this = this;
  let goodsDetail = data.detail;
  
  // 富文本转码
  if (goodsDetail.content.length > 0) {
    wxParse.wxParse('content', 'html', goodsDetail.content, _this, 0);
  }
  
  // 商品价格/划线价/库存
  data.goods_sku_id = goodsDetail.goods_sku.spec_sku_id;
  data.goods_price = goodsDetail.goods_sku.goods_price;           // 优惠价
  data.original_price = goodsDetail.goods_sku.goods_price;        // ★ 保存为原价
  data.line_price = goodsDetail.goods_sku.line_price;
  data.stock_num = goodsDetail.goods_sku.stock_num;
  
  // ★ 保存用户会员等级ID
  data.userGradeId = goodsDetail.user_grade 
    ? goodsDetail.user_grade.grade_id 
    : 0;
  
  // 商品封面图(确认弹窗)
  data.skuCoverImage = goodsDetail.goods_image;
  
  // 多规格商品封面图(确认弹窗)
  if (goodsDetail.spec_type == 20 && goodsDetail.goods_sku['image']) {
    data.skuCoverImage = goodsDetail.goods_sku['image']['file_path'];
  }
  
  // 初始化商品多规格
  if (goodsDetail.spec_type == 20) {
    data.goodsMultiSpec = _this._initManySpecData(goodsDetail.goods_multi_spec);
  }
  
  return data;
}

常见问题解答:

Q: 为什么 goods_price 和 original_price 初始值相同?

A: 因为API返回的价格已经是优惠价,我们需要保存这个值作为"参考原价"。后续在 _calcGradePrice 中会重新计算真正的原价。

Q: user_grade 可能不存在吗?

A: 是的!未登录用户或非会员用户没有 user_grade,所以要加三元运算判断。


第五课:核心 - 动态计算会员价格

这是整个功能最复杂的部分,需要在规格切换时重新计算价格。

5.1 添加 _calcGradePrice 方法

/**
 * 计算会员价格
 * @param {Object} skuItem - SKU规格项
 * @returns {Object} 价格信息 {price, originalPrice}
 */
_calcGradePrice(skuItem) {
  let detail = this.data.detail;
  
  let result = {
    price: skuItem.form.goods_price,
    originalPrice: skuItem.form.goods_price
  };
  
  // 步骤1: 检查是否启用会员价格
  if (!detail.is_enable_grade || !detail.is_user_grade) {
    return result;
  }
  
  // 步骤2: 获取用户会员等级ID
  let userGradeId = this.data.userGradeId;
  if (!userGradeId) {
    return result;
  }
  
  // 步骤3: 暂无折扣模式
  if (detail.is_alone_grade == 0) {
    return result;
  }
  
  // 步骤4: 获取商品原始价格
  result.originalPrice = skuItem.form.goods_price;
  
  // 步骤5: 根据模式计算会员价格
  if (detail.is_alone_grade == 2) {
    // ===== 单独设置模式 =====
    let aloneGradePrice = skuItem.form.alone_grade_price || {};
    
    if (aloneGradePrice && aloneGradePrice[userGradeId] !== undefined) {
      let priceValue = aloneGradePrice[userGradeId];
      
      if (detail.grade_price_type == 10) {
        // 折扣类型
        let discountRatio = priceValue / 10;
        result.price = (skuItem.form.goods_price * discountRatio).toFixed(2);
      } else {
        // 金额类型
        result.price = parseFloat(priceValue).toFixed(2);
      }
    }
  } else if (detail.is_alone_grade == 1) {
    // ===== 默认设置模式 =====
    let gradeEquities = detail.alone_grade_equity || {};
    let priceValue = gradeEquities[userGradeId];
    
    if (priceValue === undefined && detail.user_grade && detail.user_grade.equity) {
      priceValue = detail.user_grade.equity.discount;
    }
    
    if (priceValue !== undefined) {
      if (detail.grade_price_type == 10) {
        // 折扣类型
        let discountRatio = priceValue / 10;
        result.price = (skuItem.form.goods_price * discountRatio).toFixed(2);
      } else {
        // 金额类型
        result.price = parseFloat(priceValue).toFixed(2);
      }
    }
  }
  
  return result;
}

5.2 修改规格切换方法

/**
 * 更新商品规格信息
 */
_updateSpecGoods() {
  let _this = this,
    specSkuId = goodsSpecArr.join('_');
  
  // 查找当前选中的SKU
  let spec_list = _this.data.goodsMultiSpec.spec_list,
    skuItem = spec_list.find((val) => {
      return val.spec_sku_id == specSkuId;
    });
  
  if (typeof skuItem === 'object') {
    // 核心修改:调用_calcGradePrice计算会员价格
    let gradePriceInfo = _this._calcGradePrice(skuItem);
    
    _this.setData({
      goods_sku_id: skuItem.spec_sku_id,
      goods_price: gradePriceInfo.price,
      original_price: gradePriceInfo.originalPrice,
      line_price: skuItem.form.line_price,
      stock_num: skuItem.form.stock_num,
      skuCoverImage: skuItem.form.image_id > 0 
        ? skuItem.form.image_path 
        : _this.data.detail.goods_image
    });
  }
}

5.3 计算流程图

用户点击切换规格
      ↓
_updateSpecGoods() 被调用
      ↓
找到对应的SKU项
      ↓
_calcGradePrice(skuItem) 被调用
      ↓
判断会员价格模式
      ↓
├─ 暂无折扣 → 返回原价
│
├─ 单独设置 → 从SKU的alone_grade_price取值
│   ├─ 折扣类型 → 价格 × (折扣值 / 10)
│   └─ 金额类型 → 直接使用金额
│
└─ 默认设置 → 从商品配置取值
    ├─ 折扣类型 → 价格 × (折扣值 / 10)
    └─ 金额类型 → 直接使用金额
      ↓
setData 更新页面显示

第六课:前端显示优化

6.1 修改 wxml 模板

<!-- 商品价格区域 -->
<view class="money-box dis-flex flex-y-center">
  
  <!-- 会员价(已优惠) -->
  <view class="goods-price" 
        wx:if="{{ detail.is_user_grade && goods_price < original_price }}">
    <text class="col-m f-26">¥</text>
    <text class="goods-price_num col-m f-40">{{ goods_price }}</text>
  </view>
  
  <!-- 非会员价 -->
  <view class="goods-price" wx:else>
    <text class="col-m f-26">¥</text>
    <text class="goods-price_num col-m f-40">{{ goods_price }}</text>
  </view>
  
  <!-- 划线价(原价) -->
  <view class="line-price" wx:if="{{line_price > 0}}">
    <text wx:if="{{ detail.is_user_grade && goods_price < original_price }}">
      ¥{{ original_price }}
    </text>
    <text wx:else>
      ¥{{ line_price }}
    </text>
  </view>
  
  <!-- 会员价标签 -->
  <view wx:if="{{ detail.is_user_grade && goods_price < original_price }}" 
        class="tag-grade-price">
    <text>会员价</text>
  </view>
</view>

6.2 显示效果

场景 显示效果
会员登录 + 有会员价 显示优惠价,划线显示原价,标签"会员价"
会员登录 + 无会员价 显示正常价格
未登录 显示正常价格

6.3 视觉效果

┌─────────────────────────────────────────┐
│  场景: 会员登录且有会员价                 │
│  ┌────┐  ┌─────────┐  ┌──────────┐     │
│  │¥80│  │划线¥100│  │ [会员价] │     │
│  └────┘  └─────────┘  └──────────┘     │
├─────────────────────────────────────────┤
│  场景: 非会员或无会员价                  │
│  ┌────┐  ┌─────────┐                   │
│  │¥80│  │划线¥100│                   │
│  └────┘  └─────────┘                   │
└─────────────────────────────────────────┘

第七课:API层同步修改

为了让小程序端能获取到正确的 alone_grade_price 数据,需要修改API层的计算逻辑。

7.1 PHP 代码实现

/**
 * 设置商品的会员价
 * @param $user 用户信息
 * @param $goods 商品信息(引用传递)
 */
private function setGoodsGradeMoney($user, &$goods)
{
    // 会员等级状态检查
    $gradeStatus = (!empty($user) && $user['grade_id'] > 0 
        && !empty($user['grade']) && !$user['grade']['is_delete']);
    
    if (!$gradeStatus || !$goods['is_enable_grade']) {
        $goods['is_user_grade'] = false;
        return;
    }
    
    $userGradeId = $user['grade_id'];
    
    // 模式判断
    switch ($goods['is_alone_grade']) {
        case 0: // 暂无折扣
            $goods['is_user_grade'] = false;
            return;
            
        case 2: // 单独设置 - 使用SKU级别的价格
            foreach ($goods['sku'] as &$skuItem) {
                $aloneGradePrice = $skuItem['alone_grade_price'] ?: [];
                
                if (!empty($aloneGradePrice) && isset($aloneGradePrice[$userGradeId])) {
                    $priceValue = $aloneGradePrice[$userGradeId];
                    
                    if ($goods['grade_price_type'] == 10) {
                        // 折扣类型
                        $discountRatio = $priceValue / 10;
                        $skuItem['goods_price'] = helper::bcmul(
                            $skuItem['goods_price'], 
                            $discountRatio
                        );
                    } else {
                        // 金额类型
                        $skuItem['goods_price'] = $priceValue;
                    }
                    
                    $goods['is_user_grade'] = true;
                }
            }
            return;
            
        case 1: // 默认设置 - 使用全局折扣
            $priceValue = $goods['alone_grade_equity'][$userGradeId] 
                ?? $user['grade']['equity']['discount'];
            
            if ($goods['grade_price_type'] == 10) {
                $discountRatio = $priceValue / 10;
                foreach ($goods['sku'] as &$skuItem) {
                    $skuItem['goods_price'] = helper::bcmul(
                        $skuItem['goods_price'], 
                        $discountRatio
                    );
                }
            } else {
                foreach ($goods['sku'] as &$skuItem) {
                    $skuItem['goods_price'] = $priceValue;
                }
            }
            $goods['is_user_grade'] = true;
            return;
    }
}

第八课:完整数据流

┌─────────────────────────────────────────────────────────────────┐
│                        数据流向                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户打开商品页                                                  │
│      ↓                                                          │
│  API返回数据(价格已是优惠价)                                   │
│      ↓                                                          │
│  _initGoodsDetailData 保存 original_price                       │
│      ↓                                                          │
│  用户选择规格                                                    │
│      ↓                                                          │
│  _updateSpecGoods → _calcGradePrice                             │
│      ↓                                                          │
│  根据会员配置重新计算价格                                         │
│      ↓                                                          │
│  setData 更新显示                                                │
│      ↓                                                          │
│  用户看到原价和优惠价                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

第九课:关键知识点总结

知识点 说明
数据保存 初始时保存原始价格用于后续比较
条件渲染 wx:if 用于区分会员/非会员场景
方法封装 _calcGradePrice 独立封装计算逻辑
API配合 后端需要返回 SKU 级别的 alone_grade_price

课后练习

尝试自己实现以下功能:

练习1:添加原价划线效果

在CSS中添加删除线效果:

.original-price {
  text-decoration: line-through;
  color: #999;
}

在wxml中使用:

<text class="original-price">¥{{ original_price }}</text>

练习2:显示节省金额

计算并显示"节省¥XX":

// 在_calcGradePrice中添加
result.saveMoney = (result.originalPrice - result.price).toFixed(2);

在wxml中显示:

<view class="save-money" wx:if="{{saveMoney > 0}}">
  节省¥{{saveMoney}}
</view>

练习3:支持库存不足提示

当会员价库存不足时显示提示:

// 检查SKU库存
if (skuItem.form.stock_num <= 0) {
  App.showError('该规格会员价已售罄');
}

文件修改清单

文件路径 修改内容
wxapp/pages/goods/index.js 添加 original_price、userGradeId,新增 _calcGradePrice 方法
wxapp/pages/goods/index.wxml 修改价格显示区域,支持原价划线
source/application/api/model/Goods.php 重写 setGoodsGradeMoney 方法
source/application/api/model/sharing/Goods.php 重写 setGoodsGradeMoney 方法

常见问题FAQ

Q1: 切换规格时价格不更新?

检查以下内容:

  1. goodsSpecArr 全局变量是否正确更新
  2. _updateSpecGoods 方法是否被调用
  3. setData 是否正确执行

Q2: 会员价显示不正确?

排查步骤:

  1. 检查 userGradeId 是否有值
  2. 检查 alone_grade_price 数据格式是否正确
  3. 检查 grade_price_type 值是否正确(10=折扣,20=金额)

Q3: 非会员用户也显示了会员价?

检查逻辑:

// 确保有用户等级ID
if (!userGradeId) {
  return result;  // 非会员直接返回
}

结语

本教程涵盖了小程序商城会员价格功能的核心实现,通过保存原始价格、动态计算会员价、灵活显示等步骤,实现了友好的会员价格展示效果。

大纲

文章目录

    联系我们
    联系方式
    • 官方服务热线:17721141027
    • 邮箱:kf@ilaituan.com
    • QQ:20262336
    扫码添加客服
    微信
    联系我们