Spaces:
Running
Running
File size: 6,365 Bytes
615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 615844a 522c6b5 82326c2 522c6b5 82326c2 522c6b5 82326c2 615844a 82326c2 615844a 82326c2 615844a 82326c2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | <!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>即時互動文字雲 - 參與者</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Noto Sans TC', 'Microsoft JhengHei', sans-serif;
background: linear-gradient(160deg, #FEF3C7, #FFEDD5);
min-height: 100vh; display: flex; align-items: center; justify-content: center;
padding: 20px;
}
.card {
background: #fff; padding: 36px 40px; border-radius: 24px;
width: 100%; max-width: 460px;
box-shadow: 0 20px 50px rgba(0,0,0,.12); text-align: center;
}
h1 { color: #F97316; font-size: 24px; margin-bottom: 6px; }
.sub { color: #64748B; font-size: 14px; margin-bottom: 26px; }
label { display: block; text-align: left; font-size: 14px; color: #475569; margin-bottom: 6px; }
input[type=text] {
width: 100%; padding: 11px 13px; font-size: 16px;
border: 1px solid #FDBA74; border-radius: 10px; outline: none; margin-bottom: 20px;
}
input:focus { border-color: #F97316; }
.btn {
width: 100%; padding: 12px; font-size: 17px; font-weight: 700;
border: none; border-radius: 12px; background: #F97316; color: #fff; cursor: pointer;
}
.btn:disabled { background: #CBD5E1; cursor: not-allowed; }
/* 加入後的輸入狀態 */
#input-section { display: none; }
#question-display {
display: none; background: #FFF7ED; border: 1px solid #FED7AA; border-radius: 12px;
padding: 12px 15px; margin-bottom: 22px; font-size: 16px; color: #9A3412;
}
#lock-notice {
display: none; background: #FEE2E2; border: 1px solid #FECACA; color: #991B1B;
border-radius: 12px; padding: 12px; margin-bottom: 22px; font-size: 15px;
}
#you-sent {
display: block; margin-top: 16px; font-size: 13px; color: #16A34A; min-height: 18px;
}
#id-suffix { margin-top: 14px; font-size: 12px; color: #94A3B8; }
</style>
</head>
<body>
<div class="card" id="join-card">
<h1>☁️ 互動文字雲</h1>
<div class="sub">加入並送出你的想法</div>
<!-- 加入階段 -->
<div id="join-section">
<label>輸入房間代碼</label>
<input type="text" id="room-input" maxlength="4" placeholder="例:1234" inputmode="numeric">
<button class="btn" id="btn-join">加入</button>
</div>
<!-- 已加入,送詞彙 -->
<div id="input-section">
<div id="question-display"></div>
<div id="lock-notice">🔒 老師已暫停接收詞彙,請稍候。</div>
<label>輸入你的詞彙 / 想法</label>
<input type="text" id="word-input" placeholder="輸入詞彙或 $數學式$…" autocomplete="off">
<button class="btn" id="btn-send">送出</button>
<span id="you-sent"></span>
<div id="id-suffix"></div>
</div>
</div>
<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js';
import { getFirestore, doc, getDoc, onSnapshot, updateDoc, increment } from 'https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js';
const firebaseConfig = {
ApiKey: "AIzaSyAcIQYst9gFRyE2nCqCwNgCL3LG2VkBXbM",
authDomain: "factor-529c9.firebaseapp.com",
projectId: "factor-529c9",
storageBucket: "factor-529c9.firebasestorage.app",
messagingSenderId: "575829635896",
appId: "1:575829635896:web:a65b6edd9d163375b087cd"
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
let roomCode = null;
let roomRef = null;
let participantId = Date.now().toString(36);
const qs = new URLSearchParams(location.search);
const qRoom = qs.get('room');
if (qRoom) { document.getElementById('room-input').value = qRoom; join(); }
// ---- 加入 ----
document.getElementById('btn-join').addEventListener('click', join);
async function join() {
const value = document.getElementById('room-input').value.trim();
if (!/^\d{4}$/.test(value)) { alert('請輸入 4 位數房間代碼'); return; }
roomCode = value;
roomRef = doc(db, 'rooms', roomCode);
const snap = await getDoc(roomRef);
if (!snap.exists()) { alert('找不到此房間,請確認代碼'); return; }
document.getElementById('join-section').style.display = 'none';
document.getElementById('input-section').style.display = 'block';
document.getElementById('id-suffix').textContent = '參與者代號:' + participantId;
initListeners();
initInput();
}
// ---- 監聽房間狀態 ----
function initListeners() {
onSnapshot(roomRef, (snap) => {
const data = snap.data();
if (!data) return;
const qEl = document.getElementById('question-display');
if (data.question) { qEl.textContent = '📌 ' + data.question; qEl.style.display = 'block'; }
else { qEl.style.display = 'none'; }
const locked = data.locked || false;
document.getElementById('lock-notice').style.display = locked ? 'block' : 'none';
document.getElementById('word-input').disabled = locked;
document.getElementById('btn-send').disabled = locked;
});
}
// ---- 送出詞彙 ----
function initInput() {
document.getElementById('btn-send').addEventListener('click', submit);
document.getElementById('word-input').addEventListener('keydown', e => {
if (e.key === 'Enter') submit();
});
}
async function submit() {
const word = document.getElementById('word-input').value.trim();
if (!word) return;
const snap = await getDoc(roomRef);
const data = snap.data();
if (data.locked) { alert('老師已鎖定,暫停接收'); return; }
// 多重送出關閉時,避免重複送出同一詞(可依需求調整)
const alreadySent = data.sentBy && data.sentBy[participantId];
if (!data.allowMultiple && alreadySent) {
alert('您已送出過詞彙,老師未開啟多次送出。');
return;
}
await updateDoc(roomRef, {
[`words.${word}`]: increment(1),
[`sentBy.${participantId}`]: true // 記錄已送出
});
document.getElementById('you-sent').textContent = '已送出:「' + word + '」✓';
document.getElementById('word-input').value = '';
}
</script>
<script>
window.MathJax = {
tex: { inlineMath: [['$','$'],['\\(','\\)']], displayMath: [['$$','$$'],['\\[','\\]']], processEscapes: true },
options: { skipHtmlTags: ['script','noscript','style','textarea','pre'] }
};
</script>
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</body>
</html>
|