feat(user): 合并切换开发者菜单项状态并更新开发者中心UI

- 用户菜单中根据 isDeveloper 字段条件展示“申请成为开发者”或“进入开发者中心”
- 已审核通过用户菜单显示“进入开发者中心”,并设置高亮样式
- 去除冗余的独立“开发者中心”菜单条目,简化菜单结构
- 重新设计开发者中心页面UI,统一为门店中心风格
- 顶部展示开发者头像及昵称手机号信息
- 功能卡片展示“我的应用”、“API Key”和“我的工单”,异步加载数量角标
- 底部添加“开发者帮助与支持”入口,替代门店订阅消息卡片
- 未登录或非开发者状态显示相应空态提示及操作按钮
- 优化登录状态与权限校验逻辑,防止页面闪烁和无权限内容显示
This commit is contained in:
2026-07-23 18:35:53 +08:00
parent dc72e530c7
commit 226e223d96
3 changed files with 216 additions and 90 deletions

44
.idea/workspace.xml generated
View File

@@ -4,7 +4,7 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="feat(user): 补充开发者审核通过时添加店员记录功能&#10;&#10;- 引入 addShopStoreUser 接口用于新增门店店员记录&#10;- 新增 clerkStoreId 和 clerkTenantId 状态,保存当前审核店员的门店和租户信息&#10;- 在审核通过后调用 addShopStoreUser添加申请人为当前门店店员&#10;- 保证只有当 clerkStoreId 存在时才执行新增操作&#10;- 调整平台管理页 UI使其与门店中心保持一致风格&#10;- 门店中心及平台管理入口权限调整,仅超级管理员访问&#10;- 用户菜单中平台管理和门店中心入口现基于 isSuperAdmin 字段控制&#10;- 优化登录及权限校验流程,避免权限不足时页面闪烁显示&#10;- 更新工作记录文档,补充审核通过流程中新添加的店员记录步骤" />
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="fix(auth): 统一超级管理员标识为布尔值true&#10;&#10;- 将所有页面和菜单中超级管理员校验由数字1改为布尔true&#10;- 平台管理、门店中心菜单项仅在isSuperAdmin===true时显示&#10;- 门店中心页面移除店员门槛,改为超级管理员访问限制&#10;- 非超级管理员访问门店中心弹窗提示并返回&#10;- 首页工作台入口及相关访问权限判定切换为布尔判断&#10;- 优化首屏加载状态,避免权限校验闪烁和误判" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -116,23 +116,7 @@
<workItem from="1784133736973" duration="4614000" />
<workItem from="1784585070286" duration="3047000" />
<workItem from="1784638429525" duration="16147000" />
<workItem from="1784798575885" duration="2914000" />
</task>
<task id="LOCAL-00022" summary="feat(invite): 支持已登录用户直接加入应用&#10;&#10;- 后端accept接口code参数改为可选支持通过Authorization头识别登录用户&#10;- 已登录用户加入时不再弹手机号授权前端调用新增的doJoinAppForLoggedInUser方法&#10;- 未注册用户仍通过手机号授权码code加入调用doJoinAppForNewUser方法&#10;- 拆分前端加入逻辑新增handleConfirmJoin统一处理已登录加入流程&#10;- 优化按钮交互,已登录状态下按钮变为普通点击,移除协议强制勾选&#10;- 错误处理和状态提示完善,加入成功后自动跳转首页">
<option name="closed" value="true" />
<created>1775966172496</created>
<option name="number" value="00022" />
<option name="presentableId" value="LOCAL-00022" />
<option name="project" value="LOCAL" />
<updated>1775966172496</updated>
</task>
<task id="LOCAL-00023" summary="feat(developer): 完成小程序开发者中心和企业控制台改造&#10;&#10;- 设计并实现了开发者中心与企业控制台两大模块&#10;- 按用户角色区分开发者和企业客户,支持多项目类型及成员管理&#10;- 新增项目管理、应用管理、API Key管理及成员邀请等多功能页面&#10;- 实现应用版本发布、消息通知中心、权限审批与开发者申请流程&#10;- 完成CI/CD流水线、运营监控、发票管理、SSO单点登录功能&#10;- 搭建SDK下载中心、工单系统、FAQ系统、数据导入导出等模块&#10;- 优化后端API支持已登录和未注册用户不同加入应用流程&#10;- 前端按钮统一采用微信手机号授权,完善用户授权体验&#10;- 修复多个页面的JSX语法错误及依赖导入问题替换部分组件库&#10;- 增加详细的类型定义文件,提升项目类型安全&#10;- 新增超过55个页面及60个API接口扩展应用功能和服务体系&#10;- 完成全面的样式设计,实现一致的视觉风格和交互体验">
<option name="closed" value="true" />
<created>1776018406658</created>
<option name="number" value="00023" />
<option name="presentableId" value="LOCAL-00023" />
<option name="project" value="LOCAL" />
<updated>1776018406658</updated>
<workItem from="1784798575885" duration="3338000" />
</task>
<task id="LOCAL-00024" summary="feat(api-keys): 优化隐私协议同意逻辑及样式&#10;&#10;- 修正api-keys样式导入路径改为developer路径&#10;- 在开发者申请页面增加服务协议和隐私政策勾选框&#10;- 提交时校验协议是否已勾选,未勾选时提示用户&#10;- 优化申请页面协议区域样式及交互&#10;- 在用户验证页面添加隐私政策同意勾选框&#10;- 提交用户验证表单时检查协议同意状态&#10;- 增加打开服务协议和隐私政策的链接跳转&#10;- 更新项目配置增加passport/webview路由配置&#10;- 修复createTicket函数中日期格式正则表达式问题">
<option name="closed" value="true" />
@@ -510,7 +494,23 @@
<option name="project" value="LOCAL" />
<updated>1784802336157</updated>
</task>
<option name="localTasksCounter" value="71" />
<task id="LOCAL-00071" summary="feat(user): 合并并切换“申请开发者”菜单项状态&#10;&#10;- 在用户菜单中根据 isDeveloper 字段切换显示“申请成为开发者”或“进入开发者中心”&#10;- 已通过审核用户直接显示“进入开发者中心”且高亮显示&#10;- 去除冗余的独立“开发者中心”菜单条目,简化菜单结构&#10;- 调整平台管理页和门店中心入口权限为仅超级管理员可访问&#10;- 优化登录与权限校验流程,防止权限不足时页面闪烁或无访问权限空白&#10;- 更新相关文档,完善审核通过后店员记录添加流程">
<option name="closed" value="true" />
<created>1784802434477</created>
<option name="number" value="00071" />
<option name="presentableId" value="LOCAL-00071" />
<option name="project" value="LOCAL" />
<updated>1784802434477</updated>
</task>
<task id="LOCAL-00072" summary="fix(auth): 统一超级管理员标识为布尔值true&#10;&#10;- 将所有页面和菜单中超级管理员校验由数字1改为布尔true&#10;- 平台管理、门店中心菜单项仅在isSuperAdmin===true时显示&#10;- 门店中心页面移除店员门槛,改为超级管理员访问限制&#10;- 非超级管理员访问门店中心弹窗提示并返回&#10;- 首页工作台入口及相关访问权限判定切换为布尔判断&#10;- 优化首屏加载状态,避免权限校验闪烁和误判">
<option name="closed" value="true" />
<created>1784802589434</created>
<option name="number" value="00072" />
<option name="presentableId" value="LOCAL-00072" />
<option name="project" value="LOCAL" />
<updated>1784802589434</updated>
</task>
<option name="localTasksCounter" value="73" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -530,8 +530,6 @@
<component name="VcsManagerConfiguration">
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<MESSAGE value="feat(app): 添加应用入口文件和配置文件&#10;&#10;- 新增 index.tsx 作为应用的主入口文件&#10;- 新增 index.config.ts 用于应用的配置管理" />
<MESSAGE value="fix(tailwind): 更新配置以兼容微信小程序&#10;&#10;- 启用 autoprefixer 并新增 postcss-nested 插件支持嵌套语法&#10;- 扩展主题颜色,添加 primary 色系&#10;- 更新 content 路径包含 public/index.html&#10;- 禁用 preflight 以移除默认样式重置&#10;- 移除多数 corePlugins 禁用项,保留 preflight 禁用,简化兼容性配置" />
<MESSAGE value="feat(postcss): 添加插件移除微信小程序不兼容Tailwind规则&#10;&#10;- 实现 postcss-remove-wxss-incompatible 插件&#10;- 移除反斜杠转义的重要性修饰符类选择器(.\!xxx&#10;- 删除依赖 :not 和兄弟选择器的 .space-[x|y]-* 规则&#10;- 删除依赖 :not 和兄弟选择器的 .divide-[x|y]-* 规则&#10;- 解决微信小程序 WXSS 不支持的 CSS 特性问题" />
<MESSAGE value="fix(pay): 优化订阅号获取逻辑并调整支付组件样式&#10;&#10;- 优先从小程序码 scene 参数读取 subscriptionNo提升参数兼容性&#10;- 对 scene 参数进行解码,避免编码引起的问题&#10;- 移除支付组件价格显示处的固定颜色设置,增强样式灵活性" />
<MESSAGE value="fix(utils): 优化小程序端复制文本功能体验&#10;&#10;- 小程序端直接调用原生 wx.setClipboardData避免 Taro Promise 封装问题&#10;- 避免微信小程序重复显示复制成功提示和误报复制失败&#10;- 失败时仅在小程序端输出错误日志,非小程序端显示提示&#10;- 修改首页外链跳转逻辑,支持相对路径跳转,小程序内外链复制提示提升用户体验&#10;- 更新项目内剪切板使用规范文档,说明复制行为调整及符合相应规范要求" />
@@ -555,7 +553,9 @@
<MESSAGE value="fix(weapp): 修复支付页nutui组件导致内容空白问题&#10;&#10;- 替换支付页中nutui的Button、Cell、Price、Divider组件为Taro原生组件或项目自定义组件&#10;- 修正商品名称、价格类型和支付金额显示使用flex布局及Text组件自绘内容&#10;- 调整按钮样式,统一为带蓝色背景和圆角的自定义样式&#10;- 移除nutui Price组件中的thousands属性使用项目自定义Price组件&#10;- 解决微信小程序中nutui组件因div渲染缺失导致UI元素不显示的问题&#10;- 保持支付流程及状态逻辑无改动,仅优化展示兼容性和稳定性" />
<MESSAGE value="feat(user): 补充开发者审核通过时添加店员记录功能&#10;&#10;- 引入 addShopStoreUser 接口用于新增门店店员记录&#10;- 新增 clerkStoreId 和 clerkTenantId 状态,保存当前审核店员的门店和租户信息&#10;- 在审核通过后调用 addShopStoreUser添加申请人为当前门店店员&#10;- 保证只有当 clerkStoreId 存在时才执行新增操作&#10;- 更新工作记录文档,补充审核通过流程中新添加的店员记录步骤&#10;- 保持原有升级开发者逻辑和接口调用不变" />
<MESSAGE value="feat(user): 补充开发者审核通过时添加店员记录功能&#10;&#10;- 引入 addShopStoreUser 接口用于新增门店店员记录&#10;- 新增 clerkStoreId 和 clerkTenantId 状态,保存当前审核店员的门店和租户信息&#10;- 在审核通过后调用 addShopStoreUser添加申请人为当前门店店员&#10;- 保证只有当 clerkStoreId 存在时才执行新增操作&#10;- 调整平台管理页 UI使其与门店中心保持一致风格&#10;- 门店中心及平台管理入口权限调整,仅超级管理员可访问&#10;- 用户菜单中平台管理和门店中心入口现基于 isSuperAdmin 字段控制&#10;- 优化登录及权限校验流程,避免权限不足时页面闪烁显示&#10;- 更新工作记录文档,补充审核通过流程中新添加的店员记录步骤" />
<option name="LAST_COMMIT_MESSAGE" value="feat(user): 补充开发者审核通过时添加店员记录功能&#10;&#10;- 引入 addShopStoreUser 接口用于新增门店店员记录&#10;- 新增 clerkStoreId 和 clerkTenantId 状态,保存当前审核店员的门店和租户信息&#10;- 在审核通过后调用 addShopStoreUser添加申请人为当前门店店员&#10;- 保证只有当 clerkStoreId 存在时才执行新增操作&#10;- 调整平台管理页 UI使其与门店中心保持一致风格&#10;- 门店中心及平台管理入口权限调整,仅超级管理员可访问&#10;- 用户菜单中平台管理和门店中心入口现基于 isSuperAdmin 字段控制&#10;- 优化登录权限校验流程,避免权限不足时页面闪烁显示&#10;- 更新工作记录文档,补充审核通过流程中新添加的店员记录步骤" />
<MESSAGE value="feat(user): 合并并切换“申请开发者”菜单项状态&#10;&#10;- 在用户菜单中根据 isDeveloper 字段切换显示“申请成为开发者”或“进入开发者中心”&#10;- 已通过审核用户直接显示“进入开发者中心”且高亮显示&#10;- 去除冗余的独立“开发者中心”菜单条目,简化菜单结构&#10;- 调整平台管理页和门店中心入口权限仅超级管理员可访问&#10;- 优化登录权限校验流程,防止权限不足时页面闪烁或无访问权限空白&#10;- 更新相关文档,完善审核通过后店员记录添加流程" />
<MESSAGE value="fix(auth): 统一超级管理员标识为布尔值true&#10;&#10;- 将所有页面和菜单中超级管理员校验由数字1改为布尔true&#10;- 平台管理、门店中心菜单项仅在isSuperAdmin===true时显示&#10;- 门店中心页面移除店员门槛,改为超级管理员访问限制&#10;- 非超级管理员访问门店中心弹窗提示并返回&#10;- 首页工作台入口及相关访问权限判定切换为布尔判断&#10;- 优化首屏加载状态,避免权限校验闪烁和误判" />
<option name="LAST_COMMIT_MESSAGE" value="fix(auth): 统一超级管理员标识为布尔值true&#10;&#10;- 将所有页面和菜单中超级管理员校验由数字1改为布尔true&#10;- 平台管理、门店中心菜单项仅在isSuperAdmin===true时显示&#10;- 门店中心页面移除店员门槛,改为超级管理员访问限制&#10;- 非超级管理员访问门店中心弹窗提示并返回&#10;- 首页工作台入口及相关访问权限判定切换为布尔判断&#10;- 优化首屏加载状态,避免权限校验闪烁和误判" />
<option name="NON_MODAL_COMMIT_POSTPONE_SLOW_CHECKS" value="false" />
</component>
</project>

View File

@@ -65,4 +65,16 @@
**需求**:我的页面菜单里「申请成为开发者(推荐)」条目,若用户已审核通过成为开发者,该处文案/链接改为「进入开发者中心」(跳 `/pages/developer/index/index`)。
**改动**`src/pages/user/user.tsx` 菜单 `menuItems`
- 把原第 105 行固定「申请成为开发者」+ 第 107 行独立「开发者中心」(仅 isDeveloper 显示) 合并为条件式:`(user as any)?.isDeveloper ? { 进入开发者中心 → /pages/developer/index/index } : { 申请成为开发者 → /pages/user/vip-upgrade/index }`,两者均 `highlight: true`(醒目推荐样式)。去掉了冗余的独立「开发者中心」条目。
- 把原第 105 行固定「申请成为开发者」+ 第 107 行独立「开发者中心」(仅 isDeveloper 显示) 合并为条件式:`(user as any)?.isDeveloper ? { 进入开发者中心 → /pages/developer/index/index } : { 申请成为开发者 → /pages/user/vip-upgrade/index }`,两者均 `highlight: true`(醒目推荐样式)。去掉了冗余的独立「开发者中心」条目。
## 开发者中心 UI 对齐门店中心
**需求**`pages/developer/index/index` 的 UI 改成和门店中心(`pages/store/center/index`)一模一样。
**改动**(仅 `src/pages/developer/index/index.tsx`,整体重写):
- 删除原 NavBar + StatCard 3列网格 + EntryGrid 老结构,改写为门店中心同款「渐变绿头部 + 白卡片列表(图标/标题/描述/角标/箭头)+ 底部卡片」。
- 头部:开发者 avatar + 「开发者中心」标题 + 昵称·手机号(取 `(user as any)`)。
- FEATURE_CARDS 三张:我的应用(📦/蓝)、API Key(🔑/绿)、我的工单(🎫/橙),各自 desc + url角标沿用 `Badge`展示各功能的计数stats.apps/keys/tickets由 getMyApps/getAppApiKeyStats/getMyTickets 的 total 取),卡片渲染后异步填充。
- 底部卡片改为「开发者帮助与支持」(💡 蓝圈)→ `/pages/user/help-center/index`(替代门店中心的订阅消息卡,订阅模板是门店专属,开发者无对应模板)。
- 保留两个空态:!isLoggedIn「请先登录」、!isDeveloper「尚未加入开发者中心」+ 申请按钮(保留原 apply 入口store center 无此场景故无按钮);首屏用 `userLoading`(来自 useUser) 转圈防闪烁。
- 不再引入 NavBar/StatCard/EntryGrid/Loading可能内部用 nutui踩 weapp 空白坑),只用 `@tarojs/components` + 自定义 `Badge`,与门店中心一致。

View File

@@ -1,26 +1,65 @@
import React, { useState, useEffect, useCallback } from 'react'
import { View, Text, ScrollView } from '@tarojs/components'
import { View, Text, Image } from '@tarojs/components'
import Taro from '@tarojs/taro'
import NavBar from '@/components/NavBar'
import StatCard from '@/components/common/StatCard'
import EntryGrid from '@/components/common/EntryGrid'
import Loading from '@/components/common/Loading'
import Badge from '@/components/common/Badge'
import { useUser } from '@/hooks/useUser'
import { getMyApps } from '@/api/app/appProduct'
import { getAppApiKeyStats } from '@/api/app/apikey'
import { getMyTickets } from '@/api/app/ticket'
import { getPageTotal } from '@/utils/devcenter'
definePageConfig({ navigationBarTitleText: '开发者中心' })
definePageConfig({
navigationBarTitleText: '开发者中心',
})
const DeveloperCenterPage: React.FC = () => {
const { user, isLoggedIn } = useUser()
// 功能卡片定义(未来新增功能只需在这里加一项)
const FEATURE_CARDS = [
{
key: 'apps',
icon: '📦',
title: '我的应用',
desc: '管理你开发并上架的应用',
url: '/pages/developer/apps/index',
color: '#3b82f6',
bgColor: '#dbeafe',
showBadge: true,
},
{
key: 'apikeys',
icon: '🔑',
title: 'API Key',
desc: '查看与管理你的接口密钥',
url: '/pages/developer/apikeys/index',
color: '#10b981',
bgColor: '#dcfce7',
showBadge: true,
},
{
key: 'tickets',
icon: '🎫',
title: '我的工单',
desc: '提交与跟踪你的工单',
url: '/pages/developer/tickets/index',
color: '#f59e0b',
bgColor: '#fef3c7',
showBadge: true,
},
]
export default function DeveloperCenterPage() {
const { user, isLoggedIn, loading: userLoading } = useUser()
const isDeveloper = !!(user as any)?.isDeveloper
const [loading, setLoading] = useState(true)
const [stats, setStats] = useState({ apps: 0, keys: 0, tickets: 0 })
useEffect(() => {
if (isLoggedIn && isDeveloper) {
load()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoggedIn, isDeveloper])
/** 加载我的应用 / API Key / 工单数量 */
const load = useCallback(async () => {
setLoading(true)
try {
const [appsR, keysR, ticketsR] = await Promise.allSettled([
getMyApps({ page: 1, limit: 1 }),
@@ -31,84 +70,159 @@ const DeveloperCenterPage: React.FC = () => {
const keys = keysR.status === 'fulfilled' ? getPageTotal(keysR.value) : 0
const tickets = ticketsR.status === 'fulfilled' ? getPageTotal(ticketsR.value) : 0
setStats({ apps, keys, tickets })
} catch (e) {
} catch {
// ignore
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
if (isLoggedIn) load()
}, [isLoggedIn, load])
/** 获取卡片角标数字 */
const getBadgeCount = (key: string) => {
if (key === 'apps') return stats.apps
if (key === 'apikeys') return stats.keys
if (key === 'tickets') return stats.tickets
return 0
}
if (userLoading) {
return (
<View className='min-h-full bg-gray-50 flex items-center justify-center'>
<Text className='text-gray-400'>...</Text>
</View>
)
}
if (!isLoggedIn) {
return (
<View className='min-h-screen bg-gray-100'>
<NavBar title='开发者中心' />
<View className='flex flex-col items-center justify-center py-24'>
<Text className='text-gray-400 text-sm'></Text>
<View
className='mt-4 px-6 py-2 rounded-full'
style={{ background: '#0e932e' }}
onClick={() => Taro.navigateTo({ url: '/passport/login' })}
>
<Text className='text-white text-sm'></Text>
</View>
</View>
<View className='min-h-full bg-gray-50 flex flex-col items-center justify-center py-24'>
<Text className='text-5xl mb-3'>🔐</Text>
<Text className='text-base text-gray-700 mb-2'></Text>
<Text className='text-xs text-gray-400 text-center'></Text>
</View>
)
}
if (!isDeveloper) {
return (
<View className='min-h-screen bg-gray-100'>
<NavBar title='开发者中心' />
<View className='flex flex-col items-center justify-center py-20 px-10'>
<Text className='text-5xl mb-3'>🛠</Text>
<Text className='text-base text-gray-700 mb-2'></Text>
<Text className='text-xs text-gray-400 text-center mb-5 leading-relaxed'>
API Key
</Text>
<View
className='px-6 py-2 rounded-full'
style={{ background: '#2563eb' }}
onClick={() => Taro.navigateTo({ url: '/pages/user/vip-upgrade/index' })}
>
<Text className='text-white text-sm'></Text>
</View>
<View className='min-h-full bg-gray-50 flex flex-col items-center justify-center py-20 px-10'>
<Text className='text-5xl mb-3'>🛠</Text>
<Text className='text-base text-gray-700 mb-2'></Text>
<Text className='text-xs text-gray-400 text-center mb-5 leading-relaxed'>
API Key
</Text>
<View
className='px-6 py-2 rounded-full'
style={{ background: '#2563eb' }}
onClick={() => Taro.navigateTo({ url: '/pages/user/vip-upgrade/index' })}
>
<Text className='text-white text-sm'></Text>
</View>
</View>
)
}
const entries = [
{ icon: '📦', label: '我的应用', url: '/pages/developer/apps/index' },
{ icon: '🔑', label: 'API Key', url: '/pages/developer/apikeys/index' },
{ icon: '🎫', label: '我的工单', url: '/pages/developer/tickets/index' },
]
// 头部展示信息
const header = {
avatar: (user as any)?.avatar,
title: '开发者中心',
subtitle: `${((user as any)?.nickname || (user as any)?.username || '')}${((user as any)?.phone) ? ` · ${(user as any).phone}` : ''}`,
}
return (
<View className='min-h-screen bg-gray-100'>
<NavBar title='开发者中心' />
<ScrollView scrollY>
{loading ? (
<Loading />
) : (
<View className='p-4'>
<View className='grid grid-cols-3 gap-3 mb-3'>
<StatCard label='我的应用' value={stats.apps} color='#3b82f6' />
<StatCard label='API Key' value={stats.keys} color='#10b981' />
<StatCard label='我的工单' value={stats.tickets} color='#f59e0b' />
</View>
<Text className='text-base font-medium mb-2 block'></Text>
<EntryGrid items={entries} columns={3} />
<View className='h-6' />
<View className='min-h-full bg-gray-50'>
{/* 顶部信息区:头像 + 名称 */}
<View
className='pt-8 pb-12 px-5 rounded-b-3xl relative overflow-hidden'
style={{ background: 'linear-gradient(135deg, #15803d 0%, #22c55e 60%, #4ade80 100%)' }}
>
<View className='absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10'
style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
<View className='absolute -bottom-6 -left-6 w-24 h-24 rounded-full opacity-15'
style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
<View className='relative z-10 flex items-center gap-4'>
{/* 头像 */}
<View className='w-16 h-16 rounded-full bg-white/20 border-2 border-white/40 overflow-hidden flex-shrink-0'>
{header.avatar ? (
<Image
className='w-full h-full'
src={header.avatar}
mode='aspectFill'
/>
) : (
<View className='w-full h-full flex items-center justify-center'>
<Text className='text-2xl'>👤</Text>
</View>
)}
</View>
)}
</ScrollView>
{/* 信息 */}
<View className='flex-1 min-w-0'>
<Text className='text-white text-lg font-bold block truncate'>
{header.title}
</Text>
<Text className='text-white text-opacity-80 text-sm mt-1 block truncate'>
{header.subtitle}
</Text>
</View>
</View>
</View>
{/* 功能卡片区 */}
<View className='mx-3 -mt-6 relative z-20'>
<View className='flex flex-col gap-3'>
{FEATURE_CARDS.map(card => (
<View
key={card.key}
className='bg-white rounded-xl p-4 flex items-center gap-4 active:opacity-80 relative'
onClick={() => Taro.navigateTo({ url: card.url })}
>
{/* 图标 */}
<View
className='w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0'
style={{ background: card.bgColor }}
>
<Text className='text-2xl'>{card.icon}</Text>
</View>
{/* 文字 */}
<View className='flex-1 min-w-0'>
<Text className='text-gray-800 text-base font-medium block'>{card.title}</Text>
<Text className='text-gray-400 text-xs mt-0.5 block'>{card.desc}</Text>
</View>
{/* 数量角标 */}
{card.showBadge && (
<Badge count={getBadgeCount(card.key)} className='absolute -top-1 -right-1' size={20} fontSize={11} fontWeight='bold' />
)}
{/* 箭头 */}
<Text className='text-gray-300 text-lg flex-shrink-0'></Text>
</View>
))}
</View>
</View>
{/* 底部提示 */}
<View className='mx-3 mt-6 mb-6'>
{/* 开发者帮助 */}
<View
className='bg-white rounded-xl p-4 flex items-center gap-3 active:opacity-80'
onClick={() => Taro.navigateTo({ url: '/pages/user/help-center/index' })}
>
<View className='w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0'
style={{ background: '#dbeafe' }}>
<Text className='text-xl'>💡</Text>
</View>
<View className='flex-1 min-w-0'>
<Text className='text-gray-800 text-sm font-medium block'></Text>
<Text className='text-gray-400 text-xs mt-0.5 block'>
SDK
</Text>
</View>
<Text className='text-blue-500 text-sm flex-shrink-0'></Text>
</View>
</View>
</View>
)
}
export default DeveloperCenterPage