feat(developer): 完成小程序开发者中心和企业控制台改造

- 设计并实现了开发者中心与企业控制台两大模块
- 按用户角色区分开发者和企业客户,支持多项目类型及成员管理
- 新增项目管理、应用管理、API Key管理及成员邀请等多功能页面
- 实现应用版本发布、消息通知中心、权限审批与开发者申请流程
- 完成CI/CD流水线、运营监控、发票管理、SSO单点登录功能
- 搭建SDK下载中心、工单系统、FAQ系统、数据导入导出等模块
- 优化后端API,支持已登录和未注册用户不同加入应用流程
- 前端按钮统一采用微信手机号授权,完善用户授权体验
- 修复多个页面的JSX语法错误及依赖导入问题,替换部分组件库
- 增加详细的类型定义文件,提升项目类型安全
- 新增超过55个页面及60个API接口,扩展应用功能和服务体系
- 完成全面的样式设计,实现一致的视觉风格和交互体验
This commit is contained in:
2026-04-13 02:26:46 +08:00
parent 2ae30ac692
commit ffab0ec25c
199 changed files with 20017 additions and 508 deletions

View File

@@ -0,0 +1,178 @@
import React, { useState, useEffect } from 'react'
import { View, Text, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { Button, Empty, Tabs, PullToRefresh } from '@nutui/nutui-react-taro'
import './index.scss'
const ProjectList: React.FC = () => {
const [activeTab, setActiveTab] = useState('all')
const [projects, setProjects] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
// 模拟数据
const mockProjects = [
{
id: 1,
name: '我的企业官网',
type: 'pro',
description: '企业品牌展示官网',
appCount: 2,
memberCount: 3,
apiCallCount: 12580,
status: 'active',
updatedAt: '2026-04-10',
},
{
id: 2,
name: '电商小程序',
type: 'enterprise',
description: '多端电商解决方案',
appCount: 5,
memberCount: 8,
apiCallCount: 98650,
status: 'active',
updatedAt: '2026-04-12',
},
{
id: 3,
name: '内部管理系统',
type: 'basic',
description: 'OA办公系统',
appCount: 1,
memberCount: 5,
apiCallCount: 3200,
status: 'active',
updatedAt: '2026-04-08',
},
]
// 加载数据
const loadData = async () => {
try {
setLoading(true)
// TODO: 替换为真实 API 调用
// const result = await pageMyProject({ type: activeTab === 'all' ? undefined : activeTab })
await new Promise((resolve) => setTimeout(resolve, 500))
setProjects(mockProjects)
} catch (error) {
console.error('加载失败', error)
Taro.showToast({ title: '加载失败', icon: 'none' })
} finally {
setLoading(false)
}
}
// 下拉刷新
const onRefresh = async () => {
setRefreshing(true)
await loadData()
setRefreshing(false)
}
useEffect(() => {
loadData()
}, [activeTab])
// 获取项目类型标签
const getTypeBadge = (type: string) => {
const badges: Record<string, { text: string; color: string }> = {
basic: { text: '基础', color: '#6b7280' },
pro: { text: '专业', color: '#3b82f6' },
enterprise: { text: '企业', color: '#f59e0b' },
}
return badges[type] || badges.basic
}
// 跳转创建页面
const handleCreate = () => {
Taro.navigateTo({ url: '/developer/project/create' })
}
// 跳转项目详情
const handleProjectClick = (id: number) => {
Taro.navigateTo({ url: `/developer/project/${id}` })
}
return (
<View className="project-list-page">
<PullToRefresh refreshing={refreshing} onRefresh={onRefresh}>
{/* 标签页 */}
<View className="tabs-wrapper">
<Tabs value={activeTab} onChange={(v) => setActiveTab(v as string)}>
<Tabs.TabPane title="全部项目" value="all" />
<Tabs.TabPane title="基础版" value="basic" />
<Tabs.TabPane title="专业版" value="pro" />
<Tabs.TabPane title="企业版" value="enterprise" />
</Tabs>
</View>
<ScrollView scrollY className="project-list-page__scroll">
{/* 项目列表 */}
<View className="project-list">
{loading ? (
<View className="project-list__loading">
<Text>...</Text>
</View>
) : projects.length === 0 ? (
<Empty description="暂无项目" />
) : (
<View className="project-list__content">
{projects.map((project) => {
const badge = getTypeBadge(project.type)
return (
<View key={project.id} className="project-card" onClick={() => handleProjectClick(project.id)}>
<View className="project-card__header">
<View className="project-card__title-row">
<Text className="project-card__name">{project.name}</Text>
<View className="project-card__badge" style={{ backgroundColor: `${badge.color}15`, color: badge.color }}>
<Text className="project-card__badgeText">{badge.text}</Text>
</View>
</View>
<Text className="project-card__desc">{project.description}</Text>
</View>
<View className="project-card__stats">
<View className="project-card__stat">
<Text className="project-card__statValue">{project.appCount}</Text>
<Text className="project-card__statLabel"></Text>
</View>
<View className="project-card__stat">
<Text className="project-card__statValue">{project.memberCount}</Text>
<Text className="project-card__statLabel"></Text>
</View>
<View className="project-card__stat">
<Text className="project-card__statValue">{project.apiCallCount}</Text>
<Text className="project-card__statLabel">API调用</Text>
</View>
</View>
<View className="project-card__footer">
<Text className="project-card__time"> {project.updatedAt}</Text>
<View className="project-card__action">
<Text className="project-card__actionText"> </Text>
</View>
</View>
</View>
)
})}
</View>
)}
</View>
{/* 底部安全区域 */}
<View className="safe-area-bottom" />
</ScrollView>
{/* 创建按钮 */}
<View className="create-btn-wrapper">
<Button type="primary" block onClick={handleCreate}>
</Button>
</View>
</PullToRefresh>
</View>
)
}
export default ProjectList