feat(developer): 完成小程序开发者中心和企业控制台改造
- 设计并实现了开发者中心与企业控制台两大模块 - 按用户角色区分开发者和企业客户,支持多项目类型及成员管理 - 新增项目管理、应用管理、API Key管理及成员邀请等多功能页面 - 实现应用版本发布、消息通知中心、权限审批与开发者申请流程 - 完成CI/CD流水线、运营监控、发票管理、SSO单点登录功能 - 搭建SDK下载中心、工单系统、FAQ系统、数据导入导出等模块 - 优化后端API,支持已登录和未注册用户不同加入应用流程 - 前端按钮统一采用微信手机号授权,完善用户授权体验 - 修复多个页面的JSX语法错误及依赖导入问题,替换部分组件库 - 增加详细的类型定义文件,提升项目类型安全 - 新增超过55个页面及60个API接口,扩展应用功能和服务体系 - 完成全面的样式设计,实现一致的视觉风格和交互体验
This commit is contained in:
178
src/developer/project/index.tsx
Normal file
178
src/developer/project/index.tsx
Normal 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
|
||||
Reference in New Issue
Block a user