521 lines
20 KiB
HTML
521 lines
20 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>项目看板 - OA系统</title>
|
|
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
|
<!-- 统一布局样式 -->
|
|
<link rel="stylesheet" href="unified-layout.css">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
|
|
background: #f0f2f5;
|
|
overflow: hidden;
|
|
}
|
|
.layout-container {
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.navbar {
|
|
height: 50px;
|
|
background: #fff;
|
|
border-bottom: 1px solid #e6e6e6;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20px;
|
|
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
|
}
|
|
.navbar-left {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.logo {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #409EFF;
|
|
cursor: pointer;
|
|
}
|
|
.nav-menu { display: flex; gap: 8px; margin-left: 16px; }
|
|
.nav-item { padding: 8px 16px; cursor: pointer; border-radius: 4px; color: #303133; }
|
|
.nav-item:hover { background: #ecf5ff; color: #409EFF; }
|
|
.nav-item.active { background: #409EFF; color: #fff; }
|
|
.navbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
}
|
|
.main-container {
|
|
flex: 1;
|
|
display: flex;
|
|
overflow: hidden;
|
|
}
|
|
.app-main {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: #f0f2f5;
|
|
}
|
|
.content {
|
|
flex: 1;
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
background: #fff;
|
|
margin: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
.page-header {
|
|
margin-bottom: 24px;
|
|
padding-bottom: 16px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.page-title {
|
|
font-size: 20px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
}
|
|
.view-switch {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
.view-btn {
|
|
padding: 6px 16px;
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 4px;
|
|
background: white;
|
|
color: #606266;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
.view-btn.active {
|
|
background: #409EFF;
|
|
color: white;
|
|
border-color: #409EFF;
|
|
}
|
|
.board-container {
|
|
display: flex;
|
|
gap: 16px;
|
|
overflow-x: auto;
|
|
padding-bottom: 20px;
|
|
}
|
|
.board-column {
|
|
flex: 1;
|
|
min-width: 280px;
|
|
background: #f5f7fa;
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
}
|
|
.column-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 16px;
|
|
padding-bottom: 12px;
|
|
border-bottom: 2px solid #e4e7ed;
|
|
}
|
|
.column-title {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
}
|
|
.column-count {
|
|
background: #409EFF;
|
|
color: white;
|
|
border-radius: 12px;
|
|
padding: 2px 8px;
|
|
font-size: 12px;
|
|
}
|
|
.project-card {
|
|
background: white;
|
|
border-radius: 6px;
|
|
padding: 16px;
|
|
margin-bottom: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
cursor: move;
|
|
transition: all 0.3s;
|
|
}
|
|
.project-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
|
transform: translateY(-2px);
|
|
}
|
|
.project-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 12px;
|
|
}
|
|
.project-number {
|
|
font-size: 12px;
|
|
color: #909399;
|
|
margin-bottom: 4px;
|
|
}
|
|
.project-name {
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
margin-bottom: 8px;
|
|
}
|
|
.project-info {
|
|
font-size: 13px;
|
|
color: #666;
|
|
line-height: 1.6;
|
|
}
|
|
.project-info-item {
|
|
margin-bottom: 6px;
|
|
}
|
|
.project-amount {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #409EFF;
|
|
margin-top: 8px;
|
|
}
|
|
.project-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 12px;
|
|
padding-top: 12px;
|
|
border-top: 1px solid #f0f0f0;
|
|
}
|
|
.action-btn {
|
|
flex: 1;
|
|
padding: 6px 12px;
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 4px;
|
|
background: white;
|
|
color: #606266;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
text-align: center;
|
|
}
|
|
.action-btn:hover {
|
|
border-color: #409EFF;
|
|
color: #409EFF;
|
|
}
|
|
.search-bar {
|
|
background: #fafafa;
|
|
padding: 16px;
|
|
border-radius: 4px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.search-row {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
}
|
|
.form-input, .form-select {
|
|
padding: 8px 12px;
|
|
border: 1px solid #d9d9d9;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
}
|
|
.form-input {
|
|
flex: 1;
|
|
}
|
|
.form-select {
|
|
width: 150px;
|
|
}
|
|
.btn {
|
|
padding: 8px 16px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
}
|
|
.btn-primary {
|
|
background: #409EFF;
|
|
color: white;
|
|
}
|
|
.btn-default {
|
|
background: white;
|
|
color: #333;
|
|
border: 1px solid #d9d9d9;
|
|
}
|
|
.empty-column {
|
|
text-align: center;
|
|
padding: 40px 20px;
|
|
color: #909399;
|
|
font-size: 14px;
|
|
}
|
|
</style>
|
|
<!-- Vue.js -->
|
|
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
|
|
<!-- Element UI JS -->
|
|
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
|
<!-- SortableJS for drag and drop -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<div class="layout-container">
|
|
<div class="navbar">
|
|
<div class="navbar-left">
|
|
<div class="logo" onclick="window.location.href='dashboard.html'">OA系统</div>
|
|
<div class="nav-menu">
|
|
<div class="nav-item" onclick="window.location.href='dashboard.html'">首页</div>
|
|
<div class="nav-item active">商机管理</div>
|
|
<div class="nav-item" onclick="window.location.href='project-start.html'">过程管理</div>
|
|
<div class="nav-item" onclick="window.location.href='finance-invoice.html'">财务管理</div>
|
|
<div class="nav-item" onclick="window.location.href='report-project-detail.html'">报表管理</div>
|
|
</div>
|
|
</div>
|
|
<div class="navbar-right">
|
|
<span style="color: #303133;">张三</span><span style="color: #909399; margin: 0 8px;">|</span>
|
|
<a href="profile.html" style="color: #409EFF; text-decoration: none;">个人中心</a>
|
|
<span>|</span>
|
|
<a href="login.html" style="color: #409EFF; text-decoration: none;">退出</a>
|
|
</div>
|
|
</div>
|
|
<div class="main-container">
|
|
<div class="app-main">
|
|
<div class="content">
|
|
<div class="page-header">
|
|
<div class="page-title">项目看板</div>
|
|
<div style="display: flex; gap: 12px; align-items: center;">
|
|
<div class="view-switch">
|
|
<div class="view-btn active" onclick="switchView('board')">看板视图</div>
|
|
<div class="view-btn" onclick="switchView('list')">列表视图</div>
|
|
</div>
|
|
<button class="btn btn-primary" onclick="window.location.href='project-list.html'">📋 切换到列表</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="search-bar">
|
|
<div class="search-row">
|
|
<input type="text" class="form-input" placeholder="搜索项目编号、项目名称" id="searchInput">
|
|
<select class="form-select" id="filterService">
|
|
<option value="">全部服务类型</option>
|
|
<option value="工程咨询">工程咨询</option>
|
|
<option value="跟踪审计">跟踪审计</option>
|
|
<option value="结算审计">结算审计</option>
|
|
<option value="项目管理">项目管理</option>
|
|
<option value="工程监理">工程监理</option>
|
|
</select>
|
|
<button class="btn btn-primary" onclick="searchProjects()">查询</button>
|
|
<button class="btn btn-default" onclick="resetSearch()">重置</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="board-container" id="boardContainer">
|
|
<!-- 已立项 -->
|
|
<div class="board-column" data-status="initiated">
|
|
<div class="column-header">
|
|
<div class="column-title">已立项</div>
|
|
<div class="column-count" id="count-initiated">0</div>
|
|
</div>
|
|
<div class="column-content" id="column-initiated">
|
|
<!-- 项目卡片将动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 投标中 -->
|
|
<div class="board-column" data-status="bidding">
|
|
<div class="column-header">
|
|
<div class="column-title">投标中</div>
|
|
<div class="column-count" id="count-bidding">0</div>
|
|
</div>
|
|
<div class="column-content" id="column-bidding"></div>
|
|
</div>
|
|
|
|
<!-- 已中标 -->
|
|
<div class="board-column" data-status="won">
|
|
<div class="column-header">
|
|
<div class="column-title">已中标</div>
|
|
<div class="column-count" id="count-won">0</div>
|
|
</div>
|
|
<div class="column-content" id="column-won"></div>
|
|
</div>
|
|
|
|
<!-- 已签合同 -->
|
|
<div class="board-column" data-status="contract">
|
|
<div class="column-header">
|
|
<div class="column-title">已签合同</div>
|
|
<div class="column-count" id="count-contract">0</div>
|
|
</div>
|
|
<div class="column-content" id="column-contract"></div>
|
|
</div>
|
|
|
|
<!-- 已启动 -->
|
|
<div class="board-column" data-status="started">
|
|
<div class="column-header">
|
|
<div class="column-title">已启动</div>
|
|
<div class="column-count" id="count-started">0</div>
|
|
</div>
|
|
<div class="column-content" id="column-started"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="common.js"></script>
|
|
<script>
|
|
// 项目数据(模拟数据,实际应从后端获取)
|
|
const projects = [
|
|
{ id: '25-1', name: 'XX工程项目', customer: 'XX公司', serviceType: '工程咨询', amount: '¥500,000', status: 'started', owner: '张三' },
|
|
{ id: '25-2', name: 'YY工程项目', customer: 'YY公司', serviceType: '跟踪审计', amount: '¥800,000', status: 'bidding', owner: '李四' },
|
|
{ id: '25-3', name: 'ZZ工程项目', customer: 'ZZ公司', serviceType: '结算审计', amount: '¥600,000', status: 'contract', owner: '王五' },
|
|
{ id: '25-4', name: 'AA工程项目', customer: 'AA公司', serviceType: '工程监理', amount: '¥1,200,000', status: 'won', owner: '赵六' },
|
|
{ id: '25-5', name: 'BB工程项目', customer: 'BB公司', serviceType: '项目管理', amount: '¥900,000', status: 'initiated', owner: '孙七' },
|
|
{ id: '25-6', name: 'CC工程项目', customer: 'CC公司', serviceType: '工程咨询', amount: '¥450,000', status: 'initiated', owner: '周八' }
|
|
];
|
|
|
|
// 渲染看板
|
|
function renderBoard() {
|
|
// 清空所有列
|
|
document.querySelectorAll('.column-content').forEach(col => col.innerHTML = '');
|
|
|
|
// 按状态分组
|
|
const statusGroups = {
|
|
'initiated': [],
|
|
'bidding': [],
|
|
'won': [],
|
|
'contract': [],
|
|
'started': []
|
|
};
|
|
|
|
projects.forEach(project => {
|
|
if (statusGroups[project.status]) {
|
|
statusGroups[project.status].push(project);
|
|
}
|
|
});
|
|
|
|
// 渲染每个列
|
|
Object.keys(statusGroups).forEach(status => {
|
|
const column = document.getElementById('column-' + status);
|
|
const count = statusGroups[status].length;
|
|
document.getElementById('count-' + status).textContent = count;
|
|
|
|
if (count === 0) {
|
|
column.innerHTML = '<div class="empty-column">暂无项目</div>';
|
|
} else {
|
|
statusGroups[status].forEach(project => {
|
|
const card = createProjectCard(project);
|
|
column.appendChild(card);
|
|
});
|
|
}
|
|
|
|
// 初始化拖拽
|
|
initSortable(column, status);
|
|
});
|
|
}
|
|
|
|
// 创建项目卡片
|
|
function createProjectCard(project) {
|
|
const card = document.createElement('div');
|
|
card.className = 'project-card';
|
|
card.draggable = true;
|
|
card.dataset.projectId = project.id;
|
|
card.innerHTML = `
|
|
<div class="project-card-header">
|
|
<div style="flex: 1;">
|
|
<div class="project-number">${project.id}</div>
|
|
<div class="project-name">${project.name}</div>
|
|
</div>
|
|
</div>
|
|
<div class="project-info">
|
|
<div class="project-info-item">客户:${project.customer}</div>
|
|
<div class="project-info-item">服务类型:${project.serviceType}</div>
|
|
<div class="project-info-item">负责人:${project.owner}</div>
|
|
<div class="project-amount">${project.amount}</div>
|
|
</div>
|
|
<div class="project-actions">
|
|
<div class="action-btn" onclick="viewProject('${project.id}')">查看</div>
|
|
<div class="action-btn" onclick="editProject('${project.id}')">编辑</div>
|
|
</div>
|
|
`;
|
|
return card;
|
|
}
|
|
|
|
// 初始化拖拽功能
|
|
function initSortable(column, status) {
|
|
new Sortable(column, {
|
|
group: 'projects',
|
|
animation: 150,
|
|
onEnd: function(evt) {
|
|
const projectId = evt.item.dataset.projectId;
|
|
const newStatus = evt.to.closest('.board-column').dataset.status;
|
|
updateProjectStatus(projectId, newStatus);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 更新项目状态
|
|
function updateProjectStatus(projectId, newStatus) {
|
|
const project = projects.find(p => p.id === projectId);
|
|
if (project) {
|
|
project.status = newStatus;
|
|
CommonUtils.showMessage(`项目 ${projectId} 状态已更新为:${getStatusName(newStatus)}`);
|
|
renderBoard();
|
|
}
|
|
}
|
|
|
|
// 获取状态名称
|
|
function getStatusName(status) {
|
|
const statusMap = {
|
|
'initiated': '已立项',
|
|
'bidding': '投标中',
|
|
'won': '已中标',
|
|
'contract': '已签合同',
|
|
'started': '已启动'
|
|
};
|
|
return statusMap[status] || status;
|
|
}
|
|
|
|
// 查看项目
|
|
function viewProject(projectId) {
|
|
window.location.href = 'project-detail.html?id=' + projectId;
|
|
}
|
|
|
|
// 编辑项目
|
|
function editProject(projectId) {
|
|
window.location.href = 'project-list.html?edit=' + projectId;
|
|
}
|
|
|
|
// 切换视图
|
|
function switchView(view) {
|
|
document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active'));
|
|
event.target.classList.add('active');
|
|
|
|
if (view === 'list') {
|
|
window.location.href = 'project-list.html';
|
|
}
|
|
}
|
|
|
|
// 搜索项目
|
|
function searchProjects() {
|
|
const keyword = document.getElementById('searchInput').value.toLowerCase();
|
|
const serviceType = document.getElementById('filterService').value;
|
|
|
|
// 实际应用中应该调用后端API
|
|
CommonUtils.showMessage('搜索功能:' + (keyword || '全部') + (serviceType ? ' | ' + serviceType : ''));
|
|
renderBoard();
|
|
}
|
|
|
|
// 重置搜索
|
|
function resetSearch() {
|
|
document.getElementById('searchInput').value = '';
|
|
document.getElementById('filterService').value = '';
|
|
renderBoard();
|
|
}
|
|
|
|
// 页面加载时初始化
|
|
window.onload = function() {
|
|
renderBoard();
|
|
};
|
|
</script>
|
|
<script src="unified-layout.js"></script>
|
|
<script>
|
|
initUnifiedLayout('tool');
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|