diff --git a/css/pages/facilities.css b/css/pages/facilities.css index 23a5342..2c412b9 100644 --- a/css/pages/facilities.css +++ b/css/pages/facilities.css @@ -453,6 +453,27 @@ box-shadow: 0 4px 12px rgba(0,0,0,0.05); } +/* Video Embed */ +.video-embed-wrapper { + position: relative; + width: 100%; + padding-bottom: 56.25%; /* 16:9 aspect ratio */ + margin: 12px 0 20px; + border-radius: 12px; + overflow: hidden; + background: #000; + box-shadow: 0 4px 12px rgba(0,0,0,0.08); +} + +.video-embed-wrapper iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; +} + .no-results-message { text-align: center; padding: 60px; @@ -999,6 +1020,11 @@ color: #1565c0; } +.badge-video { + background: #fce4ec; + color: #c62828; +} + .sortable-item .item-content { flex: 1; border: 1px solid rgba(0,0,0,0.06) !important; diff --git a/data/facilities.json b/data/facilities.json index 563c114..37663f1 100644 --- a/data/facilities.json +++ b/data/facilities.json @@ -23,8 +23,8 @@ "content": "使用前请仔细阅读告示牌说明。指示灯熄灭前请勿离开。" }, { - "type": "text", - "content": "参考视频:BV1qPhWzdEwU" + "type": "video", + "content": "BV1qPhWzdEwU" } ] }, @@ -59,8 +59,8 @@ ], "notes": [ { - "type": "text", - "content": "参考视频:BV1g142167tJ" + "type": "video", + "content": "BV1g142167tJ" } ] }, diff --git a/facilities.html b/facilities.html index 8a03b45..994334d 100644 --- a/facilities.html +++ b/facilities.html @@ -266,6 +266,9 @@ +
@@ -278,6 +281,9 @@ +
diff --git a/js/facilities_script.js b/js/facilities_script.js index c2bd869..dc28618 100644 --- a/js/facilities_script.js +++ b/js/facilities_script.js @@ -195,10 +195,46 @@ document.addEventListener('DOMContentLoaded', () => { img.src = block.content; img.loading = 'lazy'; container.appendChild(img); + } else if (block.type === 'video') { + const bv = parseBVNumber(block.content); + if (bv) { + const wrapper = document.createElement('div'); + wrapper.className = 'video-embed-wrapper'; + const iframe = document.createElement('iframe'); + iframe.src = `https://player.bilibili.com/player.html?bvid=${bv}&autoplay=0&high_quality=1`; + iframe.allowFullscreen = true; + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups'); + iframe.loading = 'lazy'; + wrapper.appendChild(iframe); + container.appendChild(wrapper); + } else { + const p = document.createElement('p'); + p.className = 'text-secondary'; + p.innerText = '无效的视频 BV 号'; + container.appendChild(p); + } } }); } + function parseBVNumber(input) { + if (!input) return null; + input = input.trim(); + // Match BV number directly (e.g. BV1qPhWzdEwU) + const bvPattern = /^(BV[A-Za-z0-9]+)$/; + const directMatch = input.match(bvPattern); + if (directMatch) return directMatch[1]; + // Match from bilibili URL (e.g. https://www.bilibili.com/video/BV1qPhWzdEwU/...) + const urlPattern = /bilibili\.com\/video\/(BV[A-Za-z0-9]+)/; + const urlMatch = input.match(urlPattern); + if (urlMatch) return urlMatch[1]; + // Match from b23.tv short URL or other formats containing BV + const generalPattern = /(BV[A-Za-z0-9]{10,})/; + const generalMatch = input.match(generalPattern); + if (generalMatch) return generalMatch[1]; + return null; + } + // Helpers function getStatusText(status) { const map = { @@ -433,14 +469,20 @@ document.addEventListener('DOMContentLoaded', () => { div.dataset.idx = idx; div.dataset.listId = listId; - const isText = item.type === 'text'; + const typeBadgeClass = item.type === 'text' ? 'badge-text' : item.type === 'image' ? 'badge-image' : 'badge-video'; + const typeBadgeLabel = item.type === 'text' ? '文字' : item.type === 'image' ? '图片' : '视频'; + let contentHtml; + if (item.type === 'text') { + contentHtml = ``; + } else if (item.type === 'image') { + contentHtml = ``; + } else { + contentHtml = ``; + } div.innerHTML = ` - ${isText ? '文字' : '图片'} - ${isText - ? `` - : `` - } + ${typeBadgeLabel} + ${contentHtml} `; container.appendChild(div); @@ -606,8 +648,10 @@ document.addEventListener('DOMContentLoaded', () => { editorInstructions.forEach(block => { if (block.type === 'text') { html += `

${escapeHtml(block.content) || '空文字'}

`; - } else { + } else if (block.type === 'image') { html += block.content ? `` : '

空图片

'; + } else if (block.type === 'video') { + html += renderVideoPreviewHtml(block.content); } }); } else { @@ -623,8 +667,10 @@ document.addEventListener('DOMContentLoaded', () => { editorNotes.forEach(block => { if (block.type === 'text') { html += `

${escapeHtml(block.content) || '空文字'}

`; - } else { + } else if (block.type === 'image') { html += block.content ? `` : '

空图片

'; + } else if (block.type === 'video') { + html += renderVideoPreviewHtml(block.content); } }); } else { @@ -657,8 +703,8 @@ document.addEventListener('DOMContentLoaded', () => { z: parseInt(document.getElementById('editor-z').value) || 0 }, contributors: [...editorContributors], - instructions: editorInstructions.filter(i => i.content.trim() !== ''), - notes: editorNotes.filter(n => n.content.trim() !== '') + instructions: editorInstructions.filter(i => i.content.trim() !== '').map(i => i.type === 'video' ? { type: 'video', content: parseBVNumber(i.content) || i.content } : {...i}), + notes: editorNotes.filter(n => n.content.trim() !== '').map(n => n.type === 'video' ? { type: 'video', content: parseBVNumber(n.content) || n.content } : {...n}) }; const jsonStr = JSON.stringify(facilityObj, null, 4); @@ -689,6 +735,14 @@ document.addEventListener('DOMContentLoaded', () => { }); }); + function renderVideoPreviewHtml(content) { + const bv = parseBVNumber(content); + if (bv) { + return `
`; + } + return '

请输入有效的 BV 号或 bilibili 视频地址

'; + } + // --- Utility --- function escapeHtml(text) { if (!text) return '';