西藏巴青项目

index.vue 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template>
  2. <!-- 课程详情(英文路径):上视频、中标题、下日期+简介 -->
  3. <view :class="pageRootClass" class="tab-page course-detail-page">
  4. <scroll-view scroll-y class="course-detail-scroll" enable-back-to-top>
  5. <view class="course-detail-inner">
  6. <view class="course-detail-top">
  7. <video
  8. v-if="videoSrc"
  9. class="course-detail-video"
  10. :src="videoSrc"
  11. :poster="posterSrc"
  12. controls
  13. object-fit="contain"
  14. :enable-progress-gesture="true"
  15. :show-center-play-btn="true"
  16. />
  17. <view v-else class="course-detail-video course-detail-video--empty">
  18. <text class="text-body course-detail-video__ph">{{ $t('courseDetailPage.noVideo') }}</text>
  19. </view>
  20. </view>
  21. <text class="course-detail-title">{{ displayTitle }}</text>
  22. <view class="course-detail-bottom">
  23. <text class="text-body course-detail-date">{{ displayDate }}</text>
  24. <text class="text-body course-detail-intro">{{ introText }}</text>
  25. </view>
  26. </view>
  27. </scroll-view>
  28. </view>
  29. </template>
  30. <script>
  31. import tabPage from '@/mixins/tabPage'
  32. import { resolveResourceUrl } from '@/utils/resourceUrl'
  33. const POSTER = '/static/ai/hero.png'
  34. export default {
  35. mixins: [tabPage],
  36. data() {
  37. return {
  38. navTitleKey: 'courseDetailPage.navTitle',
  39. courseId: '',
  40. displayTitleRaw: '',
  41. typeCode: '',
  42. displayDate: '',
  43. introduction: '',
  44. videoSrc: '',
  45. posterSrc: POSTER
  46. }
  47. },
  48. computed: {
  49. displayTitle() {
  50. return this.displayTitleRaw || this.$t('courseDetailPage.titleFallback')
  51. },
  52. introText() {
  53. return this.introduction || this.$t('courseDetailPage.noIntro')
  54. }
  55. },
  56. onLoad(query) {
  57. const q = query || {}
  58. this.courseId = this.decodeQuery(q, 'id')
  59. this.displayTitleRaw = this.decodeQuery(q, 'title')
  60. this.typeCode = this.decodeQuery(q, 'type')
  61. this.introduction = this.decodeQuery(q, 'introduction')
  62. this.displayDate = this.decodeQuery(q, 'date') || this.$t('courseDetailPage.noDate')
  63. const src = this.decodeQuery(q, 'src')
  64. this.videoSrc = src ? resolveResourceUrl(src) : ''
  65. const cover = this.decodeQuery(q, 'cover')
  66. this.posterSrc = cover ? resolveResourceUrl(cover) : POSTER
  67. },
  68. onShow() {
  69. const p = uni.setNavigationBarTitle({
  70. title: this.displayTitleRaw || this.$t(this.navTitleKey)
  71. })
  72. if (p && typeof p.catch === 'function') {
  73. p.catch(() => {})
  74. }
  75. },
  76. methods: {
  77. decodeQuery(q, key) {
  78. const raw = q && q[key]
  79. if (raw == null || raw === '') {
  80. return ''
  81. }
  82. try {
  83. return decodeURIComponent(String(raw))
  84. } catch (e) {
  85. return String(raw)
  86. }
  87. }
  88. }
  89. }
  90. </script>
  91. <style lang="scss" scoped>
  92. @import '@/styles/morandi.scss';
  93. @import '@/styles/tab-page.scss';
  94. .course-detail-page {
  95. display: flex;
  96. flex-direction: column;
  97. min-width: 0;
  98. min-height: 100%;
  99. box-sizing: border-box;
  100. background: $morandi-bg-page;
  101. }
  102. .course-detail-scroll {
  103. flex: 1;
  104. min-height: 0;
  105. min-width: 0;
  106. }
  107. .course-detail-inner {
  108. display: flex;
  109. flex-direction: column;
  110. align-items: stretch;
  111. gap: 28rpx;
  112. min-width: 0;
  113. padding: 24rpx 24rpx 48rpx;
  114. box-sizing: border-box;
  115. }
  116. .course-detail-top {
  117. width: 100%;
  118. min-width: 0;
  119. border-radius: 16rpx;
  120. overflow: hidden;
  121. background: #000;
  122. border: 1rpx solid $morandi-border;
  123. }
  124. .course-detail-video {
  125. display: block;
  126. width: 100%;
  127. height: 400rpx;
  128. background: #1a1a1a;
  129. }
  130. .course-detail-video--empty {
  131. display: flex;
  132. align-items: center;
  133. justify-content: center;
  134. }
  135. .course-detail-video__ph {
  136. color: $morandi-text-muted;
  137. text-align: center;
  138. padding: 24rpx;
  139. }
  140. .course-detail-title {
  141. display: block;
  142. width: 100%;
  143. text-align: center;
  144. font-size: 36rpx;
  145. font-weight: 700;
  146. line-height: 1.45;
  147. color: #111827;
  148. word-break: break-word;
  149. overflow-wrap: anywhere;
  150. }
  151. .course-detail-bottom {
  152. display: flex;
  153. flex-direction: column;
  154. gap: 16rpx;
  155. min-width: 0;
  156. padding: 0 8rpx;
  157. box-sizing: border-box;
  158. }
  159. .course-detail-date {
  160. font-size: 24rpx;
  161. line-height: 1.5;
  162. color: $morandi-text-muted;
  163. text-align: center;
  164. }
  165. .course-detail-intro {
  166. font-size: 28rpx;
  167. line-height: 1.55;
  168. color: $morandi-text-secondary;
  169. text-align: left;
  170. word-break: break-word;
  171. overflow-wrap: anywhere;
  172. }
  173. .course-detail-page.lang-bo {
  174. .course-detail-title {
  175. font-size: 32rpx;
  176. line-height: 1.75;
  177. letter-spacing: 2rpx;
  178. font-family: 'Noto Sans Tibetan', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  179. }
  180. .course-detail-date {
  181. font-size: 22rpx;
  182. line-height: 1.75;
  183. letter-spacing: 2rpx;
  184. font-family: 'Noto Sans Tibetan', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  185. }
  186. .course-detail-intro {
  187. font-size: 26rpx;
  188. line-height: 1.75;
  189. letter-spacing: 2rpx;
  190. font-family: 'Noto Sans Tibetan', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  191. }
  192. }
  193. </style>