diff --git a/.workbuddy/memory/2026-09-03.md b/.workbuddy/memory/2026-09-03.md
index d4cf855..d05774a 100644
--- a/.workbuddy/memory/2026-09-03.md
+++ b/.workbuddy/memory/2026-09-03.md
@@ -115,3 +115,9 @@
- **缓存目录数据清理(用户指令「去掉的赛道 现有数据也要删除」)**:删除全部已剔除赛道历史文件 12 个(颜值造型 3 + 情感 3 + 二次元 3 + 化妆美容 3),目录现仅保留 5 赛道×3 期 = 15 文件
- 验证:py_compile OK + 断言(保留=5 赛道,颜值造型/情感不在)+ ranking 接口实测 categories=5(美食/生活vlog/旅行/小剧场/亲子)dates=3 total=250 读取正常
- 教训:用户「情感 去掉」紧接「颜值造型 去掉」=连续精简指令,需完整执行不可只当上一条确认
+
+## 工作台「账号周榜」入口 →「热点数据」多榜 tab 页
+- 用户指令:「账号周榜入口改为热点数据,里面用 tab 区分数据列表,和首页一样」
+- 改动:app.js 首页入口卡片 title 账号周榜→热点数据(desc 多榜切换)、CRUMB_TITLES.ranking + router titles.ranking→热点数据;data-pages.js renderRanking 重构为多 tab 页——RANK_TABS 四 tab(week 账号周榜/hot 视频热榜/likes 点赞榜/fans 涨粉榜,与首页 rank-preview 一致),week 显示完整周榜列表(筛选/排序/分页/关注,原逻辑保留),其余 tab 显示「建设中」占位(与首页行为一致,后端 RANKING_DIR 目前仅周榜 json,后续数据落盘即可接入)
+- 验证:node --check 两文件通过;服务 8900 存活、新 js 已生效(grep 热点数据=3/1);带 ?v= 打开 #/ranking 页面
+- 纯前端静态改动,server.js 未变无需重启服务
diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/app.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/app.js
index 6904297..d92a430 100644
--- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/app.js
+++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/app.js
@@ -696,7 +696,7 @@ async function router() {
}
// 数据功能页
if (typeof window.DataPages !== 'undefined') {
- const titles = { ranking: '账号周榜', accounts: '账号列表', account: '账号详情', video: '视频详情', rewrites: 'AI写脚本', reviews: 'AI脚本诊断' };
+ const titles = { ranking: '热点数据', accounts: '账号列表', account: '账号详情', video: '视频详情', rewrites: 'AI写脚本', reviews: 'AI脚本诊断' };
const pages = {
'ranking': () => window.DataPages.renderRanking(view),
'accounts': () => window.DataPages.renderAccounts(view),
diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
index 9625ccc..71e288a 100644
--- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
+++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
@@ -502,18 +502,64 @@ async function confirmAiScript(accountName, btn) {
}
/* ================= 周榜 ================= */
+/* ================= 热点数据(#/ranking:账号周榜为主,多榜 tab 与首页 rank-preview 一致) ================= */
async function renderRanking(view) {
- let state = { date: '', category: '', sortBy: 'comprehensiveScore', order: 'desc', page: 1, pageSize: 20, dates: [], categories: [] };
- const data = await api('/api/dsh/ranking?pageSize=20').catch((e) => ({ ok: false, error: e.message }));
- if (!data.ok) { view.innerHTML = `
${toolbarBack()}账号周榜
${esc(data.error || '加载失败')}
`; bindBack(view); return; }
- state.dates = data.dates || []; state.categories = data.categories || []; state.date = data.date; state.category = data.category;
+ // tab 与首页 rank-tabs 一致:账号周榜有本地数据文件;视频热榜/点赞榜/涨粉榜暂占位(后端 RANKING_DIR 目前仅周榜 json)
+ const RANK_TABS = [
+ ['week', '账号周榜'],
+ ['hot', '视频热榜'],
+ ['likes', '点赞榜'],
+ ['fans', '涨粉榜'],
+ ];
+ let state = { tab: 'week', date: '', category: '', sortBy: 'comprehensiveScore', order: 'desc', page: 1, pageSize: 20, dates: [], categories: [] };
+ const meta = await api('/api/dsh/ranking?pageSize=20').catch((e) => ({ ok: false, error: e.message }));
+ if (!meta.ok) {
+ view.innerHTML = `${toolbarBack()}热点数据
${esc(meta.error || '加载失败')}
`;
+ bindBack(view); return;
+ }
+ state.dates = meta.dates || []; state.categories = meta.categories || []; state.date = meta.date || (state.dates[0] || ''); state.category = meta.category || '';
- async function load() {
- const d = await api('/api/dsh/ranking?date=' + encodeURIComponent(state.date) + '&category=' + encodeURIComponent(state.category) + '&sortBy=' + state.sortBy + '&order=' + state.order + '&page=' + state.page + '&pageSize=' + state.pageSize);
- const di = state.dates.indexOf(state.date);
- const WIDE = new Set(['comprehensiveScore', 'followers', 'newFans', 'newLikes', 'newComments', 'newShares']); // 数值列(窄+右对齐)
- const sortable = (key, label) => `${label}${state.sortBy === key ? (state.order === 'asc' ? ' ↑' : ' ↓') : ''} | `;
- const rows = (d.items || []).map((r) => `
+ const tabLabel = () => { const t = RANK_TABS.find(([k]) => k === state.tab); return t ? t[1] : ''; };
+ const tabs = () => `
+
+ ${RANK_TABS.map(([k, label]) => ``).join('')}
+
`;
+ const toolbar = (withFilters) => `
+
+ ${toolbarBack()}
+
热点数据
+ ${withFilters ? `
+
+
+
+
+
` : ''}
+
`;
+
+ function bindShell() {
+ bindBack(view);
+ view.querySelectorAll('.rank-tab').forEach((t) => t.addEventListener('click', () => {
+ const k = t.dataset.rankTab;
+ if (k === state.tab) return;
+ state.tab = k; state.page = 1; render();
+ }));
+ }
+
+ function render() {
+ if (state.tab !== 'week') {
+ // 其余榜单与首页一致:建设中占位(无本地数据文件,数据获取技能落盘后即可接入)
+ view.innerHTML = `${toolbar(false)}${tabs()}
「${esc(tabLabel())}」建设中,敬请期待
`;
+ bindShell(); return;
+ }
+ (async () => {
+ const d = await api('/api/dsh/ranking?date=' + encodeURIComponent(state.date) + '&category=' + encodeURIComponent(state.category) + '&sortBy=' + state.sortBy + '&order=' + state.order + '&page=' + state.page + '&pageSize=' + state.pageSize);
+ const di = state.dates.indexOf(state.date);
+ const WIDE = new Set(['comprehensiveScore', 'followers', 'newFans', 'newLikes', 'newComments', 'newShares']); // 数值列(窄+右对齐)
+ const sortable = (key, label) => `${label}${state.sortBy === key ? (state.order === 'asc' ? ' ↑' : ' ↓') : ''} | `;
+ const rows = (d.items || []).map((r) => `
| ${r.rank ?? '-'} |
${esc(r.category || '-')} |
${r.profileUrl ? `${esc(r.accountName)}` : esc(r.accountName)} |
@@ -525,21 +571,9 @@ async function renderRanking(view) {
${fmt(r.newShares)} |
${r.inDb ? `查看` : ``} |
`).join('');
- view.innerHTML = `
+ view.innerHTML = `
-
- ${toolbarBack()}
-
账号周榜
-
-
-
-
-
-
-
+ ${toolbar(true)}${tabs()}
@@ -548,26 +582,29 @@ async function renderRanking(view) {
${rows || '| 暂无周榜数据 |
'}
- ${pagerHtml(d.total, d.page, state.pageSize, (p) => { state.page = p; load(); })}
+ ${pagerHtml(d.total, d.page, state.pageSize, (p) => { state.page = p; render(); })}
`;
- bindBack(view);
- view.querySelectorAll('.sortable').forEach((th) => th.addEventListener('click', () => {
- const k = th.dataset.sort;
- if (state.sortBy === k) state.order = state.order === 'asc' ? 'desc' : 'asc';
- else { state.sortBy = k; state.order = k === 'rank' ? 'asc' : 'desc'; }
- state.page = 1; load();
- }));
- view.querySelector('#rankCat').addEventListener('change', (e) => { state.category = e.target.value; state.page = 1; load(); });
- view.querySelector('#rankPrev').addEventListener('click', () => { const i = state.dates.indexOf(state.date) - 1; if (i >= 0) { state.date = state.dates[i]; state.page = 1; load(); } });
- view.querySelector('#rankNext').addEventListener('click', () => { const i = state.dates.indexOf(state.date) + 1; if (i < state.dates.length) { state.date = state.dates[i]; state.page = 1; load(); } });
- bindPager(view, (p) => { state.page = p; load(); });
- view.querySelectorAll('[data-import]').forEach((b) => b.addEventListener('click', () => {
- const r = JSON.parse(b.dataset.import);
- confirmSaveAccount(r.accountName, r.profileUrl || '', b);
- }));
- view._reload = load; // 静默自动刷新(保留筛选/翻页状态)
+ bindShell();
+ view.querySelectorAll('.sortable').forEach((th) => th.addEventListener('click', () => {
+ const k = th.dataset.sort;
+ if (state.sortBy === k) state.order = state.order === 'asc' ? 'desc' : 'asc';
+ else { state.sortBy = k; state.order = k === 'rank' ? 'asc' : 'desc'; }
+ state.page = 1; render();
+ }));
+ const rankCat = view.querySelector('#rankCat');
+ if (rankCat) rankCat.addEventListener('change', (e) => { state.category = e.target.value; state.page = 1; render(); });
+ const prev = view.querySelector('#rankPrev'), next = view.querySelector('#rankNext');
+ if (prev) { prev.disabled = di <= 0; prev.addEventListener('click', () => { const i = state.dates.indexOf(state.date) - 1; if (i >= 0) { state.date = state.dates[i]; state.page = 1; render(); } }); }
+ if (next) { next.disabled = di < 0 || di >= state.dates.length - 1; next.addEventListener('click', () => { const i = state.dates.indexOf(state.date) + 1; if (i < state.dates.length) { state.date = state.dates[i]; state.page = 1; render(); } }); }
+ bindPager(view, (p) => { state.page = p; render(); });
+ view.querySelectorAll('[data-import]').forEach((b) => b.addEventListener('click', () => {
+ const r = JSON.parse(b.dataset.import);
+ confirmSaveAccount(r.accountName, r.profileUrl || '', b);
+ }));
+ view._reload = render; // 静默自动刷新(保留 tab/筛选/翻页状态)
+ })();
}
- await load();
+ render();
}
/* ================= 账号列表 ================= */