Files
xinlong-shop-taro/.workbuddy/memory/2026-07-15.md
赵忠林 549e8258b6 fix(user): 修复订单入口tab错位并统一购物车角标样式
- 将订单快捷入口和我的订单tab索引改为tabIndex,修正页面跳转参数使用`?tab=`传递
- 修复因状态值与tab索引不匹配导致的订单状态页错位问题
- 更新`user.tsx`和`index.tsx`中相关订单tab配置,确保键名和逻辑一致
- 购物车入口数量角标由手写样式改为Badge组件,去除白色边框样式
- 购物车角标与用户订单图标角标样式保持一致,使用绝对定位调整位置
- `navigateTo`跳转及未登录重定向均支持传递正确的tab索引参数
2026-07-15 23:51:37 +08:00

34 KiB
Raw Blame History

线下付款下单失败排查

  • 文件:src/pages/shop/checkout.tsxsrc/api/shop/shopOrder/index.ts
  • 现象结算页选择线下付款payType=9下单时报错创建支付订单失败创建微信支付订单失败支付配置中应用ID为null或空
  • 原因:前端传参正确,但后端 POST /shop/shop-order 在 payType=9 时仍尝试创建微信支付订单,因未配置 appId 失败
  • 前端处理:
    • checkout.tsx 增加线下付款支付配置类错误的友好提示
    • createOrder 返回类型放宽为 WxPayResult | null,兼容非微信支付场景
  • 后端修复(/Users/gxwebsoft/JAVA/guilixu-java3 个文件):
    1. OrderBusinessService.javacreateOrder() 中 Integer 比较从 == 改为 .equals(),避免装箱陷阱导致 payType=8/9 拦截失效;微信支付分支前加日志
    2. ShopOrderServiceImpl.javacreateWxOrder() 方法开头增加 payType 守卫,非微信支付(1/102)直接抛异常,不走 JSAPI 分支
    3. ShopOrderController.javasaveLegacy() 方法增加 payType 检查,货到付款/线下付款/余额支付跳过 createWxOrder
  • 验证Maven compile 成功,无编译错误
  • 注意:后端服务需重启才能生效

结算页支付方式调整

  • 文件:src/pages/shop/checkout.tsx
  • 注释掉货到付款(id=8)选项,只保留线下付款(id=9)
  • 默认支付方式从 8(货到付款) 改为 9(线下付款)

门店订单修改金额功能

  • 文件:src/pages/store/orders/index.tsx
  • 新增 OpType 'editPrice',在订单卡片操作区增加橙色「修改金额」按钮
  • 仅对可操作订单(非已完成/非已关闭)显示
  • 弹窗包含当前实付展示、新金额输入digit、修改原因必填 textarea
  • 提交调用 updateShopOrder({ orderId, payPrice, comments }),金额变更记录追加到 comments
  • 复用现有 updateShopOrder APIPUT /shop/shop-order),无需新增接口

修复 VIP 会员价格显示和下单问题

  • 根因useVipStatus hook 已定义但从未被任何页面使用,所有页面直接调用 isVipMember() 读取同步缓存。该缓存仅在「个人中心」页刷新,导致 VIP 会员访问商品详情/购物车/结算页时缓存为 stale false不显示 dealerPrice
  • 修复文件(共 9 个):
    1. src/api/shop/shopOrder/model/index.tsOrderGoodsItem 新增 price?: string 字段,下单时传 VIP 单价
    2. src/pages/shop/product-detail.tsx — 引入 useVipStatus,用响应式 isVip 替换 isVipMember()
    3. src/pages/shop/checkout.tsx — 引入 useVipStatusgoodsPrice useMemo 依赖加 isVip;下单时 OrderGoodsItemprice 字段
    4. src/pages/shop/index.tsx — 引入 useVipStatus,替换 isVipMember()
    5. src/pages/shop/category.tsx — 引入 useVipStatus,替换 isVipMember()
    6. src/pages/shop/cart.tsx — 引入 useVipStatus,替换 isVipMember()
    7. src/contexts/CartContext.tsx — CartProvider 引入 useVipStatuscalcPrice 用响应式 isVip
    8. src/components/business/SkuSelector/index.tsx — 引入 useVipStatus,替换 isVipMember()
    9. src/components/common/ProductCard/index.tsx — 引入 useVipStatus,替换 isVipMember()
  • 原理useVipStatus 在组件挂载时异步调用 checkAndCacheVipStatus 查询后端并更新缓存+React statestate 变化触发重渲染,使所有价格显示/计算自动更新为 dealerPrice
  • 验证:npx taro build --type weapp 构建成功

门店订单修改金额 — 修改原因保存字段变更

  • 文件:src/pages/store/orders/index.tsxsrc/api/shop/shopOrder/model/index.ts
  • 变更:修改金额时的"修改原因"从追加到 comments 字段改为保存到 merchantRemarks(商户备注)字段
  • ShopOrder 接口新增 merchantRemarks?: string 字段
  • submitEditPriceupdateShopOrder 传参从 comments 改为 merchantRemarks

订单备注字段分离buyerRemarks / merchantRemarks / comments

  • 文件:src/api/shop/shopOrder/model/index.tssrc/pages/shop/checkout.tsxsrc/pages/store/orders/index.tsx
  • ShopOrderOrderCreateRequest 接口均新增 buyerRemarks?: string(买家备注)
  • 结算页 checkout.tsx:订单备注从 comments: remarks 改为 buyerRemarks: remarks
  • 门店订单列表 store/orders/index.tsx:订单卡片新增买家备注展示块(橙色背景,收货信息下方)
  • 字段约定:buyerRemarks=买家下单备注,merchantRemarks=商户修改金额原因,comments=系统/其他备注

修复线下付款订单状态显示

  • 文件:src/components/common/OrderCard/index.tsxsrc/pages/order/list.tsxsrc/pages/order/detail.tsx
  • 问题:线下付款(payType=9)下单成功后,订单列表和详情页显示"线下付款·待确认",用户认为状态不对
  • 修复内容:
    1. OrderCard getCardStatus:将线下付款(payType=9)与货到付款(payType=8)统一处理,!payStatus && !isCod && !isOffline 才显示"待付款",否则直接按 deliveryStatus 判断,线下付款订单显示"待发货"
    2. OrderCard showCloseButton:增加 !order.payStatus 条件,已付款订单不再显示取消按钮(需到详情页退款)
    3. OrderCard 按钮文案:从"关闭订单"改为"取消订单",与详情页一致
    4. list.tsx handleCloseOrder:弹窗标题改为"确认取消订单"toast 改为"订单已取消"
    5. detail.tsx getOrderDisplayStatus:线下付款未确认时显示"待发货"蓝色subtitle 保留付款引导文案
    6. detail.tsx getOrderPhase:保留 offline_pending 阶段,底部按钮仍为"取消订单+联系客服"(未确认收款可取消,非退款)

修复地址编辑页智能识别 bug

  • 文件:src/pages/user/address-edit.tsx
  • 问题:粘贴带标签的地址文本(如「收件人: 赵忠林 / 手机号码: 137... / 所在地区: 广西... / 详细地址: 大学东路...」)识别后:
    1. 姓名识别失败(之前只看文本开头的 2-4 个汉字,姓名不在开头)
    2. 详细地址保留了「收件人」「手机号码」「所在地区」「详细地址」等标签文字
  • 修复 parseAddressText
    1. 姓名优先匹配 (?:收件人|收货人|姓名|联系人)[:]\s*([\u4e00-\u9fa5]{2,4}),找不到再回退到 ^[\u4e00-\u9fa5]{2,4}
    2. 详细地址提取前先 strip 标签文字(收件人/手机号/所在地区/详细地址 + 中英冒号),再 strip 省市区,最后 strip 零散标签
  • 验证:用 Python 模拟Python 是用 JS 同款正则+RegionData解析截图中的输入
    • name: 赵忠林 ✓
    • phone: 13737128880 ✓
    • province/city/region: 广西壮族自治区/南宁市/西乡塘区 ✓
    • address: 西乡塘街道大学东路9号瀚林御景3栋1单元1101号房 ✓(街道+详细地址完整保留)
  • 项目 type-check (npx tsc --noEmit) 无报错

修复默认地址唯一性 bug

  • 问题:新增/编辑地址时勾选"设为默认"后,后端直接保存 isDefault=true,不清除其他地址的默认状态,导致出现多个默认地址
  • 后端修复(/Users/gxwebsoft/JAVA/guilixu-java3 个文件):
    1. ShopUserAddressService.java — 新增 clearDefault(userId, excludeId) 接口方法
    2. ShopUserAddressServiceImpl.java — 实现 clearDefault:查询该用户所有 isDefault=true 的地址(可排除指定 id批量设为 false
    3. ShopUserAddressController.javasave() 新增时若 isDefault=true 先调 clearDefault(userId, null)update() 编辑时若 isDefault=true 先调 clearDefault(userId, id) 排除当前地址
  • 前端修复(src/pages/user/address-edit.tsx
    • 保存成功后若 isDefault=true,再调一次 setDefaultAddress(savedId) 触发后端清理逻辑(双重保险)
    • 新增时从返回结果中提取 savedId
    • 修复 getShopUserAddress 返回值类型断言(as ShopUserAddress
  • 前端渲染兜底(src/hooks/useAddress.ts
    • loadAddresses 中加 ensureSingleDefault 工具函数:若后端返回多个 isDefault=true,只保留 createTime 最早的为默认,其余置 false
  • 后端 Maven 编译命令:"/Applications/IntelliJ IDEA Ultimate.app/Contents/plugins/maven/lib/maven3/bin/mvn" compileguilixu 的 mvnw 损坏,用 IntelliJ 自带 Maven
  • 编译结果:成功无报错
  • 注意:之前误改了 paopao-java 和 websopy-java 两个项目(已废弃),正确后端是 guilixu-java

用户商品浏览记录功能(全栈实现)

  • 需求:后台记录用户浏览商品的痕迹
  • 方案:去重累加模式(同一用户+商品只留一条visit_count 累加5分钟内去重防刷
  • 后端(/Users/gxwebsoft/JAVA/guilixu-java7 个文件 + 1 SQL
    1. sql/shop_goods_browse.sql — 建表 SQL含3个索引uk_user_goods 唯一 / idx_user_time / idx_goods_time
    2. ShopGoodsBrowse.java — Entity参照 ShopGoodsFavorite 范式,含 visitCount/lastVisitTime/browseSource + 关联商品快照字段)
    3. ShopGoodsBrowseParam.java — 查询参数merchantId 用 Long 与 BaseParam 一致,含 startTime/endTime 时间范围)
    4. ShopGoodsBrowseMapper.java + XML — Mapper关联 shop_goods 表查询商品名称/图片/价格)
    5. ShopGoodsBrowseService.java + Impl — Service核心 recordBrowse 去重累加逻辑查已有→5分钟内忽略/超过则累加→首次插入)
    6. ShopGoodsBrowseController.java — ControllerPOST 上报 / GET page 我的足迹 / DELETE 删单条 / DELETE 清空 / GET admin/page 后台查询)
  • 前端(xinlong-shop-taro4 个文件):
    1. src/api/shop/shopGoodsBrowse/model/index.ts — ShopGoodsBrowse + ShopGoodsBrowseParam 类型
    2. src/api/shop/shopGoodsBrowse/index.ts — reportBrowse(静默)/pageBrowseHistory/deleteBrowseRecord/clearBrowseHistory
    3. src/pages/shop/product-detail.tsx — addToHistory() 已登录时异步调 reportBrowse 上报
    4. src/pages/user/history-list/index.tsx — 改为服务端分页(登录)/ 本地兜底(未登录),支持删除单条、清空、上拉加载
    5. src/pages/user/user.tsx — 恢复浏览历史入口(取消注释)
  • 编译验证:后端 Maven compile 成功;前端 tsc --noEmit 无新增类型错误
  • 编译踩坑BaseController.success() 有 success(IPage) 重载,泛型方法返回类型用 ApiResult 避免 fail() 分支类型不兼容
  • 待执行:数据库需手动执行 shop_goods_browse.sql 建表
  • 后续可选:后台管理页面(/Users/gxwebsoft/VUE/guilixu-adminVue3+AntD Vue

后台管理端浏览记录页面guilixu-admin

  • 后端补充:ShopGoodsBrowseController 新增 DELETE /admin/{id} 管理端删除接口(不限定用户),原 DELETE /{id} 限定当前用户
  • 前端文件(/Users/gxwebsoft/VUE/guilixu-admin4 个文件):
    1. src/api/shop/shopGoodsBrowse/model/index.ts — ShopGoodsBrowse + ShopGoodsBrowseParam 类型
    2. src/api/shop/shopGoodsBrowse/index.ts — pageShopGoodsBrowse(调 /admin/page) + removeShopGoodsBrowse(调 /admin/{id})
    3. src/views/shop/shopGoodsBrowse/index.vue — 页面ele-pro-table 表格ID/用户ID/商品图片/商品名称/价格/浏览次数/浏览来源/最后浏览时间/首次浏览/操作删除)
    4. src/views/shop/shopGoodsBrowse/components/search.vue — 搜索栏用户ID/商品ID/浏览来源/时间范围)
  • 管理后台菜单机制后端动态菜单sys_menu 表),前端按 component 字段约定加载 src/views/{component}/index.vue
  • 菜单配置:后台"系统管理 → 菜单管理"新增component 填 shop/shopGoodsBrowse
  • 注意:管理后台 API 用 res.data.code/res.data.data(非小程序端的 res.coderequest 传参用 { params } 包裹
  • 编译验证:后端 Maven compile 成功;前端 vue-tsc 无 shopGoodsBrowse 相关错误
  • 待操作:后端需重新部署(新增了 admin 删除接口);管理后台需配置菜单

帮助中心联系客服入口改为直连微信客服

  • 文件:src/pages/user/help-center/index.tsx
  • 改动:点击「联系在线客服」不再跳转 /pages/user/customer-service/index,而是直接用 <Button openType="contact"> 调起微信客服
  • 关键属性:openType="contact" + sessionFrom="help_center" + showMessageCard + sendMessageTitle="欢迎咨询" + onContact 回调
  • Button 样式重置height:auto / lineHeight:normal / padding:16px / border:none / borderRadius:8px保持原视觉白底圆角卡片
  • 引入:从 @tarojs/components 新增导入 Button
  • 注意:微信客服 Button 在开发者工具中无效,需真机预览测试

订单详情页查看物流改造(显示 shopOrderDelivery

  • 需求:订单详情页点击「查看物流」跳转 /pages/order/logistics,原页面强制要求 URL 带 expressNo + expressCompany但详情页只传了 orderId导致 toast「缺少物流参数」+ 空状态
  • 后端修复(/Users/gxwebsoft/JAVA/guilixu-java1 个文件):
    • ShopOrderServiceImpl.javagetByIdRel(orderId) 原本只调 baseMapper.selectListRel(param),没有像 pageRel 那样填充 shopOrderDelivery。新增私有方法 fillShopOrderDelivery(order),在快递配送(deliveryType==0/null)且已发货(deliveryStatus>10)时调 shopOrderDeliveryService.getByOrderId(orderId) 查发货单,并关联 shopExpressService.getById(expressId) 取 expressName 填充
    • 编译验证Maven compile 成功
  • 前端类型补充(src/api/shop/shopOrder/model/index.ts
    • 新增 ShopOrderDelivery 接口(与后端 ShopOrderDelivery 实体对应deliveryId/orderId/deliveryMethod/expressId/expressName/sendName/sendPhone/sendAddress/expressNo/createTime 等)
    • ShopOrder 接口新增 shopOrderDelivery?: ShopOrderDelivery | null 字段
  • 前端物流页改造(src/pages/order/logistics.tsx
    • 不再强制要求 URL 带 expressNo/expressCompany只要求 orderId
    • getShopOrder(orderId) 加载订单(含后端填充的 shopOrderDelivery
    • 运单号优先级URL expressNo → delivery.expressNo → order.expressNo
    • 物流轨迹:仅在有 expressNo + expressCompany 代码时才查询 queryLogistics失败不阻塞发货单展示
    • 新增「发货信息」卡:发货人/联系方式/发货地址/物流单号(可复制)/发货时间/发货方式(10手动/20无需物流/30电子面单)
    • 新增「收货信息」卡:从订单读 realName/mobile/address
    • 保留原物流轨迹时间线(仅在查询到时展示)和温馨提示
    • 空状态:订单接口也失败时才显示
  • 后端需重启服务才生效

修复 VIP 价格首页不显示 + 后端不认 price 字段

  • 问题 1首页src/pages/index/index.tsxtabBar 首页)没接入 useVipStatus,热销推荐价格一直显示 product.price 而非 dealerPrice。商品详情/商城列表/购物车/分类都修了,唯独漏了 tabBar 首页
  • 问题 2后端 DTO 缺字段):前端 OrderGoodsItem 已传 price 字段,但后端 OrderCreateRequest.OrderGoodsItem(嵌套静态类)根本没有 price 字段Jackson 反序列化直接丢弃。后端 OrderBusinessService.validateAndCalculateTotal()saveOrderGoods() 都用 goods.getPrice() 兜底 —— 这就是「前端传的对、后端处理就错」的根本原因
  • 修复3 个文件)
    1. src/pages/index/index.tsx
      • 引入 useVipStatus,加 const { isVip } = useVipStatus()
      • getHotDisplayPrice()VIP + 有 dealerPrice → {price: dealerPrice, original: price},否则原逻辑
      • Price 组件传 price + original + 旁边加金色「VIP」角标
    2. /Users/gxwebsoft/JAVA/guilixu-java/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java
      • OrderGoodsItem 内部类加 private BigDecimal price; 字段 + @DecimalMin("0") 校验
    3. /Users/gxwebsoft/JAVA/guilixu-java/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java
      • validateAndCalculateTotal()saveOrderGoods() 两处重构:先取 sku无论价格来源都要校验 sku 合法性+取库存),再用「前端 price > sku.price > goods.price」优先级决定 actualPrice
      • 注意不要把 sku 查询和 price 选择耦合在同一个 if-else 链里,否则 VIP+多规格商品会跳过 sku 库存校验
  • 编译验证Maven compile 成功无报错;后端需重启服务才能生效
  • 价格字段约定(再确认)
    • goods.price - 到手价(主价格)
    • goods.salePrice - 市场价(划掉的)
    • goods.dealerPrice - VIP 专享价
    • 前端传 dealerPriceOrderGoodsItem.price,后端通过该字段下单

修复订单列表"待付款"Tab 下状态显示"待发货"

  • 文件:src/components/common/OrderCard/index.tsx
  • 问题:用户切到"待付款"Tab下方订单卡片状态文字显示"待发货",与 Tab 不一致
  • 根因:线下付款(payType=9)订单的 payStatus=false(数据库 pay_status=0后端 statusFilter=0 的 SQL 条件 pay_status=0 AND order_status=0 把它归入"待付款"Tab 是合理的(待商家确认收款)。但 OrderCard 的 getCardStatusisOffline=true!isOffline=false → 跳过"待付款"分支,进入 if (deliveryStatus === 10) → 显示"待发货"
  • 关键业务规则:
    • 货到付款(8):后端 Controller saveLegacy() 下单时已 setPayStatus(true),不会进入 statusFilter=0
    • 线下付款(9):保持 payStatus=false,会进入 statusFilter=0
    • 微信支付(1):未付款 payStatus=false,正常进入 statusFilter=0
  • 修复:getCardStatus 第 30 行去掉 && !isOffline 条件,改为 if (!payStatus && !isCod) return { text: '待付款' }。保留 !isCod(货到付款的特殊兜底,虽然正常流程不会走到这里);清理掉不再使用的 isOffline 变量
  • 决策依据:用户认可"线下付款订单出现在待付款 Tab"是合理的,因此只改前端文案判断,不动后端 SQL

修复购物车同一商品并列显示(应合并数量)

  • 现象:购物车页面同一商品并列显示多行,本应合并数量到一行
  • 根因:后端 ShopCartController.save() 直接调用 MyBatis-Plus 默认 shopCartService.save(),每次加购都 INSERT 新行,没有"同用户+商品+SKU已存在则累加数量"逻辑
  • 后端修复(/Users/gxwebsoft/JAVA/guilixu-java3 个文件):
    1. ShopCartService.java — 接口新增 boolean addToCart(ShopCart shopCart) 方法
    2. ShopCartServiceImpl.java — 实现 addToCart:按 user_id+goods_id+sku_id 查询是否已存在skuId 为 null/0 时匹配 IS NULL 或 =0存在则累加 cart_num 后 updateById不存在才 save引入 QueryWrapper
    3. ShopCartController.javasave() 接口从 shopCartService.save(shopCart) 改为 shopCartService.addToCart(shopCart)
  • 注意:ShopCart.userIdInteger 类型(不是 LonggoodsId 是 Long
  • 历史数据清理:提供 sql/shop_cart_merge_duplicates.sql,按 (user_id,goods_id,sku_id) 分组合并 cart_num 并删除重复行sku_id 可能为 NULL<=> null-safe 匹配);执行前先备份
  • 编译验证Maven compile 成功
  • 后端需重启服务才生效

购物车加购报 spec_info 字段缺失

  • 现象:添加购物车时报 Unknown column 'spec_info' in 'field list'
  • 原因:后端实体 ShopCart.java 定义了 specInfo 字段(存储"颜色:红色|尺寸:L"这类规格信息),但数据库表 shop_cart 未建 spec_info 列;ShopCartParamOrderBusinessService 均在用该字段,属设计需保留
  • 修复:新建迁移脚本 /Users/gxwebsoft/JAVA/guilixu-java/sql/shop_cart_add_spec_info.sqlALTER TABLE shop_cart ADD COLUMN spec_info VARCHAR(500) NULL ... AFTER spec,需在数据库执行

修复登录页「未授权手机号」无降级入口

  • 现象:用户反复反馈"又又又登录不了了",截图显示点击「手机号快捷登录」按钮后 toast 弹「未授权手机号」,用户被困在登录页
  • 根因:src/passport/login.tsx 第 162-173 行 onGetPhoneNumber 回调里 detail.code 为 undefined用户拒绝授权errMsg 含 user deny/fail)时只 toast 了一句生硬文案,没有任何引导。但项目里其实有 /passport/sms-login 短信登录页(且在 app.config.ts 路由表里),login.tsx 却完全没暴露这个入口。register.tsx 同样有此问题,且其 weapp 环境下隐藏了短信按钮(原 {!isWeapp && ...} 判断)
  • 修复2 个文件 + 1 个 scss
    1. src/passport/login.tsx
      • 新增 goSmsLogin() 函数:透传 redirect + 邀请参数(inviter/source/t)跳 /passport/sms-login
      • 新增 showPhoneAuthFailedModal(errMsg):按 errMsg 区分场景弹窗引导
        • user deny → "您已取消微信手机号授权,可改用短信验证码登录"
        • privacy → "微信未授权获取手机号,可改用短信验证码登录"
        • no permission → "小程序暂未获得手机号授权,可改用短信验证码登录"
        • 其他 → 通用"请尝试使用短信验证码登录"
        • 弹窗带"短信登录"按钮自动跳 sms-login"我知道了"关闭
      • 失败分支从 Taro.showToast 改为 showPhoneAuthFailedModal(errMsg)
      • 主登录按钮下方新增长期可见的「使用短信验证码登录」链接(仿 register.tsx 的 goSmsLogin
    2. src/passport/login.scss
      • 新增 .login-methods__alt / .login-methods__alt-link 样式(白色下划线小字,居中)
    3. src/passport/register.tsx
      • 同步加 showPhoneAuthFailedModal(errMsg) 弹窗引导
      • 去掉 {!isWeapp && <Button>短信验证码注册/登录</Button>} 包裹,让 weapp 下也始终显示短信按钮作为降级入口
  • 类型检查:npx tsc --noEmit 无报错
  • 业务规则:项目当前仅信任微信一键登录作为主路径,短信登录一直是兜底通道。后续如发现用户量大,可考虑直接在登录页主流程就两个按钮并排(不再让微信登录独占)

sms-login 页面 UI 重构

  • 原状:截图显示基本是裸的——只看到两个孤零零的 placeholder 文字发送验证码按钮和登录按钮都看不见。NutUI 的 Input + Button 在 Taro 小程序上经常渲染不出边框/背景,外层 flex justify-between 也没把验证码按钮挤出来
  • 改造2 个文件):
    1. src/passport/sms-login.tsx — 弃用 NutUI 改用 Taro 原生 View/Text/Input,重做整页布局
      • 渐变背景 + 浮动光圈(和 login.tsx 同一套色系 #667eea → #764ba2
      • 顶部圆形 logo140rpx 白底圆角 + 阴影)+ 标题 + 副标题
      • 白色卡片输入区(圆角 28rpx + 阴影):手机号(📞+ 灰线分隔 + 验证码(🔐 + 绿色胶囊「获取验证码」按钮)
      • 绿色渐变登录大按钮(圆角 50rpx + 阴影 + 字间距 4rpx
      • 「← 返回微信快捷登录」白色下划线链接(一键 navigateBack 回到 login.tsx避免用户被困
      • 底部协议 + 服务协议/隐私政策超链接
      • 业务逻辑state、60s 倒计时、handleSendCode/handleLogin 校验、redirect 跳转、邀请参数透传)全部保留
    2. src/passport/sms-login.scss(新文件)— 配套样式渐变背景、浮动光圈动画gradientMove + float、卡片毛玻璃、按钮 hover 效果scale 0.98、分阶段入场动画slideDown / slideUp / fadeIn
  • 类型检查:npx tsc --noEmit 无报错(项目原本的 @tarojs/taro 类型错误与本次无关)
  • 编译验证:npx taro build --type weapp 14.89s 成功
  • 设计稿预览:.workbuddy/sms-login-preview.html(按 375×812 真机尺寸渲染的 mockup可直接浏览器打开看
  • 业务规则login/register/sms-login 三个页面已统一视觉风格(同一套渐变 + 圆形 logo + 大字标题 + 白色卡片),后续新增 passport 类页面照搬这一套即可

修复首页轮播图只显示一张的问题

  • 文件:src/pages/index/index.tsx
  • 现象:后台一个广告位下配置了 3 张图片,但小程序首页轮播只显示第一张
  • 根因:原代码把每个 CmsAd 作为一个 SwiperItem,图片只取 item.imageList?.[0]?.url,所以无论后台配几张都只渲染首图
  • 修复:
    • 新增 bannerSlides:用 flatMap 把每个广告位下的 imageList 全部展开成独立轮播项key 用 adId-uid(无 uid 则 fallback 到索引)
    • 保留 item.image 单图兜底逻辑,兼容只配单张图片的老数据
    • 渲染 SwiperItem 改为遍历 bannerSlides,点击跳转仍使用对应广告位的 path
  • 验证:代码逻辑走查通过;项目原有 npx tsc --noEmit@tarojs/taro 类型定义错误与本次修改无关

首页轮播图高度改为按后台 width/height 自适应

  • 文件:src/pages/index/index.tsx
  • 原状:Swiperheight 固定写死 160px,与后台广告位配置脱节
  • 修复:
    • 新增 bannerWrapWidth 状态,页面渲染后用 Taro.createSelectorQuery().select('.index-banner-wrap').boundingClientRect() 获取容器实际宽度
    • 新增 bannerHeightuseMemo取第一个带有效 width/height 的广告位,按 容器宽度 * (height / width) 计算高度;无值或解析失败则 fallback 160px
    • 轮播容器增加 index-banner-wrap 类名用于选择器查询
    • Swiper 和「暂无轮播图」占位区的高度都改用 bannerHeight
  • 注意:宽度仍由容器 w-full + mx-3 边距控制,不直接套用后台 width 像素值;高度按比例缩放适配不同屏幕

修复「我的」页订单数量统计全为 0 的 bug

  • 文件:src/api/shop/shopUserCard/index.tssrc/api/shop/shopUserCard/model/index.tssrc/pages/user/user.tsx
  • 根因:前后端字段名不匹配
    • 后端 /api/user/orders/stats 返回 UserOrderStats DTO 字段:total/waitPay/waitDeliver/waitVerify/waitReceive/waitComment/completed/refund/deleted/canceled(参见 com.gxwebsoft.shop.dto.UserOrderStats
    • 前端 getUserOrderStats() 却读取 pending/paid/shipped/completed,导致 pending/paid/shipped 永远 undefined → 兜底为 0只有 completed 字段名巧合一致才有数
  • 附带 buguser.tsx「我的订单」快捷入口的 status 值写的是 0/1/2/3OrderListStatus0待付款 1待发货 3待收货 5已完成不一致点击「待收货」「已完成」会跳到订单列表页错误的 tab
  • 修复:
    1. UserOrderStats 接口字段与后端 DTO 完全对齐(含 total/waitPay/waitDeliver/waitVerify/waitReceive/waitComment/completed/refund/deleted/canceled
    2. getUserOrderStats() 改读后端真实字段,并用 Number(d.x) || 0 兜底
    3. user.tsx 订单快捷入口 count 改用 waitPay/waitDeliver/waitReceive/completedstatus 改为 0/1/3/5OrderListStatus 对齐
    4. localStorage 缓存 key 从 user_order_stats_v2 升级到 v3,让旧格式缓存自动失效,避免冷启动先渲染旧格式导致仍是 0
  • 关键约定:OrderListStatus 是前端订单 Tab 筛选状态的唯一真相源(-1全部/0待付款/1待发货/2待核销/3待收货/4待评价/5已完成/6退款/7已删除所有跳订单列表的入口传 status 都要对齐它

优化「我的订单」角标样式2026-07-15 22:55

  • 文件:src/pages/user/user.tsx
  • 用户反馈:截图中「已完成」下方出现了一个"0"字hot reload 之前旧版代码里 <View>{item.count}</View> 直接渲染了 count0 也会显示)。同时希望统计数字更"右上角"
  • 修改:
    1. 角标位置 top-0 right-2-top-1 -right-1:让小红点紧贴 cell 右上角外缘,更像标准徽标
    2. 角标尺寸 text-xs + 自由拉伸 → min-w-[16px] h-4 + 10px/12px 字体:单数字也保持圆形,多位数字自动变椭圆
    3. 0 判断 item.count && item.count > 0item.count && Number(item.count) > 0:用 Number() 包一层,防止后端返回字符串 "0" 时 > 0 误判

抽取通用 Badge 组件2026-07-15 23:10

  • 新文件:src/components/common/Badge/index.tsx
  • 用途:替代项目里多处复制粘贴的内联角标 Viewuser.tsx / index/index.tsx / store/center/index.tsx 都有 min-w-[16px] h-4 px-1 bg-red-500 rounded-full 这种重复写法)
  • API
    interface BadgeProps {
      count?: number | null   // 0 / null / undefined / 负数 → 不渲染
      max?: number            // 默认 99超过显示 `${max}+`
      className?: string      // 外层定位 className如 'absolute -top-1 -right-1'
      color?: string          // 默认 #ef4444red-500
      fontSize?: number       // 默认 10px
    }
    
  • user.tsx 已替换:内联 {Number(item.count) > 0 && <View ...>}<Badge count={item.count} className='absolute -top-1 -right-1' />
  • 风格遵循项目 common 组件约定:函数式组件 + 默认导出 + Taro View/Text + Tailwind className
  • 待办(未做):首页 src/pages/index/index.tsx:248 和门店中心 src/pages/store/center/index.tsx:218 也有相同内联角标写法,后续可统一替换为 Badge

抽取通用 ArrowRight 组件2026-07-15 23:13

  • 新文件:src/components/common/ArrowRight/index.tsx
  • 用途:替代项目里到处可见的 {'>'} / '>' 文本箭头
  • 实现CSS V 形border-top + border-right + rotate(45deg)),颜色用 currentColor 跟父级 text-* 类走
  • API
    interface ArrowRightProps {
      className?: string  // 颜色用 text-* 类(如 'text-gray-400'),间距用 ml-1 等
      size?: number       // V 形边长 px默认 6
      thickness?: number  // 描边粗细 px默认 1.5
    }
    
  • user.tsx 已替换 3 处 {'>'}
    1. 用户卡片头部箭头(白色 60% 透明)→ <ArrowRight className='text-white text-opacity-60' />
    2. 「全部订单 >」→ 拆成 <View className='flex items-center text-xs text-gray-400'><Text>全部订单</Text><ArrowRight className='ml-1' /></View>
    3. 菜单项末尾箭头amber-700 / gray-300<ArrowRight className={...} />
  • 关键点Tailwind 的 text-opacity-60 会通过 --tw-text-opacity 影响 colorcurrentColor 会继承这个带 opacity 的 color所以 ArrowRight 的透明度也会跟随
  • 待办(未做):其它页面也有 {'>'} 文本箭头,后续可统一替换

统一替换 index/index.tsx 和 store/center 的内联角标为 Badge2026-07-15 23:17

  • Badge 组件 API 扩展:新增 size(默认 16fontWeight(默认 'normal'参数padding 从 className 的 px-1 改为 inline style 根据 size 自动计算16→2px20→3px避免与调用方 className 冲突
  • src/pages/index/index.tsx:248数字角标16x16/10px<Badge count={newOrderCount} className='absolute -top-1 -right-1' />
    • 注意:保留了 isClerk && newOrderCount > 0 外层判断(虽然 Badge 内部 0 不渲染,但下面还有 newOrderCount === 0 的小红点逻辑,保留判断让语义更明确)
  • src/pages/store/center/index.tsx:218数字角标20x20/11px/加粗)→ <Badge count={getBadgeCount(card.key)} className='absolute -top-1 -right-1' size={20} fontSize={11} fontWeight='bold' />
    • 简化了外层判断:card.showBadge && getBadgeCount > 0card.showBadgeBadge 内部已处理 0 不渲染)
  • 项目内联角标已全部统一为 Badge 组件,共 3 处user.tsx / index/index.tsx / store/center/index.tsx

tabBar「订单」换「购物车」后的链接修正2026-07-15 23:31

  • 背景tabBar 原「订单」(pages/order/list) 改为「购物车」(pages/shop/cart)order/list 不再是 tabBar 页
  • A. 6 处 switchTab(order/list)navigateToswitchTab 只能跳 tabBar 页,否则失败):
    • pages/shop/checkout.tsx:304下单成功跳订单列表
    • pages/user/user.tsx:144,149handleOrderTabClick / handleViewAllOrders
    • pages/user/index.tsx:158,175,190订单概览数/全部订单/订单状态快捷入口)
  • B. 4 处 tabBar 判断列表更新为实际 4 个 tabBarindex/shop/shop-cart/user
    • pages/user/index.tsx:11、pages/index/index.tsx:185tabBarPages
    • passport/login.tsxtabBarUrls顺带去掉非 tabBar 的 points/index
    • passport/auth-flow.ts:12TABBAR_URLS原含错误路径 order/order
  • C. 2 处 isTabBarUrl 修正(原路径 cart/cart、category/index 不存在):
    • passport/register.tsx:88、passport/sms-login.tsx:36
  • D. pages/order/list.tsx:127 注释更新(去掉"tabBar 页"描述)
  • 遗留未改预先存在pages/user/index.tsx:140 switchTab(points/index) 积分页非 tabBar会失败

product-detail 购物车角标改用 Badge 组件2026-07-15 23:42

  • src/pages/shop/product-detail.tsx 底部购物车入口的数量角标,原为手写内联 View+Text带白边框、top:-6px/right:-10px 定位),改为 <Badge count={totalCount} className='absolute -top-1 -right-1' />
  • 用法与 user.tsx "我的订单"图标角标(:284完全一致外层已有 relative inline-flex 容器Badge 的 absolute 定位正常
  • 新增 import Badge from '@/components/common/Badge'
  • 去掉了原来的白色边框,与"我的订单"角标样式统一

修复订单快捷入口 tab 错位 bug2026-07-15 23:48

  • Bugpages/user/user.tsx "我的订单"快捷入口(待付款/待发货/待收货/已完成)把"订单状态值"0/1/3/5写入 order_tab storagenavigateTo 到 order/list 后,order/list.tsx:42useState(Number(initTab) || 0) 把传入值直接当 tabIndex 用 → 全部错位一个 tab待付款 0→全部, 待发货 1→待付款, 待收货 3 巧合对, 已完成 5 越界→0→全部
  • pages/user/index.tsx 传 0/1/2/3 也全错位一个 tab
  • order/list.tsx tabList 顺序index 0=全部 / 1=待付款 / 2=待发货 / 3=待收货 / 4=已完成
  • 修复:调用方把字段从 status(订单状态)改为 tabIndex1/2/3/4navigateTo 加 ?tab=${tabIndex}order/list 已支持 router.params.tab 优先于 storage未登录拦截的 redirect 也带 ?tab=
  • 改动:
    • src/pages/user/user.tsxhandleOrderTabClick 参数 statustabIndex + redirect/navigateTo 带 ?tab=menuItems 字段 status(0/1/3/5)tabIndex(1/2/3/4)key={item.tabIndex}onClickitem.tabIndex
    • src/pages/user/index.tsx:硬编码数组 status(0/1/2/3)tabIndex(1/2/3/4)key={item.tabIndex}、onClick + ?tab=${item.tabIndex}
  • 其他"全部订单"类入口user.tsx:149 handleViewAllOrders、user/index.tsx:22/158/175不传 ?tab=,进默认"全部" tab合理