feat: 添加前端页面和业务说明书

refactor(server): 重构服务层代码结构
feat(server): 添加基础设施、跨境电商、AI决策等核心服务
docs: 完善前端业务说明书和开发进度文档
style: 格式化代码和文档
This commit is contained in:
2026-03-18 19:12:38 +08:00
parent c932a67be2
commit 6d0d2b6157
140 changed files with 23859 additions and 5833 deletions

View File

@@ -0,0 +1,189 @@
import React, { useState } from 'react';
import { Card, Form, Input, Button, Upload, Avatar, message } from 'antd';
import { UserOutlined, SaveOutlined, ArrowLeftOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
const { TextArea } = Input;
interface ProfileData {
name: string;
email: string;
phone: string;
position: string;
department: string;
bio: string;
avatar: string;
}
const ProfileSettings: React.FC = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [avatar, setAvatar] = useState<string>('https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50');
const initialProfile: ProfileData = {
name: 'John Doe',
email: 'john.doe@example.com',
phone: '+1 (123) 456-7890',
position: 'Product Manager',
department: 'Marketing',
bio: 'Product manager with 5 years of experience in e-commerce.',
avatar: 'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50',
};
const handleSubmit = async (values: any) => {
setLoading(true);
// 模拟API调用
setTimeout(() => {
message.success('个人设置已更新');
setLoading(false);
}, 1000);
};
const handleBack = () => {
navigate('/settings');
};
const handleAvatarChange = (info: any) => {
if (info.file.status === 'done') {
// 模拟上传成功
setAvatar('https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50');
message.success('头像上传成功');
} else if (info.file.status === 'error') {
message.error('头像上传失败');
}
};
return (
<div className="profile-settings">
<div className="page-header">
<h1></h1>
<Button
icon={<ArrowLeftOutlined />}
onClick={handleBack}
>
</Button>
</div>
<Card title="个人信息">
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
initialValues={initialProfile}
>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 24 }}>
<Upload
name="avatar"
showUploadList={false}
action="/api/upload"
onChange={handleAvatarChange}
>
<Avatar size={100} icon={<UserOutlined />} src={avatar} />
</Upload>
<div style={{ marginLeft: 24 }}>
<p></p>
<p style={{ color: '#999' }}> JPG, PNG 2MB</p>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
<Form.Item
name="name"
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>
<Form.Item
name="position"
label="职位"
rules={[{ required: true, message: '请输入职位' }]}
>
<Input placeholder="请输入职位" />
</Form.Item>
<Form.Item
name="department"
label="部门"
rules={[{ required: true, message: '请输入部门' }]}
>
<Input placeholder="请输入部门" />
</Form.Item>
</div>
<Form.Item
name="bio"
label="个人简介"
>
<TextArea rows={4} placeholder="请输入个人简介" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading} icon={<SaveOutlined />}>
</Button>
</Form.Item>
</Form>
</Card>
<Card title="密码设置" style={{ marginTop: 24 }}>
<Form
layout="vertical"
>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
<Form.Item
name="currentPassword"
label="当前密码"
rules={[{ required: true, message: '请输入当前密码' }]}
>
<Input.Password placeholder="请输入当前密码" />
</Form.Item>
<Form.Item
name="newPassword"
label="新密码"
rules={[{ required: true, message: '请输入新密码' }]}
>
<Input.Password placeholder="请输入新密码" />
</Form.Item>
<Form.Item
name="confirmPassword"
label="确认密码"
rules={[{ required: true, message: '请确认新密码' }]}
>
<Input.Password placeholder="请确认新密码" />
</Form.Item>
</div>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />}>
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
};
export default ProfileSettings;

View File

@@ -0,0 +1,187 @@
import React, { useState } from 'react';
import { Card, Form, Input, Select, Button, Switch, message } from 'antd';
import { SaveOutlined, ArrowLeftOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
const { Option } = Select;
interface TenantData {
name: string;
domain: string;
industry: string;
timezone: string;
currency: string;
language: string;
enableTwoFactor: boolean;
enableNotifications: boolean;
enableAnalytics: boolean;
}
const TenantSettings: React.FC = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const initialTenant: TenantData = {
name: 'Acme Inc.',
domain: 'acme.example.com',
industry: 'E-commerce',
timezone: 'Asia/Shanghai',
currency: 'USD',
language: 'en',
enableTwoFactor: false,
enableNotifications: true,
enableAnalytics: true,
};
const handleSubmit = async (values: any) => {
setLoading(true);
// 模拟API调用
setTimeout(() => {
message.success('租户设置已更新');
setLoading(false);
}, 1000);
};
const handleBack = () => {
navigate('/settings');
};
return (
<div className="tenant-settings">
<div className="page-header">
<h1></h1>
<Button
icon={<ArrowLeftOutlined />}
onClick={handleBack}
>
</Button>
</div>
<Card title="基本信息">
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
initialValues={initialTenant}
>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
<Form.Item
name="name"
label="租户名称"
rules={[{ required: true, message: '请输入租户名称' }]}
>
<Input placeholder="请输入租户名称" />
</Form.Item>
<Form.Item
name="domain"
label="域名"
rules={[{ required: true, message: '请输入域名' }]}
>
<Input placeholder="请输入域名" />
</Form.Item>
<Form.Item
name="industry"
label="行业"
rules={[{ required: true, message: '请选择行业' }]}
>
<Select placeholder="请选择行业">
<Option value="E-commerce">E-commerce</Option>
<Option value="Retail">Retail</Option>
<Option value="Manufacturing">Manufacturing</Option>
<Option value="Technology">Technology</Option>
</Select>
</Form.Item>
<Form.Item
name="timezone"
label="时区"
rules={[{ required: true, message: '请选择时区' }]}
>
<Select placeholder="请选择时区">
<Option value="Asia/Shanghai">Asia/Shanghai</Option>
<Option value="America/New_York">America/New_York</Option>
<Option value="Europe/London">Europe/London</Option>
<Option value="Asia/Tokyo">Asia/Tokyo</Option>
</Select>
</Form.Item>
<Form.Item
name="currency"
label="货币"
rules={[{ required: true, message: '请选择货币' }]}
>
<Select placeholder="请选择货币">
<Option value="USD">USD</Option>
<Option value="CNY">CNY</Option>
<Option value="EUR">EUR</Option>
<Option value="GBP">GBP</Option>
</Select>
</Form.Item>
<Form.Item
name="language"
label="语言"
rules={[{ required: true, message: '请选择语言' }]}
>
<Select placeholder="请选择语言">
<Option value="en">English</Option>
<Option value="zh"></Option>
<Option value="ja"></Option>
<Option value="ko"></Option>
</Select>
</Form.Item>
</div>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading} icon={<SaveOutlined />}>
</Button>
</Form.Item>
</Form>
</Card>
<Card title="功能设置" style={{ marginTop: 24 }}>
<Form
layout="vertical"
initialValues={initialTenant}
>
<Form.Item
name="enableTwoFactor"
label="启用双因素认证"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name="enableNotifications"
label="启用通知"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name="enableAnalytics"
label="启用数据分析"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item>
<Button type="primary" icon={<SaveOutlined />}>
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
};
export default TenantSettings;

View File

@@ -0,0 +1,315 @@
import React, { useState, useEffect } from 'react';
import { Table, Button, Input, Select, message, Card, Modal, Form } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, ArrowLeftOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
const { Option } = Select;
const { Search } = Input;
interface User {
id: string;
name: string;
email: string;
role: string;
status: 'active' | 'inactive';
createdAt: string;
lastLogin: string;
}
const UserManagement: React.FC = () => {
const navigate = useNavigate();
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(false);
const [filters, setFilters] = useState({
role: '',
status: '',
search: '',
});
const [isModalVisible, setIsModalVisible] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
fetchUsers();
}, [filters]);
const fetchUsers = async () => {
setLoading(true);
// 模拟API调用
setTimeout(() => {
const mockUsers: User[] = [
{
id: '1',
name: 'John Doe',
email: 'john.doe@example.com',
role: 'ADMIN',
status: 'active',
createdAt: '2026-01-01',
lastLogin: '2026-03-17',
},
{
id: '2',
name: 'Jane Smith',
email: 'jane.smith@example.com',
role: 'MANAGER',
status: 'active',
createdAt: '2026-01-02',
lastLogin: '2026-03-16',
},
{
id: '3',
name: 'Bob Johnson',
email: 'bob.johnson@example.com',
role: 'OPERATOR',
status: 'active',
createdAt: '2026-01-03',
lastLogin: '2026-03-15',
},
{
id: '4',
name: 'Alice Brown',
email: 'alice.brown@example.com',
role: 'FINANCE',
status: 'inactive',
createdAt: '2026-01-04',
lastLogin: '2026-03-10',
},
];
setUsers(mockUsers);
setLoading(false);
}, 500);
};
const handleAddUser = () => {
setIsModalVisible(true);
};
const handleEditUser = (id: string) => {
message.info('编辑用户功能开发中');
};
const handleDeleteUser = (id: string) => {
message.success('用户已删除');
fetchUsers();
};
const handleSubmit = async (values: any) => {
setLoading(true);
// 模拟API调用
setTimeout(() => {
message.success('用户已添加');
setIsModalVisible(false);
setLoading(false);
fetchUsers();
}, 1000);
};
const handleBack = () => {
navigate('/settings');
};
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
},
{
title: '角色',
dataIndex: 'role',
key: 'role',
render: (role: string) => {
const roleMap = {
ADMIN: '管理员',
MANAGER: '运营主管',
OPERATOR: '运营专员',
FINANCE: '财务主管',
SOURCING: '采购专家',
LOGISTICS: '物流专家',
ANALYST: '数据分析师',
};
return roleMap[role] || role;
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
const statusMap = {
active: '活跃',
inactive: '非活跃',
};
return statusMap[status] || status;
},
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
},
{
title: '最后登录',
dataIndex: 'lastLogin',
key: 'lastLogin',
},
{
title: '操作',
key: 'action',
render: (_: any, record: User) => (
<div>
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEditUser(record.id)}
>
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDeleteUser(record.id)}
>
</Button>
</div>
),
},
];
return (
<div className="user-management">
<div className="page-header">
<h1></h1>
<div style={{ display: 'flex', gap: 16 }}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={handleAddUser}
>
</Button>
<Button
icon={<ArrowLeftOutlined />}
onClick={handleBack}
>
</Button>
</div>
</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, role: value })}
>
<Option value=""></Option>
<Option value="ADMIN"></Option>
<Option value="MANAGER"></Option>
<Option value="OPERATOR"></Option>
<Option value="FINANCE"></Option>
</Select>
<Select
placeholder="状态"
style={{ width: 120 }}
onChange={(value) => setFilters({ ...filters, status: value })}
>
<Option value=""></Option>
<Option value="active"></Option>
<Option value="inactive"></Option>
</Select>
<Button
icon={<SearchOutlined />}
onClick={fetchUsers}
>
</Button>
</div>
<Card title="用户列表">
<Table
columns={columns}
dataSource={users}
loading={loading}
rowKey="id"
pagination={{
pageSize: 10,
showSizeChanger: true,
}}
/>
</Card>
<Modal
title="添加用户"
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
name="name"
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="password"
label="密码"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password placeholder="请输入密码" />
</Form.Item>
<Form.Item
name="role"
label="角色"
rules={[{ required: true, message: '请选择角色' }]}
>
<Select placeholder="请选择角色">
<Option value="ADMIN"></Option>
<Option value="MANAGER"></Option>
<Option value="OPERATOR"></Option>
<Option value="FINANCE"></Option>
<Option value="SOURCING"></Option>
<Option value="LOGISTICS"></Option>
<Option value="ANALYST"></Option>
</Select>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" loading={loading}>
</Button>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default UserManagement;

View File

@@ -0,0 +1,13 @@
import Settings from './index';
import ProfileSettings from './ProfileSettings';
import TenantSettings from './TenantSettings';
import UserManagement from './UserManagement';
export {
Settings,
ProfileSettings,
TenantSettings,
UserManagement,
};
export default Settings;

View File

@@ -0,0 +1,115 @@
import React from 'react';
import { Card, Row, Col, Button, Tabs } from 'antd';
import { UserOutlined, TeamOutlined, SettingOutlined, LockOutlined, BellOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
const { TabPane } = Tabs;
const Settings: React.FC = () => {
const navigate = useNavigate();
return (
<div className="settings">
<div className="page-header">
<h1></h1>
</div>
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card>
<Button
type="primary"
block
icon={<UserOutlined />}
onClick={() => navigate('/settings/profile')}
>
</Button>
</Card>
</Col>
<Col span={6}>
<Card>
<Button
type="primary"
block
icon={<TeamOutlined />}
onClick={() => navigate('/settings/tenant')}
>
</Button>
</Card>
</Col>
<Col span={6}>
<Card>
<Button
type="primary"
block
icon={<UserOutlined />}
onClick={() => navigate('/settings/users')}
>
</Button>
</Card>
</Col>
<Col span={6}>
<Card>
<Button
type="primary"
block
icon={<LockOutlined />}
>
</Button>
</Card>
</Col>
</Row>
<Tabs defaultActiveKey="overview">
<TabPane tab="设置概览" key="overview">
<Row gutter={16}>
<Col span={12}>
<Card title="账户信息">
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<p></p>
</div>
</Card>
</Col>
<Col span={12}>
<Card title="通知设置">
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<p></p>
</div>
</Card>
</Col>
</Row>
</TabPane>
<TabPane tab="个人设置" key="profile">
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<Button type="primary" onClick={() => navigate('/settings/profile')}>
</Button>
</div>
</TabPane>
<TabPane tab="租户设置" key="tenant">
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<Button type="primary" onClick={() => navigate('/settings/tenant')}>
</Button>
</div>
</TabPane>
<TabPane tab="用户管理" key="users">
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<Button type="primary" onClick={() => navigate('/settings/users')}>
</Button>
</div>
</TabPane>
</Tabs>
</div>
);
};
export default Settings;