feat: add taro student learning flow

This commit is contained in:
Codex
2026-06-29 12:04:59 +08:00
parent c090008f52
commit b79c4259c0
28 changed files with 1567 additions and 12 deletions

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '资料',
});

View File

@@ -0,0 +1,70 @@
import { useEffect, useState } from 'react';
import Taro from '@tarojs/taro';
import { Button, Text, View } from '@tarojs/components';
import { loadContentAssets, signAssetDownload, signAssetPreview, type ContentAsset } from '@/services/catalog';
import '../student.css';
function openUrl(url?: string) {
if (!url) return;
if (process.env.TARO_ENV === 'h5' && typeof window !== 'undefined') {
window.open(url, '_blank', 'noopener,noreferrer');
return;
}
Taro.setClipboardData({ data: url });
}
export default function StudentAssetsPage() {
const [assets, setAssets] = useState<ContentAsset[]>([]);
const [error, setError] = useState('');
useEffect(() => {
loadContentAssets({ includeLocked: true })
.then(payload => setAssets(payload.items || []))
.catch(nextError => setError(nextError instanceof Error ? nextError.message : '资料加载失败'));
}, []);
async function preview(asset: ContentAsset) {
try {
const payload = await signAssetPreview(asset.id);
openUrl(payload.preview?.url);
} catch (nextError) {
setError(nextError instanceof Error ? nextError.message : '预览失败');
}
}
async function download(asset: ContentAsset) {
try {
const payload = await signAssetDownload(asset.id);
openUrl(payload.download?.url);
} catch (nextError) {
setError(nextError instanceof Error ? nextError.message : '下载失败');
}
}
return (
<View className='student-page'>
<View className='student-topbar'>
<View className='student-title-block'>
<Text className='student-kicker'>Assets</Text>
<Text className='student-title'></Text>
<Text className='student-subtitle'></Text>
</View>
</View>
<View className='list-stack'>
{assets.map(item => (
<View className='list-row' key={item.id}>
<Text className='row-main'>{item.title || item.fileName || '未命名资料'}</Text>
<Text className='row-meta'>{item.assetType || 'asset'} · {item.visibility || 'public'}</Text>
{item.description ? <Text className='row-meta'>{item.description}</Text> : null}
<View className='toolbar'>
<Button className='secondary-button' onClick={() => preview(item)}></Button>
<Button className='primary-button' onClick={() => download(item)}></Button>
</View>
</View>
))}
</View>
{!assets.length ? <View className='empty-state'></View> : null}
{error ? <Text className='error-text'>{error}</Text> : null}
</View>
);
}