mirror of
https://github.com/Coldsmiles/infstarweb.git
synced 2026-04-23 02:30:41 +08:00
feat: add share button and improve action button layout in announcements
This commit is contained in:
@@ -383,12 +383,41 @@
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-edit-btn-row {
|
.detail-action-btn-row {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
border-top: 1px solid rgba(0,0,0,0.05);
|
border-top: 1px solid rgba(0,0,0,0.05);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-share-announcement {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 16px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border: 1.5px solid rgba(0,0,0,0.12);
|
||||||
|
border-radius: 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-share-announcement:hover {
|
||||||
|
color: var(--accent-color);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
background: rgba(0,113,227,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-share-announcement.shared {
|
||||||
|
color: #15803d;
|
||||||
|
border-color: #34c759;
|
||||||
|
background: #e8fceb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-edit-announcement {
|
.btn-edit-announcement {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// Sort by time descending (newest first)
|
// Sort by time descending (newest first)
|
||||||
announcementsData.sort((a, b) => new Date(b.time) - new Date(a.time));
|
announcementsData.sort((a, b) => new Date(b.time) - new Date(a.time));
|
||||||
renderTimeline();
|
renderTimeline();
|
||||||
|
handleHashNavigation();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Error loading announcements:', err);
|
console.error('Error loading announcements:', err);
|
||||||
@@ -100,8 +101,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filtered.forEach((item, index) => {
|
filtered.forEach((item, index) => {
|
||||||
|
const anchorId = generateAnchorId(item);
|
||||||
const timelineItem = document.createElement('div');
|
const timelineItem = document.createElement('div');
|
||||||
timelineItem.className = 'timeline-item category-' + item.category;
|
timelineItem.className = 'timeline-item category-' + item.category;
|
||||||
|
timelineItem.id = anchorId;
|
||||||
|
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'announcement-card';
|
card.className = 'announcement-card';
|
||||||
@@ -148,20 +151,52 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
detailInner.className = 'detail-content';
|
detailInner.className = 'detail-content';
|
||||||
renderContentBlocks(detailInner, item.content);
|
renderContentBlocks(detailInner, item.content);
|
||||||
|
|
||||||
// Edit button inside detail (hidden by default)
|
// Action buttons row inside detail
|
||||||
const editRow = document.createElement('div');
|
const actionRow = document.createElement('div');
|
||||||
editRow.className = 'detail-edit-btn-row ' + (editModeEnabled ? 'edit-visible' : 'edit-hidden');
|
actionRow.className = 'detail-action-btn-row';
|
||||||
|
|
||||||
|
// Share button
|
||||||
|
const shareBtn = document.createElement('button');
|
||||||
|
shareBtn.className = 'btn-share-announcement';
|
||||||
|
shareBtn.innerHTML = '<i class="fas fa-share-alt"></i> 分享';
|
||||||
|
shareBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
var url = location.origin + location.pathname + '#' + anchorId;
|
||||||
|
navigator.clipboard.writeText(url).then(() => {
|
||||||
|
shareBtn.innerHTML = '<i class="fas fa-check"></i> 已复制链接';
|
||||||
|
shareBtn.classList.add('shared');
|
||||||
|
setTimeout(() => {
|
||||||
|
shareBtn.innerHTML = '<i class="fas fa-share-alt"></i> 分享';
|
||||||
|
shareBtn.classList.remove('shared');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(() => {
|
||||||
|
// Fallback: use a temporary input
|
||||||
|
var tmp = document.createElement('input');
|
||||||
|
tmp.value = url;
|
||||||
|
document.body.appendChild(tmp);
|
||||||
|
tmp.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(tmp);
|
||||||
|
shareBtn.innerHTML = '<i class="fas fa-check"></i> 已复制链接';
|
||||||
|
setTimeout(() => {
|
||||||
|
shareBtn.innerHTML = '<i class="fas fa-share-alt"></i> 分享';
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
actionRow.appendChild(shareBtn);
|
||||||
|
|
||||||
|
// Edit button (hidden by default)
|
||||||
const editBtn = document.createElement('button');
|
const editBtn = document.createElement('button');
|
||||||
editBtn.className = 'btn-edit-announcement';
|
editBtn.className = 'btn-edit-announcement ' + (editModeEnabled ? 'edit-visible' : 'edit-hidden');
|
||||||
editBtn.innerHTML = '<i class="fas fa-pen"></i> 编辑';
|
editBtn.innerHTML = '<i class="fas fa-pen"></i> 编辑';
|
||||||
editBtn.addEventListener('click', (e) => {
|
editBtn.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openEditor(item);
|
openEditor(item);
|
||||||
});
|
});
|
||||||
editRow.appendChild(editBtn);
|
actionRow.appendChild(editBtn);
|
||||||
|
|
||||||
detail.appendChild(detailInner);
|
detail.appendChild(detailInner);
|
||||||
detail.appendChild(editRow);
|
detail.appendChild(actionRow);
|
||||||
|
|
||||||
card.appendChild(summary);
|
card.appendChild(summary);
|
||||||
card.appendChild(detail);
|
card.appendChild(detail);
|
||||||
@@ -208,6 +243,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== Generate stable ID for announcement ==========
|
||||||
|
function generateAnchorId(item) {
|
||||||
|
// Use title + time to create a stable hash-friendly ID
|
||||||
|
var raw = (item.time || '') + '_' + (item.title || '');
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < raw.length; i++) {
|
||||||
|
hash = ((hash << 5) - hash) + raw.charCodeAt(i);
|
||||||
|
hash |= 0;
|
||||||
|
}
|
||||||
|
return 'a' + Math.abs(hash).toString(36);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Handle URL hash on load ==========
|
||||||
|
function handleHashNavigation() {
|
||||||
|
var hash = location.hash.replace('#', '');
|
||||||
|
if (!hash) return;
|
||||||
|
var target = document.getElementById(hash);
|
||||||
|
if (!target) return;
|
||||||
|
// Expand this card
|
||||||
|
var card = target.querySelector('.announcement-card');
|
||||||
|
if (card) {
|
||||||
|
timeline.querySelectorAll('.announcement-card.expanded').forEach(function(c) { c.classList.remove('expanded'); });
|
||||||
|
card.classList.add('expanded');
|
||||||
|
}
|
||||||
|
// Scroll into view with a small delay for layout
|
||||||
|
setTimeout(function() {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Helpers ==========
|
// ========== Helpers ==========
|
||||||
function getCategoryText(cat) {
|
function getCategoryText(cat) {
|
||||||
const map = { 'activity': '活动', 'maintenance': '维护', 'other': '其他' };
|
const map = { 'activity': '活动', 'maintenance': '维护', 'other': '其他' };
|
||||||
|
|||||||
Reference in New Issue
Block a user