import React, { useState, useEffect } from 'react' import { View, Text, Button, Input, actionSheet } from '@tarojs/components' import Taro, { useRouter } from '@tarojs/taro' import { usePullDownRefresh } from '@tarojs/taro' import { listProjectMember, addProjectMember, removeProjectMember } from '@/api/developer/developer' import type { ProjectMember } from '@/types/developer' import './members.scss' // 角色配置 const ROLE_CONFIG: Record = { owner: { label: '所有者', color: '#722ed1', desc: '拥有所有权限' }, admin: { label: '管理员', color: '#1890ff', desc: '管理项目设置和成员' }, developer: { label: '开发者', color: '#52c41a', desc: '开发和管理应用' }, viewer: { label: '查看者', color: '#999', desc: '仅可查看' }, } const MembersPage: React.FC = () => { const router = useRouter() const projectId = Number(router.params.id) const [loading, setLoading] = useState(false) const [refreshing, setRefreshing] = useState(false) const [list, setList] = useState([]) const [showInvite, setShowInvite] = useState(false) const [inviteForm, setInviteForm] = useState({ username: '', role: 'developer' as ProjectMember['role'], }) const [inviting, setInviting] = useState(false) // 加载数据 const loadData = async (isRefresh = false) => { if (loading) return setLoading(true) if (isRefresh) setRefreshing(true) try { const data = await listProjectMember(projectId) setList(data || []) } catch (err) { console.error('加载失败', err) Taro.showToast({ title: '加载失败', icon: 'none' }) } finally { setLoading(false) setRefreshing(false) } } useEffect(() => { loadData() }, [projectId]) // 下拉刷新 usePullDownRefresh(() => { loadData(true) }) // 邀请成员 const handleInvite = async () => { if (!inviteForm.username.trim()) { Taro.showToast({ title: '请输入用户名', icon: 'none' }) return } setInviting(true) try { await addProjectMember(projectId, { username: inviteForm.username, role: inviteForm.role, } as Partial) Taro.showToast({ title: '邀请成功', icon: 'success' }) setShowInvite(false) setInviteForm({ username: '', role: 'developer' }) loadData() } catch (err) { console.error('邀请失败', err) Taro.showToast({ title: '邀请失败', icon: 'none' }) } finally { setInviting(false) } } // 移除成员 const handleRemove = (member: ProjectMember) => { if (member.role === 'owner') { Taro.showToast({ title: '无法移除项目所有者', icon: 'none' }) return } actionSheet({ alertText: `确定将 "${member.username}" 从项目中移除吗?`, actions: [ { name: '移除', color: '#ff4d4f', type: 'warn' as const }, ], confirmText: '取消', }).then(res => { if (res.confirm) { doRemove(member.id!) } }).catch(() => {}) } const doRemove = async (memberId: number) => { try { await removeProjectMember(projectId, memberId) Taro.showToast({ title: '已移除', icon: 'success' }) loadData() } catch (err) { console.error('移除失败', err) Taro.showToast({ title: '移除失败', icon: 'none' }) } } // 修改角色 const handleChangeRole = (member: ProjectMember) => { if (member.role === 'owner') { Taro.showToast({ title: '无法修改所有者角色', icon: 'none' }) return } const roles = ['admin', 'developer', 'viewer'] const options = roles.map(role => ({ name: ROLE_CONFIG[role].label, })) actionSheet({ title: `修改 "${member.username}" 的角色`, actions: options, confirmText: '取消', }).then(res => { if (res.confirm === false && res.errMsg?.includes('cancel')) return const role = roles[res.confirm as number] if (role) { updateRole(member.id!, role as ProjectMember['role']) } }).catch(() => {}) } const updateRole = async (memberId: number, role: ProjectMember['role']) => { try { await addProjectMember(projectId, { id: memberId, role } as Partial) Taro.showToast({ title: '已更新角色', icon: 'success' }) loadData() } catch (err) { console.error('更新失败', err) Taro.showToast({ title: '更新失败', icon: 'none' }) } } // 格式化日期 const formatDate = (dateStr?: string) => { if (!dateStr) return '-' const date = new Date(dateStr) return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` } return ( {/* 头部 */} 👥 项目成员 {/* 统计 */} {list.length} 总成员 {list.filter(m => m.role === 'admin' || m.role === 'owner').length} 管理员 {list.filter(m => m.role === 'developer').length} 开发者 {list.filter(m => m.role === 'viewer').length} 查看者 {/* 成员列表 */} {list.length === 0 && !loading ? ( 暂无成员 点击「邀请」添加项目成员 ) : ( list.map((member) => ( {member.avatar ? ( ) : ( {(member.username || '?').charAt(0).toUpperCase()} )} {member.username} {ROLE_CONFIG[member.role || 'viewer']?.label} {ROLE_CONFIG[member.role || 'viewer']?.desc} 加入于 {formatDate(member.joinedAt)} {member.role !== 'owner' && ( handleChangeRole(member)} > 修改角色 handleRemove(member)} > 移除 )} )) )} {loading && list.length === 0 && ( 加载中... )} {/* 邀请弹窗 */} {showInvite && ( setShowInvite(false)} /> 邀请项目成员 用户名 * setInviteForm(prev => ({ ...prev, username: e.detail.value }))} /> 角色 {(['admin', 'developer', 'viewer'] as const).map((role) => ( setInviteForm(prev => ({ ...prev, role }))} > {ROLE_CONFIG[role].label} ))} {ROLE_CONFIG[inviteForm.role]?.desc} )} ) } export default MembersPage