294 lines
16 KiB
HTML
294 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>项目信息管理系统 - UI预览</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/5.12.2/reset.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/dayjs.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/locale/zh-cn.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/5.12.2/antd.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/@ant-design/icons/5.2.6/index.min.js"></script>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
background: #f0f2f5;
|
|
}
|
|
#root {
|
|
min-height: 100vh;
|
|
}
|
|
.ant-layout {
|
|
min-height: 100vh;
|
|
}
|
|
.ant-layout-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 24px;
|
|
background: #001529;
|
|
}
|
|
.ant-layout-sider {
|
|
box-shadow: 2px 0 8px rgba(0,0,0,0.15);
|
|
}
|
|
.ant-layout-content {
|
|
margin: 24px 16px;
|
|
padding: 24px;
|
|
min-height: 280px;
|
|
}
|
|
.logo {
|
|
color: white;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
.dashboard-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
gap: 24px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.dashboard-card {
|
|
background: white;
|
|
padding: 24px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
.dashboard-card h3 {
|
|
margin: 0 0 8px 0;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
.dashboard-card .number {
|
|
margin: 0;
|
|
font-size: 32px;
|
|
font-weight: 600;
|
|
color: #1890ff;
|
|
}
|
|
.preview-section {
|
|
margin-bottom: 48px;
|
|
}
|
|
.preview-section h2 {
|
|
border-bottom: 2px solid #1890ff;
|
|
padding-bottom: 12px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.mockup {
|
|
background: white;
|
|
padding: 24px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
margin-bottom: 24px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<script>
|
|
const { useState } = React;
|
|
const { Layout, Menu, Card, Table, Button, Form, Input, Select, DatePicker, Space, Dropdown, Avatar, Tag, Modal, message, Statistic } = antd;
|
|
const { Header, Sider, Content } = Layout;
|
|
const { ProjectOutlined, UserOutlined, DashboardOutlined, LogoutOutlined, SettingOutlined, FileTextOutlined, TeamOutlined, PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } = icons;
|
|
dayjs.locale('zh-cn');
|
|
|
|
const { Option } = Select;
|
|
const { RangePicker } = DatePicker;
|
|
|
|
const menuItems = [
|
|
{ key: 'dashboard', icon: React.createElement(DashboardOutlined), label: '仪表盘' },
|
|
{ key: 'projects', icon: React.createElement(FileTextOutlined), label: '项目管理' },
|
|
{ key: 'users', icon: React.createElement(TeamOutlined), label: '用户管理' },
|
|
];
|
|
|
|
const projectColumns = [
|
|
{ title: '项目编号', dataIndex: 'project_no', key: 'project_no' },
|
|
{ title: '合同编号', dataIndex: 'contract_no', key: 'contract_no' },
|
|
{ title: '项目名称', dataIndex: 'name', key: 'name' },
|
|
{ title: '预算', dataIndex: 'budget', key: 'budget', render: (val) => `¥${val.toLocaleString()}` },
|
|
{ title: '已付款', dataIndex: 'payment_amount', key: 'payment_amount', render: (val) => `¥${val.toLocaleString()}` },
|
|
{ title: '状态', dataIndex: 'status', key: 'status', render: (status) => {
|
|
const colorMap = { '新建': 'blue', '进行中': 'green', '已完成': 'default', '已暂停': 'orange', '已取消': 'red' };
|
|
return React.createElement(Tag, { color: colorMap[status] || 'default' }, status);
|
|
}},
|
|
{ title: '所属部门', dataIndex: 'department', key: 'department' },
|
|
{ title: '创建人', dataIndex: 'created_by_name', key: 'created_by_name' },
|
|
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', render: (val) => dayjs(val).format('YYYY-MM-DD') },
|
|
{
|
|
title: '操作',
|
|
key: 'action',
|
|
render: () => React.createElement(Space, null,
|
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EyeOutlined) }, '查看'),
|
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EditOutlined) }, '编辑'),
|
|
React.createElement(Button, { type: 'link', size: 'small', danger: true, icon: React.createElement(DeleteOutlined) }, '删除')
|
|
)
|
|
}
|
|
];
|
|
|
|
const userColumns = [
|
|
{ title: '用户名', dataIndex: 'username', key: 'username' },
|
|
{ title: '真实姓名', dataIndex: 'real_name', key: 'real_name' },
|
|
{ title: '部门', dataIndex: 'department', key: 'department' },
|
|
{ title: '角色', dataIndex: 'role', key: 'role', render: (role) => {
|
|
const roleMap = { 'admin': '管理员', 'market': '市场部', 'other': '其他部门' };
|
|
return React.createElement(Tag, { color: role === 'admin' ? 'red' : 'blue' }, roleMap[role] || role);
|
|
}},
|
|
{ title: '邮箱', dataIndex: 'email', key: 'email' },
|
|
{ title: '状态', dataIndex: 'is_active', key: 'is_active', render: (active) => React.createElement(Tag, { color: active ? 'green' : 'default' }, active ? '正常' : '禁用') },
|
|
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', render: (val) => dayjs(val).format('YYYY-MM-DD') },
|
|
{
|
|
title: '操作',
|
|
key: 'action',
|
|
render: () => React.createElement(Space, null,
|
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EditOutlined) }, '编辑'),
|
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(SettingOutlined) }, '重置密码'),
|
|
React.createElement(Button, { type: 'link', size: 'small', danger: true, icon: React.createElement(DeleteOutlined) }, '删除')
|
|
)
|
|
}
|
|
];
|
|
|
|
const projectData = [
|
|
{ key: 1, project_no: 'PRJ2026001', contract_no: 'CT2026001', name: '某公司官网开发', budget: 50000, payment_amount: 25000, status: '进行中', department: '市场部', created_by_name: '张三', created_at: '2026-01-24' },
|
|
{ key: 2, project_no: 'PRJ2026002', contract_no: 'CT2026002', name: '电商平台开发', budget: 100000, payment_amount: 50000, status: '新建', department: '市场部', created_by_name: '李四', created_at: '2026-01-23' },
|
|
{ key: 3, project_no: 'PRJ2026003', contract_no: 'CT2026003', name: '移动APP开发', budget: 150000, payment_amount: 150000, status: '已完成', department: '技术部', created_by_name: '王五', created_at: '2026-01-20' },
|
|
{ key: 4, project_no: 'PRJ2026004', contract_no: 'CT2026004', name: '数据分析系统', budget: 80000, payment_amount: 30000, status: '进行中', department: '市场部', created_by_name: '张三', created_at: '2026-01-18' },
|
|
{ key: 5, project_no: 'PRJ2026005', contract_no: 'CT2026005', name: '企业OA系统', budget: 200000, payment_amount: 0, status: '已暂停', department: '技术部', created_by_name: '赵六', created_at: '2026-01-15' },
|
|
];
|
|
|
|
const userData = [
|
|
{ key: 1, username: 'admin', real_name: '管理员', department: '管理部', role: 'admin', email: 'admin@example.com', is_active: true, created_at: '2026-01-10' },
|
|
{ key: 2, username: 'zhangsan', real_name: '张三', department: '市场部', role: 'market', email: 'zhangsan@example.com', is_active: true, created_at: '2026-01-15' },
|
|
{ key: 3, username: 'lisi', real_name: '李四', department: '市场部', role: 'market', email: 'lisi@example.com', is_active: true, created_at: '2026-01-16' },
|
|
{ key: 4, username: 'wangwu', real_name: '王五', department: '技术部', role: 'other', email: 'wangwu@example.com', is_active: true, created_at: '2026-01-17' },
|
|
{ key: 5, username: 'zhaoliu', real_name: '赵六', department: '技术部', role: 'other', email: 'zhaoliu@example.com', is_active: false, created_at: '2026-01-18' },
|
|
];
|
|
|
|
function App() {
|
|
const [currentView, setCurrentView] = useState('dashboard');
|
|
const [selectedMenu, setSelectedMenu] = useState('dashboard');
|
|
const [userModalVisible, setUserModalVisible] = useState(false);
|
|
const [projectModalVisible, setProjectModalVisible] = useState(false);
|
|
|
|
const handleMenuClick = ({ key }) => {
|
|
setSelectedMenu(key);
|
|
setCurrentView(key);
|
|
};
|
|
|
|
const Dashboard = () => React.createElement('div', null,
|
|
React.createElement('div', { className: 'dashboard-cards' },
|
|
React.createElement(Card, null,
|
|
React.createElement(Statistic, { title: '总项目数', value: 15, prefix: React.createElement(ProjectOutlined) })
|
|
),
|
|
React.createElement(Card, null,
|
|
React.createElement(Statistic, { title: '进行中', value: 8, valueStyle: { color: '#3f8600' }, prefix: React.createElement(FileTextOutlined) })
|
|
),
|
|
React.createElement(Card, null,
|
|
React.createElement(Statistic, { title: '已完成', value: 5, prefix: React.createElement(FileTextOutlined) })
|
|
),
|
|
React.createElement(Card, null,
|
|
React.createElement(Statistic, { title: '本月预算', value: 500000, prefix: '¥' })
|
|
)
|
|
),
|
|
React.createElement(Card, { title: '最近项目' },
|
|
React.createElement(Table, {
|
|
columns: projectColumns,
|
|
dataSource: projectData.slice(0, 3),
|
|
pagination: false
|
|
})
|
|
)
|
|
);
|
|
|
|
const ProjectList = () => React.createElement('div', null,
|
|
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between' } },
|
|
React.createElement(Space, null,
|
|
React.createElement(Input, { placeholder: '搜索项目...', style: { width: 200 } }),
|
|
React.createElement(Select, { placeholder: '状态', style: { width: 120 } },
|
|
React.createElement(Option, { value: '新建' }, '新建'),
|
|
React.createElement(Option, { value: '进行中' }, '进行中'),
|
|
React.createElement(Option, { value: '已完成' }, '已完成')
|
|
),
|
|
React.createElement(Select, { placeholder: '部门', style: { width: 120 } },
|
|
React.createElement(Option, { value: '市场部' }, '市场部'),
|
|
React.createElement(Option, { value: '技术部' }, '技术部')
|
|
)
|
|
),
|
|
React.createElement(Button, { type: 'primary', icon: React.createElement(PlusOutlined) }, '新建项目')
|
|
),
|
|
React.createElement(Card, null,
|
|
React.createElement(Table, {
|
|
columns: projectColumns,
|
|
dataSource: projectData,
|
|
pagination: { pageSize: 10 }
|
|
})
|
|
)
|
|
);
|
|
|
|
const UserList = () => React.createElement('div', null,
|
|
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between' } },
|
|
React.createElement(Space, null,
|
|
React.createElement(Input, { placeholder: '搜索用户...', style: { width: 200 } }),
|
|
React.createElement(Select, { placeholder: '角色', style: { width: 120 } },
|
|
React.createElement(Option, { value: 'admin' }, '管理员'),
|
|
React.createElement(Option, { value: 'market' }, '市场部'),
|
|
React.createElement(Option, { value: 'other' }, '其他部门')
|
|
)
|
|
),
|
|
React.createElement(Button, { type: 'primary', icon: React.createElement(PlusOutlined) }, '新建用户')
|
|
),
|
|
React.createElement(Card, null,
|
|
React.createElement(Table, {
|
|
columns: userColumns,
|
|
dataSource: userData,
|
|
pagination: { pageSize: 10 }
|
|
})
|
|
)
|
|
);
|
|
|
|
const content = {
|
|
dashboard: Dashboard,
|
|
projects: ProjectList,
|
|
users: UserList
|
|
}[currentView] || Dashboard;
|
|
|
|
return React.createElement(Layout, null,
|
|
React.createElement(Header, null,
|
|
React.createElement('div', { className: 'logo' },
|
|
React.createElement(ProjectOutlined),
|
|
' 项目信息管理系统'
|
|
),
|
|
React.createElement('div', { className: 'user-info' },
|
|
React.createElement(Avatar, { icon: React.createElement(UserOutlined) }),
|
|
React.createElement('span', { style: { color: 'white' } }, '管理员'),
|
|
React.createElement(Button, { type: 'text', icon: React.createElement(LogoutOutlined), style: { color: 'white' } }, '退出')
|
|
)
|
|
),
|
|
React.createElement(Layout, null,
|
|
React.createElement(Sider, { width: 256, style: { background: '#fff' } },
|
|
React.createElement(Menu, {
|
|
mode: 'inline',
|
|
selectedKeys: [selectedMenu],
|
|
items: menuItems,
|
|
onClick: handleMenuClick
|
|
})
|
|
),
|
|
React.createElement(Content, { style: { background: '#fff' } }, content())
|
|
)
|
|
);
|
|
}
|
|
|
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
root.render(React.createElement(App));
|
|
</script>
|
|
</body>
|
|
</html>
|