406 lines
14 KiB
HTML
406 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>待办事项 - OA系统</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
background: #f5f5f5;
|
||
max-width: 414px;
|
||
margin: 0 auto;
|
||
min-height: 100vh;
|
||
padding-bottom: 60px;
|
||
}
|
||
.header {
|
||
background: #1890ff;
|
||
color: white;
|
||
padding: 12px 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
}
|
||
.header-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
}
|
||
.badge {
|
||
background: #ff4d4f;
|
||
color: white;
|
||
border-radius: 10px;
|
||
padding: 2px 8px;
|
||
font-size: 12px;
|
||
margin-left: 8px;
|
||
}
|
||
.tab-nav {
|
||
background: white;
|
||
display: flex;
|
||
padding: 0 16px;
|
||
border-bottom: 1px solid #e5e5e5;
|
||
position: sticky;
|
||
top: 44px;
|
||
z-index: 99;
|
||
}
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 12px 0;
|
||
color: #666;
|
||
font-size: 14px;
|
||
position: relative;
|
||
cursor: pointer;
|
||
}
|
||
.tab-item.active {
|
||
color: #1890ff;
|
||
font-weight: 500;
|
||
}
|
||
.tab-item.active::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 2px;
|
||
background: #1890ff;
|
||
}
|
||
.content {
|
||
padding: 16px;
|
||
background: #ffffff;
|
||
min-height: calc(100vh - 44px - 60px);
|
||
}
|
||
.card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
margin-bottom: 12px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
}
|
||
.todo-item {
|
||
padding: 16px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
.todo-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.todo-item:active {
|
||
background: #f5f5f5;
|
||
}
|
||
.todo-title {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.todo-meta {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-bottom: 8px;
|
||
line-height: 1.5;
|
||
}
|
||
.todo-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 12px;
|
||
}
|
||
.status-badge {
|
||
padding: 4px 10px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
.status-pending {
|
||
background: #fff7e6;
|
||
color: #d46b08;
|
||
}
|
||
.status-urgent {
|
||
background: #fff2e8;
|
||
color: #ff4d4f;
|
||
}
|
||
.time-ago {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: #999;
|
||
}
|
||
.empty-icon {
|
||
font-size: 48px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.quick-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 8px;
|
||
}
|
||
.btn-action {
|
||
flex: 1;
|
||
padding: 8px;
|
||
border: 1px solid #e5e5e5;
|
||
border-radius: 4px;
|
||
background: white;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
.btn-approve {
|
||
border-color: #52c41a;
|
||
color: #52c41a;
|
||
}
|
||
.btn-reject {
|
||
border-color: #ff4d4f;
|
||
color: #ff4d4f;
|
||
}
|
||
.bottom-nav {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 100%;
|
||
max-width: 414px;
|
||
background: white;
|
||
border-top: 1px solid #e5e5e5;
|
||
display: flex;
|
||
padding: 8px 0;
|
||
z-index: 100;
|
||
}
|
||
.nav-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 8px;
|
||
color: #666;
|
||
font-size: 12px;
|
||
text-decoration: none;
|
||
cursor: pointer;
|
||
}
|
||
.nav-item.active { color: #1890ff; }
|
||
.nav-icon { font-size: 20px; margin-bottom: 4px; }
|
||
.fab {
|
||
position: fixed;
|
||
right: 16px;
|
||
bottom: 80px;
|
||
width: 56px;
|
||
height: 56px;
|
||
border-radius: 50%;
|
||
background: #1890ff;
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 26px;
|
||
box-shadow: 0 8px 24px rgba(24,144,255,0.3);
|
||
cursor: pointer;
|
||
}
|
||
.toast {
|
||
position: fixed;
|
||
top: 20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(0,0,0,0.75);
|
||
color: #fff;
|
||
padding: 10px 16px;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
z-index: 200;
|
||
animation: fade 0.3s;
|
||
}
|
||
@keyframes fade {
|
||
from { opacity: 0; transform: translate(-50%, -10px); }
|
||
to { opacity: 1; transform: translate(-50%, 0); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<div class="header-title">待办事项 <span class="badge" id="todoCount">0</span></div>
|
||
<div style="font-size: 14px;" onclick="pullRefresh()">刷新</div>
|
||
</div>
|
||
|
||
<div class="tab-nav">
|
||
<div class="tab-item active" onclick="switchTab(event,'all')">全部</div>
|
||
<div class="tab-item" onclick="switchTab(event,'pending')">待审核</div>
|
||
<div class="tab-item" onclick="switchTab(event,'done')">已办</div>
|
||
<div class="tab-item" onclick="switchTab(event,'my')">我的</div>
|
||
</div>
|
||
|
||
<div class="content" id="listContainer"></div>
|
||
|
||
<div class="fab" onclick="openQuickActions()">+</div>
|
||
|
||
<div class="bottom-nav">
|
||
<a href="todo.html" class="nav-item active">
|
||
<div class="nav-icon">📋</div>
|
||
<div>待办</div>
|
||
</a>
|
||
<a href="message.html" class="nav-item">
|
||
<div class="nav-icon">🔔</div>
|
||
<div>消息</div>
|
||
</a>
|
||
<a href="project.html" class="nav-item">
|
||
<div class="nav-icon">📁</div>
|
||
<div>项目</div>
|
||
</a>
|
||
<a href="output.html" class="nav-item">
|
||
<div class="nav-icon">📝</div>
|
||
<div>成果</div>
|
||
</a>
|
||
<a href="profile.html" class="nav-item">
|
||
<div class="nav-icon">👤</div>
|
||
<div>我的</div>
|
||
</a>
|
||
</div>
|
||
|
||
<script>
|
||
const tasks = [
|
||
{ id:'LX-001', title:'立项申请 - XX工程', project:'25-1', applicant:'张三', time:'2025-01-15 14:30', ago:'2小时前', status:'pending', tag:'待审核', type:'initiation' },
|
||
{ id:'QD-002', title:'启动申请 - YY工程', project:'25-2', applicant:'李四', time:'2025-01-15 16:20', ago:'30分钟前', status:'urgent', tag:'紧急', type:'start' },
|
||
{ id:'CG-003', title:'成果提交 - ZZ工程', project:'25-3', applicant:'王工', time:'2025-01-15 10:15', ago:'6小时前', status:'pending', tag:'待审核', type:'output', outputType:'final', multiSubmit:true, allStaff:['张三','李四','王五'], submittedStaff:['张三','李四'] },
|
||
{ id:'QK-004', title:'请款单 - XX工程', project:'25-1', applicant:'赵六', time:'2025-01-10 09:10', ago:'2天前', status:'done', tag:'已办', type:'request' }
|
||
];
|
||
let activeTab = 'all';
|
||
|
||
function toast(msg) {
|
||
const t = document.createElement('div');
|
||
t.className = 'toast';
|
||
t.innerText = msg;
|
||
document.body.appendChild(t);
|
||
setTimeout(()=>t.remove(), 1500);
|
||
}
|
||
|
||
function switchTab(e, tab) {
|
||
activeTab = tab;
|
||
document.querySelectorAll('.tab-item').forEach(i=>i.classList.remove('active'));
|
||
e.target.classList.add('active');
|
||
renderList();
|
||
}
|
||
|
||
function viewDetail(id) {
|
||
const task = tasks.find(t => t.id === id);
|
||
let url = 'approval-detail.html?id=' + encodeURIComponent(id);
|
||
if (task && task.type) {
|
||
url += '&type=' + task.type;
|
||
if (task.outputType) {
|
||
url += '&outputType=' + task.outputType;
|
||
}
|
||
}
|
||
window.location.href = url;
|
||
}
|
||
|
||
function quickApprove(id) {
|
||
toast('已通过:' + id);
|
||
markDone(id);
|
||
}
|
||
|
||
function quickReject(id) {
|
||
const reason = prompt('请输入驳回原因:');
|
||
if (reason) {
|
||
toast('已驳回:' + id);
|
||
markDone(id);
|
||
}
|
||
}
|
||
|
||
function quickTransfer(id) {
|
||
const user = prompt('转办给:');
|
||
if (user) {
|
||
toast('已转办给 ' + user);
|
||
}
|
||
}
|
||
|
||
function markDone(id) {
|
||
const item = tasks.find(t=>t.id===id);
|
||
if (item) item.status = 'done';
|
||
renderList();
|
||
}
|
||
|
||
function pullRefresh() {
|
||
toast('已刷新');
|
||
renderList();
|
||
}
|
||
|
||
function openQuickActions() {
|
||
const choice = prompt('快捷动作:1 发起请款 2 发起开票 3 发起出差');
|
||
if (choice === '1') toast('打开请款申请');
|
||
else if (choice === '2') toast('打开开票申请');
|
||
else if (choice === '3') toast('打开出差申请');
|
||
}
|
||
|
||
function renderCard(item) {
|
||
const statusClass = item.status === 'urgent' ? 'status-urgent' : item.status === 'done' ? '' : 'status-pending';
|
||
const statusText = item.tag;
|
||
|
||
// 多人提交提示
|
||
let multiSubmitTip = '';
|
||
if (item.multiSubmit && item.type === 'output') {
|
||
const pendingStaff = item.allStaff.filter(s => !item.submittedStaff.includes(s));
|
||
multiSubmitTip = `
|
||
<div style="background: #e6f7ff; padding: 8px; border-radius: 4px; margin-top: 8px; font-size: 12px; color: #666;">
|
||
<div style="margin-bottom: 4px;">📋 多人提交:已提交 ${item.submittedStaff.length}/${item.allStaff.length} 人</div>
|
||
${pendingStaff.length > 0 ? `<div style="color: #ff4d4f;">待提交:${pendingStaff.join('、')}</div>` : '<div style="color: #52c41a;">✓ 所有人员已提交,等待组长审核</div>'}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
return `
|
||
<div class="todo-item" onclick="viewDetail('${item.id}')">
|
||
<div class="todo-title">
|
||
<span>${item.title}</span>
|
||
${item.status === 'done' ? '' : `<span class="status-badge ${statusClass}">${statusText}</span>`}
|
||
</div>
|
||
<div class="todo-meta">
|
||
项目编号:${item.project}<br>
|
||
申请人:${item.applicant} | 申请时间:${item.time}
|
||
</div>
|
||
${multiSubmitTip}
|
||
<div class="todo-footer">
|
||
<span class="time-ago">${item.ago}</span>
|
||
${item.status === 'done' ? '<span style="color:#52c41a;font-size:12px;">已处理</span>' : `
|
||
<div class="quick-actions">
|
||
<button class="btn-action btn-approve" onclick="event.stopPropagation(); quickApprove('${item.id}')">通过</button>
|
||
<button class="btn-action btn-reject" onclick="event.stopPropagation(); quickReject('${item.id}')">驳回</button>
|
||
<button class="btn-action" onclick="event.stopPropagation(); quickTransfer('${item.id}')">转办</button>
|
||
</div>`}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderList() {
|
||
const container = document.getElementById('listContainer');
|
||
let list = tasks;
|
||
if (activeTab === 'pending') list = tasks.filter(t=>t.status==='pending' || t.status==='urgent');
|
||
if (activeTab === 'done') list = tasks.filter(t=>t.status==='done');
|
||
if (activeTab === 'my') list = tasks.filter(t=>t.applicant==='张三');
|
||
document.getElementById('todoCount').innerText = tasks.filter(t=>t.status!=='done').length;
|
||
if (!list.length) {
|
||
container.innerHTML = `
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📝</div>
|
||
<div>暂无内容</div>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
container.innerHTML = '<div class="card">' + list.map(renderCard).join('') + '</div>';
|
||
}
|
||
|
||
renderList();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|