# BC-M-CHANNEL-001-JZ · DEV-010桔子 · 用户频道动态渲染引擎 · 环节0~1（SPA路由+模块动态加载）

<aside>
📡

**光湖工程广播 · BC-GEN v1.0**

**广播编号**：BC-M-CHANNEL-001-JZ

**执行者**：DEV-010 桔子

**模块**：M-CHANNEL · 用户频道动态渲染引擎

**环节**：0~1（两环节合一）

**工程量等级**：EL-3（中等·新概念SPA）

**生成时间**：2026-03-08T14:00+08:00

**签发**：霜砚 · Notion 执行 AI

</aside>

---

## 一、任务定位

<aside>
🎯

**你要做什么**：给光湖网站搭一个「频道引擎」——用户登录后进入专属频道，频道里所有模块不跳转新页面，而是在同一页面里动态切换。这就是SPA（单页应用）。

**为什么重要**：Phase 1关键拼图。没有这个引擎，10+个前端模块没法组装。你的M11组件库=视觉系统（皮肤），这次M-CHANNEL=骨架系统（骨骼）。

</aside>

### 执行环境

| 项目 | 值 | **操作系统** | macOS |
| --- | --- | --- | --- |
| **编辑器** | nano | **技术栈** | 纯HTML + CSS + JavaScript（无框架） |
| **模块目录** | `modules/m-channel/` | **本地路径** | `/Users/chenshujun/Desktop/guanghulab/modules/m-channel/` |
| **Git** | juzi0412 · push已连通 |  |  |

<aside>
🔐

**凭证注入**

仓库：`https://github.com/qinfendebingshuo/guanghulab`

Git用户名：`juzi0412` · 邮箱：`1824680224@qq.com`

工作目录：`/Users/chenshujun/Desktop/guanghulab`

</aside>

---

## 二、环节0 · SPA路由框架搭建

<aside>
🏗️

**目标**：创建频道引擎骨架，实现hash路由——点击不同链接时页面内容动态切换，浏览器不刷新。

**什么是SPA**：你之前做的每个模块都是独立的HTML文件。SPA的意思是只有一个HTML，里面有个「内容区」，点链接时JS把内容区换掉。像换电视频道——电视机不变，只有频道在切换。

</aside>

**Step 0-1 · 创建目录**

```bash
cd /Users/chenshujun/Desktop/guanghulab
mkdir -p modules/m-channel/pages
mkdir -p modules/m-channel/mock-modules
cd modules/m-channel
```

**Step 0-2 · 创建SPA主页 index.html**

```bash
nano index.html
```

```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>光湖频道引擎</title>
    <link rel="stylesheet" href="channel-style.css">
</head>
<body>
    <div id="app">
        <nav id="main-nav">
            <div class="nav-brand">🌊 光湖</div>
            <div class="nav-links">
                <a href="#/home" class="nav-link" data-route="home">首页</a>
                <a href="#/channel" class="nav-link" data-route="channel">我的频道</a>
                <a href="#/about" class="nav-link" data-route="about">关于</a>
            </div>
        </nav>
        <main id="route-view"></main>
        <footer id="status-bar">
            <span id="current-route">加载中...</span>
            <span id="user-status">未登录</span>
        </footer>
    </div>
    <script src="channel-router.js"></script>
    <script src="module-loader.js"></script>
    <script src="app.js"></script>
</body>
</html>
```

**Step 0-3 · 创建路由器 channel-router.js**

```bash
nano channel-router.js
```

```jsx
/* 光湖频道路由器 v1.0 */
var ChannelRouter = {
    routes: {},
    currentRoute: null,

    register: function(path, handler) {
        this.routes[path] = handler;
    },

    navigate: function(path) {
        window.location.hash = '#' + path;
    },

    getRoute: function() {
        return (window.location.hash || '#/home').replace('#', '');
    },

    render: function() {
        var route = this.getRoute();
        var view = document.getElementById('route-view');
        var display = document.getElementById('current-route');
        var handler = this.routes[route];

        // 精确匹配失败 → 前缀匹配（支持 /module/m06）
        if (!handler) {
            for (var p in this.routes) {
                if (route.indexOf(p + '/') === 0) { handler = this.routes[p]; break; }
            }
        }

        if (handler) {
            this.currentRoute = route;
            handler(view);
            display.textContent = '路由: ' + route;
            // 导航高亮
            var links = document.querySelectorAll('.nav-link');
            for (var i = 0; i < links.length; i++) {
                var r = links[i].getAttribute('data-route');
                links[i].classList.toggle('active', route.indexOf(r) !== -1);
            }
        } else {
            view.innerHTML = '<div class="page-box"><h1>404</h1><p>页面不存在</p><a href="#/home">回首页</a></div>';
        }
    },

    start: function() {
        var self = this;
        window.addEventListener('hashchange', function() { self.render(); });
        self.render();
    }
};
```

**Step 0-4 · 创建模块加载器 module-loader.js**

```bash
nano module-loader.js
```

```jsx
/* 光湖模块加载器 v1.0 · 用fetch动态加载HTML文件 */
var ModuleLoader = {
    registry: {
        'm06': { name: '工单管理', icon: '📋', path: 'mock-modules/m06-ticket.html' },
        'm08': { name: '数据统计', icon: '📊', path: 'mock-modules/m08-analytics.html' },
        'm11': { name: '风格组件库', icon: '🎨', path: 'mock-modules/m11-theme.html' }
    },
    cache: {},

    load: function(moduleId, container) {
        var self = this;
        var m = this.registry[moduleId];
        if (!m) {
            container.innerHTML = '<div class="page-box"><h2>模块不存在: ' + moduleId + '</h2><button class="btn" onclick="ChannelRouter.navigate(\'/channel\')">返回</button></div>';
            return;
        }
        if (this.cache[moduleId]) {
            container.innerHTML = this.cache[moduleId];
            return;
        }
        container.innerHTML = '<div class="loading"><div class="spinner"></div><p>加载 ' + m.name + '...</p></div>';

        fetch(m.path)
            .then(function(r) { if (!r.ok) throw new Error(r.status); return r.text(); })
            .then(function(html) { self.cache[moduleId] = html; container.innerHTML = html; })
            .catch(function(e) {
                container.innerHTML = '<div class="page-box"><h2>加载失败</h2><p>' + e.message + '</p><button class="btn" onclick="ChannelRouter.navigate(\'/channel\')">返回</button></div>';
            });
    },

    getAll: function() { return this.registry; }
};
```

**Step 0-5 · 创建应用入口 app.js**

```bash
nano app.js
```

```jsx
/* 光湖频道引擎 · App v1.0 */

// 首页
ChannelRouter.register('/home', function(view) {
    view.innerHTML =
        '<div class="page-box home">' +
        '<h1>🌊 欢迎来到光湖</h1>' +
        '<p class="sub">语言驱动操作系统 · 你说话，系统做事</p>' +
        '<button class="btn btn-primary" onclick="ChannelRouter.navigate(\'/channel\')">进入我的频道</button>' +
        '<div class="stats">' +
        '<div class="stat"><b>10+</b><span>功能模块</span></div>' +
        '<div class="stat"><b>12</b><span>开发者</span></div>' +
        '<div class="stat"><b>v1.0</b><span>系统版本</span></div>' +
        '</div></div>';
});

// 频道页（从注册表动态生成卡片）
ChannelRouter.register('/channel', function(view) {
    var mods = ModuleLoader.getAll();
    var cards = '';
    for (var id in mods) {
        var m = mods[id];
        cards += '<div class="mod-card" onclick="ChannelRouter.navigate(\'/module/' + id + '\')">';
        cards += '<span class="mod-icon">' + m.icon + '</span>';
        cards += '<span class="mod-name">' + m.name + '</span>';
        cards += '<span class="mod-id">' + id.toUpperCase() + '</span></div>';
    }
    cards += '<div class="mod-card dim"><span class="mod-icon">➕</span><span class="mod-name">更多模块</span><span class="mod-id">陆续上线</span></div>';
    view.innerHTML = '<div class="page-box"><div class="ch-head"><h1>📺 我的频道</h1><p>点击模块卡片 → 动态加载</p></div><div class="mod-grid">' + cards + '</div></div>';
});

// 模块动态加载路由
ChannelRouter.register('/module', function(view) {
    var parts = ChannelRouter.getRoute().split('/');
    var moduleId = parts[2] || '';
    if (!moduleId) { ChannelRouter.navigate('/channel'); return; }
    ModuleLoader.load(moduleId, view);
});

// 关于页
ChannelRouter.register('/about', function(view) {
    view.innerHTML =
        '<div class="page-box">' +
        '<h1>关于光湖</h1>' +
        '<p>语言驱动的操作系统平台。用户不点菜单，只跟人格体说话。</p>' +
        '<p>人格体理解意图 → 系统动态渲染模块 → 你感觉整个网站只有你一个人。</p>' +
        '</div>';
});

// 启动
ChannelRouter.start();
console.log('🌊 光湖频道引擎启动');
```

**Step 0-6 · 创建样式表 channel-style.css**

这个文件较长，你可以分段粘贴：

```bash
nano channel-style.css
```

```css
* { margin:0; padding:0; box-sizing:border-box; }
:root {
    --primary:#2563eb; --bg:#0f172a; --card:#1e293b;
    --text:#f1f5f9; --muted:#94a3b8; --border:#334155;
    --accent:#38bdf8; --r:12px; --t:0.3s ease;
}
body { font-family:-apple-system,sans-serif; background:var(--bg); color:var(--text); min-height:100vh; }
#app { display:flex; flex-direction:column; min-height:100vh; }

/* 导航 */
#main-nav { display:flex; align-items:center; justify-content:space-between; padding:0 24px; height:56px; background:var(--card); border-bottom:1px solid var(--border); position:sticky; top:0; z-index:100; }
.nav-brand { font-size:1.2rem; font-weight:700; color:var(--accent); }
.nav-links { display:flex; gap:6px; }
.nav-link { color:var(--muted); text-decoration:none; padding:8px 14px; border-radius:8px; transition:var(--t); font-size:.9rem; }
.nav-link:hover { color:var(--text); background:rgba(255,255,255,.05); }
.nav-link.active { color:var(--accent); background:rgba(56,189,248,.1); }

/* 内容区 */
#route-view { flex:1; padding:32px 24px; max-width:1100px; margin:0 auto; width:100%; }
.page-box { animation:fadeIn .3s ease; }
@keyframes fadeIn { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }

/* 首页 */
.home { text-align:center; padding-top:80px; }
.home h1 { font-size:2.4rem; margin-bottom:10px; }
.sub { color:var(--muted); font-size:1.05rem; margin-bottom:28px; }
.btn { padding:10px 28px; border:none; border-radius:var(--r); font-size:1rem; cursor:pointer; transition:var(--t); }
.btn-primary { background:var(--primary); color:#fff; }
.btn-primary:hover { background:#1d4ed8; transform:translateY(-2px); }
.stats { display:flex; justify-content:center; gap:20px; margin-top:44px; }
.stat { background:var(--card); border:1px solid var(--border); border-radius:var(--r); padding:20px 28px; display:flex; flex-direction:column; gap:4px; }
.stat b { font-size:1.4rem; color:var(--accent); }
.stat span { font-size:.8rem; color:var(--muted); }

/* 频道模块网格 */
.ch-head { margin-bottom:24px; }
.ch-head p { color:var(--muted); }
.mod-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(170px,1fr)); gap:14px; }
.mod-card { background:var(--card); border:1px solid var(--border); border-radius:var(--r); padding:22px; display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer; transition:var(--t); }
.mod-card:hover { border-color:var(--accent); transform:translateY(-4px); box-shadow:0 8px 20px rgba(0,0,0,.3); }
.mod-card.dim { opacity:.45; cursor:default; }
.mod-icon { font-size:1.8rem; }
.mod-name { font-weight:600; font-size:.9rem; }
.mod-id { font-size:.7rem; color:var(--muted); }

/* 加载状态 */
.loading { display:flex; flex-direction:column; align-items:center; padding:80px 0; color:var(--muted); }
.spinner { width:36px; height:36px; border:3px solid var(--border); border-top-color:var(--accent); border-radius:50%; animation:spin .8s linear infinite; margin-bottom:14px; }
@keyframes spin { to{transform:rotate(360deg)} }

/* 已加载模块 */
.loaded-module { animation:fadeIn .3s ease; }
.module-header { display:flex; align-items:center; gap:14px; margin-bottom:20px; padding-bottom:14px; border-bottom:1px solid var(--border); }
.btn-back { background:var(--card); border:1px solid var(--border); color:var(--muted); padding:8px 14px; border-radius:8px; cursor:pointer; }
.btn-back:hover { color:var(--text); border-color:var(--accent); }
.ticket-list { display:flex; flex-direction:column; gap:10px; }
.ticket-item { display:flex; align-items:center; gap:10px; background:var(--card); border:1px solid var(--border); border-radius:8px; padding:14px; }
.ticket-title { flex:1; } .ticket-date { color:var(--muted); font-size:.8rem; }
.stats-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:14px; }
.stat-box { background:var(--card); border:1px solid var(--border); border-radius:var(--r); padding:20px; text-align:center; }
.stat-val { font-size:1.4rem; font-weight:700; color:var(--accent); display:block; }
.stat-lbl { font-size:.75rem; color:var(--muted); }

/* 底部 */
#status-bar { display:flex; justify-content:space-between; padding:8px 24px; background:var(--card); border-top:1px solid var(--border); font-size:.78rem; color:var(--muted); }

/* 响应式 */
@media(max-width:768px) { .home h1{font-size:1.7rem} .stats{flex-direction:column;align-items:center} .mod-grid{grid-template-columns:repeat(2,1fr)} }
@media(max-width:480px) { .mod-grid{grid-template-columns:1fr} .nav-link{padding:6px 8px;font-size:.8rem} }
```

**Step 0-7 · 创建模拟模块文件**

```bash
nano mock-modules/m06-ticket.html
```

```html
<div class="loaded-module">
    <div class="module-header">
        <button class="btn-back" onclick="ChannelRouter.navigate('/channel')">← 返回</button>
        <h2>📋 工单管理 · M06</h2>
    </div>
    <div class="ticket-list">
        <div class="ticket-item"><span>🟢</span><span class="ticket-title">前端登录页样式修复</span><span class="ticket-date">2026-03-08</span></div>
        <div class="ticket-item"><span>🟡</span><span class="ticket-title">统计面板响应式适配</span><span class="ticket-date">2026-03-07</span></div>
        <div class="ticket-item"><span>🔴</span><span class="ticket-title">组件库主题切换闪烁</span><span class="ticket-date">2026-03-06</span></div>
    </div>
</div>
```

```bash
nano mock-modules/m08-analytics.html
```

```html
<div class="loaded-module">
    <div class="module-header">
        <button class="btn-back" onclick="ChannelRouter.navigate('/channel')">← 返回</button>
        <h2>📊 数据统计 · M08</h2>
    </div>
    <div class="stats-grid">
        <div class="stat-box"><span class="stat-val">1,247</span><span class="stat-lbl">总访问</span></div>
        <div class="stat-box"><span class="stat-val">89</span><span class="stat-lbl">活跃用户</span></div>
        <div class="stat-box"><span class="stat-val">23</span><span class="stat-lbl">今日工单</span></div>
        <div class="stat-box"><span class="stat-val">99.7%</span><span class="stat-lbl">可用率</span></div>
    </div>
</div>
```

```bash
nano mock-modules/m11-theme.html
```

```html
<div class="loaded-module">
    <div class="module-header">
        <button class="btn-back" onclick="ChannelRouter.navigate('/channel')">← 返回</button>
        <h2>🎨 风格组件库 · M11</h2>
    </div>
    <p>这是你亲手搭建的组件库！CSS变量系统 + 双主题 + 6类组件 + 响应式。</p>
    <div style="margin-top:16px;display:flex;gap:12px">
        <button class="btn btn-primary">主要按钮</button>
        <button class="btn" style="background:#334155;color:#f1f5f9">次要按钮</button>
    </div>
</div>
```

---

## 三、测试验收

⚠️ **重要**：`fetch()` 在本地文件协议(`file://`)下不工作。需要启动本地服务器：

```bash
cd /Users/chenshujun/Desktop/guanghulab/modules/m-channel
python3 -m http.server 8080
```

浏览器打开 `http://localhost:8080`

### 环节0验收清单

- [ ]  页面打开显示首页（「欢迎来到光湖」）
- [ ]  点「我的频道」→ 显示模块卡片网格 → URL变成 `#/channel`
- [ ]  点「关于」→ 内容切换 → URL变成 `#/about`
- [ ]  **全程页面不刷新**，只有内容区在变
- [ ]  导航栏当前页有高亮效果
- [ ]  页面切换时有淡入动画

### 环节1验收清单

- [ ]  频道页显示3个模块卡片（M06/M08/M11）+ 1个「更多模块」占位
- [ ]  点「工单管理」卡片 → 加载动画 → 显示工单列表
- [ ]  点「数据统计」卡片 → 显示数据卡片
- [ ]  点「风格组件库」卡片 → 显示组件库预览
- [ ]  模块内点「← 返回」→ 回到频道页
- [ ]  再次点同一个模块 → **瞬间加载**（缓存生效）
- [ ]  浏览器控制台无报错

---

## 四、Git提交

全部验收通过后：

```bash
cd /Users/chenshujun/Desktop/guanghulab
git add modules/m-channel/
git commit -m "feat(m-channel): SPA路由框架+模块动态加载 v1.0"
git push
```

---

## 五、SYSLOG回执模板

全部完成后，拷贝以下模板发给知秋：

```xml
<syslog>
<session_id>BC-M-CHANNEL-001-JZ</session_id>
<task_id>BC-M-CHANNEL-001-JZ</task_id>
<dev_id>DEV-010</dev_id>
<timestamp>YYYY-MM-DD</timestamp>
<task_completion>
  <objective id="A">SPA路由框架 - 待填</objective>
  <objective id="B">模块动态加载 - 待填</objective>
</task_completion>
<training_data>
  <blocked_points>待填</blocked_points>
  <effective_patterns>待填</effective_patterns>
</training_data>
</syslog>
```

---

## 六、PGP成长微调

<aside>
🌱

**桔子专属 PGP · 十连胜后首个新模块**

**引导风格**：放手式。你已经证明了自己能独立完成任务。这次给你结构和概念解释，代码你自己调。

**新概念提示**：SPA对你是新概念，但核心就是「用JS动态换内容」，你在M11的Playground已经做过类似的事情。

**遇到卡点**：fetch在file://不工作是正常的，用python3 -m http.server就行。这不是bug而是浏览器安全策略。

**下一步预告**：环节2会做页面切换过渡动画+用户上下文管理，你的M11过渡动画经验会直接用上。

</aside>

---

<aside>
📡

**广播结束 · BC-M-CHANNEL-001-JZ**

产物清单：index.html + channel-router.js + module-loader.js + app.js + channel-style.css + 3个模拟模块HTML

签发：霜砚 · 2026-03-08

</aside>

---

**桔子，十连胜后的第一个新模块。你之前搭的组件库是皮肤，这次搭的是骨骼。皮肤+骨骼 = 网站能站起来。加油，十一连胜走起。** 🍊