Initial commit: ERP system with advance verification fixes
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import React from 'react';
|
||||
import { Card, Typography, Descriptions, Tag, Row, Col, Progress, Divider } from 'antd';
|
||||
import {
|
||||
CloudServerOutlined,
|
||||
DatabaseOutlined,
|
||||
NodeIndexOutlined,
|
||||
CheckCircleOutlined,
|
||||
InfoCircleOutlined
|
||||
} from '@ant-design/icons';
|
||||
|
||||
const { Title, Paragraph, Text } = Typography;
|
||||
|
||||
const AboutPage: React.FC = () => {
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={3} style={{ marginBottom: 8 }}>关于系统</Title>
|
||||
<Paragraph type="secondary">系统信息与版本</Paragraph>
|
||||
</div>
|
||||
|
||||
<Row gutter={24}>
|
||||
<Col span={16}>
|
||||
<Card title={<><InfoCircleOutlined /> 系统信息</>}>
|
||||
<Descriptions bordered column={2}>
|
||||
<Descriptions.Item label="系统名称">轻远电力老挝ERP</Descriptions.Item>
|
||||
<Descriptions.Item label="系统版本">V1.0.0</Descriptions.Item>
|
||||
<Descriptions.Item label="开发团队">轻远电力信息技术部</Descriptions.Item>
|
||||
<Descriptions.Item label="上线日期">2026年3月</Descriptions.Item>
|
||||
<Descriptions.Item label="技术架构">
|
||||
<Tag color="blue">React 18</Tag>
|
||||
<Tag color="green">Ant Design 5</Tag>
|
||||
<Tag color="purple">Node.js</Tag>
|
||||
<Tag color="orange">PostgreSQL</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="部署环境">
|
||||
<Tag color="cyan">腾讯云服务器</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="前端框架">Vite + React + TypeScript</Descriptions.Item>
|
||||
<Descriptions.Item label="后端框架">Express.js + PostgreSQL</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
<Card title={<><CheckCircleOutlined /> 功能模块</>} style={{ marginTop: 24 }}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>项目管理</Title>
|
||||
<Text type="secondary">项目创建、进度跟踪、合同管理</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>财务管理</Title>
|
||||
<Text type="secondary">预支、报销、付款申请、核销</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>采购管理</Title>
|
||||
<Text type="secondary">商品管理、供应商管理</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>合作伙伴</Title>
|
||||
<Text type="secondary">供应商、分包商、客户管理</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>施工管理</Title>
|
||||
<Text type="secondary">施工日志、里程碑管理</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Card size="small" style={{ textAlign: 'center' }}>
|
||||
<Title level={5}>预算报价</Title>
|
||||
<Text type="secondary">项目预算、报价管理</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col span={8}>
|
||||
<Card title={<><CloudServerOutlined /> 服务器状态</>}>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Text type="secondary">CPU使用率</Text>
|
||||
<Progress percent={45} status="active" />
|
||||
</div>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Text type="secondary">内存使用</Text>
|
||||
<Progress percent={60} strokeColor="#52c41a" />
|
||||
</div>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Text type="secondary">磁盘空间</Text>
|
||||
<Progress percent={35} strokeColor="#1890ff" />
|
||||
</div>
|
||||
<Divider />
|
||||
<Descriptions column={1} size="small">
|
||||
<Descriptions.Item label="服务器IP">43.161.248.209</Descriptions.Item>
|
||||
<Descriptions.Item label="操作系统">OpenCloudOS 9</Descriptions.Item>
|
||||
<Descriptions.Item label="Node版本">v22.22.1</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
<Card title={<><DatabaseOutlined /> 数据库状态</>} style={{ marginTop: 24 }}>
|
||||
<div style={{ textAlign: 'center', padding: 20 }}>
|
||||
<CheckCircleOutlined style={{ fontSize: 48, color: '#52c41a' }} />
|
||||
<Title level={4} style={{ margin: '16px 0 8px' }}>运行正常</Title>
|
||||
<Text type="secondary">PostgreSQL 15</Text>
|
||||
</div>
|
||||
<Divider />
|
||||
<Descriptions column={1} size="small">
|
||||
<Descriptions.Item label="数据库名">company_finance_db</Descriptions.Item>
|
||||
<Descriptions.Item label="连接状态">正常</Descriptions.Item>
|
||||
<Descriptions.Item label="最近备份">2026-03-19 00:00</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card style={{ marginTop: 24, background: '#f6ffed', borderColor: '#b7eb8f' }}>
|
||||
<Text>© 2026 轻远电力老挝ERP系统 - 版本 V1.0.0</Text>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutPage;
|
||||
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import { Card, Typography, Button, Table, Tag, Space, Modal, Form, Input, DatePicker, message, Row, Col, Progress } from 'antd';
|
||||
import { DownloadOutlined, UploadOutlined, DeleteOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Title, Paragraph, Text } = Typography;
|
||||
|
||||
const BackupPage: React.FC = () => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [backuping, setBackuping] = React.useState(false);
|
||||
|
||||
const columns = [
|
||||
{ title: '备份名称', dataIndex: 'name', key: 'name' },
|
||||
{ title: '备份时间', dataIndex: 'time', key: 'time' },
|
||||
{ title: '文件大小', dataIndex: 'size', key: 'size' },
|
||||
{ title: '备份类型', dataIndex: 'type', key: 'type', render: (v: string) => <Tag color={v === 'auto' ? 'blue' : 'green'}>{v === 'auto' ? '自动' : '手动'}</Tag> },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', render: (v: string) => <Tag color={v === 'success' ? 'success' : 'error'}>{v === 'success' ? '成功' : '失败'}</Tag> },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: () => (
|
||||
<Space>
|
||||
<Button size="small" type="link" icon={<DownloadOutlined />}>下载</Button>
|
||||
<Button size="small" type="link" icon={<UploadOutlined />}>恢复</Button>
|
||||
<Button size="small" danger type="link" icon={<DeleteOutlined />}>删除</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const data = [
|
||||
{ key: '1', name: 'backup-20260319.sql', time: '2026-03-19 00:00', size: '15.2 MB', type: 'auto', status: 'success' },
|
||||
{ key: '2', name: 'backup-20260318.sql', time: '2026-03-18 00:00', size: '14.8 MB', type: 'auto', status: 'success' },
|
||||
{ key: '3', name: 'backup-manual-20260317.sql', time: '2026-03-17 15:30', size: '14.5 MB', type: 'manual', status: 'success' },
|
||||
];
|
||||
|
||||
const handleBackup = () => {
|
||||
setBackuping(true);
|
||||
setTimeout(() => {
|
||||
message.success('备份创建成功');
|
||||
setBackuping(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={3} style={{ marginBottom: 8 }}>数据备份</Title>
|
||||
<Paragraph type="secondary">管理系统数据备份与恢复</Paragraph>
|
||||
</div>
|
||||
|
||||
<Row gutter={16} style={{ marginBottom: 24 }}>
|
||||
<Col span={6}>
|
||||
<Card>
|
||||
<Text type="secondary">总备份数</Text>
|
||||
<Title level={2} style={{ margin: '8px 0 0' }}>3</Title>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card>
|
||||
<Text type="secondary">总大小</Text>
|
||||
<Title level={2} style={{ margin: '8px 0 0' }}>44.5 MB</Title>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card>
|
||||
<Text type="secondary">最近备份</Text>
|
||||
<Title level={4} style={{ margin: '8px 0 0' }}>2026-03-19 00:00</Title>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card>
|
||||
<Text type="secondary">存储空间</Text>
|
||||
<Progress percent={30} size="small" style={{ marginTop: 8 }} />
|
||||
<Text type="secondary">300 MB / 1 GB</Text>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card
|
||||
title="备份列表"
|
||||
extra={
|
||||
<Space>
|
||||
<Button icon={<ClockCircleOutlined />}>自动备份设置</Button>
|
||||
<Button type="primary" icon={<DownloadOutlined />} loading={backuping} onClick={handleBackup}>
|
||||
立即备份
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table columns={columns} dataSource={data} loading={loading} pagination={{ pageSize: 10 }} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackupPage;
|
||||
@@ -0,0 +1,221 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Typography, Table, Button, Space, Modal, Form, Select, Input, message, Tag, Steps, Divider, Switch, Badge } from 'antd';
|
||||
import { EditOutlined, PlusOutlined, SettingOutlined, CheckCircleOutlined, ClockCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Title, Paragraph, Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
interface ProcessNode {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
roleName: string;
|
||||
order: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
const ProcessManagement: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [editingNode, setEditingNode] = useState<ProcessNode | null>(null);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 流程节点数据
|
||||
const [nodes, setNodes] = useState<ProcessNode[]>([
|
||||
{ id: '1', name: '发起申请', role: 'applicant', roleName: '申请人(任意角色)', order: 1, enabled: true },
|
||||
{ id: '2', name: '审批', role: 'admin', roleName: '管理员', order: 2, enabled: true },
|
||||
{ id: '3', name: '执行付款', role: 'admin', roleName: '管理员', order: 3, enabled: true },
|
||||
]);
|
||||
|
||||
// 角色选项
|
||||
const roleOptions = [
|
||||
{ value: 'applicant', label: '申请人(任意角色)' },
|
||||
{ value: 'admin', label: '管理员' },
|
||||
{ value: 'finance', label: '财务专员' },
|
||||
{ value: 'manager', label: '项目经理' },
|
||||
];
|
||||
|
||||
// 流程类型
|
||||
const processTypes = [
|
||||
{ key: 'advance', name: '预支申请', description: '员工预支款项申请流程' },
|
||||
{ key: 'reimbursement', name: '报销申请', description: '费用报销申请流程' },
|
||||
{ key: 'payment', name: '付款申请', description: '供应商付款申请流程' },
|
||||
{ key: 'verification', name: '核销申请', description: '单据核销申请流程' },
|
||||
];
|
||||
|
||||
const handleEdit = (node: ProcessNode) => {
|
||||
setEditingNode(node);
|
||||
form.setFieldsValue({
|
||||
role: node.role
|
||||
});
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
form.validateFields().then(values => {
|
||||
if (editingNode) {
|
||||
const updatedNodes = nodes.map(n => {
|
||||
if (n.id === editingNode.id) {
|
||||
const roleOption = roleOptions.find(r => r.value === values.role);
|
||||
return { ...n, role: values.role, roleName: roleOption?.label || values.role };
|
||||
}
|
||||
return n;
|
||||
});
|
||||
setNodes(updatedNodes);
|
||||
message.success('节点配置已保存');
|
||||
}
|
||||
setModalVisible(false);
|
||||
});
|
||||
};
|
||||
|
||||
const getStatusTag = (enabled: boolean) => {
|
||||
return enabled ? <Tag color="success">已启用</Tag> : <Tag color="default">已禁用</Tag>;
|
||||
};
|
||||
|
||||
const getStepStatus = (order: number) => {
|
||||
if (order === 1) return 'finish';
|
||||
if (order === 2) return 'process';
|
||||
return 'wait';
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '顺序',
|
||||
dataIndex: 'order',
|
||||
key: 'order',
|
||||
width: 80,
|
||||
render: (v: number) => <Badge count={v} style={{ backgroundColor: '#1890ff' }} />
|
||||
},
|
||||
{ title: '节点名称', dataIndex: 'name', key: 'name', width: 150 },
|
||||
{
|
||||
title: '执行角色',
|
||||
dataIndex: 'roleName',
|
||||
key: 'roleName',
|
||||
render: (v: string, r: ProcessNode) => (
|
||||
<Space>
|
||||
<Tag color={r.role === 'admin' ? 'blue' : r.role === 'finance' ? 'green' : 'default'}>
|
||||
{v}
|
||||
</Tag>
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
key: 'enabled',
|
||||
width: 100,
|
||||
render: (v: boolean) => getStatusTag(v)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 120,
|
||||
render: (_: any, record: ProcessNode) => (
|
||||
<Space>
|
||||
<Button size="small" icon={<EditOutlined />} onClick={() => handleEdit(record)}>
|
||||
编辑
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={3} style={{ marginBottom: 8 }}>流程管理</Title>
|
||||
<Paragraph type="secondary">
|
||||
配置财务申请的审批流程节点,支持自定义执行角色
|
||||
</Paragraph>
|
||||
</div>
|
||||
|
||||
{/* 流程图示 */}
|
||||
<Card title="当前流程图" style={{ marginBottom: 24 }}>
|
||||
<Steps current={1} style={{ marginTop: 16 }}>
|
||||
{nodes.filter(n => n.enabled).map((node, index) => (
|
||||
<Steps.Step
|
||||
key={node.id}
|
||||
title={node.name}
|
||||
description={node.roleName}
|
||||
status={getStepStatus(node.order)}
|
||||
icon={
|
||||
node.order === 1 ? <PlusOutlined /> :
|
||||
node.order === 2 ? <CheckCircleOutlined /> :
|
||||
<SyncOutlined />
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Steps>
|
||||
<Divider />
|
||||
<Paragraph type="secondary" style={{ marginBottom: 0 }}>
|
||||
<Text strong>说明:</Text>
|
||||
当前流程为「申请人 → 管理员审批 → 管理员执行」,后续可在下方修改执行角色为财务专员。
|
||||
</Paragraph>
|
||||
</Card>
|
||||
|
||||
{/* 节点配置表 */}
|
||||
<Card title="节点配置">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={nodes}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
size="middle"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 流程类型说明 */}
|
||||
<Card title="适用流程" style={{ marginTop: 24 }}>
|
||||
<Table
|
||||
columns={[
|
||||
{ title: '流程类型', dataIndex: 'name', key: 'name', width: 150 },
|
||||
{ title: '说明', dataIndex: 'description', key: 'description' },
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: () => <Tag color="success">已启用</Tag>
|
||||
}
|
||||
]}
|
||||
dataSource={processTypes}
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
size="middle"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 编辑节点弹窗 */}
|
||||
<Modal
|
||||
title={`编辑节点:${editingNode?.name}`}
|
||||
open={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onOk={handleSave}
|
||||
width={500}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item label="节点名称">
|
||||
<Input value={editingNode?.name} disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="role"
|
||||
label="执行角色"
|
||||
rules={[{ required: true, message: '请选择执行角色' }]}
|
||||
>
|
||||
<Select placeholder="选择执行角色">
|
||||
{roleOptions.map(opt => (
|
||||
<Option key={opt.value} value={opt.value}>{opt.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ padding: 12, background: '#fffbe6', borderRadius: 6, marginTop: 16 }}>
|
||||
<Text type="warning">
|
||||
⚠️ 修改执行角色会影响所有使用此流程的申请。建议在公司有财务专员后再将执行节点改为财务角色。
|
||||
</Text>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProcessManagement;
|
||||
Reference in New Issue
Block a user