备份:PWA配置前的完整版本

包含所有最新开发的功能和修复,作为PWA正确配置前的备份点。
This commit is contained in:
a273825743
2026-06-13 12:44:48 +08:00
parent 5fd19822a2
commit 706dcc24eb
83 changed files with 26590 additions and 13379 deletions
+64 -62
View File
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Card, Button, Table, Space, Modal, Form, Input, Select, InputNumber, Tag, Steps, message, Popconfirm, Drawer, List, Checkbox, Radio, Empty, Spin, Typography, Row, Col } from 'antd';
import { PlusOutlined, CopyOutlined, EditOutlined, DeleteOutlined, EyeOutlined, ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import apiClient from '../../utils/request';
import { useLanguageStore } from '../../store/languageStore';
const { Title, Text, Paragraph } = Typography;
const { Option } = Select;
@@ -26,6 +27,7 @@ interface Template {
}
const ProcessTemplates: React.FC = () => {
const { t, currentLanguage } = useLanguageStore();
const [templates, setTemplates] = useState<Template[]>([]);
const [loading, setLoading] = useState(false);
const [createModalVisible, setCreateModalVisible] = useState(false);
@@ -46,7 +48,7 @@ const ProcessTemplates: React.FC = () => {
try {
const res = await apiClient.get('/process-templates');
if (res.data.success) setTemplates(res.data.data);
} catch (e) { message.error('获取模板列表失败'); }
} catch (e) { message.error(t('processTemplate.getListFailed')); }
finally { setLoading(false); }
};
@@ -58,32 +60,32 @@ const ProcessTemplates: React.FC = () => {
setCreateModalVisible(true);
};
const handleEdit = (t: Template) => {
form.setFieldsValue({ name: t.name, description: t.description });
setPhases(t.phases || []);
setCurrentStep(1);
setEditingTemplateId(t.id);
const handleEdit = (template: Template) => {
form.setFieldsValue({ name: template.name, description: template.description });
setPhases(template.phases || []);
setCurrentStep(0);
setEditingTemplateId(template.id);
setCreateModalVisible(true);
};
const handleCopy = async (t: Template) => {
const handleCopy = async (template: Template) => {
try {
await apiClient.post(`/process-templates/${t.id}/copy`, { name: `${t.name} (副本)` });
message.success('复制成功');
await apiClient.post(`/process-templates/${template.id}/copy`, { name: `${template.name} (副本)` });
message.success(t('processTemplate.copySuccess'));
fetchTemplates();
} catch (e) { message.error('复制失败'); }
} catch (e) { message.error(t('processTemplate.copyFailed')); }
};
const handleDelete = async (id: number) => {
try {
await apiClient.delete(`/process-templates/${id}`);
message.success('删除成功');
message.success(t('processTemplate.deleteSuccess'));
fetchTemplates();
} catch (e) { message.error('删除失败'); }
} catch (e) { message.error(t('processTemplate.deleteFailed')); }
};
const handleView = (t: Template) => {
setCurrentTemplate(t);
const handleView = (template: Template) => {
setCurrentTemplate(template);
setViewDrawerVisible(true);
};
@@ -91,19 +93,19 @@ const ProcessTemplates: React.FC = () => {
try {
const name = form.getFieldValue('name');
const description = form.getFieldValue('description');
if (!name) { message.warning('请输入模板名称'); setCurrentStep(0); return; }
if (phases.length === 0) { message.warning('请至少添加一个阶段'); setCurrentStep(1); return; }
if (!name) { message.warning(t('processTemplate.nameRequired')); setCurrentStep(0); return; }
if (phases.length === 0) { message.warning(t('processTemplate.phaseRequired')); setCurrentStep(1); return; }
if (editingTemplateId) {
await apiClient.put(`/process-templates/${editingTemplateId}`, { name, description, phases });
message.success('模板更新成功');
message.success(t('processTemplate.updateSuccess'));
} else {
await apiClient.post('/process-templates', { name, description, phases });
message.success('模板创建成功');
message.success(t('processTemplate.createSuccess'));
}
setCreateModalVisible(false);
setEditingTemplateId(null);
fetchTemplates();
} catch (e) { message.error('保存失败'); }
} catch (e) { message.error(t('common.saveFailed')); }
};
const addPhase = () => {
@@ -135,7 +137,7 @@ const ProcessTemplates: React.FC = () => {
depends: values.depends || [],
sub_items: subItems,
};
if (!phaseData.name) { message.warning('阶段名称不能为空'); return; }
if (!phaseData.name) { message.warning(t('processTemplate.phaseNameEmpty')); return; }
const newPhases = [...phases];
if (editingPhaseIndex >= 0) {
phaseData.order = newPhases[editingPhaseIndex].order;
@@ -162,7 +164,7 @@ const ProcessTemplates: React.FC = () => {
setPhases(newPhases);
};
const getTypeTag = (type: string) => type === 'parallel' ? <Tag color="blue"></Tag> : <Tag color="green"></Tag>;
const getTypeTag = (type: string) => type === 'parallel' ? <Tag color="blue">{t('processTemplate.parallelLabel')}</Tag> : <Tag color="green">{t('processTemplate.serialLabel')}</Tag>;
const getDependsNames = (depends: number[]) => {
return depends.map(d => {
@@ -172,69 +174,69 @@ const ProcessTemplates: React.FC = () => {
};
const columns = [
{ title: '模板名称', dataIndex: 'name', key: 'name', render: (text: string, r: Template) => <Space>{text}{r.is_system && <Tag color="gold"></Tag>}</Space> },
{ title: '阶段数', key: 'phases', render: (_: unknown, r: Template) => r.phases?.length || 0 },
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
{ title: '操作', key: 'action', render: (_: unknown, r: Template) => (
{ title: t('processTemplate.templateName'), dataIndex: 'name', key: 'name', render: (text: string, r: Template) => <Space>{text}{r.is_system && <Tag color="gold">{t('processTemplate.systemPreset')}</Tag>}</Space> },
{ title: t('processTemplate.phaseCount'), key: 'phases', render: (_: unknown, r: Template) => r.phases?.length || 0 },
{ title: t('processTemplate.desc'), dataIndex: 'description', key: 'description', ellipsis: true },
{ title: t('processTemplate.action'), key: 'action', render: (_: unknown, r: Template) => (
<Space>
<Button size="small" icon={<EyeOutlined />} onClick={() => handleView(r)}></Button>
<Button size="small" type="primary" icon={<EditOutlined />} onClick={() => handleEdit(r)}></Button>
<Button size="small" icon={<CopyOutlined />} onClick={() => handleCopy(r)}></Button>
{!r.is_system && <Popconfirm title="确定删除?" onConfirm={() => handleDelete(r.id)}><Button size="small" danger icon={<DeleteOutlined />}></Button></Popconfirm>}
<Button size="small" icon={<EyeOutlined />} onClick={() => handleView(r)}>{t('common.view')}</Button>
<Button size="small" type="primary" icon={<EditOutlined />} onClick={() => handleEdit(r)}>{t('common.edit')}</Button>
<Button size="small" icon={<CopyOutlined />} onClick={() => handleCopy(r)}>{t('processTemplate.copy')}</Button>
{!r.is_system && <Popconfirm title={t('processTemplate.confirmDelete')} onConfirm={() => handleDelete(r.id)}><Button size="small" danger icon={<DeleteOutlined />}>{t('processTemplate.delete')}</Button></Popconfirm>}
</Space>
)},
];
return (
<div style={{ padding: 24 }}>
<Card title="工程模板管理" extra={<Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}></Button>}>
<Card title={t('processTemplate.title')} extra={<Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}>{t('processTemplate.newTemplate')}</Button>}>
<Table columns={columns} dataSource={templates} rowKey="id" loading={loading} pagination={false} />
</Card>
<Modal title={editingTemplateId ? "编辑工程模板" : "新建工程模板"} open={createModalVisible} onCancel={() => setCreateModalVisible(false)} width={800} footer={[
<Button key="cancel" onClick={() => setCreateModalVisible(false)}></Button>,
currentStep > 0 && <Button key="prev" onClick={() => setCurrentStep(currentStep - 1)}></Button>,
<Modal title={editingTemplateId ? t('processTemplate.editTemplate') : t('processTemplate.newTemplate')} open={createModalVisible} onCancel={() => setCreateModalVisible(false)} width={800} footer={[
<Button key="cancel" onClick={() => setCreateModalVisible(false)}>{t('processTemplate.cancel')}</Button>,
currentStep > 0 && <Button key="prev" onClick={() => setCurrentStep(currentStep - 1)}>{t('processTemplate.prev')}</Button>,
currentStep < 2 && <Button key="next" type="primary" onClick={() => {
if (currentStep === 0 && !form.getFieldValue('name')) { message.warning('请输入模板名称'); return; }
if (currentStep === 0 && !form.getFieldValue('name')) { message.warning(t('processTemplate.nameRequired')); return; }
setCurrentStep(currentStep + 1);
}}></Button>,
currentStep === 2 && <Button key="save" type="primary" onClick={handleSaveTemplate}>{editingTemplateId ? '保存修改' : '确认创建'}</Button>,
}}>{t('processTemplate.next')}</Button>,
currentStep === 2 && <Button key="save" type="primary" onClick={handleSaveTemplate}>{editingTemplateId ? t('processTemplate.saveEdit') : t('processTemplate.confirmCreate')}</Button>,
]}>
<Steps current={currentStep} size="small" style={{ marginBottom: 24 }}>
<Steps.Step title="基本信息" />
<Steps.Step title="设计阶段" />
<Steps.Step title="预览确认" />
<Steps.Step title={t('processTemplate.basicInfo')} />
<Steps.Step title={t('processTemplate.designPhase')} />
<Steps.Step title={t('processTemplate.preview')} />
</Steps>
{currentStep === 0 && (
<Form form={form} layout="vertical">
<Form.Item name="name" label="模板名称" rules={[{ required: true }]}>
<Input placeholder="例如:配电安装工程" />
<Form.Item name="name" label={t('processTemplate.templateName')} rules={[{ required: true }]}>
<Input placeholder={t('processTemplate.namePlaceholder')} />
</Form.Item>
<Form.Item name="description" label="模板描述">
<TextArea rows={3} placeholder="描述该模板适用的工程类型" />
<Form.Item name="description" label={t('processTemplate.templateDescription')}>
<TextArea rows={3} placeholder={t('processTemplate.descPlaceholder')} />
</Form.Item>
</Form>
)}
{currentStep === 1 && (
<div>
{phases.length === 0 ? <Empty description="暂无阶段,请点击下方添加" /> : (
{phases.length === 0 ? <Empty description={t('processTemplate.noPhase')} /> : (
<List bordered dataSource={phases} renderItem={(phase, index) => (
<List.Item actions={[
<Button size="small" icon={<EditOutlined />} onClick={() => editPhase(index)}></Button>,
<Button size="small" icon={<EditOutlined />} onClick={() => editPhase(index)}>{t('common.edit')}</Button>,
<Button size="small" icon={<ArrowUpOutlined />} onClick={() => movePhase(index, 'up')} disabled={index === 0} />,
<Button size="small" icon={<ArrowDownOutlined />} onClick={() => movePhase(index, 'down')} disabled={index === phases.length - 1} />,
<Popconfirm title="确定删除?" onConfirm={() => removePhase(index)}><Button size="small" danger icon={<DeleteOutlined />} /></Popconfirm>,
<Popconfirm title={t('processTemplate.confirmDelete')} onConfirm={() => removePhase(index)}><Button size="small" danger icon={<DeleteOutlined />} /></Popconfirm>,
]}>
<List.Item.Meta
title={<Space>{phase.order}. {phase.name} {getTypeTag(phase.type)} <Text type="secondary">{getDependsNames(phase.depends)}</Text></Space>}
description={phase.sub_items?.length > 0 ? `子项:${phase.sub_items.join('、')}` : '无子项'}
title={<Space>{phase.order}. {phase.name} {getTypeTag(phase.type)} <Text type="secondary">{t('processTemplate.dependencyLabel')}{getDependsNames(phase.depends)}</Text></Space>}
description={phase.sub_items?.length > 0 ? `子项:${phase.sub_items.join('、')}` : t('processTemplate.emptySubItems')}
/>
</List.Item>
)} />
)}
<Button type="dashed" block icon={<PlusOutlined />} style={{ marginTop: 16 }} onClick={addPhase}></Button>
<Button type="dashed" block icon={<PlusOutlined />} style={{ marginTop: 16 }} onClick={addPhase}>{t('processTemplate.addPhase')}</Button>
</div>
)}
@@ -247,7 +249,7 @@ const ProcessTemplates: React.FC = () => {
<List.Item.Meta
title={<Space>{phase.order}. {phase.name} {getTypeTag(phase.type)}</Space>}
description={<>
<Text type="secondary">{getDependsNames(phase.depends)}</Text><br />
<Text type="secondary">{t('processTemplate.dependencyLabel')}{getDependsNames(phase.depends)}</Text><br />
{phase.sub_items?.length > 0 && <Text>{phase.sub_items.join('、')}</Text>}
</>}
/>
@@ -257,26 +259,26 @@ const ProcessTemplates: React.FC = () => {
)}
</Modal>
<Drawer title="阶段编辑" open={phaseDrawerVisible} onClose={() => setPhaseDrawerVisible(false)} width={400} extra={<Button type="primary" onClick={savePhase}></Button>}>
<Drawer title={t('processTemplate.phaseEdit')} open={phaseDrawerVisible} onClose={() => setPhaseDrawerVisible(false)} width={400} extra={<Button type="primary" onClick={savePhase}>{t('processTemplate.save')}</Button>}>
<Form form={phaseForm} layout="vertical">
<Form.Item name="name" label="阶段名称" rules={[{ required: true }]}>
<Input placeholder="例如:物资采购" />
<Form.Item name="name" label={t('processTemplate.phaseName')} rules={[{ required: true }]}>
<Input placeholder={t('processTemplate.phaseNamePlaceholder')} />
</Form.Item>
<Form.Item name="type" label="阶段类型">
<Form.Item name="type" label={t('processTemplate.phaseType')}>
<Radio.Group>
<Radio value="serial"></Radio>
<Radio value="parallel"></Radio>
<Radio value="serial">{t('processTemplate.serial')}</Radio>
<Radio value="parallel">{t('processTemplate.parallel')}</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="depends" label="依赖关系(哪些阶段完成后才能开始)">
<Form.Item name="depends" label={t('processTemplate.dependency')}>
<Checkbox.Group>
{phases.filter((_, i) => i !== editingPhaseIndex).map(p => (
<Checkbox key={p.order} value={p.order} style={{ display: 'block' }}>{p.order}. {p.name}</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item name="sub_items_text" label="子项列表(每行一个)">
<TextArea rows={6} placeholder={"电杆采购\n变压器采购\n电缆采购"} />
<Form.Item name="sub_items_text" label={t('processTemplate.subItems')}>
<TextArea rows={6} placeholder={t('processTemplate.subItemsPlaceholder')} />
</Form.Item>
</Form>
</Drawer>
@@ -288,9 +290,9 @@ const ProcessTemplates: React.FC = () => {
<List bordered dataSource={currentTemplate.phases || []} renderItem={(phase: PhaseItem) => (
<List.Item>
<List.Item.Meta
title={<Space>{phase.order}. {phase.name} {phase.type === 'parallel' ? <Tag color="blue"></Tag> : <Tag color="green"></Tag>}</Space>}
title={<Space>{phase.order}. {phase.name} {phase.type === 'parallel' ? <Tag color="blue">{t('processTemplate.parallelLabel')}</Tag> : <Tag color="green">{t('processTemplate.serialLabel')}</Tag>}</Space>}
description={<>
<Text type="secondary">{phase.depends?.join('、') || '无'}</Text><br />
<Text type="secondary">{t('processTemplate.dependencyLabelShort')}{'阶段'}{phase.depends?.join('、') || '无'}</Text><br />
{phase.sub_items?.length > 0 && <Text>{phase.sub_items.join('、')}</Text>}
</>}
/>
@@ -303,4 +305,4 @@ const ProcessTemplates: React.FC = () => {
);
};
export default ProcessTemplates;
export default ProcessTemplates;