a3eb5c7b2d
从百度网盘 /apps/bypy/dofile/ 下载的完整项目结构,包含: - dofile/: Flask 后端(端口 5000)+ 静态前端(端口 6002) - officefile/: 项目相关文档 - .gitignore: Python venv / 日志 / 缓存 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
370 lines
14 KiB
JavaScript
370 lines
14 KiB
JavaScript
// Meetings Management JavaScript
|
|
|
|
let currentMeetings = [];
|
|
let currentFilter = 'all';
|
|
|
|
// 检查登录状态
|
|
async function checkLogin() {
|
|
try {
|
|
const response = await window.DAELAPI.auth.checkAuth();
|
|
if (!response.authenticated) {
|
|
window.location.href = 'login.html';
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (error) {
|
|
window.location.href = 'login.html';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 加载组会列表
|
|
async function loadMeetings() {
|
|
try {
|
|
const response = await window.DAELAPI.meetings.getMeetings();
|
|
currentMeetings = response.meetings;
|
|
displayMeetings(currentMeetings);
|
|
} catch (error) {
|
|
console.error('Failed to load meetings:', error);
|
|
document.getElementById('meetings-list').innerHTML = '<p class="text-red-600">加载失败</p>';
|
|
}
|
|
}
|
|
|
|
// 显示组会列表
|
|
function displayMeetings(meetings) {
|
|
const container = document.getElementById('meetings-list');
|
|
|
|
// 根据筛选器过滤
|
|
let filteredMeetings = meetings;
|
|
if (currentFilter !== 'all') {
|
|
filteredMeetings = meetings.filter(m => m.status === currentFilter);
|
|
}
|
|
|
|
if (filteredMeetings.length === 0) {
|
|
container.innerHTML = '<p class="text-gray-500 text-center py-12">暂无组会</p>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = filteredMeetings.map(meeting => {
|
|
const date = new Date(meeting.date);
|
|
const isOwner = meeting.organizer_id === (window.currentUser?.id);
|
|
|
|
return `
|
|
<div class="bg-white border border-gray-200 p-6 rounded-lg hover:shadow-md transition-shadow">
|
|
<div class="flex justify-between items-start">
|
|
<div class="flex-1">
|
|
<div class="flex items-center gap-3 mb-2">
|
|
<h3 class="text-xl font-bold">${meeting.title}</h3>
|
|
<span class="text-xs px-2 py-1 rounded ${getStatusColor(meeting.status)}">${getStatusText(meeting.status)}</span>
|
|
<span class="text-xs px-2 py-1 rounded bg-gray-100">${getTypeText(meeting.meeting_type)}</span>
|
|
</div>
|
|
<p class="text-gray-600 text-sm mb-3">${meeting.description || ''}</p>
|
|
<div class="flex gap-6 text-sm text-gray-500">
|
|
<span>📅 ${date.toLocaleDateString('zh-CN')}</span>
|
|
<span>🕐 ${meeting.time || ''}</span>
|
|
<span>📍 ${meeting.location || '未指定'}</span>
|
|
<span>👤 组织者: ${meeting.organizer_name || ''}</span>
|
|
<span>👥 报名: ${meeting.attendee_count || 0} 人</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2 ml-4">
|
|
<button onclick="viewMeeting(${meeting.id})" class="text-sm text-blue-600 hover:text-blue-800 px-3 py-1 border border-blue-600 rounded">查看</button>
|
|
${isOwner ? `
|
|
<button onclick="editMeeting(${meeting.id})" class="text-sm text-gray-600 hover:text-gray-800 px-3 py-1 border border-gray-600 rounded">编辑</button>
|
|
<button onclick="deleteMeeting(${meeting.id})" class="text-sm text-red-600 hover:text-red-800 px-3 py-1 border border-red-600 rounded">删除</button>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
// 状态颜色
|
|
function getStatusColor(status) {
|
|
const colors = {
|
|
'planned': 'bg-blue-100 text-blue-800',
|
|
'ongoing': 'bg-green-100 text-green-800',
|
|
'completed': 'bg-gray-100 text-gray-800',
|
|
'cancelled': 'bg-red-100 text-red-800'
|
|
};
|
|
return colors[status] || 'bg-gray-100 text-gray-800';
|
|
}
|
|
|
|
// 状态文本
|
|
function getStatusText(status) {
|
|
const texts = {
|
|
'planned': '计划中',
|
|
'ongoing': '进行中',
|
|
'completed': '已结束',
|
|
'cancelled': '已取消'
|
|
};
|
|
return texts[status] || status;
|
|
}
|
|
|
|
// 类型文本
|
|
function getTypeText(type) {
|
|
const texts = {
|
|
'weekly': '周会',
|
|
'monthly': '月会',
|
|
'special': '专题会'
|
|
};
|
|
return texts[type] || type;
|
|
}
|
|
|
|
// 显示组会表单
|
|
function showMeetingForm(meetingId = null) {
|
|
const modal = document.getElementById('meeting-modal');
|
|
const form = document.getElementById('meeting-form');
|
|
const title = document.getElementById('meeting-modal-title');
|
|
|
|
if (meetingId) {
|
|
const meeting = currentMeetings.find(m => m.id === meetingId);
|
|
if (meeting) {
|
|
title.textContent = '编辑组会';
|
|
document.getElementById('meeting-id').value = meeting.id;
|
|
document.getElementById('meeting-title').value = meeting.title;
|
|
document.getElementById('meeting-description').value = meeting.description || '';
|
|
document.getElementById('meeting-date').value = meeting.date;
|
|
document.getElementById('meeting-time').value = meeting.time || '';
|
|
document.getElementById('meeting-location').value = meeting.location || '';
|
|
document.getElementById('meeting-type').value = meeting.meeting_type;
|
|
document.getElementById('meeting-status').value = meeting.status;
|
|
}
|
|
} else {
|
|
title.textContent = '创建组会';
|
|
form.reset();
|
|
document.getElementById('meeting-id').value = '';
|
|
// 设置默认日期为今天
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('meeting-date').value = today;
|
|
}
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
|
|
// 关闭组会表单
|
|
function closeMeetingForm() {
|
|
document.getElementById('meeting-modal').classList.add('hidden');
|
|
}
|
|
|
|
// 保存组会
|
|
async function saveMeeting(event) {
|
|
event.preventDefault();
|
|
|
|
const meetingId = document.getElementById('meeting-id').value;
|
|
const meetingData = {
|
|
title: document.getElementById('meeting-title').value,
|
|
description: document.getElementById('meeting-description').value,
|
|
date: document.getElementById('meeting-date').value,
|
|
time: document.getElementById('meeting-time').value,
|
|
location: document.getElementById('meeting-location').value,
|
|
meeting_type: document.getElementById('meeting-type').value,
|
|
status: document.getElementById('meeting-status').value
|
|
};
|
|
|
|
try {
|
|
if (meetingId) {
|
|
await window.DAELAPI.meetings.updateMeeting(meetingId, meetingData);
|
|
} else {
|
|
await window.DAELAPI.meetings.createMeeting(meetingData);
|
|
}
|
|
closeMeetingForm();
|
|
loadMeetings();
|
|
} catch (error) {
|
|
alert('保存失败: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 查看组会详情
|
|
async function viewMeeting(meetingId) {
|
|
try {
|
|
const response = await window.DAELAPI.meetings.getMeeting(meetingId);
|
|
const meeting = response.meeting;
|
|
displayMeetingDetail(meeting);
|
|
} catch (error) {
|
|
alert('加载失败: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 显示组会详情
|
|
function displayMeetingDetail(meeting) {
|
|
const modal = document.getElementById('meeting-detail-modal');
|
|
const container = document.getElementById('meeting-detail-content');
|
|
const date = new Date(meeting.date);
|
|
const isOwner = meeting.organizer_id === (window.currentUser?.id);
|
|
|
|
container.innerHTML = `
|
|
<div class="flex justify-between items-start mb-6">
|
|
<h3 class="text-2xl font-bold">${meeting.title}</h3>
|
|
<button onclick="closeMeetingDetail()" class="text-gray-400 hover:text-gray-600">✕</button>
|
|
</div>
|
|
|
|
<div class="space-y-4 mb-6">
|
|
<div>
|
|
<span class="text-sm text-gray-500">日期时间:</span>
|
|
<span class="text-sm">${date.toLocaleDateString('zh-CN')} ${meeting.time || ''}</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-500">地点:</span>
|
|
<span class="text-sm">${meeting.location || '未指定'}</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-500">组织者:</span>
|
|
<span class="text-sm">${meeting.organizer_name || ''}</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-500">类型:</span>
|
|
<span class="text-sm">${getTypeText(meeting.meeting_type)}</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-sm text-gray-500">状态:</span>
|
|
<span class="text-sm ${getStatusColor(meeting.status)} px-2 py-1 rounded">${getStatusText(meeting.status)}</span>
|
|
</div>
|
|
${meeting.description ? `
|
|
<div>
|
|
<span class="text-sm text-gray-500">描述:</span>
|
|
<p class="text-sm mt-1">${meeting.description}</p>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<h4 class="font-bold mb-3">参会人员 (${meeting.attendees?.length || 0})</h4>
|
|
<div class="space-y-2">
|
|
${meeting.attendees && meeting.attendees.length > 0 ?
|
|
meeting.attendees.map(attendee => `
|
|
<div class="flex items-center justify-between p-2 bg-gray-50 rounded">
|
|
<span class="text-sm">${attendee.username}</span>
|
|
<span class="text-xs text-gray-500">${getStatusText(attendee.status)}</span>
|
|
</div>
|
|
`).join('') :
|
|
'<p class="text-sm text-gray-500">暂无报名</p>'
|
|
}
|
|
</div>
|
|
${!isOwner && meeting.status === 'planned' ? `
|
|
<button onclick="registerForMeeting(${meeting.id})" class="mt-3 bg-black text-white px-4 py-2 rounded text-sm hover:bg-gray-800">
|
|
报名参会
|
|
</button>
|
|
` : ''}
|
|
</div>
|
|
|
|
${meeting.meeting_notes ? `
|
|
<div class="mb-6">
|
|
<h4 class="font-bold mb-3">会议记录</h4>
|
|
<div class="p-4 bg-gray-50 rounded text-sm whitespace-pre-wrap">${meeting.meeting_notes}</div>
|
|
</div>
|
|
` : ''}
|
|
|
|
${meeting.files && meeting.files.length > 0 ? `
|
|
<div class="mb-6">
|
|
<h4 class="font-bold mb-3">附件 (${meeting.files.length})</h4>
|
|
<div class="space-y-2">
|
|
${meeting.files.map(file => `
|
|
<div class="flex items-center justify-between p-2 bg-gray-50 rounded">
|
|
<span class="text-sm">${file.name}</span>
|
|
<button onclick="window.DAELAPI.meetings.downloadMeetingFile(${meeting.id}, ${file.id})"
|
|
class="text-sm text-blue-600 hover:text-blue-800">
|
|
下载
|
|
</button>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
` : ''}
|
|
|
|
${isOwner ? `
|
|
<div class="flex gap-4">
|
|
<button onclick="editMeeting(${meeting.id})" class="bg-black text-white px-4 py-2 rounded text-sm hover:bg-gray-800">
|
|
编辑
|
|
</button>
|
|
<button onclick="deleteMeeting(${meeting.id})" class="bg-red-600 text-white px-4 py-2 rounded text-sm hover:bg-red-700">
|
|
删除
|
|
</button>
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
|
|
// 关闭组会详情
|
|
function closeMeetingDetail() {
|
|
document.getElementById('meeting-detail-modal').classList.add('hidden');
|
|
}
|
|
|
|
// 编辑组会
|
|
function editMeeting(meetingId) {
|
|
closeMeetingDetail();
|
|
showMeetingForm(meetingId);
|
|
}
|
|
|
|
// 删除组会
|
|
async function deleteMeeting(meetingId) {
|
|
if (!confirm('确定要删除这个组会吗?')) return;
|
|
|
|
try {
|
|
await window.DAELAPI.meetings.deleteMeeting(meetingId);
|
|
closeMeetingDetail();
|
|
loadMeetings();
|
|
} catch (error) {
|
|
alert('删除失败: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 报名参会
|
|
async function registerForMeeting(meetingId) {
|
|
try {
|
|
await window.DAELAPI.meetings.registerMeeting(meetingId);
|
|
alert('报名成功!');
|
|
viewMeeting(meetingId); // 刷新详情
|
|
} catch (error) {
|
|
alert('报名失败: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 筛选器切换
|
|
document.querySelectorAll('.filter-tab').forEach(tab => {
|
|
tab.addEventListener('click', () => {
|
|
document.querySelectorAll('.filter-tab').forEach(t => {
|
|
t.classList.remove('active', 'border-black', 'text-black');
|
|
t.classList.add('text-gray-500');
|
|
});
|
|
tab.classList.add('active', 'border-black', 'text-black');
|
|
tab.classList.remove('text-gray-500');
|
|
|
|
currentFilter = tab.dataset.filter;
|
|
displayMeetings(currentMeetings);
|
|
});
|
|
});
|
|
|
|
// 初始化
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
if (await checkLogin()) {
|
|
// 获取当前用户信息
|
|
try {
|
|
const authResponse = await window.DAELAPI.auth.checkAuth();
|
|
window.currentUser = authResponse.user;
|
|
} catch (error) {
|
|
console.error('Failed to get user info:', error);
|
|
}
|
|
|
|
loadMeetings();
|
|
|
|
// 退出按钮
|
|
document.getElementById('logout-btn').addEventListener('click', async () => {
|
|
await logout();
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|