- 将原来 switchTab 跳转“订单”页改为 navigateTo,以适配非 tabBar 页面 - 统一更新多个文件中关于订单页的跳转方式共计 6 处 - 更新 tabBar 配置,从包含“订单”页改为实际的四个 tabBar 页面 - 修正 isTabBarUrl 相关判断,去除不存在或非 tabBar 路径 - 调整多个 tabBar 判断列表,保证逻辑一致性与正确性 - 修改购物车页图片压缩宽度加入限制,优化资源加载 - 更新订单列表页注释,去除“tabBar 页”描述 - 备注遗留问题,部分积分页跳转仍用错误方式,待进一步修复
348 lines
32 KiB
Markdown
348 lines
32 KiB
Markdown
## 线下付款下单失败排查
|
||
- 文件:`src/pages/shop/checkout.tsx`、`src/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-java`,3 个文件):
|
||
1. `OrderBusinessService.java` — `createOrder()` 中 Integer 比较从 `==` 改为 `.equals()`,避免装箱陷阱导致 payType=8/9 拦截失效;微信支付分支前加日志
|
||
2. `ShopOrderServiceImpl.java` — `createWxOrder()` 方法开头增加 payType 守卫,非微信支付(1/102)直接抛异常,不走 JSAPI 分支
|
||
3. `ShopOrderController.java` — `saveLegacy()` 方法增加 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` API(PUT `/shop/shop-order`),无需新增接口
|
||
|
||
## 修复 VIP 会员价格显示和下单问题
|
||
- **根因**:`useVipStatus` hook 已定义但从未被任何页面使用,所有页面直接调用 `isVipMember()` 读取同步缓存。该缓存仅在「个人中心」页刷新,导致 VIP 会员访问商品详情/购物车/结算页时缓存为 stale false,不显示 dealerPrice
|
||
- **修复文件**(共 9 个):
|
||
1. `src/api/shop/shopOrder/model/index.ts` — `OrderGoodsItem` 新增 `price?: string` 字段,下单时传 VIP 单价
|
||
2. `src/pages/shop/product-detail.tsx` — 引入 `useVipStatus`,用响应式 `isVip` 替换 `isVipMember()`
|
||
3. `src/pages/shop/checkout.tsx` — 引入 `useVipStatus`,`goodsPrice` useMemo 依赖加 `isVip`;下单时 `OrderGoodsItem` 传 `price` 字段
|
||
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 引入 `useVipStatus`,`calcPrice` 用响应式 `isVip`
|
||
8. `src/components/business/SkuSelector/index.tsx` — 引入 `useVipStatus`,替换 `isVipMember()`
|
||
9. `src/components/common/ProductCard/index.tsx` — 引入 `useVipStatus`,替换 `isVipMember()`
|
||
- **原理**:`useVipStatus` 在组件挂载时异步调用 `checkAndCacheVipStatus` 查询后端并更新缓存+React state,state 变化触发重渲染,使所有价格显示/计算自动更新为 dealerPrice
|
||
- 验证:`npx taro build --type weapp` 构建成功
|
||
|
||
## 门店订单修改金额 — 修改原因保存字段变更
|
||
- 文件:`src/pages/store/orders/index.tsx`、`src/api/shop/shopOrder/model/index.ts`
|
||
- 变更:修改金额时的"修改原因"从追加到 `comments` 字段改为保存到 `merchantRemarks`(商户备注)字段
|
||
- `ShopOrder` 接口新增 `merchantRemarks?: string` 字段
|
||
- `submitEditPrice` 中 `updateShopOrder` 传参从 `comments` 改为 `merchantRemarks`
|
||
|
||
## 订单备注字段分离(buyerRemarks / merchantRemarks / comments)
|
||
- 文件:`src/api/shop/shopOrder/model/index.ts`、`src/pages/shop/checkout.tsx`、`src/pages/store/orders/index.tsx`
|
||
- `ShopOrder` 和 `OrderCreateRequest` 接口均新增 `buyerRemarks?: string`(买家备注)
|
||
- 结算页 `checkout.tsx`:订单备注从 `comments: remarks` 改为 `buyerRemarks: remarks`
|
||
- 门店订单列表 `store/orders/index.tsx`:订单卡片新增买家备注展示块(橙色背景,收货信息下方)
|
||
- 字段约定:`buyerRemarks`=买家下单备注,`merchantRemarks`=商户修改金额原因,`comments`=系统/其他备注
|
||
|
||
## 修复线下付款订单状态显示
|
||
- 文件:`src/components/common/OrderCard/index.tsx`、`src/pages/order/list.tsx`、`src/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-java`,3 个文件):
|
||
1. `ShopUserAddressService.java` — 新增 `clearDefault(userId, excludeId)` 接口方法
|
||
2. `ShopUserAddressServiceImpl.java` — 实现 `clearDefault`:查询该用户所有 `isDefault=true` 的地址(可排除指定 id),批量设为 false
|
||
3. `ShopUserAddressController.java` — `save()` 新增时若 `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" compile`(guilixu 的 mvnw 损坏,用 IntelliJ 自带 Maven)
|
||
- 编译结果:成功无报错
|
||
- 注意:之前误改了 paopao-java 和 websopy-java 两个项目(已废弃),正确后端是 guilixu-java
|
||
|
||
## 用户商品浏览记录功能(全栈实现)
|
||
- 需求:后台记录用户浏览商品的痕迹
|
||
- 方案:去重累加模式(同一用户+商品只留一条,visit_count 累加,5分钟内去重防刷)
|
||
- 后端(`/Users/gxwebsoft/JAVA/guilixu-java`,7 个文件 + 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` — Controller(POST 上报 / GET page 我的足迹 / DELETE 删单条 / DELETE 清空 / GET admin/page 后台查询)
|
||
- 前端(`xinlong-shop-taro`,4 个文件):
|
||
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<T>) 重载,泛型方法返回类型用 ApiResult<?> 避免 fail() 分支类型不兼容
|
||
- 待执行:数据库需手动执行 `shop_goods_browse.sql` 建表
|
||
- 后续可选:后台管理页面(`/Users/gxwebsoft/VUE/guilixu-admin`,Vue3+AntD Vue)
|
||
|
||
## 后台管理端浏览记录页面(guilixu-admin)
|
||
- 后端补充:`ShopGoodsBrowseController` 新增 `DELETE /admin/{id}` 管理端删除接口(不限定用户),原 `DELETE /{id}` 限定当前用户
|
||
- 前端文件(`/Users/gxwebsoft/VUE/guilixu-admin`,4 个文件):
|
||
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.code`),request 传参用 `{ 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-java`,1 个文件):
|
||
- `ShopOrderServiceImpl.java` — `getByIdRel(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.tsx`(tabBar 首页)没接入 `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 专享价
|
||
- 前端传 `dealerPrice` 到 `OrderGoodsItem.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 的 `getCardStatus` 因 `isOffline=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-java`,3 个文件):
|
||
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.java` — `save()` 接口从 `shopCartService.save(shopCart)` 改为 `shopCartService.addToCart(shopCart)`
|
||
- 注意:`ShopCart.userId` 是 `Integer` 类型(不是 Long),`goodsId` 是 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` 列;`ShopCartParam`、`OrderBusinessService` 均在用该字段,属设计需保留
|
||
- 修复:新建迁移脚本 `/Users/gxwebsoft/JAVA/guilixu-java/sql/shop_cart_add_spec_info.sql`,`ALTER 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`)
|
||
- 顶部圆形 logo(140rpx 白底圆角 + 阴影)+ 标题 + 副标题
|
||
- 白色卡片输入区(圆角 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`
|
||
- 原状:`Swiper` 的 `height` 固定写死 `160px`,与后台广告位配置脱节
|
||
- 修复:
|
||
- 新增 `bannerWrapWidth` 状态,页面渲染后用 `Taro.createSelectorQuery().select('.index-banner-wrap').boundingClientRect()` 获取容器实际宽度
|
||
- 新增 `bannerHeight`(useMemo):取第一个带有效 `width/height` 的广告位,按 `容器宽度 * (height / width)` 计算高度;无值或解析失败则 fallback `160px`
|
||
- 轮播容器增加 `index-banner-wrap` 类名用于选择器查询
|
||
- `Swiper` 和「暂无轮播图」占位区的高度都改用 `bannerHeight`
|
||
- 注意:宽度仍由容器 `w-full` + `mx-3` 边距控制,不直接套用后台 `width` 像素值;高度按比例缩放适配不同屏幕
|
||
|
||
|
||
|
||
## 修复「我的」页订单数量统计全为 0 的 bug
|
||
- 文件:`src/api/shop/shopUserCard/index.ts`、`src/api/shop/shopUserCard/model/index.ts`、`src/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` 字段名巧合一致才有数
|
||
- 附带 bug:user.tsx「我的订单」快捷入口的 `status` 值写的是 0/1/2/3,与 `OrderListStatus`(0待付款 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/completed`,status 改为 `0/1/3/5` 与 `OrderListStatus` 对齐
|
||
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>` 直接渲染了 count,0 也会显示)。同时希望统计数字更"右上角"
|
||
- 修改:
|
||
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 > 0` → `item.count && Number(item.count) > 0`:用 `Number()` 包一层,防止后端返回字符串 "0" 时 `> 0` 误判
|
||
|
||
## 抽取通用 Badge 组件(2026-07-15 23:10)
|
||
- 新文件:`src/components/common/Badge/index.tsx`
|
||
- 用途:替代项目里多处复制粘贴的内联角标 View(user.tsx / index/index.tsx / store/center/index.tsx 都有 `min-w-[16px] h-4 px-1 bg-red-500 rounded-full` 这种重复写法)
|
||
- API:
|
||
```ts
|
||
interface BadgeProps {
|
||
count?: number | null // 0 / null / undefined / 负数 → 不渲染
|
||
max?: number // 默认 99,超过显示 `${max}+`
|
||
className?: string // 外层定位 className,如 'absolute -top-1 -right-1'
|
||
color?: string // 默认 #ef4444(red-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:
|
||
```ts
|
||
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` 影响 color,currentColor 会继承这个带 opacity 的 color,所以 ArrowRight 的透明度也会跟随
|
||
- 待办(未做):其它页面也有 `{'>'}` 文本箭头,后续可统一替换
|
||
|
||
## 统一替换 index/index.tsx 和 store/center 的内联角标为 Badge(2026-07-15 23:17)
|
||
- Badge 组件 API 扩展:新增 `size`(默认 16)和 `fontWeight`(默认 'normal')参数;padding 从 className 的 `px-1` 改为 inline style 根据 size 自动计算(16→2px,20→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 > 0` → `card.showBadge`(Badge 内部已处理 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)` → `navigateTo`(switchTab 只能跳 tabBar 页,否则失败):
|
||
- pages/shop/checkout.tsx:304(下单成功跳订单列表)
|
||
- pages/user/user.tsx:144,149(handleOrderTabClick / handleViewAllOrders)
|
||
- pages/user/index.tsx:158,175,190(订单概览数/全部订单/订单状态快捷入口)
|
||
- B. 4 处 tabBar 判断列表更新为实际 4 个 tabBar(index/shop/shop-cart/user):
|
||
- pages/user/index.tsx:11、pages/index/index.tsx:185(tabBarPages)
|
||
- passport/login.tsx(tabBarUrls,顺带去掉非 tabBar 的 points/index)
|
||
- passport/auth-flow.ts:12(TABBAR_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,会失败
|