feat: support announcement image upload
# Conflicts: # database.py # db/migrations.py # routes/admin_api/core.py # static/admin/.vite/manifest.json # static/admin/assets/AnnouncementsPage-Btl9JP7M.js # static/admin/assets/EmailPage-CwqlBGU2.js # static/admin/assets/FeedbacksPage-B_qDNL3q.js # static/admin/assets/LogsPage-DzdymdrQ.js # static/admin/assets/ReportPage-Bp26gOA-.js # static/admin/assets/SettingsPage-__r25pN8.js # static/admin/assets/SystemPage-C1OfxrU-.js # static/admin/assets/UsersPage-DhnABKcY.js # static/admin/assets/email-By53DCWv.js # static/admin/assets/email-ByiJ74rd.js # static/admin/assets/email-DkWacopQ.js # static/admin/assets/index-D5wU2pVd.js # static/admin/assets/tasks-1acmkoIX.js # static/admin/assets/update-DdQLVpC3.js # static/admin/assets/users-B1w166uc.js # static/admin/assets/users-CPJP5r-B.js # static/admin/assets/users-CnIyvFWm.js # static/admin/index.html # static/app/.vite/manifest.json # static/app/assets/AccountsPage-C48gJL8c.js # static/app/assets/AccountsPage-D387XNsv.js # static/app/assets/AccountsPage-DBJCAsJz.js # static/app/assets/LoginPage-BgK_Vl6X.js # static/app/assets/RegisterPage-CwADxWfe.js # static/app/assets/ResetPasswordPage-CVfZX_5z.js # static/app/assets/SchedulesPage-CWuZpJ5h.js # static/app/assets/SchedulesPage-Dw-mXbG5.js # static/app/assets/SchedulesPage-DwzGOBuc.js # static/app/assets/ScreenshotsPage-C6vX2U3V.js # static/app/assets/ScreenshotsPage-CreOSjVc.js # static/app/assets/ScreenshotsPage-DuTeRzLR.js # static/app/assets/VerifyResultPage-BzGlCgtE.js # static/app/assets/VerifyResultPage-CN_nr4V6.js # static/app/assets/VerifyResultPage-CNbQc83z.js # static/app/assets/accounts-BFaVMUve.js # static/app/assets/accounts-BYq3lLev.js # static/app/assets/accounts-Bc9j2moH.js # static/app/assets/auth-Dk_ApO4B.js # static/app/assets/index-BIng7uZJ.css # static/app/assets/index-CDxVo_1Z.js # static/app/index.html
This commit is contained in:
@@ -1233,6 +1233,18 @@
|
||||
<label>公告内容</label>
|
||||
<textarea id="announcementContent" rows="5" placeholder="请输入公告内容(将以弹窗形式展示)"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>公告图片(可选)</label>
|
||||
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
|
||||
<button class="btn btn-secondary" onclick="triggerAnnouncementImageUpload()">+ 上传图片</button>
|
||||
<button class="btn" onclick="clearAnnouncementImage()" style="background: #eee;">移除</button>
|
||||
<input type="file" id="announcementImageFile" accept="image/*" style="display: none;" onchange="uploadAnnouncementImageFile()">
|
||||
<input type="text" id="announcementImageUrl" placeholder="上传后自动填充" readonly style="flex: 1; min-width: 220px;">
|
||||
</div>
|
||||
<div id="announcementImagePreview" style="display: none; margin-top: 8px;">
|
||||
<img id="announcementImagePreviewImg" src="" alt="公告图片预览" style="max-width: 260px; max-height: 160px; border-radius: 8px; border: 1px solid #e5e7eb; object-fit: contain;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<button class="btn btn-primary" onclick="createAnnouncement(true)">发布并启用</button>
|
||||
<button class="btn btn-secondary" onclick="createAnnouncement(false)">保存但不启用</button>
|
||||
@@ -1632,6 +1644,7 @@
|
||||
<th style="width: 70px;">ID</th>
|
||||
<th>标题</th>
|
||||
<th style="width: 90px;">状态</th>
|
||||
<th style="width: 70px;">图片</th>
|
||||
<th style="width: 170px;">创建时间</th>
|
||||
<th style="width: 220px;">操作</th>
|
||||
</tr>
|
||||
@@ -1646,6 +1659,7 @@
|
||||
${a.is_active ? '启用' : '停用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>${a.image_url ? '有图' : '-'}</td>
|
||||
<td>${a.created_at || '-'}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
@@ -1670,17 +1684,82 @@
|
||||
const content = document.getElementById('announcementContent');
|
||||
if (title) title.value = '';
|
||||
if (content) content.value = '';
|
||||
clearAnnouncementImage();
|
||||
}
|
||||
|
||||
function triggerAnnouncementImageUpload() {
|
||||
const input = document.getElementById('announcementImageFile');
|
||||
if (input) input.click();
|
||||
}
|
||||
|
||||
async function uploadAnnouncementImageFile() {
|
||||
const input = document.getElementById('announcementImageFile');
|
||||
const urlInput = document.getElementById('announcementImageUrl');
|
||||
const file = input?.files?.[0];
|
||||
if (!file || !urlInput) return;
|
||||
|
||||
if (file.type && !String(file.type).startsWith('image/')) {
|
||||
showNotification('请选择图片文件', 'error');
|
||||
input.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
try {
|
||||
const response = await fetch('/yuyx/api/announcements/upload_image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data?.success) {
|
||||
showNotification(data?.error || '上传失败', 'error');
|
||||
return;
|
||||
}
|
||||
urlInput.value = data.url || '';
|
||||
updateAnnouncementImagePreview();
|
||||
showNotification('上传成功', 'success');
|
||||
} catch (e) {
|
||||
showNotification('上传失败', 'error');
|
||||
} finally {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function clearAnnouncementImage() {
|
||||
const imageUrl = document.getElementById('announcementImageUrl');
|
||||
const imageFile = document.getElementById('announcementImageFile');
|
||||
if (imageUrl) imageUrl.value = '';
|
||||
if (imageFile) imageFile.value = '';
|
||||
updateAnnouncementImagePreview();
|
||||
}
|
||||
|
||||
function updateAnnouncementImagePreview() {
|
||||
const imageUrl = document.getElementById('announcementImageUrl');
|
||||
const previewWrap = document.getElementById('announcementImagePreview');
|
||||
const previewImg = document.getElementById('announcementImagePreviewImg');
|
||||
if (!imageUrl || !previewWrap || !previewImg) return;
|
||||
const url = String(imageUrl.value || '').trim();
|
||||
if (url) {
|
||||
previewImg.src = url;
|
||||
previewWrap.style.display = 'block';
|
||||
} else {
|
||||
previewImg.removeAttribute('src');
|
||||
previewWrap.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function viewAnnouncement(id) {
|
||||
const announcement = announcements.find(a => a.id === id);
|
||||
if (!announcement) return;
|
||||
alert(`标题:${announcement.title || ''}\n\n内容:\n${announcement.content || ''}`);
|
||||
const imageLine = announcement.image_url ? `\n图片:${announcement.image_url}` : '';
|
||||
alert(`标题:${announcement.title || ''}${imageLine}\n\n内容:\n${announcement.content || ''}`);
|
||||
}
|
||||
|
||||
async function createAnnouncement(isActive) {
|
||||
const title = (document.getElementById('announcementTitle')?.value || '').trim();
|
||||
const content = (document.getElementById('announcementContent')?.value || '').trim();
|
||||
const image_url = (document.getElementById('announcementImageUrl')?.value || '').trim();
|
||||
if (!title || !content) {
|
||||
showNotification('标题和内容不能为空', 'error');
|
||||
return;
|
||||
@@ -1690,7 +1769,7 @@
|
||||
const response = await fetch('/yuyx/api/announcements', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, content, is_active: !!isActive })
|
||||
body: JSON.stringify({ title, content, image_url, is_active: !!isActive })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user