feat: 完成全部接口替换为新后端接口

This commit is contained in:
R524809
2026-10-19 13:19:07 +08:00
parent 8de06ce66b
commit 997042789d
40 changed files with 1946 additions and 785 deletions
+9 -15
View File
@@ -1,3 +1,5 @@
import { fetchAgeRecommendations } from '../../services/site-api';
import { resolvePreviewUrl } from '../../config/assets';
import { appSharePageMethods } from '../../base/pageMixin';
import { AGE_BANDS, AgeBandKey } from '../../core/data/difficulty';
import { parseMiniProgramUrl } from '../../utils/index';
@@ -91,24 +93,17 @@ async function fetchAgeRecommended(
ageKey: AgeBandKey,
): Promise<RecommendedItemView[]> {
try {
const res = await wx.cloud.callFunction({
name: 'ageRecommendedQuery',
data: { ageKey },
});
const payload = (res && (res.result as any)) || null;
if (!payload?.success) return [];
const items = (payload.data?.items || []) as any[];
const response = await fetchAgeRecommendations(ageKey);
const items = response.items;
return items
.map((it) => ({
title: String(it.title || ''),
image: String(it.previewImg || ''),
image: resolvePreviewUrl(it.previewImg),
subtitle: String(it.subtitle || ''),
ageBand: String(it.ageBand || it.ageRange || ''),
difficulty: toDifficultyLabel(
it.difficultyLabel || it.difficulty || '',
),
ageBand: `${it.ageMin}-${it.ageMax}岁`,
difficulty: toDifficultyLabel(it.difficulty || ''),
path: String(it.path || ''),
worksheetId: String(it.worksheetId || ''),
worksheetId: String(it.id || ''),
}))
.filter((it) => it.title);
} catch (e) {
@@ -174,8 +169,7 @@ Page({
onTapCard(e: WechatMiniprogram.CustomEvent) {
const detail = (e.detail || {}) as { title?: string; path?: string };
const path =
detail.path ||
(e.currentTarget.dataset.path as string | undefined);
detail.path || (e.currentTarget.dataset.path as string | undefined);
const title =
detail.title ||
(e.currentTarget.dataset.title as string | undefined);
+2 -1
View File
@@ -1,3 +1,4 @@
import { resolvePreviewUrl } from '../../config/assets';
import type { ApiCategory, ApiWorksheet } from '../../services/content-api';
export interface CategoryTab {
@@ -95,7 +96,7 @@ export function buildCategoryDataset(
title: row.title,
subtitle: row.subtitle,
icon: icons.get(row.category) || '📄',
previewImg: row.previewImg || '',
previewImg: resolvePreviewUrl(row.previewImg),
ageBand: `${row.ageMin}-${row.ageMax}岁`,
difficultyLabel: DIFFICULTY_LABELS[row.difficulty - 1] || '基础',
path: row.path,
+6
View File
@@ -222,6 +222,9 @@ Page({
this.setData({
favoriteList: records.map((r) => mapFavoriteToItem(r)),
});
} catch {
this.setData({ favoriteList: [] });
wx.showToast({ title: '收藏加载失败,请重试', icon: 'none' });
} finally {
this.setData({ favLoading: false });
}
@@ -232,6 +235,9 @@ Page({
try {
const records = await getDownloadLogs(1, DOWNLOAD_HISTORY_MAX);
this.setData({ downloadSections: groupDownloadsByDate(records) });
} catch {
this.setData({ downloadSections: [] });
wx.showToast({ title: '下载记录加载失败,请重试', icon: 'none' });
} finally {
this.setData({ dlLoading: false });
}
+8 -6
View File
@@ -1,3 +1,4 @@
import { publicAssetUrl } from '../../config/assets';
import { CATEGORY_LIST_WITH_ALL } from '../../core/data/categories';
import { AGE_BANDS } from '../../core/data/difficulty';
import { CategoryId } from '../../core/models/category';
@@ -91,8 +92,9 @@ export const HOME_DAILY_CHECKIN_ITEMS: HomeDisplayItem[] = [
ageBand: '5-8岁',
difficulty: '基础',
icon: '📝',
previewImg:
'cloud://cloud1-9gifs7a2756e2c87.636c-cloud1-9gifs7a2756e2c87-1351593184/assets/previews/chinese/hanzi-daily-checkin.jpg',
previewImg: publicAssetUrl(
'assets/previews/chinese/hanzi-daily-checkin.jpg',
),
path: '/chinesePages/handwritingSheet/handwritingSheet?id=hanzi-daily-checkin',
available: true,
},
@@ -104,8 +106,7 @@ export const HOME_DAILY_CHECKIN_ITEMS: HomeDisplayItem[] = [
ageBand: '6-8岁',
difficulty: '基础',
icon: '🔤',
previewImg:
'cloud://cloud1-9gifs7a2756e2c87.636c-cloud1-9gifs7a2756e2c87-1351593184/assets/previews/pinyin/pinyin-daily.jpg',
previewImg: publicAssetUrl('assets/previews/pinyin/pinyin-daily.jpg'),
path: '/pinyinPages/pinyinDictation/pinyinDictation?id=pinyin-daily',
available: true,
},
@@ -117,8 +118,9 @@ export const HOME_DAILY_CHECKIN_ITEMS: HomeDisplayItem[] = [
ageBand: '6-8岁',
difficulty: '基础',
icon: '🔠',
previewImg:
'cloud://cloud1-9gifs7a2756e2c87.636c-cloud1-9gifs7a2756e2c87-1351593184/assets/previews/english/letter-tracing-daily-checkin.jpg',
previewImg: publicAssetUrl(
'assets/previews/english/letter-tracing-daily-checkin.jpg',
),
path: '/englishPages/letterTracing/letterTracing?id=letter-tracing-daily-checkin',
available: true,
},
+20 -5
View File
@@ -1,3 +1,4 @@
import { resolvePreviewUrl } from '../../config/assets';
import { appSharePageMethods } from '../../base/pageMixin';
import { NAV_INNER_PX } from '../../utils/navMetrics';
import {
@@ -52,6 +53,7 @@ const TAB_BAR_PATHS = new Set([
function toItemView(item: HomeDisplayItem): HomeDisplayItemView {
return {
...item,
previewImg: resolvePreviewUrl(item.previewImg),
};
}
@@ -218,11 +220,11 @@ Page({
void this.initData();
},
async initData() {
wx.showLoading({ title: '加载中', mask: false });
async initData(override?: PageConfigData) {
if (!override) wx.showLoading({ title: '加载中', mask: false });
const app = getApp<IAppOption>();
const config = await app.getPageConfig();
const config = override || (await app.getPageConfig());
let homeData: HomeDisplayDataset;
@@ -249,13 +251,26 @@ Page({
searchPlaceholder: homeData.searchPlaceholder,
tabs,
ageBands,
activeAgeBand: homeData.ageBands[0]?.key || '3-4',
activeAgeBand:
override &&
homeData.ageBands.some(
(band) => band.key === this.data.activeAgeBand,
)
? this.data.activeAgeBand
: homeData.ageBands[0]?.key || '3-4',
activeTab: tabs.some((tab) => tab.id === this.data.activeTab)
? this.data.activeTab
: 'all',
heroSwiperCurrent: Math.min(
this.data.heroSwiperCurrent,
Math.max(0, homeData.featured.length - 1),
),
featuredItems: homeData.featured.map(toItemView),
hotRecommends: homeData.hot.slice(0, 3).map(toItemView),
sections,
});
wx.hideLoading();
if (!override) wx.hideLoading();
},
// ── Tab & Navigation ──
+15 -3
View File
@@ -123,7 +123,7 @@ page {
box-sizing: border-box;
}
.profile-hero__stat+.profile-hero__stat {
.profile-hero__stat + .profile-hero__stat {
margin-left: 32rpx;
}
@@ -164,7 +164,7 @@ page {
box-sizing: border-box;
}
.profile-grid__cell+.profile-grid__cell {
.profile-grid__cell + .profile-grid__cell {
margin-left: 32rpx;
}
@@ -461,4 +461,16 @@ page {
.profile-editor__action--disabled {
opacity: 0.6;
}
}
/* 仅在账户恢复需要用户主动确认时展示,正常静默登录不出现。 */
.profile-account-restart {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
padding: 24rpx;
margin-bottom: @section-gap-xs;
border-radius: @radius-xl;
background: @bg-white;
font-size: 26rpx;
}
+52 -30
View File
@@ -1,7 +1,7 @@
import { uploadAsset, memberStats } from '../../services/member-api';
import { getUser as getServerUser, startAccount } from '../../utils/auth';
import { disablePageShareMenu } from '../../base/pageMixin';
import { APP_VERSION } from '../../config/config';
import { getDownloadLogCount } from '../../utils/downloadLogs';
import { getFavoriteCount } from '../../utils/favorites';
import {
getCachedUser,
getUser,
@@ -22,6 +22,8 @@ Page({
draftNickName: '',
draftAvatarUrl: '/assets/imgs/doodle-head.png',
isSavingProfile: false,
serverAccountActionRequired: false,
isStartingAccount: false,
},
onLoad() {
@@ -35,17 +37,52 @@ Page({
},
onShow() {
void this.checkServerAccount();
this.syncUserInfoFromApp();
void this.refreshStats();
},
/** 正常登录始终静默;账户不存在时才展示用户主动重新开始的入口。 */
async checkServerAccount() {
try {
await getServerUser();
this.setData({ serverAccountActionRequired: false });
} catch (error) {
const reason = (error as { reason?: string }).reason;
this.setData({
serverAccountActionRequired:
reason === 'ACCOUNT_NOT_FOUND' ||
reason === 'ACCOUNT_RESTART_REQUIRED',
});
}
},
async onStartServerAccount() {
if (this.data.isStartingAccount) return;
this.setData({ isStartingAccount: true });
try {
await startAccount();
this.setData({ serverAccountActionRequired: false });
wx.showToast({ title: '账户已就绪', icon: 'success' });
} catch {
wx.showToast({ title: '账户暂不可用,请稍后重试', icon: 'none' });
} finally {
this.setData({ isStartingAccount: false });
}
},
async syncUserInfoFromApp() {
const app = getApp<IAppOption>();
const cachedUser = app.globalData.user || getCachedUser();
if (cachedUser) {
this.applyUserInfo(cachedUser);
}
if (cachedUser) this.applyUserInfo(cachedUser);
else
this.setData({
userName: '微信用户',
avatarUrl: this.DEFAULT_AVATAR_URL,
printCount: 0,
favoriteCount: 0,
});
try {
const latestUser = await getUser();
@@ -69,13 +106,11 @@ Page({
async refreshStats() {
try {
const [printCount, favoriteCount] = await Promise.all([
getDownloadLogCount(),
getFavoriteCount(),
]);
const stats = await memberStats();
const { downloadCount: printCount, favoriteCount } = stats;
this.setData({ printCount, favoriteCount });
} catch (e) {
console.error('refreshStats failed', e);
} catch {
this.setData({ printCount: 0, favoriteCount: 0 });
}
},
@@ -121,12 +156,12 @@ Page({
this.setData({ isSavingProfile: true });
try {
const avatarUrl = await this.persistAvatarIfNeeded(
const avatarAssetId = await this.persistAvatarIfNeeded(
this.data.draftAvatarUrl,
);
const user = await updateUserProfile({
nickName,
avatarUrl,
avatarAssetId,
});
this.applyUserInfo(user);
@@ -140,24 +175,11 @@ Page({
}
},
async persistAvatarIfNeeded(avatarUrl: string): Promise<string> {
if (!avatarUrl) return avatarUrl;
if (
avatarUrl.startsWith('cloud://') ||
avatarUrl.startsWith('http://') ||
avatarUrl.startsWith('https://')
) {
return avatarUrl;
}
async persistAvatarIfNeeded(avatarUrl: string): Promise<string | null> {
const user = await getUser();
const cloudPath = `user-avatars/${user._id}-${Date.now()}.png`;
const uploadRes = await wx.cloud.uploadFile({
cloudPath,
filePath: avatarUrl,
});
return uploadRes.fileID;
if (!avatarUrl || avatarUrl === this.DEFAULT_AVATAR_URL) return null;
if (avatarUrl === user.avatarUrl) return user.avatarAssetId || null;
return (await uploadAsset(avatarUrl, 'avatar')).id;
},
onGoToDownloads() {
+13
View File
@@ -38,6 +38,19 @@
</view>
</view>
<view
wx:if="{{serverAccountActionRequired}}"
class="profile-account-restart">
<text>账户需要重新开始使用</text>
<button
size="mini"
loading="{{isStartingAccount}}"
disabled="{{isStartingAccount}}"
bindtap="onStartServerAccount">
开始使用账户
</button>
</view>
<!-- 双格入口 -->
<view class="profile-grid">
<view class="profile-grid__cell" bindtap="onGoToFavorites">