feat:生产初版页面
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"navigationBarTitleText": "分类",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"backgroundColor": "#F6F6F6",
|
||||
"usingComponents": {
|
||||
"worksheet-card": "../../components/shared/worksheet-card/worksheet-card",
|
||||
"detail-card": "../../components/shared/detail-card/detail-card"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
.category-page {
|
||||
min-height: 100vh;
|
||||
background: #f6f6f6;
|
||||
padding-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.category-page__header {
|
||||
padding: 24rpx 32rpx 16rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.category-page__header-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-page__filters {
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.category-page__filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.category-page__filter-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.category-page__filter-label {
|
||||
flex-shrink: 0;
|
||||
width: 72rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.category-page__pills {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.category-page__pill {
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-page__pill--active {
|
||||
background: #f7ee47;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-page__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.category-page__card-wrap {
|
||||
margin: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.category-page__empty {
|
||||
padding: 120rpx 48rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.category-page__empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import type { WorksheetType } from '../../core/models/worksheet';
|
||||
import {
|
||||
getWorksheetPath,
|
||||
getWorksheetsByAge,
|
||||
getWorksheetsByCategory,
|
||||
} from '../../core/data/worksheets';
|
||||
|
||||
type AgeFilterKey = 'all' | '3-4' | '4-5' | '5-6' | '6-7' | '7-8';
|
||||
type DiffFilterKey = 'all' | 'beginner' | 'basic' | 'intermediate';
|
||||
|
||||
const AGE_BAND: Record<Exclude<AgeFilterKey, 'all'>, [number, number]> = {
|
||||
'3-4': [3, 4],
|
||||
'4-5': [4, 5],
|
||||
'5-6': [5, 6],
|
||||
'6-7': [6, 7],
|
||||
'7-8': [7, 8],
|
||||
};
|
||||
|
||||
const DIFF_LABEL: Record<string, string> = {
|
||||
beginner: '入门',
|
||||
basic: '基础',
|
||||
intermediate: '进阶',
|
||||
advanced: '进阶+',
|
||||
};
|
||||
|
||||
type DisplayItem = WorksheetType & {
|
||||
ageRangeLabel: string;
|
||||
difficultyLabel: string;
|
||||
};
|
||||
|
||||
function filterByAgeBand(
|
||||
list: WorksheetType[],
|
||||
band: AgeFilterKey,
|
||||
): WorksheetType[] {
|
||||
if (band === 'all') return list;
|
||||
const [lo, hi] = AGE_BAND[band];
|
||||
const map = new Map<string, WorksheetType>();
|
||||
for (let age = lo; age <= hi; age += 1) {
|
||||
getWorksheetsByAge(list, age).forEach((w) => map.set(w.id, w));
|
||||
}
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
function filterByDifficulty(
|
||||
list: WorksheetType[],
|
||||
diff: DiffFilterKey,
|
||||
): WorksheetType[] {
|
||||
if (diff === 'all') return list;
|
||||
return list.filter((w) => w.difficulty === diff);
|
||||
}
|
||||
|
||||
function toDisplayList(list: WorksheetType[]): DisplayItem[] {
|
||||
return list.map((w) => ({
|
||||
...w,
|
||||
ageRangeLabel:
|
||||
w.ageRange && w.ageRange.length === 2
|
||||
? `${w.ageRange[0]}-${w.ageRange[1]}岁`
|
||||
: '',
|
||||
difficultyLabel: w.difficulty ? DIFF_LABEL[w.difficulty] || '' : '',
|
||||
}));
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
title: '分类',
|
||||
sourceItems: [] as WorksheetType[],
|
||||
items: [] as DisplayItem[],
|
||||
ageFilters: [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: '3-4', label: '3-4岁' },
|
||||
{ key: '4-5', label: '4-5岁' },
|
||||
{ key: '5-6', label: '5-6岁' },
|
||||
{ key: '6-7', label: '6-7岁' },
|
||||
{ key: '7-8', label: '7-8岁' },
|
||||
],
|
||||
diffFilters: [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'beginner', label: '⭐ 入门' },
|
||||
{ key: 'basic', label: '⭐⭐ 基础' },
|
||||
{ key: 'intermediate', label: '⭐⭐⭐ 进阶' },
|
||||
],
|
||||
activeAge: 'all' as AgeFilterKey,
|
||||
activeDiff: 'all' as DiffFilterKey,
|
||||
},
|
||||
|
||||
onLoad(options: {
|
||||
category?: string;
|
||||
minAge?: string;
|
||||
maxAge?: string;
|
||||
title?: string;
|
||||
}) {
|
||||
const category = options.category || 'all';
|
||||
let base = getWorksheetsByCategory(category);
|
||||
|
||||
const minAge = options.minAge != null ? Number(options.minAge) : NaN;
|
||||
const maxAge = options.maxAge != null ? Number(options.maxAge) : NaN;
|
||||
if (!Number.isNaN(minAge) && !Number.isNaN(maxAge)) {
|
||||
const map = new Map<string, WorksheetType>();
|
||||
const lo = Math.min(minAge, maxAge);
|
||||
const hi = Math.max(minAge, maxAge);
|
||||
for (let age = lo; age <= hi; age += 1) {
|
||||
getWorksheetsByAge(base, age).forEach((w) =>
|
||||
map.set(w.id, w),
|
||||
);
|
||||
}
|
||||
base = Array.from(map.values());
|
||||
}
|
||||
|
||||
const title = options.title
|
||||
? decodeURIComponent(options.title)
|
||||
: '分类';
|
||||
|
||||
this.setData({
|
||||
title,
|
||||
sourceItems: base,
|
||||
});
|
||||
wx.setNavigationBarTitle({ title });
|
||||
this.applyFilters();
|
||||
},
|
||||
|
||||
applyFilters() {
|
||||
const { sourceItems, activeAge, activeDiff } = this.data;
|
||||
let next = filterByAgeBand(sourceItems, activeAge);
|
||||
next = filterByDifficulty(next, activeDiff);
|
||||
this.setData({ items: toDisplayList(next) });
|
||||
},
|
||||
|
||||
onAgeFilter(e: WechatMiniprogram.TouchEvent) {
|
||||
const key = e.currentTarget.dataset.key as AgeFilterKey;
|
||||
if (!key) return;
|
||||
this.setData({ activeAge: key }, () => this.applyFilters());
|
||||
},
|
||||
|
||||
onDiffFilter(e: WechatMiniprogram.TouchEvent) {
|
||||
const key = e.currentTarget.dataset.key as DiffFilterKey;
|
||||
if (!key) return;
|
||||
this.setData({ activeDiff: key }, () => this.applyFilters());
|
||||
},
|
||||
|
||||
buildWorksheetUrl(w: WorksheetType): string {
|
||||
let url = getWorksheetPath(w);
|
||||
if (!url) return '';
|
||||
if (w.mode) {
|
||||
url += url.includes('?') ? `&mode=${w.mode}` : `?mode=${w.mode}`;
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
onCardTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const id = e.currentTarget.dataset.id as string;
|
||||
const w = this.data.items.find((item) => item.id === id);
|
||||
if (!w) return;
|
||||
const url = this.buildWorksheetUrl(w);
|
||||
if (!url) {
|
||||
wx.showToast({ title: '暂无法打开', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.navigateTo({ url });
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
<view class="category-page">
|
||||
<view class="category-page__header">
|
||||
<text class="category-page__header-title">{{title}}</text>
|
||||
</view>
|
||||
|
||||
<view class="category-page__filters">
|
||||
<view class="category-page__filter-row">
|
||||
<text class="category-page__filter-label">年龄</text>
|
||||
<view class="category-page__pills">
|
||||
<view
|
||||
wx:for="{{ageFilters}}"
|
||||
wx:key="key"
|
||||
class="category-page__pill {{activeAge === item.key ? 'category-page__pill--active' : ''}}"
|
||||
data-key="{{item.key}}"
|
||||
bindtap="onAgeFilter"
|
||||
>
|
||||
{{item.label}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="category-page__filter-row">
|
||||
<text class="category-page__filter-label">难度</text>
|
||||
<view class="category-page__pills">
|
||||
<view
|
||||
wx:for="{{diffFilters}}"
|
||||
wx:key="key"
|
||||
class="category-page__pill {{activeDiff === item.key ? 'category-page__pill--active' : ''}}"
|
||||
data-key="{{item.key}}"
|
||||
bindtap="onDiffFilter"
|
||||
>
|
||||
{{item.label}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{items.length}}" class="category-page__list">
|
||||
<view
|
||||
wx:for="{{items}}"
|
||||
wx:key="id"
|
||||
class="category-page__card-wrap"
|
||||
>
|
||||
<detail-card
|
||||
title="{{item.title}}"
|
||||
preview-bg="{{item.previewBg}}"
|
||||
preview-text="{{item.previewText}}"
|
||||
age-range="{{item.ageRangeLabel}}"
|
||||
difficulty="{{item.difficultyLabel}}"
|
||||
data-id="{{item.id}}"
|
||||
bind:tap="onCardTap"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:else class="category-page__empty">
|
||||
<text class="category-page__empty-text">暂无符合条件的练习纸</text>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user