feat: 我的、设置、打印指南页面开发完成
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user