Files
doodle-mini/miniprogram/pages/category/category.ts
T
2026-10-16 17:33:24 +08:00

233 lines
7.0 KiB
TypeScript

import { appSharePageMethods } from '../../base/pageMixin';
import { takePendingCategoryTabId } from '../../utils/index';
import tracker from '../../utils/tracker';
import { ApiRequestError } from '../../services/api-request';
import {
getWorksheet,
listAllWorksheets,
listCategories,
} from '../../services/content-api';
import {
BASE_TABS,
buildCategoryDataset,
getCategoryItems,
type CategoryDataset,
type CategoryItem,
} from './category.model';
const TAB_BAR_PATHS = new Set([
'/pages/home/home',
'/pages/category/category',
'/pages/age/age',
'/pages/favorites/favorites',
'/pages/profile/profile',
]);
const SKELETON_ITEMS: CategoryItem[] = Array.from(
{ length: 6 },
(_, index) => ({
id: `skeleton-${index}`,
title: '\u00a0',
subtitle: '\u00a0',
icon: '',
previewImg: '',
ageBand: '',
difficultyLabel: '',
path: '',
available: false,
likes: '',
downloads: '',
date: '',
updatedAt: '',
skeleton: true,
}),
);
Page({
data: {
searchPlaceholder: '搜索练习纸...',
searchKeyword: '',
sidebarTabs: BASE_TABS,
activeCategoryId: 'all',
displayItems: SKELETON_ITEMS,
scrollIntoViewId: '',
loading: true,
errorMessage: '',
},
_defaultCategoryId: 'all',
_categoryData: null as CategoryDataset | null,
_loadVersion: 0,
_opening: false,
onLoad(options: Record<string, string>) {
tracker.reportPageView('pages/category/category');
if (options.id && options.id !== 'all') {
this._defaultCategoryId = options.id;
this.setData({ activeCategoryId: options.id });
}
},
onShow() {
void this.loadWorksheets(takePendingCategoryTabId() ?? undefined);
},
onHide() {
this._loadVersion++;
},
onUnload() {
this._loadVersion++;
},
/** 每次进入重新读取公开目录与完整分页;失败清空内容,不回退静态数据或旧云服务。 */
async loadWorksheets(overrideCategoryId?: string) {
const version = ++this._loadVersion;
this._categoryData = null;
this.setData({
displayItems: SKELETON_ITEMS,
sidebarTabs: BASE_TABS,
loading: true,
errorMessage: '',
});
try {
const [categories, worksheets] = await Promise.all([
listCategories(),
listAllWorksheets(),
]);
if (version !== this._loadVersion) return;
const dataset = buildCategoryDataset(categories, worksheets);
const desiredId =
overrideCategoryId ||
this.data.activeCategoryId ||
this._defaultCategoryId;
const activeId = dataset.sidebarTabs.some(
(tab) => tab.id === desiredId,
)
? desiredId
: 'all';
this._categoryData = dataset;
this.setData({
loading: false,
sidebarTabs: dataset.sidebarTabs,
activeCategoryId: activeId,
displayItems: getCategoryItems(
dataset,
activeId,
this.data.searchKeyword,
),
scrollIntoViewId: '',
errorMessage: '',
});
} catch (error) {
if (version !== this._loadVersion) return;
this._categoryData = null;
this.setData({
loading: false,
displayItems: [],
sidebarTabs: BASE_TABS,
errorMessage:
error instanceof Error
? error.message
: '加载失败,请稍后重试',
});
}
},
onRetry() {
void this.loadWorksheets();
},
onTapCategory(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string;
if (
!this._categoryData ||
this.data.loading ||
!id ||
id === this.data.activeCategoryId ||
!this._categoryData.sidebarTabs.some((tab) => tab.id === id)
)
return;
this.setData({
activeCategoryId: id,
displayItems: getCategoryItems(
this._categoryData,
id,
this.data.searchKeyword,
),
scrollIntoViewId: '',
});
tracker.report('category_select', 'pages/category/category', {
category_id: id,
});
},
onSearchInput(e: WechatMiniprogram.Input) {
const keyword = (e.detail.value ?? '').trim();
this.setData({
searchKeyword: keyword,
displayItems: getCategoryItems(
this._categoryData,
this.data.activeCategoryId,
keyword,
),
});
},
onClearSearch() {
this.setData({
searchKeyword: '',
displayItems: getCategoryItems(
this._categoryData,
this.data.activeCategoryId,
),
});
},
/** 点击时重新检查详情,已下架或网络不可用时不进入内置生成页。 */
async onTapItem(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string;
if (
this._opening ||
this.data.loading ||
!id ||
!this.data.displayItems.some(
(item) => item.id === id && item.available,
)
)
return;
this._opening = true;
const version = this._loadVersion;
try {
const row = await getWorksheet(id);
if (version !== this._loadVersion) return;
if (!row.path) throw new Error('内容暂不可用');
tracker.reportWorksheetClick('pages/category/category', {
worksheet_id: row.id,
worksheet_title: row.title,
category_id: this.data.activeCategoryId,
source: 'category_list',
});
const fail = () =>
wx.showToast({ title: '暂时无法打开该内容', icon: 'none' });
if (TAB_BAR_PATHS.has(row.path))
wx.switchTab({ url: row.path, fail });
else wx.navigateTo({ url: row.path, fail });
} catch (error) {
if (version !== this._loadVersion) return;
wx.showToast({
title:
error instanceof Error
? error.message
: '暂时无法打开,请稍后重试',
icon: 'none',
});
if (error instanceof ApiRequestError && error.statusCode === 404)
void this.loadWorksheets();
} finally {
this._opening = false;
}
},
...appSharePageMethods,
});