import React, { useState, useEffect } from 'react'; import { Card, Typography, Button, Space, Table, Tag, Modal, Form, Input, InputNumber, DatePicker, Select, message, Radio, Upload, Row, Col, Divider, Popconfirm } from 'antd'; import { PlusOutlined, DeleteOutlined, UploadOutlined, EditOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import axios from 'axios'; import dayjs from 'dayjs'; import { useAuthStore } from '../../store/authStore'; const { Title, Paragraph, Text } = Typography; const { Option } = Select; const { TextArea } = Input; const CURRENCIES = [ { value: 'CNY', label: '人民币', symbol: '¥' }, { value: 'USD', label: '美元', symbol: '$' }, { value: 'LAK', label: '老挝基普', symbol: '₭' }, { value: 'THB', label: '泰铢', symbol: '฿' }, ]; interface PaymentNode { id?: number; node_name: string; percentage: number; node_amount: number; trigger_condition: string; } interface UnitPriceItem { id?: number; item_name: string; unit: string; quantity: number; unit_price: number; total_price: number; } const ProjectsPage: React.FC = () => { const [isMobile, setIsMobile] = useState(false); const [projects, setProjects] = useState([]); const [customers, setCustomers] = useState([]); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [editingProject, setEditingProject] = useState(null); const [form] = Form.useForm(); const navigate = useNavigate(); // 当前用户信息 - 使用zustand authStore const { user: currentUser } = useAuthStore(); const isAdmin = currentUser?.role === 'admin'; // 表单监听值 const settlementType = Form.useWatch('settlement_type', form); const contractAmount = Form.useWatch('contract_amount', form); const currency = Form.useWatch('currency', form); const contractDays = Form.useWatch('contract_days', form); const startDate = Form.useWatch('start_date', form); // 付款节点和单价列表 const [paymentNodes, setPaymentNodes] = useState([]); const [unitPriceList, setUnitPriceList] = useState([]); useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth <= 768); checkMobile(); window.addEventListener('resize', checkMobile); return () => window.removeEventListener('resize', checkMobile); }, []); useEffect(() => { fetchProjects(); fetchCustomers(); fetchUsers(); }, []); // 自动计算结束日期 useEffect(() => { if (startDate && contractDays) { const endDate = startDate.add(contractDays, 'day'); form.setFieldsValue({ expected_end_date: endDate }); } }, [startDate, contractDays, form]); const fetchProjects = async () => { setLoading(true); try { const res = await axios.get('/api/projects'); if (res.data.success) setProjects(res.data.data); } catch (error) { console.error('获取项目失败:', error); } finally { setLoading(false); } }; const fetchCustomers = async () => { try { const res = await axios.get('/api/customers'); if (res.data.success) setCustomers(res.data.data); } catch (error) { console.error('获取客户列表失败:', error); } }; const fetchUsers = async () => { try { const res = await axios.get('/api/users'); if (res.data.success) setUsers(res.data.data); } catch (error) { console.error('获取用户列表失败:', error); } }; // 添加付款节点 const addPaymentNode = () => { setPaymentNodes([...paymentNodes, { node_name: '', percentage: 0, node_amount: 0, trigger_condition: '' }]); }; // 删除付款节点 const removePaymentNode = (index: number) => { const newNodes = paymentNodes.filter((_, i) => i !== index); setPaymentNodes(newNodes); }; // 更新付款节点 const updatePaymentNode = (index: number, field: string, value: any) => { const newNodes = [...paymentNodes]; newNodes[index] = { ...newNodes[index], [field]: value }; if (field === 'percentage' && contractAmount) { newNodes[index].node_amount = contractAmount * value / 100; } setPaymentNodes(newNodes); }; // 添加单价项 const addUnitPriceItem = () => { setUnitPriceList([...unitPriceList, { item_name: '', unit: '', quantity: 0, unit_price: 0, total_price: 0 }]); }; // 删除单价项 const removeUnitPriceItem = (index: number) => { const newItems = unitPriceList.filter((_, i) => i !== index); setUnitPriceList(newItems); }; // 更新单价项 const updateUnitPriceItem = (index: number, field: string, value: any) => { const newItems = [...unitPriceList]; newItems[index] = { ...newItems[index], [field]: value }; if (field === 'quantity' || field === 'unit_price') { const item = newItems[index]; item.total_price = (item.quantity || 0) * (item.unit_price || 0); } setUnitPriceList(newItems); }; // 计算单价结算总金额 const totalUnitPrice = unitPriceList.reduce((sum, item) => sum + (item.total_price || 0), 0); const handleCreate = () => { setEditingProject(null); form.resetFields(); setPaymentNodes([]); setUnitPriceList([]); setModalVisible(true); }; const handleEdit = (record: any) => { setEditingProject(record); form.setFieldsValue({ ...record, start_date: record.start_date ? dayjs(record.start_date) : null, expected_end_date: record.expected_end_date ? dayjs(record.expected_end_date) : null, }); setPaymentNodes(record.payment_nodes || []); setUnitPriceList(record.unit_price_list || []); setModalVisible(true); }; const handleDelete = async (id: number) => { try { const res = await axios.delete('/api/projects/' + id); if (res.data.success) { message.success('删除成功'); fetchProjects(); } } catch (error) { message.error('删除失败'); } }; const handleSubmit = async () => { try { const values = await form.validateFields(); const totalPercentage = paymentNodes.reduce((sum, node) => sum + (node.percentage || 0), 0); if (paymentNodes.length > 0 && totalPercentage > 100) { message.error('付款节点比例总和不能超过100%'); return; } const projectData = { ...values, start_date: values.start_date?.format('YYYY-MM-DD'), expected_end_date: values.expected_end_date?.format('YYYY-MM-DD'), contract_amount: settlementType === 'unit' ? totalUnitPrice : values.contract_amount, payment_nodes: paymentNodes, unit_price_list: settlementType === 'unit' ? unitPriceList : [], status: editingProject ? values.status : 'planning', }; if (editingProject) { const res = await axios.put('/api/projects/' + editingProject.id, projectData); if (res.data.success) { message.success('更新成功'); setModalVisible(false); fetchProjects(); } } else { const res = await axios.post('/api/projects', projectData); if (res.data.success) { message.success('创建成功'); setModalVisible(false); fetchProjects(); } } } catch (error) { message.error('操作失败'); } }; const getStatusTag = (status: string) => { const statusMap: Record = { planning: { color: 'default', text: '规划中' }, active: { color: 'processing', text: '进行中' }, completed: { color: 'success', text: '已完成' }, suspended: { color: 'warning', text: '已暂停' }, }; const config = statusMap[status] || { color: 'default', text: status }; return {config.text}; }; const formatAmount = (val: number, curr: string = 'CNY') => { const c = CURRENCIES.find(item => item.value === curr); const symbol = c?.symbol || '¥'; return symbol + ' ' + (val || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 }); }; const columns = [ { title: '项目编号', dataIndex: 'project_code', width: 120 }, { title: '项目名称', dataIndex: 'name', ellipsis: true }, { title: '客户', dataIndex: 'customer_name', render: (v: string) => v || '-' }, { title: '项目经理', dataIndex: 'manager_name', render: (v: string) => v || '-' }, { title: '合同金额', dataIndex: 'contract_amount', render: (v: number, r: any) => formatAmount(v, r.currency) }, { title: '状态', dataIndex: 'status', render: (status: string) => getStatusTag(status) }, { title: '开始日期', dataIndex: 'start_date' }, { title: '操作', key: 'action', width: isAdmin ? 200 : 80, render: (_: any, record: any) => ( {isAdmin && ( <> handleDelete(record.id)}> )} )} ]; return (
项目管理 管理项目信息、进度和预算
} onClick={handleCreate}>新建项目} > {isAdmin && ( setModalVisible(false)} width={1000} style={{ top: 20 }} okText="确定" cancelText="取消" >