import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Card, Typography, Button, Space, Table, Tag, message, Spin, Modal, Input, Form, Select, InputNumber, DatePicker, Row, Col } from 'antd'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; import apiClient from '../../utils/request'; import { useAuthStore } from '../../store/authStore'; import dayjs from 'dayjs'; const { Title, Paragraph } = Typography; interface Project { id: number project_code: string name: string customer_id: number customer_name?: string status: string budget: string spent: string start_date: string end_date: string description: string manager_name?: string progress?: number contract_amount?: number location?: string } const ProjectsPage: React.FC = () => { const navigate = useNavigate(); const [isMobile, setIsMobile] = useState(false); const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(true); const [deleteModalVisible, setDeleteModalVisible] = useState(false); const [deleteProjectId, setDeleteProjectId] = useState(null); const [deletePassword, setDeletePassword] = useState(''); const [deleteLoading, setDeleteLoading] = useState(false); const [createModalVisible, setCreateModalVisible] = useState(false); const [createLoading, setCreateLoading] = useState(false); const [form] = Form.useForm(); const [customers, setCustomers] = useState([]); const [users, setUsers] = useState([]); const { user: currentUser } = useAuthStore(); const isAdmin = currentUser?.role === 'admin' || false; useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth <= 768); checkMobile(); window.addEventListener('resize', checkMobile); return () => window.removeEventListener('resize', checkMobile); }, []); useEffect(() => { fetchProjects(); }, []); const fetchProjects = async () => { try { const response = await apiClient.get('/projects'); if (response.data.success) { setProjects(response.data.data.map((p: Project) => ({ ...p, key: p.id.toString(), progress: p.status === 'completed' ? 100 : (p.phase_progress || 0), manager_name: p.manager_name || '未分配' }))); } else { message.error(`获取项目列表失败: ${response.data.message}`); } } catch (error) { message.error('获取项目列表失败'); } finally { setLoading(false); } }; const fetchCreateOptions = async () => { try { const [custRes, userRes] = await Promise.all([ apiClient.get('/customers'), apiClient.get('/users'), ]); if (custRes.data.success) setCustomers(custRes.data.data || []); if (userRes.data.success) setUsers(userRes.data.data || []); } catch (e) { /* ignore */ } }; const handleCreateClick = () => { form.resetFields(); form.setFieldsValue({ status: 'in_progress', start_date: dayjs() }); fetchCreateOptions(); setCreateModalVisible(true); }; const handleCreateSubmit = async () => { try { const values = await form.validateFields(); setCreateLoading(true); const payload = { ...values, manager_id: values.project_manager_id, start_date: values.start_date?.format('YYYY-MM-DD'), end_date: values.end_date?.format('YYYY-MM-DD') || null, contract_amount: values.contract_amount || 0, }; const res = await apiClient.post('/projects', payload); if (res.data.success) { message.success('项目创建成功'); setCreateModalVisible(false); fetchProjects(); } else { message.error(res.data.message || '创建失败'); } } catch (e: any) { if (e.response?.data?.message) message.error(e.response.data.message); } setCreateLoading(false); }; const handleDeleteProject = (projectId: number) => { setDeleteProjectId(projectId); setDeletePassword(''); setDeleteModalVisible(true); }; const handleDeleteConfirm = async () => { if (!deleteProjectId) return; if (deletePassword !== 'X123c321@') { message.error('密码错误'); return; } setDeleteLoading(true); try { const response = await apiClient.delete(`/projects/${deleteProjectId}`, { headers: { 'x-user-role': 'admin' } }); if (response.data.success) { message.success('项目删除成功'); setDeleteModalVisible(false); fetchProjects(); } else { message.error(response.data.message || '删除失败'); } } catch (error) { message.error('删除项目失败'); } finally { setDeleteLoading(false); } }; const desktopColumns = [ { title: '项目名称', dataIndex: 'name', key: 'name', width: 250, ellipsis: true, render: (text: string, record: Project) => ( navigate(`/projects/${record.id}`)}>{text} ), }, { title: '项目经理', dataIndex: 'manager_name', key: 'manager_name', width: 100 }, { title: '预算', dataIndex: 'budget', key: 'budget', width: 120, render: (amount: string) => { const val = parseFloat(amount || '0'); return val > 0 ? `¥${(val / 10000).toFixed(1)}万` : '-'; }, }, { title: '进度', dataIndex: 'progress', key: 'progress', width: 120, render: (progress: number) => (
80 ? '#52c41a' : progress > 50 ? '#1890ff' : '#faad14', borderRadius: 10, height: 8, width: `${progress}%`, }} />
{progress}%
), }, { title: '状态', dataIndex: 'status', key: 'status', width: 100, render: (status: string) => { const statusMap: Record = { planning: { color: 'blue', text: '规划中' }, in_progress: { color: 'processing', text: '进行中' }, completed: { color: 'success', text: '已完成' }, suspended: { color: 'warning', text: '已暂停' }, active: { color: 'processing', text: '进行中' }, }; const config = statusMap[status] || { color: 'default', text: status }; return {config.text}; }, }, { title: '操作', key: 'action', width: 140, render: (_: unknown, record: Project) => ( {isAdmin && } ), }, ]; const mobileColumns = [ { title: '项目', dataIndex: 'name', key: 'name', ellipsis: true, render: (text: string, record: Project) => ( navigate(`/projects/${record.id}`)}>{text} ), }, { title: '进度', dataIndex: 'progress', key: 'progress', width: 80, render: (progress: number) => (
80 ? '#52c41a' : progress > 50 ? '#1890ff' : '#faad14', borderRadius: 4, height: 6, width: `${progress}%`, }} />
{progress}%
), }, { title: '状态', dataIndex: 'status', key: 'status', width: 70, render: (status: string) => { const statusMap: Record = { planning: { color: 'blue', text: '规划' }, in_progress: { color: 'processing', text: '进行中' }, completed: { color: 'success', text: '完成' }, suspended: { color: 'warning', text: '暂停' }, active: { color: 'processing', text: '进行中' }, }; const config = statusMap[status] || { color: 'default', text: status }; return {config.text}; }, }, { title: '操作', key: 'action', width: 60, render: (_: unknown, record: Project) => ( ), }, ]; return (
项目管理 管理项目信息、进度和预算
{loading ? (
) : ( )} setCreateModalVisible(false)} confirmLoading={createLoading} okText="创建" cancelText="取消" width={isMobile ? '95%' : 560} style={{ top: isMobile ? 10 : 40 }} destroyOnClose >
({ value: u.id, label: u.name || u.username }))} /> setDeleteModalVisible(false)} confirmLoading={deleteLoading} okText="确认删除" cancelText="取消" >

确定要删除这个项目吗?此操作不可恢复。

请输入管理员密码确认删除操作:

setDeletePassword(e.target.value)} size="large" />
); }; export default ProjectsPage;