feat: 我的、设置、打印指南页面开发完成

This commit is contained in:
R524809
2026-05-07 16:32:49 +08:00
parent 0737c6165e
commit c732111e12
43 changed files with 1702 additions and 837 deletions
+99 -27
View File
@@ -17,6 +17,13 @@ type CategoryDataset = {
}>;
};
const DIFFICULTY_LABELS: Record<number, string> = {
1: '入门',
2: '基础',
3: '进阶',
4: '挑战',
};
const SIDEBAR_TABS: CategoryTab[] = CATEGORY_LIST_WITH_ALL.map(
({ id, name, icon }) => ({
id,
@@ -33,9 +40,74 @@ const TAB_BAR_PATHS = new Set([
'/pages/profile/profile',
]);
// Page-level mutable state (shared across methods, set once on load)
// Page-level mutable state (shared across methods)
let _categoryData: CategoryDataset | null = null;
/** 将云端 worksheet 原始数据转换为 CategoryItem */
function toDisplayItem(raw: Record<string, any>): CategoryItem {
const difficulty = (Number(raw.difficulty) || 2) as 1 | 2 | 3 | 4;
const ageMin = Number(raw.ageMin) || 0;
const ageMax = Number(raw.ageMax) || 0;
return {
id: String(raw._id || ''),
title: String(raw.title || ''),
subtitle: String(raw.subtitle || ''),
previewImg: String(raw.previewImg || ''),
ageBand: `${ageMin}-${ageMax}`,
ageMin,
ageMax,
difficulty,
difficultyLabel: DIFFICULTY_LABELS[difficulty] || '基础',
path: String(raw.path || ''),
available: true,
likes: Number(raw.likes) || 0,
downloads: Number(raw.downloads) || 0,
date: raw.updatedAt
? new Date(raw.updatedAt).toISOString().slice(0, 10)
: new Date().toISOString().slice(0, 10),
updatedAt: raw.updatedAt ? String(raw.updatedAt) : '',
};
}
/** 排序:updatedAt 最新的 2 个置顶,其余按 downloads 降序 */
function sortWorksheets(items: CategoryItem[]): CategoryItem[] {
const byTime = [...items].sort(
(a, b) =>
new Date(b.updatedAt || '').getTime() -
new Date(a.updatedAt || '').getTime(),
);
const recent = byTime.slice(0, 2);
const recentIds = new Set(recent.map((w) => w.id));
const rest = byTime
.filter((w) => !recentIds.has(w.id))
.sort((a, b) => (b.downloads || 0) - (a.downloads || 0));
return [...recent, ...rest];
}
/** 将云端 worksheet 数组按分类分组,构建 CategoryDataset */
function buildCategoryDataFromCloud(
rawList: Record<string, any>[],
): CategoryDataset {
const itemsByCategory: Record<string, CategoryItem[]> = {};
for (const raw of rawList) {
const item = toDisplayItem(raw);
const cat = String(raw.category || '');
if (!itemsByCategory[cat]) itemsByCategory[cat] = [];
itemsByCategory[cat].push(item);
}
const categories = CATEGORY_LIST_WITH_ALL.filter((c) => c.id !== 'all').map(
(cat) => ({
id: cat.id,
name: cat.name,
icon: cat.icon,
items: sortWorksheets(itemsByCategory[cat.id] || []),
}),
);
return { searchPlaceholder: '搜索练习纸...', categories };
}
function buildAllItems(): CategoryItem[] {
if (!_categoryData) return [];
const items: CategoryItem[] = [];
@@ -44,7 +116,7 @@ function buildAllItems(): CategoryItem[] {
items.push({ ...item });
}
}
return items;
return sortWorksheets(items);
}
function getItemsByCategory(categoryId: string): CategoryItem[] {
@@ -69,7 +141,7 @@ function filterByKeyword(
}
// Skeleton placeholder items for loading state
const SKELETON_ITEMS = Array.from({ length: 4 }, (_, i) => ({
const SKELETON_ITEMS = Array.from({ length: 6 }, (_, i) => ({
id: `skeleton-${i}`,
title: '\u00A0',
subtitle: '\u00A0',
@@ -87,45 +159,45 @@ Page({
loading: true,
},
_defaultCategoryId: 'all',
onLoad(options: Record<string, string>) {
void this.initData(options);
if (options.id && options.id !== 'all') {
this._defaultCategoryId = options.id;
this.setData({ activeCategoryId: options.id });
}
},
async initData(options: Record<string, string>) {
wx.showLoading({ title: '加载中', mask: false });
onShow() {
_categoryData = null;
this.setData({ displayItems: SKELETON_ITEMS, loading: true });
void this.loadWorksheets();
},
const app = getApp<IAppOption>();
const config = await app.getPageConfig();
async loadWorksheets() {
try {
const res = await wx.cloud.callFunction({
name: 'worksheetsQuery',
data: { status: 'active' },
});
const result = (res?.result as any) || {};
if (!result.success) throw new Error(result.message || '查询失败');
// Use cloud config if available, otherwise fallback to local static data
if (config?.category?.categories?.length) {
_categoryData = {
searchPlaceholder:
config.category.searchPlaceholder || '搜索练习纸...',
categories: config.category.categories.map((cat) => ({
id: cat.id,
name: cat.name,
icon: cat.icon,
items: (cat.items || []).map(
(raw) => raw as unknown as CategoryItem,
),
})),
};
} else {
_categoryData = buildCategoryDataFromCloud(result.data || []);
} catch {
_categoryData = CATEGORY_DATA;
}
const activeId = options.id && options.id !== 'all' ? options.id : 'all';
const activeId = this.data.activeCategoryId || this._defaultCategoryId;
const items = getItemsByCategory(activeId);
const filtered = filterByKeyword(items, this.data.searchKeyword);
this.setData({
loading: false,
searchPlaceholder: _categoryData.searchPlaceholder,
activeCategoryId: activeId,
displayItems: items,
displayItems: filtered,
});
wx.hideLoading();
},
onTapCategory(e: WechatMiniprogram.TouchEvent) {