feat: 添加前端页面和业务说明书
refactor(server): 重构服务层代码结构 feat(server): 添加基础设施、跨境电商、AI决策等核心服务 docs: 完善前端业务说明书和开发进度文档 style: 格式化代码和文档
This commit is contained in:
323
dashboard/src/pages/Suppliers/SupplierDetail.tsx
Normal file
323
dashboard/src/pages/Suppliers/SupplierDetail.tsx
Normal file
@@ -0,0 +1,323 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Row, Col, Button, Form, Input, Select, message, Tabs, Table } from 'antd';
|
||||
import { ArrowLeftOutlined, SaveOutlined, UserOutlined, PhoneOutlined, MailOutlined, HomeOutlined, DollarOutlined, ClockOutlined, ShoppingCartOutlined } from '@ant-design/icons';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
const { Option } = Select;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
interface Supplier {
|
||||
id: string;
|
||||
name: string;
|
||||
contactName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
category: string;
|
||||
status: 'active' | 'inactive' | 'pending';
|
||||
rating: number;
|
||||
leadTime: number;
|
||||
minOrder: number;
|
||||
paymentTerms: string;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
interface Product {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
moq: number;
|
||||
leadTime: number;
|
||||
}
|
||||
|
||||
const SupplierDetail: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [supplier, setSupplier] = useState<Supplier | null>(null);
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
fetchSupplierDetail();
|
||||
}, [id]);
|
||||
|
||||
const fetchSupplierDetail = async () => {
|
||||
setLoading(true);
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
const mockSupplier: Supplier = {
|
||||
id: id || '1',
|
||||
name: 'Supplier A',
|
||||
contactName: 'John Doe',
|
||||
email: 'john@supplier-a.com',
|
||||
phone: '+1 (123) 456-7890',
|
||||
address: '123 Main St, New York, NY',
|
||||
category: 'Electronics',
|
||||
status: 'active',
|
||||
rating: 4.5,
|
||||
leadTime: 7,
|
||||
minOrder: 100,
|
||||
paymentTerms: 'Net 30',
|
||||
notes: 'Preferred supplier for electronics components',
|
||||
};
|
||||
|
||||
const mockProducts: Product[] = [
|
||||
{
|
||||
id: '1',
|
||||
sku: 'COMP-001',
|
||||
name: 'Component 1',
|
||||
price: 10.99,
|
||||
stock: 1000,
|
||||
moq: 50,
|
||||
leadTime: 5,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
sku: 'COMP-002',
|
||||
name: 'Component 2',
|
||||
price: 15.99,
|
||||
stock: 500,
|
||||
moq: 25,
|
||||
leadTime: 3,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
sku: 'COMP-003',
|
||||
name: 'Component 3',
|
||||
price: 20.99,
|
||||
stock: 750,
|
||||
moq: 30,
|
||||
leadTime: 7,
|
||||
},
|
||||
];
|
||||
|
||||
setSupplier(mockSupplier);
|
||||
setProducts(mockProducts);
|
||||
form.setFieldsValue(mockSupplier);
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: any) => {
|
||||
setLoading(true);
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
message.success('供应商信息更新成功');
|
||||
setLoading(false);
|
||||
fetchSupplierDetail();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
navigate('/suppliers');
|
||||
};
|
||||
|
||||
const productColumns = [
|
||||
{
|
||||
title: 'SKU',
|
||||
dataIndex: 'sku',
|
||||
key: 'sku',
|
||||
},
|
||||
{
|
||||
title: '产品名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '价格',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
render: (price: number) => `$${price.toFixed(2)}`,
|
||||
},
|
||||
{
|
||||
title: '库存',
|
||||
dataIndex: 'stock',
|
||||
key: 'stock',
|
||||
},
|
||||
{
|
||||
title: '最小起订量',
|
||||
dataIndex: 'moq',
|
||||
key: 'moq',
|
||||
},
|
||||
{
|
||||
title: '交货期(天)',
|
||||
dataIndex: 'leadTime',
|
||||
key: 'leadTime',
|
||||
},
|
||||
];
|
||||
|
||||
if (!supplier) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="supplier-detail">
|
||||
<div className="page-header">
|
||||
<h1>供应商详情 - {supplier.name}</h1>
|
||||
<Button
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={handleBack}
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Tabs defaultActiveKey="info">
|
||||
<TabPane tab="基本信息" key="info">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSubmit}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="供应商名称"
|
||||
rules={[{ required: true, message: '请输入供应商名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入供应商名称" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="contactName"
|
||||
label="联系人"
|
||||
rules={[{ required: true, message: '请输入联系人' }]}
|
||||
>
|
||||
<Input placeholder="请输入联系人" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="email"
|
||||
label="邮箱"
|
||||
rules={[{ required: true, message: '请输入邮箱' }]}
|
||||
>
|
||||
<Input placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="phone"
|
||||
label="电话"
|
||||
rules={[{ required: true, message: '请输入电话' }]}
|
||||
>
|
||||
<Input placeholder="请输入电话" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="address"
|
||||
label="地址"
|
||||
rules={[{ required: true, message: '请输入地址' }]}
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="category"
|
||||
label="类别"
|
||||
rules={[{ required: true, message: '请选择类别' }]}
|
||||
>
|
||||
<Select placeholder="请选择类别">
|
||||
<Option value="Electronics">Electronics</Option>
|
||||
<Option value="Clothing">Clothing</Option>
|
||||
<Option value="Home Goods">Home Goods</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="status"
|
||||
label="状态"
|
||||
rules={[{ required: true, message: '请选择状态' }]}
|
||||
>
|
||||
<Select placeholder="请选择状态">
|
||||
<Option value="active">活跃</Option>
|
||||
<Option value="inactive">非活跃</Option>
|
||||
<Option value="pending">待审核</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="leadTime"
|
||||
label="交货期(天)"
|
||||
rules={[{ required: true, message: '请输入交货期' }]}
|
||||
>
|
||||
<Input type="number" placeholder="请输入交货期" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="minOrder"
|
||||
label="最小订单量"
|
||||
rules={[{ required: true, message: '请输入最小订单量' }]}
|
||||
>
|
||||
<Input type="number" placeholder="请输入最小订单量" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="paymentTerms"
|
||||
label="付款条件"
|
||||
rules={[{ required: true, message: '请输入付款条件' }]}
|
||||
>
|
||||
<Input placeholder="请输入付款条件" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="notes"
|
||||
label="备注"
|
||||
>
|
||||
<Input.TextArea rows={3} placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={loading} icon={<SaveOutlined />}>
|
||||
保存
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="产品列表" key="products">
|
||||
<Card title="供应商产品">
|
||||
<Table
|
||||
columns={productColumns}
|
||||
dataSource={products}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showSizeChanger: true,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="历史订单" key="orders">
|
||||
<Card title="历史订单">
|
||||
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
||||
<p>历史订单列表</p>
|
||||
</div>
|
||||
</Card>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="绩效评估" key="performance">
|
||||
<Card title="绩效评估">
|
||||
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
||||
<p>绩效评估图表</p>
|
||||
</div>
|
||||
</Card>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierDetail;
|
||||
9
dashboard/src/pages/Suppliers/index.ts
Normal file
9
dashboard/src/pages/Suppliers/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import Suppliers from './index';
|
||||
import SupplierDetail from './SupplierDetail';
|
||||
|
||||
export {
|
||||
Suppliers,
|
||||
SupplierDetail,
|
||||
};
|
||||
|
||||
export default Suppliers;
|
||||
255
dashboard/src/pages/Suppliers/index.tsx
Normal file
255
dashboard/src/pages/Suppliers/index.tsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Button, Input, Select, message, Card } from 'antd';
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UserOutlined, PhoneOutlined, MailOutlined, HomeOutlined } from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Search } = Input;
|
||||
|
||||
interface Supplier {
|
||||
id: string;
|
||||
name: string;
|
||||
contactName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
category: string;
|
||||
status: 'active' | 'inactive' | 'pending';
|
||||
rating: number;
|
||||
leadTime: number;
|
||||
minOrder: number;
|
||||
}
|
||||
|
||||
const Suppliers: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [filters, setFilters] = useState({
|
||||
category: '',
|
||||
status: '',
|
||||
search: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchSuppliers();
|
||||
}, [filters]);
|
||||
|
||||
const fetchSuppliers = async () => {
|
||||
setLoading(true);
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
const mockSuppliers: Supplier[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Supplier A',
|
||||
contactName: 'John Doe',
|
||||
email: 'john@supplier-a.com',
|
||||
phone: '+1 (123) 456-7890',
|
||||
address: '123 Main St, New York, NY',
|
||||
category: 'Electronics',
|
||||
status: 'active',
|
||||
rating: 4.5,
|
||||
leadTime: 7,
|
||||
minOrder: 100,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Supplier B',
|
||||
contactName: 'Jane Smith',
|
||||
email: 'jane@supplier-b.com',
|
||||
phone: '+1 (987) 654-3210',
|
||||
address: '456 Oak Ave, Los Angeles, CA',
|
||||
category: 'Clothing',
|
||||
status: 'active',
|
||||
rating: 4.2,
|
||||
leadTime: 5,
|
||||
minOrder: 50,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Supplier C',
|
||||
contactName: 'Bob Johnson',
|
||||
email: 'bob@supplier-c.com',
|
||||
phone: '+1 (555) 123-4567',
|
||||
address: '789 Pine Rd, Chicago, IL',
|
||||
category: 'Home Goods',
|
||||
status: 'inactive',
|
||||
rating: 3.8,
|
||||
leadTime: 10,
|
||||
minOrder: 200,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Supplier D',
|
||||
contactName: 'Alice Brown',
|
||||
email: 'alice@supplier-d.com',
|
||||
phone: '+1 (222) 333-4444',
|
||||
address: '321 Cedar St, Miami, FL',
|
||||
category: 'Electronics',
|
||||
status: 'pending',
|
||||
rating: 4.0,
|
||||
leadTime: 3,
|
||||
minOrder: 75,
|
||||
},
|
||||
];
|
||||
setSuppliers(mockSuppliers);
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleAddSupplier = () => {
|
||||
message.info('添加供应商功能开发中');
|
||||
};
|
||||
|
||||
const handleEditSupplier = (id: string) => {
|
||||
navigate(`/suppliers/${id}`);
|
||||
};
|
||||
|
||||
const handleDeleteSupplier = (id: string) => {
|
||||
message.success('供应商已删除');
|
||||
fetchSuppliers();
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '供应商名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'contactName',
|
||||
key: 'contactName',
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
},
|
||||
{
|
||||
title: '电话',
|
||||
dataIndex: 'phone',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: '类别',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (status: string) => {
|
||||
const statusMap = {
|
||||
active: '活跃',
|
||||
inactive: '非活跃',
|
||||
pending: '待审核',
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '评分',
|
||||
dataIndex: 'rating',
|
||||
key: 'rating',
|
||||
},
|
||||
{
|
||||
title: '交货期(天)',
|
||||
dataIndex: 'leadTime',
|
||||
key: 'leadTime',
|
||||
},
|
||||
{
|
||||
title: '最小订单量',
|
||||
dataIndex: 'minOrder',
|
||||
key: 'minOrder',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: (_: any, record: Supplier) => (
|
||||
<div>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleEditSupplier(record.id)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleDeleteSupplier(record.id)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="suppliers">
|
||||
<div className="page-header">
|
||||
<h1>供应商管理</h1>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddSupplier}
|
||||
>
|
||||
添加供应商
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="filter-section" style={{ marginBottom: 24, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||
<Search
|
||||
placeholder="搜索供应商名称或联系人"
|
||||
style={{ width: 300 }}
|
||||
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
||||
/>
|
||||
<Select
|
||||
placeholder="类别"
|
||||
style={{ width: 120 }}
|
||||
onChange={(value) => setFilters({ ...filters, category: value })}
|
||||
>
|
||||
<Option value="">全部</Option>
|
||||
<Option value="Electronics">Electronics</Option>
|
||||
<Option value="Clothing">Clothing</Option>
|
||||
<Option value="Home Goods">Home Goods</Option>
|
||||
</Select>
|
||||
<Select
|
||||
placeholder="状态"
|
||||
style={{ width: 120 }}
|
||||
onChange={(value) => setFilters({ ...filters, status: value })}
|
||||
>
|
||||
<Option value="">全部</Option>
|
||||
<Option value="active">活跃</Option>
|
||||
<Option value="inactive">非活跃</Option>
|
||||
<Option value="pending">待审核</Option>
|
||||
</Select>
|
||||
<Button
|
||||
icon={<SearchOutlined />}
|
||||
onClick={fetchSuppliers}
|
||||
>
|
||||
筛选
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card title="供应商列表">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showSizeChanger: true,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Suppliers;
|
||||
Reference in New Issue
Block a user