四零语境前端代码仓库
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.

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