首页 > 学习中心 > 开发技术 > 技术随笔

文档大纲

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

    本教程来源于来团科技真实开发项目,将详细介绍如何在小程序端实现会员价格功能,包括:后台新会员价格结构分析,前端数据结构设计,动态计算会员价格等。

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

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

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

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

    课程概览

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

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

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

    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.jsdata 中添加两个新字段:

    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_priceoriginal_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_priceuserGradeId,新增 _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;  // 非会员直接返回
    }
    

    结语

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

    推荐商品

    更多
    来团GEO-AI搜索优化系统 用AI打造企业品牌

    来团GEO-AI搜索优化系统 用AI打造企业品牌

    来团科技GEO优化&AI搜索优化系统,是通过大模型内容投喂+训练,将企业品牌及产品信息在多平台AI生成的答案中获取优先展现,更精准触达潜在目标客户,让企业品牌出现在AI搜索里。让客户一搜就看到你,实现一问就有你,一查就信你,一看就找你的营销效果。

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

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

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

    微名通名片 VIP年卡会员 | SVIP永久会员

    微名通名片 VIP年卡会员 | SVIP永久会员

    来团科技微名通不止是电子名片,更是你的商业连接器。比起传统名片,它更像你的 “迷你商业工具”:信息多、好携带、能互动,还不浪费纸张。不管是跑业务、拓人脉,还是展示企业,一张「微名通」电子名片,就能帮你把商机揣在手机里。

    来团LTCRM客户管理系统 可独立部署

    来团LTCRM客户管理系统 可独立部署

    来团科技CRM客户管理系统,帮你把 “线索→成交→回款” 全流程管明白。这就是一套 “让销售省心、老板放心” 的客户管理工具,从获客到回款,帮你把生意攥在手里。

    大纲

    文档目录

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