232 lines
6.5 KiB
TypeScript
232 lines
6.5 KiB
TypeScript
import { CATEGORY_LIST } from '../../core/data/categories';
|
|
|
|
type WorksheetItem = {
|
|
_id: string;
|
|
title: string;
|
|
subtitle: string;
|
|
category: string;
|
|
previewImg: string;
|
|
ageMin: number;
|
|
ageMax: number;
|
|
difficulty: 1 | 2 | 3 | 4;
|
|
tags: string[];
|
|
tagsText: string;
|
|
status: 'draft' | 'active' | 'hidden';
|
|
sortOrder: number;
|
|
downloads: number;
|
|
likes: number;
|
|
updatedAt: string;
|
|
// display helpers
|
|
ageBand: string;
|
|
difficultyLabel: string;
|
|
statusText: string;
|
|
actionText: string;
|
|
actionTarget: string;
|
|
actionType: string;
|
|
};
|
|
|
|
type Stats = {
|
|
total: number;
|
|
draft: number;
|
|
active: number;
|
|
hidden: number;
|
|
};
|
|
|
|
type CloudFunctionResult<T> = {
|
|
success?: boolean;
|
|
message?: string;
|
|
data?: T;
|
|
stats?: Stats;
|
|
};
|
|
|
|
const DIFFICULTY_LABELS: Record<number, string> = {
|
|
1: '入门',
|
|
2: '基础',
|
|
3: '进阶',
|
|
4: '挑战',
|
|
};
|
|
|
|
const STATUS_CONFIG: Record<
|
|
string,
|
|
{ text: string; action: string; target: string; actionType: string }
|
|
> = {
|
|
draft: {
|
|
text: '草稿',
|
|
action: '激活',
|
|
target: 'active',
|
|
actionType: 'green',
|
|
},
|
|
active: {
|
|
text: '线上',
|
|
action: '下架',
|
|
target: 'hidden',
|
|
actionType: 'default',
|
|
},
|
|
hidden: {
|
|
text: '已隐藏',
|
|
action: '恢复',
|
|
target: 'draft',
|
|
actionType: 'primary',
|
|
},
|
|
};
|
|
|
|
function formatWorksheet(raw: Record<string, unknown>): WorksheetItem {
|
|
const status = String(raw.status || 'draft');
|
|
const cfg = STATUS_CONFIG[status] || STATUS_CONFIG.draft;
|
|
const ageMin = Number(raw.ageMin) || 0;
|
|
const ageMax = Number(raw.ageMax) || 0;
|
|
const difficulty = (Number(raw.difficulty) || 2) as 1 | 2 | 3 | 4;
|
|
|
|
return {
|
|
_id: String(raw._id || ''),
|
|
title: String(raw.title || ''),
|
|
subtitle: String(raw.subtitle || ''),
|
|
category: String(raw.category || ''),
|
|
previewImg: String(raw.previewImg || ''),
|
|
ageMin,
|
|
ageMax,
|
|
difficulty,
|
|
tags: Array.isArray(raw.tags) ? raw.tags.map(String) : [],
|
|
tagsText: Array.isArray(raw.tags)
|
|
? raw.tags.map(String).join('、')
|
|
: '',
|
|
status: status as WorksheetItem['status'],
|
|
sortOrder: Number(raw.sortOrder) || 0,
|
|
downloads: Number(raw.downloads) || 0,
|
|
likes: Number(raw.likes) || 0,
|
|
updatedAt: raw.updatedAt ? String(raw.updatedAt) : '',
|
|
ageBand: `${ageMin}-${ageMax}岁`,
|
|
difficultyLabel: DIFFICULTY_LABELS[difficulty] || '基础',
|
|
statusText: cfg.text,
|
|
actionText: cfg.action,
|
|
actionTarget: cfg.target,
|
|
actionType: cfg.actionType,
|
|
};
|
|
}
|
|
|
|
async function callCloudFunction<T>(
|
|
name: string,
|
|
data?: Record<string, unknown>,
|
|
): Promise<CloudFunctionResult<T>> {
|
|
const response = (await wx.cloud.callFunction({
|
|
name,
|
|
data: data || {},
|
|
})) as { result?: CloudFunctionResult<T> };
|
|
return response.result || {};
|
|
}
|
|
|
|
Page({
|
|
data: {
|
|
categories: CATEGORY_LIST,
|
|
activeCategoryId: CATEGORY_LIST[0]?.id || 'math',
|
|
worksheets: [] as WorksheetItem[],
|
|
stats: { total: 0, draft: 0, active: 0, hidden: 0 } as Stats,
|
|
loading: false,
|
|
updatingId: '',
|
|
building: false,
|
|
},
|
|
|
|
onLoad() {
|
|
void this.loadWorksheets();
|
|
},
|
|
|
|
async onPullDownRefresh() {
|
|
await this.loadWorksheets();
|
|
wx.stopPullDownRefresh();
|
|
},
|
|
|
|
async loadWorksheets() {
|
|
this.setData({ loading: true });
|
|
|
|
try {
|
|
const result = await callCloudFunction<WorksheetItem[]>(
|
|
'worksheetsQuery',
|
|
{
|
|
category: this.data.activeCategoryId,
|
|
rawStats: true,
|
|
},
|
|
);
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.message || '查询失败');
|
|
}
|
|
|
|
const worksheets = (result.data || [])
|
|
.map((raw) =>
|
|
formatWorksheet(raw as unknown as Record<string, unknown>),
|
|
)
|
|
.sort((a, b) => b.downloads - a.downloads);
|
|
|
|
this.setData({
|
|
loading: false,
|
|
worksheets,
|
|
stats: result.stats || {
|
|
total: 0,
|
|
draft: 0,
|
|
active: 0,
|
|
hidden: 0,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
this.setData({ loading: false });
|
|
wx.showToast({
|
|
title: error instanceof Error ? error.message : '查询失败',
|
|
icon: 'none',
|
|
});
|
|
}
|
|
},
|
|
|
|
onCategoryTap(e: WechatMiniprogram.TouchEvent) {
|
|
const id = e.currentTarget.dataset.id as string;
|
|
if (!id || id === this.data.activeCategoryId) return;
|
|
this.setData({ activeCategoryId: id });
|
|
void this.loadWorksheets();
|
|
},
|
|
|
|
async onStatusTap(e: WechatMiniprogram.TouchEvent) {
|
|
const id = e.currentTarget.dataset.id as string;
|
|
const target = e.currentTarget.dataset.target as string;
|
|
if (!id || !target || this.data.updatingId) return;
|
|
|
|
const ws = this.data.worksheets.find((w) => w._id === id);
|
|
if (!ws) return;
|
|
|
|
const confirmed = await new Promise<boolean>((resolve) => {
|
|
wx.showModal({
|
|
title: `${ws.actionText}`,
|
|
content: `确认将「${ws.title}」${ws.actionText}吗?`,
|
|
success: (res) => resolve(!!res.confirm),
|
|
fail: () => resolve(false),
|
|
});
|
|
});
|
|
if (!confirmed) return;
|
|
|
|
this.setData({ updatingId: id });
|
|
|
|
try {
|
|
const result = await callCloudFunction('worksheetsUpdateStatus', {
|
|
id,
|
|
status: target,
|
|
});
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.message || '操作失败');
|
|
}
|
|
|
|
wx.showToast({ title: `${ws.actionText}成功`, icon: 'success' });
|
|
await this.loadWorksheets();
|
|
} catch (error) {
|
|
wx.showToast({
|
|
title: error instanceof Error ? error.message : '操作失败',
|
|
icon: 'none',
|
|
});
|
|
} finally {
|
|
this.setData({ updatingId: '' });
|
|
}
|
|
},
|
|
|
|
// [v3.0] 分类页已改为实时接口查询,不再需要手动更新分类数据
|
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
onBuildTap() {},
|
|
});
|