51 lines
4.5 KiB
Vue
51 lines
4.5 KiB
Vue
<template>
|
||
<view class="page" :style="{ paddingTop: navBarHeight + 'px' }">
|
||
<CustomNavBar title="练习的改变" show-back />
|
||
<view class="content">
|
||
<view v-if="loading" class="state">正在读取案例…</view>
|
||
<view v-else-if="error" class="state">{{ error }}<button @tap="reload">重新加载</button></view>
|
||
<template v-else-if="item">
|
||
<text class="eyebrow">{{ item.displayName }} · 真实练习记录</text><text class="title">{{ item.title }}</text>
|
||
<view class="stat"><text class="number">{{ item.lessonCount }}</text><text>节课,记录这一段改变</text></view>
|
||
<TeachingCaseGallery :frames="item.frames" :lesson-count="item.lessonCount" @refresh="reload" />
|
||
<view v-if="item.background" class="story"><text class="label">练习的起点</text><text class="body">{{ item.background }}</text></view>
|
||
<view class="story"><text class="label">看得见,也感受得到</text><text class="body">{{ item.achievement }}</text></view>
|
||
<text class="notice">经学员单独授权公开分享。每个人的基础、练习频率与感受不同,案例不代表相同课时下的效果承诺。</text>
|
||
<view class="invite"><text class="invite-title">你的练习,从了解自己开始。</text><text class="notice">找到适合自己的节奏,和老师一起开始。</text><button class="primary" @tap="explore">了解课程与体验</button></view>
|
||
<button class="share" open-type="share">分享这段改变</button>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { onLoad, onShow, onShareAppMessage } from '@dcloudio/uni-app'
|
||
import type { TeachingCasePublic } from '@mp-pilates/shared'
|
||
import CustomNavBar from '../../components/CustomNavBar.vue'
|
||
import TeachingCaseGallery from '../../components/TeachingCaseGallery.vue'
|
||
import { getSystemLayout } from '../../utils/system'
|
||
import { get, HttpRequestError } from '../../utils/request'
|
||
const navBarHeight = getSystemLayout().navBarHeight
|
||
const id = ref(''), item = ref<TeachingCasePublic | null>(null), loading = ref(true), error = ref('')
|
||
let sequence = 0
|
||
async function reload() {
|
||
const seq = ++sequence; loading.value = true; error.value = ''; item.value = null
|
||
try { if (!id.value) throw new Error('案例链接不完整'); const result = await get<TeachingCasePublic>(`/teaching-cases/${encodeURIComponent(id.value)}`); if (seq === sequence) item.value = result }
|
||
catch (e) { if (seq === sequence) error.value = e instanceof HttpRequestError && e.statusCode === 404 ? '案例已下架或暂未公开。' : '案例暂时无法读取,请重试。' }
|
||
finally { if (seq === sequence) loading.value = false }
|
||
}
|
||
function explore() {
|
||
// A shared detail link can be the launch page, before home has registered events.
|
||
uni.setStorageSync('home:card-shop-requested', true)
|
||
uni.switchTab({ url: '/pages/home/index', fail: () => {
|
||
uni.removeStorageSync('home:card-shop-requested')
|
||
uni.showToast({ title: '打开课程失败,请重试', icon: 'none' })
|
||
} })
|
||
}
|
||
onLoad(query => { id.value = String(query?.id || '') }); onShow(reload)
|
||
onShareAppMessage(() => ({ title: item.value?.title || '教学案例', path: item.value ? `/pages/home/teaching-case?id=${item.value.id}` : '/pages/home/teaching-cases' }))
|
||
</script>
|
||
<style scoped>
|
||
.page{min-height:100vh;background:#fbf9f6;box-sizing:border-box;}.content{padding:42rpx 32rpx 80rpx;}.eyebrow{font-size:23rpx;color:#7b866e;letter-spacing:2rpx;}.title{display:block;font-family:'Songti SC','STSong',serif;font-size:46rpx;line-height:1.5;color:#454c3f;margin:22rpx 0;}.stat{display:flex;align-items:baseline;gap:18rpx;color:#7a806a;font-size:25rpx;margin:26rpx 0 34rpx;}.number{font-family:'Baskerville',serif;font-size:80rpx;line-height:1;color:#5c7151;}.story{padding:34rpx 0;border-bottom:1rpx solid #e5e0d5;}.label{display:block;font-size:24rpx;color:#8b806d;margin-bottom:18rpx;}.body{display:block;font-size:29rpx;color:#555448;line-height:1.9;white-space:pre-wrap;overflow-wrap:anywhere;}.notice{display:block;font-size:22rpx;line-height:1.9;color:#918775;margin-top:25rpx;}.invite{margin-top:40rpx;padding:28rpx;background:#eef0e6;border-radius:18rpx;}.invite-title{font-family:'Songti SC','STSong',serif;font-size:32rpx;color:#55634b;}.primary{background:#586c50;color:#fff;font-size:28rpx;line-height:88rpx;margin-top:26rpx;}.share{margin-top:22rpx;background:transparent;font-size:25rpx;color:#6f7a62;line-height:88rpx;}.share::after{border:0;}.state{text-align:center;padding:80rpx 0;font-size:28rpx;color:#857b6a;}
|
||
</style>
|