diff --git a/AGENTS.md b/AGENTS.md
index 5a5cabf..d9e1ed6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -81,7 +81,7 @@
**待確認:**
- developer-rules 健檢尚未執行
- `sechskies_fans`(新獨立專案)本機尚未建 MySQL 資料庫、尚未跑過 `admin/setup.php`;Render 部署中,資料庫主機未定
-- `portal-nav.js` 新增的「水晶練習室」項目目前是 `data-coming-soon` 佔位(href=`#`),等 sechskies_fans 網址確定後要補上真實外部連結並移除 coming-soon 標記
+- `portal-nav.js` 新增的「小黃練習室」項目目前是 `data-coming-soon` 佔位(href=`#`),等 sechskies_fans 網址確定後要補上真實外部連結並移除 coming-soon 標記
**歷史定位(文案不可再寫錯):**
- 水晶熱舞社是水晶男孩粉絲自主成立的舞蹈社團,並非水晶男孩推廣部旗下組織
@@ -96,7 +96,7 @@
- `extra/site-guide.html` 同步更新:全員發瘋 → 世紀級寶藏
- 新增 `admin/` 推廣部後臺第一階段:登入、控制台、管理者帳號、通用 CRUD、媒體上傳及公開狀態總覽
- 水晶熱舞社/水晶練習室/偶像練習生已於 2026-08-04 拆成獨立專案 `sechskies_fans`(另一個 repo,路徑 `d:\Developer\projects\sechskies_fans`,不在本站範圍):30TH 應援報名表單、練舞清單、後台 CRUD 與管理者登入都已搬走並改用自己的資料庫;`extra/site-guide.html` 與本站 `admin/bootstrap.php`/`admin/database/schema.sql` 已同步移除相關項目
-- `portal-nav.js` 頂欄新增第 7 項「水晶練習室」(Yellow Note 之後),連到獨立專案 sechskies_fans;`extra/site-guide.html` 同步新增對應項目;目前是 coming-soon 佔位,尚未接上真實網址
+- `portal-nav.js` 頂欄新增第 7 項「小黃練習室」(Yellow Note 之後),連到獨立專案 sechskies_fans;`extra/site-guide.html` 同步新增對應項目;目前是 coming-soon 佔位,尚未接上真實網址
- `variety/` 資料夾整個改名為 `vedio/`,同步修正 portal-nav.js、site-guide.html、style.css 註解、docs/site-structure.md、portal-nav-rules.md 等連結
---
diff --git a/CLAUDE.md b/CLAUDE.md
index d22503e..caa50c4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -67,7 +67,7 @@
5. **commit 前網站不會更新**。本地端強制重整用 `Ctrl+Shift+R`;GitHub Pages 需 `git push` 後等約 1 分鐘。
-6. **頂層 `
` 固定 5 個,沒有例外**(淪陷瞬間、完整的六顆、現場的震撼、黃色留聲機、Yellow Note)。「水晶練習室」籌備中佔位也收進「Yellow Note」子選單,不再獨立於頂層。新頁面/佔位一律收進其中一項的子選單,不要自己開新的頂層項目。詳見 `developer-rules/rules/portal-nav-rules.md` §8。
+6. **頂層 `` 固定 5 個,沒有例外**(淪陷瞬間、完整的六顆、現場的震撼、黃色留聲機、Yellow Note)。「小黃練習室」籌備中佔位也收進「Yellow Note」子選單,不再獨立於頂層。新頁面/佔位一律收進其中一項的子選單,不要自己開新的頂層項目。詳見 `developer-rules/rules/portal-nav-rules.md` §8。
---
@@ -75,7 +75,7 @@
-**最後更新:** 2026-08-18
+**最後更新:** 2026-08-19
**進行中:**
- `calendar2026.html` 的 `` 標題「水晶萬年曆」還在想更好的名字(要求:不用「水晶」前綴、不重複站上已用意象詞、要有典故/查證過的出處),已否決多輪提案,暫緩
@@ -84,10 +84,12 @@
- developer-rules 健檢尚未執行
**已完成:**
+- 新增「打歌舞台管理」後台模組(key `stages`,歌曲管理正下方):`admin/database/schema.sql` 新增 `stage_performances` 表(`song_id` 外鍵關聯 `songs`,欄位含播出日期/電視台/節目名稱/舞台標題/YouTube 連結/備註/排序/公開狀態四態);`admin/bootstrap.php` 的 `modules()` 加對應設定,沿用既有通用 CRUD(`records.php`/`record-form.php`/`publishing.php`),沒新增獨立頁面。概念取自 `待確認/三個詞專案/php-admin-practice/`(THREE WORDS·10TH 打歌舞台練習專案)的資料結構,但該資料夾本身維持原樣不動、資料庫也不共用,純粹借用欄位設計。**尚待做:**既有 `sechskies_cms` 資料庫要重新匯入一次 `schema.sql`(`CREATE TABLE IF NOT EXISTS` 不會動到既有表,只會補上新表)
+- 「水晶練習室」全站更名為「小黃練習室」(英文副標 CRYSTAL PRACTICE ROOM → YEKKI PRACTICE ROOM,「小黃」英文簡稱定為 Yekki,「水晶男孩」簡稱 Jekki,站上不再使用 crystal 字樣指稱練習室):`yellow-note/practice-room.html`(title/h1/h2/hero 文案/CSS 註解)、`assets/js/portal-nav.js`(Yellow Note 子選單標籤與註解)、`extra/site-guide.html` 心智圖同步;CLAUDE.md/AGENTS.md/portal-nav-rules.md 內提到此頁名稱的歷史記錄一併同步(已拆到獨立專案 sechskies_fans 的舊「水晶熱舞社/水晶練習室/偶像練習生」歷史敘述維持原樣,不屬於本站現名範圍)
- `yellow-note/minister.html`(視覺進化論)Journey 時間軸重構:3 個大 `` 拆成每個月獨立一個 `story-node`(`data-era="2016.07"` 格式),套用 `.merged-storyline` 既有奇偶交錯排版,不再手寫「07 月|」字樣。真實照片只有 `2018.07`「415 天健身挑戰」(`fitness-calendar.png`,7/13 李宰鎮生日發起的每日健身計畫,年份待查證);其餘節點 `data-photo-pending="true"`、不渲染 ``,等後台控制方式定案後再補真照片
- `yellow-note/minister.html` 與 `calendar2026.html` 內文提到「水晶廢人字幕組」處都連到頻道(`youtube.com/channel/UCJnhlm_t9C7BmcILcjHDk_w`),minister.html 用 `.story-external-inline` 外部連結樣式
-- `yellow-note/practice-room.html`(水晶練習室)練舞清單接上第一支影片(SECHSKIES 回歸編舞練習),新增 `yellow-note/dance-practices-data.php` 靜態資料端點頂著(完整 MySQL 後台版在獨立專案 `sechskies_fans`,未部署)
-- portal-nav 全開檢查用的 `TEMP_OPEN_ALL_FOR_REVIEW` 已還原為 `false`;`minister.html` 正式從 `COMING_SOON_PAGES` 移除(今天做完,永久開放),「水晶練習室」改為永久真連結(今天做完),「隊長的氣場」維持籌備中(今天沒動它,回到原狀)
+- `yellow-note/practice-room.html`(小黃練習室)練舞清單接上第一支影片(SECHSKIES 回歸編舞練習),新增 `yellow-note/dance-practices-data.php` 靜態資料端點頂著(完整 MySQL 後台版在獨立專案 `sechskies_fans`,未部署)
+- portal-nav 全開檢查用的 `TEMP_OPEN_ALL_FOR_REVIEW` 已還原為 `false`;`minister.html` 正式從 `COMING_SOON_PAGES` 移除(今天做完,永久開放),「小黃練習室」改為永久真連結(今天做完),「隊長的氣場」維持籌備中(今天沒動它,回到原狀)
- 建立 CLAUDE.md 跨裝置上下文檔案
- 新增 `vedio/bangxiao.html`(世紀級寶藏 / 爆笑養老院,甜蜜負擔計量器)
- `vedio/variety-shows.html` 重構:移除多層包裝,對齊 albums.html 扁平結構
@@ -95,7 +97,7 @@
- `extra/site-guide.html` 同步更新:全員發瘋 → 世紀級寶藏
- `variety/` 資料夾整個改名為 `vedio/`,同步修正 portal-nav.js、site-guide.html、style.css 註解、docs/site-structure.md、portal-nav-rules.md 等連結
- `calendar2026.html` 新增「粉絲應援 / 字幕作品」(`fanwork`) 分類,收錄「水晶廢人字幕組」頻道全部 8 支影片+ MTV 我愛偶像報導影片(4/4,重啟活動20週年),依上架日期分布;`assets/css/calendar.css` 新增對應淺金配色(比生日金淺、hover 提亮)
-- 修正 portal-nav.js 兩次頂層項目迴歸:「世紀級寶藏」移回「淪陷瞬間」子選單、「水晶練習室」移入「Yellow Note」子選單;`portal-nav-rules.md` 新增 §8 頂層 5 主項上限規則(無例外),`extra/site-guide.html` 心智圖同步
+- 修正 portal-nav.js 兩次頂層項目迴歸:「世紀級寶藏」移回「淪陷瞬間」子選單、「小黃練習室」移入「Yellow Note」子選單;`portal-nav-rules.md` 新增 §8 頂層 5 主項上限規則(無例外),`extra/site-guide.html` 心智圖同步
---
diff --git a/admin/admins.php b/admin/admins.php
index 324713f..b6df7ac 100644
--- a/admin/admins.php
+++ b/admin/admins.php
@@ -10,7 +10,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim((string) ($_POST['username'] ?? ''));
$name = trim((string) ($_POST['display_name'] ?? ''));
$password = (string) ($_POST['password'] ?? '');
- if (!preg_match('/^[A-Za-z0-9_.-]{4,50}$/', $username) || mb_strlen($name) < 2 || strlen($password) < 10) {
+ if (!preg_match('/^[A-Za-z0-9_.-]{4,50}$/', $username) || mb_strlen($name) < 2 || strlen($password) < 6) {
$error = '請確認帳號、名稱與至少 10 字元的密碼。';
} else {
try {
@@ -30,6 +30,6 @@ $users = db()->query('SELECT id, username, display_name, email, is_active, last_
render_header('管理者帳號', 'admins');
?>
= h($error) ?>
-
+
| 帳號 | 顯示名稱 | 信箱 | 最後登入 | 狀態 | 操作 |
|---|
| = h($user['username']) ?> | = h($user['display_name']) ?> | = h($user['email']) ?> | = h($user['last_login_at'] ?? '尚未登入') ?> | = $user['is_active'] ? '啟用' : '停用' ?> | |
['title', 'album_id', 'track_no', 'is_title_track', 'status', 'updated_at'],
],
+ 'stages' => [
+ 'label' => '打歌舞台管理', 'icon' => 'mic', 'table' => 'stage_performances', 'order' => 'stage_date DESC, sort_order, id',
+ 'fields' => [
+ 'song_id' => ['label' => '所屬歌曲', 'type' => 'relation', 'table' => 'songs', 'required' => true],
+ 'stage_date' => ['label' => '播出日期', 'type' => 'date', 'required' => true],
+ 'station' => ['label' => '電視台', 'type' => 'text', 'required' => true],
+ 'program' => ['label' => '節目名稱', 'type' => 'text', 'required' => true],
+ 'title' => ['label' => '舞台標題', 'type' => 'text', 'required' => true],
+ 'youtube_url' => ['label' => 'YouTube 連結', 'type' => 'url'],
+ 'note' => ['label' => '備註', 'type' => 'textarea'],
+ 'sort_order' => ['label' => '排序', 'type' => 'number', 'default' => '0'],
+ 'status' => ['label' => '公開狀態', 'type' => 'status', 'required' => true],
+ ],
+ 'list' => ['title', 'song_id', 'stage_date', 'station', 'program', 'status', 'updated_at'],
+ ],
'concerts' => [
'label' => '演唱會管理', 'icon' => 'ticket-perforated', 'table' => 'concert_events', 'order' => 'event_date DESC, id DESC',
'fields' => [
diff --git a/admin/database/schema.sql b/admin/database/schema.sql
index f70fd5d..8a097aa 100644
--- a/admin/database/schema.sql
+++ b/admin/database/schema.sql
@@ -60,6 +60,22 @@ CREATE TABLE IF NOT EXISTS songs (
CONSTRAINT fk_songs_album FOREIGN KEY (album_id) REFERENCES albums(id) ON DELETE CASCADE
) ENGINE=InnoDB;
+CREATE TABLE IF NOT EXISTS stage_performances (
+ id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
+ song_id INT UNSIGNED NOT NULL,
+ stage_date DATE NOT NULL,
+ station VARCHAR(80) NOT NULL,
+ program VARCHAR(120) NOT NULL,
+ title VARCHAR(160) NOT NULL,
+ youtube_url VARCHAR(500) NULL,
+ note TEXT NULL,
+ sort_order INT NOT NULL DEFAULT 0,
+ status ENUM('draft','coming_soon','published','archived') NOT NULL DEFAULT 'draft',
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ CONSTRAINT fk_stage_song FOREIGN KEY (song_id) REFERENCES songs(id) ON DELETE CASCADE
+) ENGINE=InnoDB;
+
CREATE TABLE IF NOT EXISTS concert_events (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tour_name VARCHAR(180) NOT NULL,
diff --git a/admin/setup.php b/admin/setup.php
index 3f20505..0c95e1a 100644
--- a/admin/setup.php
+++ b/admin/setup.php
@@ -22,8 +22,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $error === '') {
$error = '帳號須為 4–50 個英數字或 ._-。';
} elseif (mb_strlen($displayName) < 2) {
$error = '請填寫管理者顯示名稱。';
- } elseif (strlen($password) < 10) {
- $error = '密碼至少需要 10 個字元。';
+ } elseif (strlen($password) < 6) {
+ $error = '密碼至少需要 6 個字元。';
} else {
$stmt = db()->prepare('INSERT INTO admin_users (username, password_hash, display_name, email) VALUES (?, ?, ?, ?)');
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $displayName, $email ?: null]);
@@ -33,4 +33,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $error === '') {
?>
初始化後臺
CONTENT MANAGEMENT SYSTEMFIRST-TIME SETUP
建立第一位管理者
水晶男孩推廣部內容管理系統
= h($error) ?>
-
+