โชว์การ์ดพิเศษที่ได้ + ปุ่มไปต่อ
This commit is contained in:
@@ -2758,6 +2758,36 @@ code {
|
||||
}
|
||||
|
||||
/* ===== Evidence Cards admin panel ===== */
|
||||
.cm-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.cm-pick {
|
||||
border: 1px solid rgba(124, 154, 255, 0.25);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
background: rgba(20, 26, 48, 0.4);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.cm-pick-label {
|
||||
font-weight: 800;
|
||||
font-size: 0.92rem;
|
||||
color: #cdd6ff;
|
||||
}
|
||||
.cm-pick-prev {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: contain;
|
||||
background: #0a0e1a;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(124, 154, 255, 0.18);
|
||||
}
|
||||
.cm-pick select { width: 100%; }
|
||||
|
||||
.ev-suspect-block {
|
||||
border: 1px solid rgba(124, 154, 255, 0.25);
|
||||
border-radius: 12px;
|
||||
|
||||
@@ -4184,6 +4184,144 @@
|
||||
if (saveBtn) saveBtn.addEventListener('click', saveEvidenceCardsPanel);
|
||||
})();
|
||||
|
||||
/* ===== รูปคดี & Cutscene (LobbyA → LobbyB) ===== */
|
||||
var CASE_MEDIA_API = '/Game/api/case-media';
|
||||
var CASE_MEDIA_IMAGES_API = '/Game/api/case-media-images';
|
||||
var CASE_MEDIA_COUNT = 15;
|
||||
var caseMediaData = null;
|
||||
var caseMediaImages = null; // { case:[...], cutscene:[...] }
|
||||
var caseMediaCurrent = '1';
|
||||
|
||||
function caseMediaPopulateSelect() {
|
||||
var sel = el('case-media-case');
|
||||
if (!sel || sel.options.length) return;
|
||||
for (var i = 1; i <= CASE_MEDIA_COUNT; i++) {
|
||||
var o = document.createElement('option');
|
||||
o.value = String(i); o.textContent = 'คดี ' + i;
|
||||
sel.appendChild(o);
|
||||
}
|
||||
}
|
||||
|
||||
function loadCaseMediaPanel() {
|
||||
caseMediaPopulateSelect();
|
||||
if (caseMediaImages == null) {
|
||||
fetch(CASE_MEDIA_IMAGES_API + '?_=' + Date.now(), { credentials: 'include', cache: 'no-store' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (j) { caseMediaImages = (j && j.images) || { case: [], cutscene: [] }; renderCaseMedia(caseMediaCurrent); })
|
||||
.catch(function () { caseMediaImages = { case: [], cutscene: [] }; });
|
||||
}
|
||||
fetch(CASE_MEDIA_API + '?_=' + Date.now(), { credentials: 'include', cache: 'no-store' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (j) {
|
||||
caseMediaData = (j && j.cases) ? j : { cases: {} };
|
||||
var sel = el('case-media-case');
|
||||
if (sel) caseMediaCurrent = sel.value || '1';
|
||||
renderCaseMedia(caseMediaCurrent);
|
||||
})
|
||||
.catch(function (e) {
|
||||
setMsg('case-media-msg', (e.message || 'โหลดไม่ได้') + ' — เช็คว่า Node รัน Game/server.js และ /Game/api/case-media ไม่ 404', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function caseMediaGetCase(cid) {
|
||||
if (!caseMediaData) caseMediaData = { cases: {} };
|
||||
if (!caseMediaData.cases) caseMediaData.cases = {};
|
||||
var n = parseInt(cid, 10) || 1;
|
||||
if (!caseMediaData.cases[cid]) caseMediaData.cases[cid] = {};
|
||||
var c = caseMediaData.cases[cid];
|
||||
if (typeof c.name !== 'string') c.name = 'คดีที่ ' + n;
|
||||
if (typeof c.art !== 'string') c.art = '/Main-Lobby/IMAGE/case/case-' + n + '.png';
|
||||
if (typeof c.detail !== 'string') c.detail = '/Main-Lobby/IMAGE/case/case-detail-' + n + '.png';
|
||||
if (!Array.isArray(c.story)) c.story = [];
|
||||
if (typeof c.story[0] !== 'string') c.story[0] = '/Main-Lobby/IMAGE/cutscene/case-' + n + '-story-1.png';
|
||||
if (typeof c.story[1] !== 'string') c.story[1] = '/Main-Lobby/IMAGE/cutscene/case-' + n + '-story-2.png';
|
||||
return c;
|
||||
}
|
||||
|
||||
/** ตัวเลือกรูป (dropdown + พรีวิว) — kind = 'case' | 'cutscene' */
|
||||
function caseMediaImagePicker(label, kind, current, onChange) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.className = 'cm-pick';
|
||||
var lab = document.createElement('div'); lab.className = 'cm-pick-label'; lab.textContent = label;
|
||||
var prev = document.createElement('img'); prev.className = 'cm-pick-prev'; prev.alt = ''; prev.src = current;
|
||||
prev.onerror = function () { this.style.opacity = '0.25'; };
|
||||
var sel = document.createElement('select'); sel.className = 'admin-inp-num';
|
||||
var list = (caseMediaImages && caseMediaImages[kind]) ? caseMediaImages[kind].slice() : [];
|
||||
if (current && list.indexOf(current) < 0) list.unshift(current);
|
||||
list.forEach(function (u) {
|
||||
var o = document.createElement('option');
|
||||
o.value = u; o.textContent = u.split('/').pop();
|
||||
if (u === current) o.selected = true;
|
||||
sel.appendChild(o);
|
||||
});
|
||||
sel.addEventListener('change', function () { prev.src = sel.value; onChange(sel.value); });
|
||||
wrap.appendChild(lab); wrap.appendChild(prev); wrap.appendChild(sel);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function renderCaseMedia(cid) {
|
||||
caseMediaCurrent = cid;
|
||||
var tag = el('case-media-set-tag');
|
||||
if (tag) tag.textContent = 'กำลังแก้: คดี ' + cid;
|
||||
var root = el('case-media-root');
|
||||
if (!root) return;
|
||||
var c = caseMediaGetCase(cid);
|
||||
root.innerHTML = '';
|
||||
|
||||
var nameLabel = document.createElement('label');
|
||||
nameLabel.className = 'admin-field';
|
||||
nameLabel.textContent = 'ชื่อคดี';
|
||||
var nameInp = document.createElement('input');
|
||||
nameInp.type = 'text'; nameInp.maxLength = 80; nameInp.value = c.name || '';
|
||||
nameInp.addEventListener('input', function () { c.name = nameInp.value; });
|
||||
nameLabel.appendChild(nameInp);
|
||||
root.appendChild(nameLabel);
|
||||
|
||||
var grid = document.createElement('div');
|
||||
grid.className = 'cm-grid';
|
||||
grid.appendChild(caseMediaImagePicker('รูปเลือกคดี (การ์ด)', 'case', c.art, function (v) { c.art = v; }));
|
||||
grid.appendChild(caseMediaImagePicker('รูปรายละเอียดคดี', 'case', c.detail, function (v) { c.detail = v; }));
|
||||
grid.appendChild(caseMediaImagePicker('Cutscene รูปที่ 1', 'cutscene', c.story[0], function (v) { c.story[0] = v; }));
|
||||
grid.appendChild(caseMediaImagePicker('Cutscene รูปที่ 2', 'cutscene', c.story[1], function (v) { c.story[1] = v; }));
|
||||
root.appendChild(grid);
|
||||
}
|
||||
|
||||
function saveCaseMediaPanel() {
|
||||
if (!caseMediaData) return;
|
||||
var btn = el('btn-case-media-save');
|
||||
if (btn) btn.disabled = true;
|
||||
setMsg('case-media-msg', 'กำลังบันทึก…', '');
|
||||
fetch(CASE_MEDIA_API, {
|
||||
method: 'PUT',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(caseMediaData),
|
||||
}).then(function (r) {
|
||||
return r.text().then(function (t) {
|
||||
var j = {};
|
||||
try { j = t ? JSON.parse(t) : {}; } catch (e) { /* ignore */ }
|
||||
if (!r.ok || !j.ok) throw new Error((j && j.error) || ('HTTP ' + r.status));
|
||||
return j;
|
||||
});
|
||||
}).then(function (j) {
|
||||
if (j && j.cases) caseMediaData = { cases: j.cases };
|
||||
renderCaseMedia(caseMediaCurrent);
|
||||
setMsg('case-media-msg', 'บันทึกแล้ว — มีผลในเกมเมื่อรีเฟรช LobbyA/LobbyB', 'ok');
|
||||
}).catch(function (e) {
|
||||
setMsg('case-media-msg', e.message || 'บันทึกไม่ได้', 'error');
|
||||
}).then(function () {
|
||||
if (btn) btn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
(function bindCaseMediaPanel() {
|
||||
caseMediaPopulateSelect();
|
||||
var caseSel = el('case-media-case');
|
||||
if (caseSel) caseSel.addEventListener('change', function () { renderCaseMedia(caseSel.value || '1'); });
|
||||
var saveBtn = el('btn-case-media-save');
|
||||
if (saveBtn) saveBtn.addEventListener('click', saveCaseMediaPanel);
|
||||
})();
|
||||
|
||||
function setTab(name) {
|
||||
document.querySelectorAll('.tab').forEach(function (t) {
|
||||
var on = t.getAttribute('data-tab') === name;
|
||||
@@ -4212,6 +4350,7 @@
|
||||
if (name === 'quiz') loadQuizSettingsPanel();
|
||||
if (name === 'special-quiz') loadSpecialQuizPanel();
|
||||
if (name === 'evidence-cards') loadEvidenceCardsPanel();
|
||||
if (name === 'case-media') loadCaseMediaPanel();
|
||||
if (name === 'quiz-carry') loadQuizCarryPanel();
|
||||
if (name === 'quiz-battle') loadQbBattlePanel();
|
||||
if (name === 'jump-survive') loadJumpSurviveTimingPanel();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="admin.css?v=36">
|
||||
<link rel="stylesheet" href="admin.css?v=37">
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#admin-main">ข้ามไปเนื้อหา</a>
|
||||
@@ -84,6 +84,7 @@
|
||||
<div class="admin-tab-sep" aria-hidden="true"><span>คำถาม & การ์ดพิเศษ</span></div>
|
||||
<button type="button" class="tab" data-tab="special-quiz" role="tab" id="tab-special-quiz" aria-controls="tab-panel-special-quiz"><span class="tab-label">ชุดคำถามพิเศษ</span><span class="tab-desc">15 ชุด (Level × Case) · การ์ดรางวัล</span></button>
|
||||
<button type="button" class="tab" data-tab="evidence-cards" role="tab" id="tab-evidence-cards" aria-controls="tab-panel-evidence-cards"><span class="tab-label">การ์ดหลักฐาน</span><span class="tab-desc">3 คดี × ผู้ต้องสงสัย × 3 ใบ · รูป/ข้อความ</span></button>
|
||||
<button type="button" class="tab" data-tab="case-media" role="tab" id="tab-case-media" aria-controls="tab-panel-case-media"><span class="tab-label">รูปคดี & Cutscene</span><span class="tab-desc">15 คดี · รูปเลือกคดี/รายละเอียด/เรื่องราว 2 รูป</span></button>
|
||||
|
||||
<div class="admin-tab-sep" aria-hidden="true"><span>ระบบ & บัญชี</span></div>
|
||||
<button type="button" class="tab" data-tab="characters" role="tab" id="tab-characters" aria-controls="tab-panel-characters"><span class="tab-label">ตัวละคร</span><span class="tab-desc">อัปโหลด · เลือกใช้ในเกม</span></button>
|
||||
@@ -332,6 +333,28 @@
|
||||
<p id="evidence-cards-msg" class="msg" role="status"></p>
|
||||
</section>
|
||||
|
||||
<section id="tab-panel-case-media" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-case-media">
|
||||
<h2 class="sq-title">รูปคดี & Cutscene — LobbyA → LobbyB</h2>
|
||||
<p class="muted">มี <strong>15 คดี</strong> · แต่ละคดีตั้งได้: <strong>ชื่อคดี</strong>, <strong>รูปการ์ดเลือกคดี</strong>, <strong>รูปรายละเอียดคดี</strong>, และ <strong>Cutscene 2 รูป</strong> (โชว์ก่อนเข้า LobbyB · ต้องกด «ไปต่อ») · เลือกรูปจากที่อัปโหลดไว้ใน <code>/Main-Lobby/IMAGE/case</code> และ <code>/Main-Lobby/IMAGE/cutscene</code> · เก็บที่ <code>/Game/data/case-media.json</code></p>
|
||||
|
||||
<fieldset class="sq-fieldset sq-pick">
|
||||
<legend>เลือกคดีที่จะแก้</legend>
|
||||
<div class="sq-pick-row">
|
||||
<label class="admin-field">คดี (Case)
|
||||
<select id="case-media-case" class="admin-inp-num sq-select"></select>
|
||||
</label>
|
||||
<span id="case-media-set-tag" class="sq-set-tag">กำลังแก้: คดี 1</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div id="case-media-root"></div>
|
||||
|
||||
<div class="quiz-admin-actions">
|
||||
<button type="button" class="btn btn-primary" id="btn-case-media-save">บันทึกรูปคดีทั้งหมด (15 คดี)</button>
|
||||
</div>
|
||||
<p id="case-media-msg" class="msg" role="status"></p>
|
||||
</section>
|
||||
|
||||
<section id="tab-panel-quiz-carry" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-quiz-carry">
|
||||
<h2>คำถามหลายตัวเลือก (หยิบมาวาง)</h2>
|
||||
<p class="muted">ใช้กับฉากประเภท <strong>ตอบคำถาม — หยิบคำตอบมาวางกลาง</strong> ใน Editor — ผู้เล่นหยิบตัวเลือกไปวางโซนกลาง · ถ้ามีรายการที่นี่ เกมจะใช้<strong>เฉพาะชุดนี้</strong> (ไม่ผสมกับคำถามถูก/ผิดแท็บคำถามเกม) · เก็บที่ <code>/Game/data/quiz-settings.json</code> ฟิลด์ <code>carryQuestions</code></p>
|
||||
@@ -1036,6 +1059,6 @@
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script src="admin.js?v=77"></script>
|
||||
<script src="admin.js?v=78"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user