import React, { useState, useEffect } from 'react'; import { Download, Plus, Edit, Trash2, Smartphone, Monitor, Apple, Search, X, ChevronDown, ChevronUp, Package, Hash, Link, FileText, HardDrive, Cpu } from 'lucide-react'; import apiClient from '../utils/apiClient'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import ConfirmDialog from '../components/ConfirmDialog'; import FormModal from '../components/FormModal'; import Toast from '../components/Toast'; import './ClientManagement.css'; const ClientManagement = ({ user }) => { const [clients, setClients] = useState([]); const [loading, setLoading] = useState(true); const [showClientModal, setShowClientModal] = useState(false); const [isEditing, setIsEditing] = useState(false); const [deleteConfirmInfo, setDeleteConfirmInfo] = useState(null); const [selectedClient, setSelectedClient] = useState(null); const [filterPlatformType, setFilterPlatformType] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [expandedNotes, setExpandedNotes] = useState({}); const [toasts, setToasts] = useState([]); const [formData, setFormData] = useState({ platform_type: 'mobile', platform_name: 'ios', version: '', version_code: '', download_url: '', file_size: '', release_notes: '', is_active: true, is_latest: false, min_system_version: '' }); const platformOptions = { mobile: [ { value: 'ios', label: 'iOS', icon: }, { value: 'android', label: 'Android', icon: } ], desktop: [ { value: 'windows', label: 'Windows', icon: }, { value: 'mac_intel', label: 'Mac (Intel)', icon: }, { value: 'mac_m', label: 'Mac (M系列)', icon: }, { value: 'linux', label: 'Linux', icon: } ], terminal: [ { value: 'android', label: 'Android终端', icon: }, { value: 'mcu', label: '单片机', icon: } ] }; // Toast helper functions const showToast = (message, type = 'info') => { const id = Date.now(); setToasts(prev => [...prev, { id, message, type }]); }; const removeToast = (id) => { setToasts(prev => prev.filter(toast => toast.id !== id)); }; useEffect(() => { fetchClients(); }, []); const fetchClients = async () => { setLoading(true); try { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.CLIENT_DOWNLOADS.LIST)); console.log('Client downloads response:', response); setClients(response.data.clients || []); } catch (error) { console.error('获取客户端列表失败:', error); } finally { setLoading(false); } }; const handleCreate = async () => { try { // 验证必填字段 if (!formData.version_code || !formData.version || !formData.download_url) { showToast('请填写所有必填字段', 'warning'); return; } const payload = { ...formData, version_code: parseInt(formData.version_code, 10), file_size: formData.file_size ? parseInt(formData.file_size, 10) : null }; // 验证转换后的数字 if (isNaN(payload.version_code)) { showToast('版本代码必须是有效的数字', 'warning'); return; } if (formData.file_size && isNaN(payload.file_size)) { showToast('文件大小必须是有效的数字', 'warning'); return; } await apiClient.post(buildApiUrl(API_ENDPOINTS.CLIENT_DOWNLOADS.CREATE), payload); handleCloseModal(); fetchClients(); showToast('客户端创建成功', 'success'); } catch (error) { console.error('创建客户端失败:', error); showToast(error.response?.data?.message || '创建失败,请重试', 'error'); } }; const handleUpdate = async () => { try { // 验证必填字段 if (!formData.version_code || !formData.version || !formData.download_url) { showToast('请填写所有必填字段', 'warning'); return; } const payload = { version: formData.version, version_code: parseInt(formData.version_code, 10), download_url: formData.download_url, file_size: formData.file_size ? parseInt(formData.file_size, 10) : null, release_notes: formData.release_notes, is_active: formData.is_active, is_latest: formData.is_latest, min_system_version: formData.min_system_version }; // 验证转换后的数字 if (isNaN(payload.version_code)) { showToast('版本代码必须是有效的数字', 'warning'); return; } if (formData.file_size && isNaN(payload.file_size)) { showToast('文件大小必须是有效的数字', 'warning'); return; } await apiClient.put( buildApiUrl(API_ENDPOINTS.CLIENT_DOWNLOADS.UPDATE(selectedClient.id)), payload ); handleCloseModal(); fetchClients(); showToast('客户端更新成功', 'success'); } catch (error) { console.error('更新客户端失败:', error); showToast(error.response?.data?.message || '更新失败,请重试', 'error'); } }; const handleDelete = async () => { try { await apiClient.delete(buildApiUrl(API_ENDPOINTS.CLIENT_DOWNLOADS.DELETE(deleteConfirmInfo.id))); setDeleteConfirmInfo(null); setSelectedClient(null); showToast('删除成功', 'success'); fetchClients(); } catch (error) { console.error('删除客户端失败:', error); showToast('删除失败,请重试', 'error'); setDeleteConfirmInfo(null); } }; const handleOpenModal = (client = null) => { if (client) { setIsEditing(true); setSelectedClient(client); setFormData({ platform_type: client.platform_type, platform_name: client.platform_name, version: client.version, version_code: String(client.version_code), download_url: client.download_url, file_size: client.file_size ? String(client.file_size) : '', release_notes: client.release_notes || '', is_active: client.is_active, is_latest: client.is_latest, min_system_version: client.min_system_version || '' }); } else { setIsEditing(false); setSelectedClient(null); setFormData({ platform_type: 'mobile', platform_name: 'ios', version: '', version_code: '', download_url: '', file_size: '', release_notes: '', is_active: true, is_latest: false, min_system_version: '' }); } setShowClientModal(true); }; const handleCloseModal = () => { setShowClientModal(false); setIsEditing(false); setSelectedClient(null); resetForm(); }; const handleSave = async () => { if (isEditing) { await handleUpdate(); } else { await handleCreate(); } }; const handleInputChange = (field, value) => { setFormData(prev => ({ ...prev, [field]: value })); }; const openEditModal = (client) => { handleOpenModal(client); }; const openDeleteModal = (client) => { setDeleteConfirmInfo({ id: client.id, platform_name: getPlatformLabel(client.platform_name), version: client.version }); }; const resetForm = () => { setFormData({ platform_type: 'mobile', platform_name: 'ios', version: '', version_code: '', download_url: '', file_size: '', release_notes: '', is_active: true, is_latest: false, min_system_version: '' }); setSelectedClient(null); }; const getPlatformLabel = (platformName) => { const allOptions = [...platformOptions.mobile, ...platformOptions.desktop, ...platformOptions.terminal]; const option = allOptions.find(opt => opt.value === platformName); return option ? option.label : platformName; }; const formatFileSize = (bytes) => { if (!bytes) return '-'; const mb = bytes / (1024 * 1024); return `${mb.toFixed(2)} MB`; }; const toggleNotes = (clientId) => { setExpandedNotes(prev => ({ ...prev, [clientId]: !prev[clientId] })); }; const filteredClients = clients.filter(client => { if (filterPlatformType && client.platform_type !== filterPlatformType) { return false; } if (searchQuery) { const query = searchQuery.toLowerCase(); return ( client.version.toLowerCase().includes(query) || getPlatformLabel(client.platform_name).toLowerCase().includes(query) || (client.release_notes && client.release_notes.toLowerCase().includes(query)) ); } return true; }); const groupedClients = { mobile: filteredClients.filter(c => c.platform_type === 'mobile'), desktop: filteredClients.filter(c => c.platform_type === 'desktop'), terminal: filteredClients.filter(c => c.platform_type === 'terminal') }; if (loading) { return
加载中...
; } return (

客户端下载管理

setSearchQuery(e.target.value)} /> {searchQuery && ( )}
{['mobile', 'desktop', 'terminal'].map(type => { const typeClients = groupedClients[type]; if (typeClients.length === 0 && filterPlatformType && filterPlatformType !== type) { return null; } const typeConfig = { mobile: { icon: , label: '移动端' }, desktop: { icon: , label: '桌面端' }, terminal: { icon: , label: '专用终端' } }; return (

{typeConfig[type].icon} {typeConfig[type].label} ({typeClients.length})

{typeClients.length === 0 ? (
暂无客户端
) : (
{typeClients.map(client => (

{getPlatformLabel(client.platform_name)}

{client.is_latest && 最新} {!client.is_active && 未启用}
版本: {client.version}
版本代码: {client.version_code}
文件大小: {formatFileSize(client.file_size)}
{client.min_system_version && (
系统要求: {client.min_system_version}
)} {client.release_notes && (
toggleNotes(client.id)} style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} > 更新说明 {expandedNotes[client.id] ? : }
{expandedNotes[client.id] && (

{client.release_notes}

)}
)}
))}
)}
); })}
{/* 客户端表单模态框 */} } > {formData && ( <>
handleInputChange('version', e.target.value)} />
{ const value = e.target.value; if (value === '' || /^\d+$/.test(value)) { handleInputChange('version_code', value); } }} min="0" step="1" />
handleInputChange('download_url', e.target.value)} />
{ const value = e.target.value; if (value === '' || /^\d+$/.test(value)) { handleInputChange('file_size', value); } }} min="0" step="1" />
handleInputChange('min_system_version', e.target.value)} />