someone-oa/pc/message-center.html
2025-12-11 19:04:46 +08:00

396 lines
20 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">
<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; }
.content { flex: 1; padding: 20px; overflow-y: auto; }
.grid { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
.card {
background: #fff;
border: 1px solid #e6e6e6;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
padding: 20px;
}
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.card-title { font-size: 16px; font-weight: 500; color: #303133; }
.filters { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.form-input, .form-select {
height: 36px;
padding: 0 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
min-width: 150px;
}
.btn { padding: 8px 14px; border: none; border-radius: 4px; cursor: pointer; }
.btn-primary { background: #409EFF; color: #fff; }
.btn-default { background: #fff; color: #303133; border: 1px solid #dcdfe6; }
.tabs { display: flex; gap: 10px; margin-bottom: 10px; }
.tab { padding: 10px 14px; background: #f5f7fa; border: 1px solid #e4e7ed; border-radius: 4px; cursor: pointer; color: #606266; }
.tab.active { background: #409EFF; color: #fff; border-color: #409EFF; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { border: 1px solid #ebeef5; padding: 10px 12px; color: #606266; font-size: 14px; }
.table th { background: #f5f7fa; text-align: left; }
.status-tag { padding: 2px 8px; border-radius: 10px; font-size: 12px; color: #fff; }
.status-unread { background: #f56c6c; }
.status-read { background: #67c23a; }
.tag { display: inline-block; padding: 2px 8px; border-radius: 10px; background: #ecf5ff; color: #409EFF; font-size: 12px; margin-right: 6px; }
.setting-row { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #f0f0f0; }
.setting-row:last-child { border-bottom: none; }
.switch { width: 46px; height: 24px; border-radius: 12px; background: #dcdfe6; position: relative; cursor: pointer; transition: all 0.2s; }
.switch.on { background: #409EFF; }
.switch-circle { width: 20px; height: 20px; background: #fff; border-radius: 50%; position: absolute; top: 2px; left: 2px; transition: all 0.2s; }
.switch.on .switch-circle { left: 24px; }
.channel-row { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.pill { padding: 4px 10px; border-radius: 12px; border: 1px solid #dcdfe6; color: #606266; cursor: pointer; }
.pill.active { background: #409EFF; color: #fff; border-color: #409EFF; }
</style>
</head>
<body>
<div id="app">
<div class="layout-container">
<div class="navbar">
<div class="navbar-left">
<div class="logo" onclick="goHome()">OA系统</div>
<div class="nav-menu">
<div class="nav-item" onclick="window.location.href='dashboard.html'">首页</div>
<div class="nav-item" onclick="window.location.href='project-initiation.html'">商机管理</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 class="nav-item active">消息中心</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 style="color: #909399;">|</span>
<a href="login.html" style="color: #409EFF; text-decoration: none;">退出</a>
</div>
</div>
<div class="content">
<div class="grid">
<!-- 消息列表 -->
<div class="card">
<div class="card-header">
<div class="card-title">消息中心</div>
<div style="display:flex;gap:8px;">
<button class="btn btn-default" onclick="markAllRead()">全部设为已读</button>
<button class="btn btn-default" onclick="exportMsg()">导出</button>
</div>
</div>
<div class="tabs">
<div class="tab active" id="tab-all" onclick="switchTab('all')">全部</div>
<div class="tab" id="tab-approval" onclick="switchTab('approval')">审批提醒</div>
<div class="tab" id="tab-announcement" onclick="switchTab('announcement')">公告</div>
<div class="tab" id="tab-system" onclick="switchTab('system')">系统通知</div>
</div>
<div class="filters">
<select id="filter-status" class="form-select">
<option value="">阅读状态</option>
<option value="unread">未读</option>
<option value="read">已读</option>
</select>
<input type="date" id="filter-start" class="form-input">
<span style="align-self:center;color:#909399;"></span>
<input type="date" id="filter-end" class="form-input">
<input type="text" id="filter-keyword" class="form-input" placeholder="标题/内容/来源">
<button class="btn btn-primary" onclick="applyFilters()">查询</button>
<button class="btn btn-default" onclick="resetFilters()">重置</button>
</div>
<table class="table">
<thead>
<tr>
<th style="width:40px;"><input type="checkbox" id="select-all" onchange="toggleAll(this)"></th>
<th>标题</th>
<th>类型</th>
<th>状态</th>
<th>时间</th>
<th style="width:120px;">操作</th>
</tr>
</thead>
<tbody id="msg-body"></tbody>
</table>
</div>
<!-- 提醒策略 -->
<div class="card">
<div class="card-header">
<div class="card-title">提醒策略</div>
<button class="btn btn-primary" onclick="saveStrategy()">保存策略</button>
</div>
<div style="font-size:13px;color:#909399;margin-bottom:8px;">设置渠道、时间窗和频率,避免打扰</div>
<div class="setting-row">
<div>
<div style="color:#303133;">审批提醒</div>
<div style="color:#909399;font-size:13px;">待办、驳回、超时提醒</div>
</div>
<div class="channel-row">
<div class="pill active" data-key="approval" data-channel="site" onclick="toggleChannel(this)">站内信</div>
<div class="pill" data-key="approval" data-channel="email" onclick="toggleChannel(this)">邮件</div>
<div class="pill" data-key="approval" data-channel="wechat" onclick="toggleChannel(this)">企业微信</div>
</div>
<div class="switch on" data-key="approval" onclick="toggleSwitch(this)">
<div class="switch-circle"></div>
</div>
</div>
<div class="setting-row">
<div>
<div style="color:#303133;">公告推送</div>
<div style="color:#909399;font-size:13px;">紧急公告可外发,普通公告仅站内</div>
</div>
<div class="channel-row">
<div class="pill active" data-key="announcement" data-channel="site" onclick="toggleChannel(this)">站内信</div>
<div class="pill" data-key="announcement" data-channel="email" onclick="toggleChannel(this)">邮件</div>
</div>
<div class="switch on" data-key="announcement" onclick="toggleSwitch(this)">
<div class="switch-circle"></div>
</div>
</div>
<div class="setting-row">
<div>
<div style="color:#303133;">系统通知</div>
<div style="color:#909399;font-size:13px;">安全提示、导出完成、批处理结果</div>
</div>
<div class="channel-row">
<div class="pill active" data-key="system" data-channel="site" onclick="toggleChannel(this)">站内信</div>
<div class="pill" data-key="system" data-channel="email" onclick="toggleChannel(this)">邮件</div>
</div>
<div class="switch on" data-key="system" onclick="toggleSwitch(this)">
<div class="switch-circle"></div>
</div>
</div>
<div class="setting-row">
<div>
<div style="color:#303133;">时间窗</div>
<div style="color:#909399;font-size:13px;">避免夜间打扰,紧急消息除外</div>
</div>
<div style="display:flex;gap:6px;align-items:center;">
<input type="time" id="quiet-start" class="form-input" value="22:00">
<span style="color:#909399;"></span>
<input type="time" id="quiet-end" class="form-input" value="08:00">
</div>
<div class="switch on" data-key="quiet" onclick="toggleSwitch(this)">
<div class="switch-circle"></div>
</div>
</div>
<div class="setting-row">
<div>
<div style="color:#303133;">频率限制</div>
<div style="color:#909399;font-size:13px;">同一事项在30分钟内只提醒一次</div>
</div>
<div style="display:flex;align-items:center;gap:6px;">
<input type="number" id="freq-minute" class="form-input" style="width:90px;" value="30" min="5">
<span style="color:#909399;">分钟</span>
</div>
<div class="switch on" data-key="freq" onclick="toggleSwitch(this)">
<div class="switch-circle"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="common.js"></script>
<script src="unified-layout.js"></script>
<script>
const messages = [
{ id: 'MSG-001', title: '您有1条待办立项审批', type: 'approval', content: '项目 25-1 XX工程进入部门审核', status: 'unread', time: '2024-12-02 10:20' },
{ id: 'MSG-002', title: '公告:年终安全检查', type: 'announcement', content: '12月安全巡检安排', status: 'unread', time: '2024-12-01 09:00' },
{ id: 'MSG-003', title: '系统通知:导出完成', type: 'system', content: '审批中心列表导出完成', status: 'read', time: '2024-11-30 18:10' }
];
let activeTab = 'all';
let filtered = [...messages];
function switchTab(tab) {
activeTab = tab;
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.getElementById(`tab-${tab}`).classList.add('active');
applyFilters();
}
function applyFilters() {
const status = document.getElementById('filter-status').value;
const start = document.getElementById('filter-start').value;
const end = document.getElementById('filter-end').value;
const kw = document.getElementById('filter-keyword').value.trim();
filtered = messages.filter(m => {
if (activeTab !== 'all' && m.type !== activeTab) return false;
if (status && m.status !== status) return false;
if (start && new Date(m.time) < new Date(start)) return false;
if (end && new Date(m.time) > new Date(end + ' 23:59:59')) return false;
if (kw && !(m.title.includes(kw) || m.content.includes(kw))) return false;
return true;
});
renderTable();
}
function resetFilters() {
document.getElementById('filter-status').value = '';
document.getElementById('filter-start').value = '';
document.getElementById('filter-end').value = '';
document.getElementById('filter-keyword').value = '';
applyFilters();
}
function toggleAll(checkbox) {
document.querySelectorAll('.row-check').forEach(cb => cb.checked = checkbox.checked);
}
function statusTag(status) {
return status === 'unread'
? '<span class="status-tag status-unread">未读</span>'
: '<span class="status-tag status-read">已读</span>';
}
function renderTable() {
const tbody = document.getElementById('msg-body');
tbody.innerHTML = '';
filtered.forEach(m => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td><input type="checkbox" class="row-check" value="${m.id}"></td>
<td>
<div style="font-weight: ${m.status === 'unread' ? '600' : '400'}; color:#303133;">${m.title}</div>
<div style="color:#909399;font-size:12px;margin-top:4px;">${m.content}</div>
</td>
<td>
${m.type === 'approval' ? '<span class="tag">审批</span>' : ''}
${m.type === 'announcement' ? '<span class="tag">公告</span>' : ''}
${m.type === 'system' ? '<span class="tag">系统</span>' : ''}
</td>
<td>${statusTag(m.status)}</td>
<td>${m.time}</td>
<td>
${m.status === 'unread' ? `<span class="btn btn-default" style="padding:6px 10px;" onclick="markRead('${m.id}')">标为已读</span>` : ''}
<span class="btn btn-default" style="padding:6px 10px;" onclick="viewMsg('${m.id}')">查看</span>
</td>
`;
tbody.appendChild(tr);
});
document.getElementById('select-all').checked = false;
}
function getSelectedIds() {
return Array.from(document.querySelectorAll('.row-check'))
.filter(cb => cb.checked)
.map(cb => cb.value);
}
function markRead(id) {
const item = messages.find(m => m.id === id);
if (item) item.status = 'read';
CommonUtils.showMessage('已标记为已读');
applyFilters();
}
function markAllRead() {
messages.forEach(m => m.status = 'read');
CommonUtils.showMessage('全部已读');
applyFilters();
}
function viewMsg(id) {
const item = messages.find(m => m.id === id);
if (!item) return;
const tag = item.type === 'approval' ? '审批' : item.type === 'announcement' ? '公告' : '系统';
CommonUtils.createModal('消息详情', `
<div style="margin-bottom:8px;font-weight:600;">${item.title}</div>
<div style="color:#909399;font-size:13px;margin-bottom:12px;">类型:${tag} | 时间:${item.time}</div>
<div style="line-height:1.6;color:#303133;">${item.content}</div>
`, () => true);
item.status = 'read';
renderTable();
}
function exportMsg() {
CommonUtils.exportExcel('消息中心.xlsx');
}
// 提醒策略交互
const channelState = {
approval: new Set(['site']),
announcement: new Set(['site']),
system: new Set(['site'])
};
const switchState = {
approval: true,
announcement: true,
system: true,
quiet: true,
freq: true
};
function toggleSwitch(el) {
const key = el.getAttribute('data-key');
switchState[key] = !switchState[key];
el.classList.toggle('on', switchState[key]);
}
function toggleChannel(el) {
const key = el.getAttribute('data-key');
const channel = el.getAttribute('data-channel');
const set = channelState[key];
if (set.has(channel)) {
set.delete(channel);
el.classList.remove('active');
} else {
set.add(channel);
el.classList.add('active');
}
}
function saveStrategy() {
const quietStart = document.getElementById('quiet-start').value;
const quietEnd = document.getElementById('quiet-end').value;
const freq = document.getElementById('freq-minute').value;
if (freq && Number(freq) < 5) {
CommonUtils.showMessage('频率限制不得低于5分钟', 'error');
return;
}
CommonUtils.showMessage('提醒策略已保存');
}
function goHome() { window.location.href = 'dashboard.html'; }
initUnifiedLayout('message');
// 初始化
renderTable();
</script>
</body>
</html>