fix(weapp): 修复支付页nutui组件导致内容空白问题

- 替换支付页中nutui的Button、Cell、Price、Divider组件为Taro原生组件或项目自定义组件
- 修正商品名称、价格类型和支付金额显示,使用flex布局及Text组件自绘内容
- 调整按钮样式,统一为带蓝色背景和圆角的自定义样式
- 移除nutui Price组件中的thousands属性,使用项目自定义Price组件
- 解决微信小程序中nutui组件因div渲染缺失导致UI元素不显示的问题
- 保持支付流程及状态逻辑无改动,仅优化展示兼容性和稳定性
This commit is contained in:
2026-07-23 17:23:21 +08:00
parent 2ec3383fd0
commit e986222ca7
4 changed files with 92 additions and 38 deletions

30
.idea/workspace.xml generated
View File

@@ -4,10 +4,8 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="feat(weapp): 新增开发者中心与平台管理入口及相关功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务,菜单是否显示基于登录接口返回">
<change beforePath="$PROJECT_DIR$/.workbuddy/memory/2026-07-22.md" beforeDir="false" afterPath="$PROJECT_DIR$/.workbuddy/memory/2026-07-22.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.workbuddy/memory/MEMORY.md" beforeDir="false" afterPath="$PROJECT_DIR$/.workbuddy/memory/MEMORY.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/user/vip-review/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/vip-review/index.tsx" afterDir="false" />
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="feat(weapp): 新增开发者中心与平台管理入口及功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务,菜单是否显示基于登录接口返回的isDeveloper/isAdmin字段&#10;- vip-review审核通过流程调整为只调用setUserDeveloper后台替换角色为developer并置isDeveloper=1">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -119,15 +117,7 @@
<workItem from="1784131049625" duration="2304000" />
<workItem from="1784133736973" duration="4614000" />
<workItem from="1784585070286" duration="3047000" />
<workItem from="1784638429525" duration="14271000" />
</task>
<task id="LOCAL-00016" summary="fix(passport): 添加邀请接受接口调试日志&#10;&#10;- 在接受邀请请求前打印token和请求URL、参数&#10;- 在请求响应后输出接口响应及返回数据&#10;- 在调用失败时打印错误信息及异常日志&#10;- 保持请求流程和状态管理不变,增强调试能力">
<option name="closed" value="true" />
<created>1775909811705</created>
<option name="number" value="00016" />
<option name="presentableId" value="LOCAL-00016" />
<option name="project" value="LOCAL" />
<updated>1775909811705</updated>
<workItem from="1784638429525" duration="16147000" />
</task>
<task id="LOCAL-00017" summary="fix(passport): 优化邀请码接受失败的用户提示和处理&#10;&#10;- 针对“用户不存在”、“用户创建失败”及“请先登录”提示,改为弹窗提示需登录&#10;- 在弹窗中增加“去登录”按钮点击后跳转到登录页面并携带重定向token参数&#10;- 其他错误仍使用原Toast提示方式展示错误信息&#10;- 保留错误日志打印,便于排查异常情况">
<option name="closed" value="true" />
@@ -513,7 +503,15 @@
<option name="project" value="LOCAL" />
<updated>1784707342940</updated>
</task>
<option name="localTasksCounter" value="65" />
<task id="LOCAL-00065" summary="feat(weapp): 新增开发者中心与平台管理入口及功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务菜单是否显示基于登录接口返回的isDeveloper/isAdmin字段&#10;- vip-review审核通过流程调整为只调用setUserDeveloper后台替换角色为developer并置isDeveloper=1">
<option name="closed" value="true" />
<created>1784709736683</created>
<option name="number" value="00065" />
<option name="presentableId" value="LOCAL-00065" />
<option name="project" value="LOCAL" />
<updated>1784709736683</updated>
</task>
<option name="localTasksCounter" value="66" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -533,7 +531,6 @@
<component name="VcsManagerConfiguration">
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<MESSAGE value="feat(api-keys): 优化隐私协议同意逻辑及样式&#10;&#10;- 修正api-keys样式导入路径改为developer路径&#10;- 在开发者申请页面增加服务协议和隐私政策勾选框&#10;- 提交时校验协议是否已勾选,未勾选时提示用户&#10;- 优化申请页面协议区域样式及交互&#10;- 在用户验证页面添加隐私政策同意勾选框&#10;- 提交用户验证表单时检查协议同意状态&#10;- 增加打开服务协议和隐私政策的链接跳转&#10;- 更新项目配置增加passport/webview路由配置&#10;- 修复createTicket函数中日期格式正则表达式问题" />
<MESSAGE value="refactor(config): 移除开发者中心和企业控制台的自定义导航样式&#10;&#10;- 删除了开发者中心页面配置中的 navigationStyle: 'custom'&#10;- 删除了企业控制台页面配置中的 navigationStyle: 'custom'&#10;- 简化了页面配置,保持默认导航样式&#10;- 有助于提升页面兼容性和维护性" />
<MESSAGE value="feat(userVerify): 添加阿里云身份证二要素核验功能&#10;&#10;- 在接口层新增 verifyIdCard 接口,实现姓名与身份证号核验&#10;- userVerify 页面提交时增加身份核验逻辑,核验失败阻止提交&#10;- 调用核验接口时显示加载状态,并根据结果提示用户&#10;- 后端依赖 aliyun cloudauth提供实人认证相关服务接口&#10;- 完成基本的错误捕获与用户提示,提升实名认证流程安全性" />
<MESSAGE value="refactor(api): 规范前端API接口地址并统一路径前缀&#10;&#10;- 修正开发者、小程序和企业相关API的基础URL避免重复添加/api前缀&#10;- 统一调整developer、enterprise、invite等模块接口路径保持与后端Controller一致&#10;- 新增权限申请及消息通知相关API并完善相关函数实现&#10;- 规范请求参数传递,移除多余包装,改用直接传递对象方式&#10;- 新增API Key重置、版本发布/回滚等接口支持&#10;&#10;feat(invite): 重构邀请登录流程支持未注册快速加入&#10;&#10;- 完整重写invite页分离已注册用户和未注册用户的按钮显示和交互&#10;- 实现未注册用户微信手机号授权后自动注册登录及自动加入应用&#10;- 已注册用户直接确认加入应用,无需手机号授权弹窗&#10;- 统一使用getPhoneNumber按钮处理两种状态并修复“授权码不能为空”报错&#10;- 前端配合后端改造支持已登录用户可直接通过Authorization头加入应用&#10;&#10;fix(developer): 修复开发者中心应用加载问题&#10;&#10;- 修复只查询创建的应用导致邀请加入的应用不显示的问题&#10;- 同时请求创建应用和参与应用接口,合并并去重应用列表&#10;- 新增pageJoinedApp接口调用及合并逻辑提高应用列表完整性和体验" />
@@ -558,7 +555,8 @@
<MESSAGE value="refactor(user): 统一品牌名称为“威数谱软件”&#10;&#10;- 将项目中所有“威硕谱软件”名称修改为“威数谱软件”&#10;- 更新协议页面、欢迎消息、礼品卡等多处文本内容&#10;- 修改小程序配置文件中的项目名称和显示标题&#10;- 优化开发者申请页部分文案,明确开发者身份描述&#10;- 保证所有界面及配置中的品牌名称统一一致" />
<MESSAGE value="refactor(user): 将VIP身份统一改为开发者标记&#10;&#10;- 审核通过时不再新增角色而是更新用户isDeveloper布尔标记&#10;- 后端UserService新增markAsDeveloper方法通过updateById设置isDeveloper&#10;- UserController新增PUT接口更新开发者标记权限使用sys:userRole:save&#10;- 前端api新增setUserDeveloper调用新的审核接口&#10;- vip-review页面移除角色相关代码改为调用setUserDeveloper方法&#10;- 约定开发者身份即sys_user.is_developer字段不再依赖角色表记录&#10;- app.config.ts追加开发者中心与平台管理相关页面路由&#10;- 新增开发者中心、平台管理的多个API模块apikey、appProduct、appDomain、developer请求等&#10;- 添加公共工具方法和复用组件用于开发者中心及平台管理功能&#10;- 统一品牌名称为“威数谱软件”,更新文案及界面展现一致性" />
<MESSAGE value="feat(weapp): 新增开发者中心与平台管理入口及相关功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务,菜单是否显示基于登录接口返回" />
<option name="LAST_COMMIT_MESSAGE" value="feat(weapp): 新增开发者中心与平台管理入口及相关功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务,菜单是否显示基于登录接口返回" />
<MESSAGE value="feat(weapp): 新增开发者中心与平台管理入口及功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务,菜单是否显示基于登录接口返回的isDeveloper/isAdmin字段&#10;- vip-review审核通过流程调整为只调用setUserDeveloper后台替换角色为developer并置isDeveloper=1" />
<option name="LAST_COMMIT_MESSAGE" value="feat(weapp): 新增开发者中心与平台管理入口及功能&#10;&#10;- 小程序端新增开发者中心和平台管理页面模块,完善功能入口&#10;- 新增开发者中心页面概览、我的应用、API Key和工单&#10;- 新增平台管理页面管理首页、应用审核、Git权限审核、域名绑定审核和工单处理&#10;- 复刻PC端/api/app/*相关API接口基地址使用WEBSOPY_API_BASE_URL&#10;- 增加公共组件StatCard、EntryGrid、CellRow以支持新页面UI&#10;- 修改首页增加开发者中心和平台管理角色入口,仅对对应用户显示&#10;- 用户页面菜单新增开发者中心和平台管理入口条件展示&#10;- 约定开发者身份使用sys_user.is_developer布尔字段不再依赖角色表&#10;- 更新登录态业务菜单是否显示基于登录接口返回的isDeveloper/isAdmin字段&#10;- vip-review审核通过流程调整为只调用setUserDeveloper后台替换角色为developer并置isDeveloper=1" />
<option name="NON_MODAL_COMMIT_POSTPONE_SLOW_CHECKS" value="false" />
</component>
</project>

View File

@@ -0,0 +1,23 @@
# 2026-07-23
## 扫码进入小程序后支付页空白 bug
**现象**:用户从 web 端订单详情生成 QR 码 → 微信扫码进入小程序 → `passport/pay` 页面只显示卡片标题"确认支付"、"支付金额"标签、底部提示文案,**商品名称/价格类型/价格/分隔线/取消按钮/立即支付按钮全部缺失**,无法继续支付流程。
**根因**`@nutui/nutui-react-taro@2.7.4` 的 Cell / Price / Divider / Button 内部用 `React.createElement("div", ...)`,而 `dist/base.wxml` 里只有 `<view>` / `<text>` / `<button>` 模板没有 `<div>`。这些组件在 weapp 中"空壳化"。截图里只显示纯 Text/View 和图标(来自 `@nutui/icons-react-taro` 的 SVG没有显示的内容全部是 nutui-react-taro 组件。
**修复**:只改 `src/passport/pay/index.tsx` 一个文件,把 4 个 nutui 组件换成 Taro 原生 / 项目自定义组件:
- `import { Button, Cell, Price, Divider } from '@nutui/nutui-react-taro'``import { Button } from '@tarojs/components'` + `import Price from '@/components/common/Price'`
- `<Cell>``<View>` + `<Text>` 自绘(用 `flex justify-between px-4 py-3 border-b border-gray-100`
- `<Price size="large" thousands>``<Price size="large">`(项目自定义组件)
- `<Divider>``border-b border-gray-100` 替代
- `<Button type="primary" block>``<Button className="bg-blue-500 text-white rounded-full">`className 控制颜色)
- error/paid/loading 三个分支里的 `<Button type="primary">` 也一并替换
**验证**
- `npm run build:weapp``Compiled successfully in 14.45s`
- `dist/passport/pay/index.js``nut-cell|nut-button|nut-divider|nut-price` 全部为 0按钮编译成 `l.zx`Taro Button 局部变量名)
- 文件最终 413 行diff 集中在 import + 渲染区逻辑数据获取、JSAPI 支付、状态判断 payStatus vs status没动
**遗留**项目其它页面passport/{login,forget,register,setting,unified-qr,qr-login}、pages/order/*、pages/booking/*、components/common/{FixedButton,EmptyState}、components/QRLoginScanner也用了 nutui Button/Checkbox/Input/Card/Tabs/Tag 等。如果以后用户反馈"按钮/输入框看不到或不可点",按同样模式替换即可。这次没改,避免一次改动面太大。

View File

@@ -15,3 +15,24 @@
- 「按 userId 置一个标志位」的范式:`new User(){{setUserId(); setXxx();}}``userService.updateById(u)`(参考 `updateStatus`/`updateRecommend`/`auditUser`)。
- 用户变更后如需同步 websopy`syncMessageProducer.sendUserSyncMessage("websopy","UPDATE",user)`(在 `UserServiceImpl` 内,`@Autowired(required=false)` 注入)。
- 用户相关代码都在 `com.gxwebsoft.common.system`entity/service/controller业务应用数据在 `com.gxwebsoft.websopy`(无 Controller
## ⚠️ Taro 小程序 nutui-react-taro 兼容性陷阱2026-07-23 确认)
**问题**`@nutui/nutui-react-taro@2.7.4`**Cell / Price / Divider / Button** 等展示类组件,内部用 `React.createElement("div", ...)` 构建布局。Taro 4 的 babel/swc 没有把这些 `div` 转成 `view`,而 `dist/base.wxml` 里完全没有 `<div>` 模板,导致这些组件在 weapp 中"空壳化"——外层元素不存在,内层内容(`<div>`)被丢弃。
**症状**:组件位置完全空白 / 按钮不可见 / 价格显示为空。Text/View 与 `@nutui/icons-react-taro` 的图标Tips/Check/Close/Clock基于 SVG渲染正常。
**解决办法**(最小改动):在该页面用 Taro 原生组件替代:
| nutui不可用 | Taro 原生 / 项目自定义 |
|---|---|
| `import { Button, Cell, Price, Divider } from '@nutui/nutui-react-taro'` | `import { Button } from '@tarojs/components'` + `import Price from '@/components/common/Price'` |
| `<Cell title="X" description={Y} />` | `<View className="flex justify-between px-4 py-3 border-b border-gray-100"><Text className="text-gray-600">X</Text><Text className="text-gray-800">{Y}</Text></View>` |
| `<Price price={n} size="large" thousands />` | `<Price price={n} size="large" />`**用项目自定义 Price**,不用 nutui 的) |
| `<Divider />` | 用 `border-b border-gray-100` 替代 |
| `<Button type="primary" block loading={x} onClick={y}>...</Button>` | `<Button loading={x} onClick={y} className="flex-1 bg-blue-500 text-white rounded-full border-0">...</Button>`className 控制颜色,`loading` prop 仍然支持) |
**适用范围判断**项目里其它页面qr-login、register、passport/forget 等)也用了 nutui 的 Cell/Card/Divider/Button遇到同类"组件消失"现象时按同样模式替换即可。已修:`src/passport/pay/index.tsx`2026-07-23扫码进入小程序后支付页空白的 bug
**验证手段**:跑 `npm run build:weapp` 后在 `dist/<page>/index.js` 里 grep `nut-cell|nut-button|nut-divider|nut-price`,应为 0grep `l.zx`Taro Button 在 weapp 编译后的局部变量名)应出现 2 次(取消 + 立即支付)。
**已知的 nutui 组件清单**(按 2026-07-23 grep 结果):`Button``passport/pay, passport/login/setting/forget/register, pages/order/*, pages/booking/*, components/common/{FixedButton,EmptyState}` 等大量页面;`Cell``passport/pay, pages/order/order.tsx``Divider``passport/pay, passport/qr-login``Price``passport/pay`。修复顺序建议pay已修→ 视觉影响最大的页面 → 批量。

View File

@@ -1,13 +1,13 @@
import React, { useState, useEffect, useCallback } from 'react'
import { View, Text } from '@tarojs/components'
import { Button, Cell, Price, Divider } from '@nutui/nutui-react-taro'
import { Check, Close, Tips, Clock } from '@nutui/icons-react-taro'
import { View, Text, Button } from '@tarojs/components'
import { Tips, Check, Close, Clock } from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import { request } from '@/utils/request'
import { getOpenId, loginByOpenId } from '@/api/passport/wx-login'
import type { User } from '@/api/system/user/model'
import { saveStorageByLoginUser } from '@/utils/server'
import { TenantId, WebsopyBaseUrl } from '@/config/app'
import Price from '@/components/common/Price'
/**
* 小程序支付页面
@@ -312,7 +312,12 @@ const PayPage: React.FC = () => {
<Close className="text-red-500 mb-4" size="48" />
<Text className="block text-gray-800 text-lg mb-2"></Text>
<Text className="block text-gray-500 mb-6">{errorMsg}</Text>
<Button type="primary" onClick={handleBack}></Button>
<Button
onClick={handleBack}
className="bg-blue-500 text-white rounded-full px-6"
>
</Button>
</View>
</View>
)
@@ -333,7 +338,12 @@ const PayPage: React.FC = () => {
¥{Number(detail.payPrice).toFixed(2)}
</Text>
) : null}
<Button type="primary" onClick={handleBack}></Button>
<Button
onClick={handleBack}
className="bg-blue-500 text-white rounded-full px-6"
>
</Button>
</View>
</View>
)
@@ -347,21 +357,26 @@ const PayPage: React.FC = () => {
<View className="bg-white rounded-lg shadow-sm p-4 mb-4">
<Text className="block text-lg font-bold text-gray-800 mb-3"></Text>
<Cell title="商品名称" description={detail?.productName || '-'} />
<Cell title="价格类型" description={getPriceDesc()} />
<View className="flex items-center justify-between px-4 py-3">
<Text className="text-gray-600"></Text>
<Price
price={detail?.payPrice}
size="large"
thousands
/>
{/* 商品名称 */}
<View className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
<Text className="text-gray-600"></Text>
<Text className="text-gray-800">{detail?.productName || '-'}</Text>
</View>
<Divider />
{/* 价格类型 */}
<View className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
<Text className="text-gray-600"></Text>
<Text className="text-gray-800">{getPriceDesc() || '-'}</Text>
</View>
<View className="flex items-start px-4 py-2">
{/* 支付金额 */}
<View className="flex items-center justify-between px-4 py-3">
<Text className="text-gray-600"></Text>
<Price price={detail?.payPrice} size="large" />
</View>
{/* 提示 */}
<View className="flex items-start px-4 py-2 mt-2 border-t border-gray-100">
<Tips className="text-orange-500 mr-2 mt-1" size="16" />
<Text className="text-sm text-gray-500">
使
@@ -373,18 +388,15 @@ const PayPage: React.FC = () => {
<View className="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-gray-100">
<View className="flex gap-3">
<Button
block
onClick={handleBack}
className="flex-1"
className="flex-1 bg-gray-100 text-gray-700 rounded-full border-0"
>
</Button>
<Button
type="primary"
block
loading={paying}
onClick={handlePay}
className="flex-1"
className="flex-1 bg-blue-500 text-white rounded-full border-0"
>
{paying ? '支付中...' : '立即支付'}
</Button>