Files
yunhaifinance/frontend/src/pages/construction/ConstructionOverview.tsx
T
a273825743 706dcc24eb 备份:PWA配置前的完整版本
包含所有最新开发的功能和修复,作为PWA正确配置前的备份点。
2026-06-13 12:44:48 +08:00

129 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import { Card, Button, Progress, Tag, List, Spin, message, Empty, Space, Typography } from 'antd';
import { ArrowLeftOutlined, RightOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import apiClient from '../../utils/request';
import { useLanguageStore } from '../../store/languageStore';
const { Title, Text } = Typography;
interface ProjectPhase {
id: number;
phase_name: string;
phase_order: number;
phase_type: string;
status: string;
}
interface Project {
id: number;
name: string;
status: string;
current_phase: string;
phase_progress: number;
contract_amount: number;
phases: ProjectPhase[];
}
const ConstructionOverview: React.FC = () => {
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const { t, currentLanguage } = useLanguageStore();
useEffect(() => { fetchProjects(); }, []);
const fetchProjects = async () => {
setLoading(true);
try {
const res = await apiClient.get('/construction/my-projects');
if (res.data.success) {
const projectsData = res.data.data || [];
const enriched = await Promise.all(projectsData.map(async (p: Project) => {
try {
const phaseRes = await apiClient.get(`/projects/${p.id}/phases`);
return { ...p, phases: phaseRes.data.data || [] };
} catch { return { ...p, phases: [] }; }
}));
setProjects(enriched);
}
} catch (e) { message.error(t('construction.getListFailed')); }
finally { setLoading(false); }
};
const getStatusColor = (status: string) => {
const map: Record<string, string> = { active: 'green', in_progress: 'processing', planning: 'blue', completed: 'default', suspended: 'orange' };
return map[status] || 'default';
};
const getStatusText = (status: string) => {
const map: Record<string, string> = { active: t('construction.underConstruction'), in_progress: t('construction.underConstruction'), planning: t('construction.pendingStart'), completed: t('construction.completed'), suspended: t('construction.paused') };
return map[status] || status;
};
const activeProjects = projects.filter(p => p.status !== 'completed');
const completedProjects = projects.filter(p => p.status === 'completed');
return (
<div style={{ padding: 24 }}>
<Card title={<Title level={4} style={{ margin: 0 }}>{t('construction.overview')}</Title>}>
<Spin spinning={loading}>
{activeProjects.length === 0 && completedProjects.length === 0 ? (
<Empty description={t('construction.noProjects')} />
) : (
<>
{activeProjects.length > 0 && (
<>
<Text strong style={{ fontSize: 16 }}>{t('construction.underConstruction')}{activeProjects.length}{t('common.unit')}</Text>
<List
style={{ marginTop: 16 }}
dataSource={activeProjects}
renderItem={(project) => (
<List.Item
actions={[<Button type="primary" icon={<RightOutlined />} onClick={() => navigate(`/construction/progress/${project.id}`)}>{t('construction.enter')}</Button>]}
>
<List.Item.Meta
title={<Space><Text strong>{project.name}</Text><Tag color={getStatusColor(project.status)}>{getStatusText(project.status)}</Tag></Space>}
description={
<div style={{ marginTop: 8 }}>
<div style={{ marginBottom: 4 }}>
<Text type="secondary">{t('construction.currentPhase')}{project.current_phase || t('common.notSet')}</Text>
{project.phases.length > 0 && <Text type="secondary" style={{ marginLeft: 16 }}>{project.phases.filter(p => p.status === 'completed').length}/{project.phases.length}</Text>}
</div>
<Progress percent={project.phase_progress || 0} size="small" strokeColor="#1890ff" />
</div>
}
/>
</List.Item>
)}
/>
</>
)}
{completedProjects.length > 0 && (
<>
<Text strong style={{ fontSize: 16, marginTop: 24, display: 'block' }}>{t('construction.completedProjects')}{completedProjects.length}{t('common.unit')}</Text>
<List
style={{ marginTop: 16 }}
dataSource={completedProjects}
renderItem={(project) => (
<List.Item
actions={[<Button icon={<RightOutlined />} onClick={() => navigate(`/construction/progress/${project.id}`)}>{t('common.view')}</Button>]}
>
<List.Item.Meta
title={<Space><Text>{project.name}</Text><Tag>{t('construction.completed')}</Tag></Space>}
description={<Progress percent={100} size="small" />}
/>
</List.Item>
)}
/>
</>
)}
</>
)}
</Spin>
</Card>
</div>
);
};
export default ConstructionOverview;