AudioPreview.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <template>
  2. <div ref="audioArea" class="audio-area" :style="getAreaStyle()">
  3. <SerialNumberPosition :property="data.property" />
  4. <div ref="audioAreaBox" class="main">
  5. <ul v-if="'independent' === data.property.view_method" class="view-independent">
  6. <li v-for="(file, i) in data.file_list" :key="i">
  7. <AudioPlay
  8. view-size="middle"
  9. :file-id="file.file_id"
  10. :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
  11. :audio-index="i"
  12. />
  13. </li>
  14. </ul>
  15. <ul v-else-if="'icon' === data.property.view_method" class="view-icon">
  16. <li v-for="(file, i) in data.file_list" :key="i">
  17. <AudioPlay view-size="small" :file-id="file.file_id" :audio-index="i" />
  18. </li>
  19. </ul>
  20. <div v-else class="view-list">
  21. <el-carousel
  22. ref="audio_carousel"
  23. indicator-position="none"
  24. direction="vertical"
  25. :autoplay="false"
  26. :interval="0"
  27. >
  28. <el-carousel-item v-for="(file, i) in data.file_list" :key="i">
  29. <AudioPlay
  30. view-size="big"
  31. :file-id="file.file_id"
  32. :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
  33. :show-slider="true"
  34. :cur-audio-index="curAudioIndex"
  35. @changeFile="changeFile"
  36. />
  37. </el-carousel-item>
  38. </el-carousel>
  39. <div :style="{ height: elementHeight - 140 + 'px', overflowY: viewScroll }">
  40. <ul class="view-list-bottom">
  41. <li v-for="(file, i) in data.file_list" :key="i" @click="handleAudioClick(i)">
  42. <AudioPlay
  43. view-size="list"
  44. :file-id="file.file_id"
  45. :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
  46. :show-slider="false"
  47. :audio-index="i"
  48. :cur-audio-index="curAudioIndex"
  49. />
  50. </li>
  51. </ul>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. </template>
  57. <script>
  58. import { getAudioData } from '@/views/book/courseware/data/audio';
  59. import PreviewMixin from '../common/PreviewMixin';
  60. import AudioPlay from '../common/AudioPlay.vue';
  61. export default {
  62. name: 'AudioPreview',
  63. components: { AudioPlay },
  64. mixins: [PreviewMixin],
  65. data() {
  66. return {
  67. data: getAudioData(),
  68. curAudioIndex: 0,
  69. elementWidth: 0,
  70. elementHeight: 0,
  71. fileLen: 0,
  72. viewScroll: 'hidden',
  73. elementID: '',
  74. observersMap: {},
  75. };
  76. },
  77. watch: {
  78. data: {
  79. handler(val) {
  80. this.fileLen = val.file_list.length;
  81. if (this.fileLen > 0 && this.data.property.view_method === 'list') {
  82. const ele = this.$refs.audioAreaBox;
  83. this.elementWidth = ele.clientWidth;
  84. this.elementHeight = ele.clientHeight;
  85. const mainEle = this.$refs.audioArea;
  86. // 检查元素是否包含已知的类名
  87. mainEle.classList.forEach((className) => {
  88. // 排除已知的类名
  89. if (className !== 'audio-area') {
  90. // 打印另一个类名
  91. this.elementID = className;
  92. }
  93. });
  94. }
  95. },
  96. deep: true,
  97. },
  98. elementWidth(newWidth) {
  99. if (this.data.property.view_method === 'list') this.elementWidth = newWidth;
  100. },
  101. elementHeight(newHeight) {
  102. if (this.data.property.view_method === 'list') {
  103. this.elementHeight = newHeight;
  104. this.isViewScroll();
  105. }
  106. },
  107. },
  108. mounted() {
  109. this.$nextTick(() => {
  110. const canvasElement = document.querySelector('.canvas');
  111. if (!canvasElement) return;
  112. const instanceName = `observer_${this.elementID}`;
  113. this.observersMap[instanceName] = new ResizeObserver((entries) => {
  114. for (let entry of entries) {
  115. this.elementWidth = entry.contentRect.width;
  116. this.elementHeight = entry.contentRect.height;
  117. }
  118. });
  119. this.observersMap[instanceName].observe(this.$el);
  120. });
  121. },
  122. beforeDestroy() {
  123. Object.values(this.observersMap).forEach((observer) => {
  124. observer.disconnect();
  125. });
  126. },
  127. methods: {
  128. handleAudioClick(index) {
  129. // 获取 Carousel 实例
  130. const carousel = this.$refs.audio_carousel;
  131. // 切换到对应索引的文件
  132. carousel.setActiveItem(index);
  133. this.curAudioIndex = index;
  134. },
  135. changeFile(type) {
  136. // 获取 Carousel 实例
  137. const carousel = this.$refs.audio_carousel;
  138. // 切换到对应索引的文件
  139. if (type === 'prev') {
  140. carousel.prev();
  141. this.curAudioIndex += -1;
  142. } else {
  143. carousel.next();
  144. this.curAudioIndex += 1;
  145. }
  146. if (this.curAudioIndex >= this.data.file_id_list.length) {
  147. this.curAudioIndex = 0;
  148. }
  149. if (this.curAudioIndex < 0) {
  150. this.curAudioIndex = this.data.file_id_list.length - 1;
  151. }
  152. },
  153. isViewScroll() {
  154. const ulHeight = this.fileLen * 44;
  155. if (ulHeight > this.elementHeight - 140) {
  156. this.viewScroll = 'scroll';
  157. } else {
  158. this.viewScroll = 'hidden';
  159. }
  160. },
  161. },
  162. };
  163. </script>
  164. <style lang="scss" scoped>
  165. .audio-area {
  166. display: grid;
  167. gap: 6px;
  168. padding: 8px;
  169. > .main {
  170. margin: 4px auto;
  171. > span {
  172. display: flex;
  173. }
  174. }
  175. .main {
  176. grid-area: main;
  177. width: 100%;
  178. .view-independent {
  179. display: flex;
  180. flex-wrap: wrap;
  181. gap: 16px;
  182. > li {
  183. min-width: 280px;
  184. }
  185. }
  186. .view-icon {
  187. display: flex;
  188. flex-wrap: wrap;
  189. gap: 16px 50px;
  190. }
  191. .view-list {
  192. display: flex;
  193. flex-direction: column;
  194. :deep .el-carousel {
  195. flex: 1;
  196. background-color: #f8f8f8;
  197. border-radius: 4px;
  198. &__container {
  199. height: 128px;
  200. }
  201. &__item {
  202. transition: none !important;
  203. }
  204. }
  205. .view-list-bottom {
  206. display: flex;
  207. flex-direction: column;
  208. li {
  209. border-top: 1px solid #ccc;
  210. }
  211. }
  212. }
  213. }
  214. }
  215. </style>