someone-oa/mobile/todo.html
2025-12-11 18:08:46 +08:00

406 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>