四零语境前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
4.8 KiB

  1. <template>
  2. <view class="custom-tabbar" :class="{ 'tabbar-hidden': !showNavbar }">
  3. <!-- 音频控制栏组件 -->
  4. <AudioControls
  5. :current-page="currentPage"
  6. :course-id="courseId"
  7. :voice-id="voiceId"
  8. :book-pages="bookPages"
  9. :is-text-page="isTextPage"
  10. :should-load-audio="shouldLoadAudio"
  11. :is-member="isMember"
  12. :current-page-requires-member="currentPageRequiresMember"
  13. :page-pay="pagePay"
  14. :is-word-audio-playing="isWordAudioPlaying"
  15. @previous-page="previousPage"
  16. @next-page="nextPage"
  17. @audio-state-change="onAudioStateChange"
  18. @highlight-change="onHighlightChange"
  19. @voice-change-complete="onVoiceChangeComplete"
  20. @voice-change-error="onVoiceChangeError"
  21. ref="audioControls"
  22. />
  23. <view style="background-color: #fff;position: relative;z-index: 100">
  24. <view class="tabbar-content">
  25. <view class="tabbar-left">
  26. <view class="tab-button" @click="toggleCoursePopup">
  27. <image src="/static/course-icon.png" class="tab-icon" />
  28. <text class="tab-text">课程</text>
  29. </view>
  30. <view class="tab-button" @click="toggleSound">
  31. <image src="/static/voice-switch-icon.png" class="tab-icon" />
  32. <text class="tab-text">音色切换</text>
  33. </view>
  34. </view>
  35. <view class="tabbar-right">
  36. <view class="page-controls">
  37. <view class="page-numbers">
  38. <view
  39. v-for="(page, index) in bookPages"
  40. :key="index"
  41. class="page-number"
  42. :class="{ 'active': (index + 1) === currentPage }"
  43. @click="goToPage(index + 1)"
  44. >
  45. {{ index + 1 }}
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. <uv-safe-bottom></uv-safe-bottom>
  52. </view>
  53. </view>
  54. </template>
  55. <script>
  56. import AudioControls from '../AudioControls.vue'
  57. export default {
  58. name: 'CustomTabbar',
  59. components: {
  60. AudioControls
  61. },
  62. props: {
  63. showNavbar: {
  64. type: Boolean,
  65. default: true
  66. },
  67. currentPage: {
  68. type: Number,
  69. default: 1
  70. },
  71. courseId: {
  72. type: String,
  73. default: ''
  74. },
  75. voiceId: {
  76. type: Number,
  77. default: null
  78. },
  79. bookPages: {
  80. type: Array,
  81. default: () => []
  82. },
  83. isTextPage: {
  84. type: Boolean,
  85. default: false
  86. },
  87. shouldLoadAudio: {
  88. type: Boolean,
  89. default: false
  90. },
  91. isMember: {
  92. type: Boolean,
  93. default: false
  94. },
  95. currentPageRequiresMember: {
  96. type: Boolean,
  97. default: false
  98. },
  99. pagePay: {
  100. type: Array,
  101. default: () => []
  102. },
  103. isWordAudioPlaying: {
  104. type: Boolean,
  105. default: false
  106. }
  107. },
  108. methods: {
  109. toggleCoursePopup() {
  110. this.$emit('toggle-course-popup')
  111. },
  112. toggleSound() {
  113. this.$emit('toggle-sound')
  114. },
  115. goToPage(pageNumber) {
  116. this.$emit('go-to-page', pageNumber)
  117. },
  118. previousPage() {
  119. this.$emit('previous-page')
  120. },
  121. nextPage() {
  122. this.$emit('next-page')
  123. },
  124. onAudioStateChange(audioState) {
  125. this.$emit('audio-state-change', audioState)
  126. },
  127. onHighlightChange(highlightData) {
  128. this.$emit('highlight-change', highlightData)
  129. },
  130. onVoiceChangeComplete(data) {
  131. this.$emit('voice-change-complete', data)
  132. },
  133. onVoiceChangeError(error) {
  134. this.$emit('voice-change-error', error)
  135. }
  136. }
  137. }
  138. </script>
  139. <style lang="scss" scoped>
  140. .custom-tabbar {
  141. position: fixed;
  142. bottom: 0;
  143. left: 0;
  144. right: 0;
  145. border-top: 1rpx solid #EEEEEE;
  146. z-index: 1000;
  147. transition: transform 0.3s ease;
  148. &.tabbar-hidden {
  149. transform: translateY(100%);
  150. }
  151. }
  152. .tabbar-content {
  153. display: flex;
  154. align-items: center;
  155. justify-content: space-between;
  156. padding: 24rpx 62rpx;
  157. height: 88rpx;
  158. }
  159. .tabbar-left {
  160. display: flex;
  161. align-items: center;
  162. gap: 35rpx;
  163. }
  164. .tab-button {
  165. display: flex;
  166. align-items: center;
  167. flex-direction: column;
  168. gap: 8rpx;
  169. }
  170. .tab-icon {
  171. width: 52rpx;
  172. height: 52rpx;
  173. }
  174. .tab-text {
  175. font-family: PingFang SC;
  176. font-size: 22rpx;
  177. color: #999;
  178. line-height: 24rpx;
  179. }
  180. .tabbar-right {
  181. flex: 1;
  182. display: flex;
  183. justify-content: flex-end;
  184. }
  185. .page-controls {
  186. display: flex;
  187. align-items: center;
  188. }
  189. .page-numbers {
  190. display: flex;
  191. align-items: center;
  192. gap: 8rpx;
  193. overflow-x: auto;
  194. max-width: 400rpx;
  195. &::-webkit-scrollbar {
  196. display: none;
  197. }
  198. }
  199. .page-number {
  200. min-width: 84rpx;
  201. height: 58rpx;
  202. display: flex;
  203. align-items: center;
  204. justify-content: center;
  205. border-radius: 100rpx;
  206. font-family: PingFang SC;
  207. font-size: 30rpx;
  208. color: #3B3D3D;
  209. background-color: transparent;
  210. border: 1px solid #3B3D3D;
  211. transition: all 0.3s ease;
  212. &.active {
  213. border: 1px solid #06DADC;
  214. color: #06DADC;
  215. }
  216. }
  217. </style>