index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <template>
  2. <view class="word-view-page" :style="{backgroundImage: 'url(' + courseBjFun() + ')'}">
  3. <view class="icon-title-navBar-box">
  4. <view @click="handleBack" class="nav-bar-icon"></view>
  5. <text class="nav-bar-title">学习</text>
  6. </view>
  7. <view class="ezy-tab-border">
  8. <template v-for="item in data.wordList">
  9. <mainCardVue :active-word="data.activeWord" :pageData="data" :active-words="activeWords"
  10. @play-audio="handlePlayAudio" @goXiangjie="goXiangjie" @swiper-change="handleSwiperChange"
  11. :is-playing="isAudioPlaying" v-if="item.id == data.activeId" :key="item.id">
  12. </mainCardVue>
  13. </template>
  14. </view>
  15. <view class="word-view-bottom">
  16. <view class="collect-btn" v-if="userCode != 'Visitor'" @click="handleShouCang">
  17. <icon :class="{active: data.collectFlag}"></icon>
  18. <view>收藏</view>
  19. </view>
  20. <view class="bottom-btn-box">
  21. <view class="word-view-btn" @click="prevWord" v-if="!isFirst&&isLearnRecord!=0">上一词</view>
  22. <view class="word-view-btn" @click="nextWord" v-if="!isLast&&isLearnRecord!=0">下一词</view>
  23. <view class="word-view-btn" v-if="isLast&&isLearnRecord!=0" @click="handleComplete">完成</view>
  24. </view>
  25. </view>
  26. </view>
  27. </template>
  28. <script setup>
  29. import mainCardVue from "./components/mainCard.vue";
  30. import {
  31. ref,
  32. reactive,
  33. computed,
  34. nextTick,
  35. onUnmounted
  36. } from "vue";
  37. import {
  38. onLoad
  39. } from "@dcloudio/uni-app";
  40. import * as httpApi from "@/api/word.js"
  41. import {
  42. getUserIdentity,
  43. } from "@/utils/common.js"
  44. import {
  45. useAudioCache,
  46. audioPlayer
  47. } from "./components/useAudio.js"
  48. import {
  49. getWordCancel
  50. } from "../../api/word";
  51. import cacheManager from "../../utils/cacheManager";
  52. const userCode = getUserIdentity();
  53. const {
  54. cacheAudio,
  55. clearAudioCache
  56. } = useAudioCache();
  57. const AudioP = new audioPlayer();
  58. const isLearnRecord = ref(null)
  59. const isLearnStatus = ref(null)
  60. const isAudioPlaying = ref(false) // 音频播放状态
  61. // 监听音频播放事件
  62. uni.$on('danci-audio-play', updateAudioPlayingTrue)
  63. uni.$on('danci-audio-ended', updateAudioPlayingFalse)
  64. onUnmounted(() => {
  65. uni.$off('danci-audio-play',updateAudioPlayingTrue)
  66. uni.$off('danci-audio-ended',updateAudioPlayingFalse)
  67. })
  68. function updateAudioPlayingFalse() {
  69. isAudioPlaying.value = false
  70. }
  71. function updateAudioPlayingTrue() {
  72. isAudioPlaying.value = true
  73. }
  74. function handleSwiperChange(index) {
  75. return false
  76. // 客户暂时不要
  77. if (isAudioPlaying.value) return; // 如果正在播放,不允许切换
  78. // 无论切换到哪个页面,都播放单词的主发音(yinpin)
  79. if (data.activeWord && data.activeWord.yinpin) {
  80. handlePlayAudio({
  81. url: data.activeWord.yinpin,
  82. code: 'swiper-auto-play'
  83. });
  84. }
  85. }
  86. function courseBjFun() {
  87. return 'static/images/course/course-cjdc-bj.png'
  88. }
  89. function chunkArray(arr, chunkSize) {
  90. const result = [];
  91. for (let i = 0; i < arr.length; i += chunkSize) {
  92. result.push(arr.slice(i, i + chunkSize));
  93. }
  94. return result;
  95. }
  96. const data = reactive({
  97. jieId: null, // 节/单元ID
  98. activeId: null, // 当前单词
  99. wordList: [], // 单词列表
  100. arrayList: [], // 整合数据
  101. activeWord: null, // 单词详情数据
  102. collectFlag: 0, // 收藏状态
  103. subjectId: null, // 学科ID
  104. levelId: null, // 等级
  105. typeId: null, // 类型
  106. tipFlag: null, // 提示
  107. youkeZhangId: null, // 章ID
  108. isLearnStatus: null, // 学习记录状态
  109. })
  110. onLoad(({
  111. jieId,
  112. wordId,
  113. subjectId,
  114. levelId,
  115. typeId,
  116. tipFlag,
  117. isLearnStatus,
  118. youkeZhangId
  119. }) => {
  120. data.jieId = jieId;
  121. isLearnRecord.value = jieId;
  122. data.activeId = wordId;
  123. data.isLearnStatus = isLearnStatus;
  124. data.subjectId = subjectId;
  125. data.levelId = levelId;
  126. data.typeId = typeId;
  127. data.tipFlag = tipFlag;
  128. data.youkeZhangId = youkeZhangId;
  129. // 获取单词列表数据
  130. initWordInfo();
  131. // 清理过期文件
  132. clearAudioCache();
  133. })
  134. // 是否是最后一题
  135. const isLast = computed(() => {
  136. if (!data.wordList.length) {
  137. return false
  138. }
  139. return data.activeId == data.wordList[data.wordList.length - 1].id;
  140. })
  141. const isFirst = computed(() => {
  142. if (!data.wordList.length) {
  143. return false
  144. }
  145. return data.activeId == data.wordList[0].id;
  146. })
  147. const activeWords = computed(() => {
  148. if (!data.activeId) {
  149. return null
  150. }
  151. return data.arrayList.find(item => item.some(cit => cit.id == data.activeId))
  152. })
  153. function nextWord() {
  154. const index = data.wordList.findIndex(item => item.id == data.activeId);
  155. if (index < data.wordList.length - 1) {
  156. if (userCode !== 'Visitor') {
  157. uni.redirectTo({
  158. url: `/pages/newEnglish/index?jieId=${data.jieId}&wordId=${data.wordList[index + 1].id }`
  159. })
  160. } else {
  161. uni.redirectTo({
  162. url: `/pages/newEnglish/index?jieId=${data.jieId}&wordId=${data.wordList[index + 1].id }&levelId=${data.levelId}&typeId=${data.typeId}&subjectId=${data.subjectId}&tipFlag=${data.tipFlag}&youkeZhangId=${data.youkeZhangId}`
  163. })
  164. }
  165. }
  166. }
  167. function prevWord() {
  168. const index = data.wordList.findIndex(item => item.id == data.activeId);
  169. if (index > 0) {
  170. if (userCode !== 'Visitor') {
  171. uni.redirectTo({
  172. url: `/pages/newEnglish/index?jieId=${data.jieId}&wordId=${data.wordList[index - 1].id }`
  173. })
  174. } else {
  175. uni.redirectTo({
  176. url: `/pages/newEnglish/index?jieId=${data.jieId}&wordId=${data.wordList[index - 1].id }&levelId=${data.levelId}&typeId=${data.typeId}&subjectId=${data.subjectId}&tipFlag=${data.tipFlag}&youkeZhangId=${data.youkeZhangId}`
  177. })
  178. }
  179. }
  180. }
  181. function handleBack() {
  182. // 返回单词列表
  183. if (userCode !== 'Visitor') {
  184. if (data.jieId == 0) {
  185. uni.redirectTo({
  186. url: `/pages/my/learnRecord?jieId=${data.jieId}&isLearnStatus=${data.isLearnStatus}&levelId=${data.levelId}&typeId=${data.typeId}&subjectId=${data.subjectId}&tipFlag=${data.tipFlag}&youkeZhangId=${data.youkeZhangId}`
  187. })
  188. } else {
  189. uni.redirectTo({
  190. url: `/pages/wordList/wordList?jieId=${data.jieId}`
  191. })
  192. }
  193. } else {
  194. const youkePageData = JSON.stringify({
  195. subjectId: data.subjectId,
  196. levelId: data.levelId,
  197. typeId: data.typeId,
  198. tipFlag: data.tipFlag,
  199. youkeZhangId: data.youkeZhangId,
  200. jieId: data.jieId
  201. })
  202. uni.redirectTo({
  203. url: `/pages/wordList/wordList?youkePageData=${youkePageData}`
  204. })
  205. }
  206. }
  207. function handleComplete() {
  208. // 返回岛
  209. if (userCode !== 'Visitor') {
  210. // let jieInfo = cacheManager.getUnitInfo('zhangInfo', data.jieId);
  211. // if (jieInfo.lastFlag == 1) {
  212. // // 更新岛状态
  213. // cacheManager.updateObject('zhangInfo', {
  214. // curZid: jieInfo.nextZid
  215. // })
  216. // }
  217. uni.redirectTo({
  218. url: `/pages/study/index`
  219. })
  220. } else {
  221. uni.redirectTo({
  222. url: `/pages/study/index?levelId=${data.levelId}&typeId=${data.typeId}&subjectId=${data.subjectId}&tipFlag=${data.tipFlag}&youkeZhangId=${data.youkeZhangId}&jieId=${data.jieId}`
  223. })
  224. }
  225. }
  226. function handleShouCang() {
  227. if (data.collectFlag == 1) {
  228. httpApi.getWordCancel({
  229. wordId: data.activeId
  230. }).then(res => {
  231. uni.showToast({
  232. title: '操作成功'
  233. })
  234. data.collectFlag = 0;
  235. })
  236. } else {
  237. httpApi.getWordShouCang({
  238. wordId: data.activeId
  239. }).then(res => {
  240. uni.showToast({
  241. title: '操作成功'
  242. })
  243. data.collectFlag = 1;
  244. })
  245. }
  246. }
  247. function initWordInfo() {
  248. if (userCode !== 'Visitor') {
  249. httpApi.getWordInfo({
  250. jieId: data.jieId,
  251. wordId: data.activeId
  252. }).then(res => {
  253. data.activeWord = res.data;
  254. data.wordList = res.data.danciList; // 单词组
  255. data.collectFlag = res.data.collectFlag; // 收藏状态
  256. if (data.wordList.length) {
  257. data.arrayList = chunkArray(data.wordList, 5); // 将1维单词数组转换为2维数组
  258. }
  259. // console.log('res.data',res.data)
  260. handleCacheAudio()
  261. //autoPlayAudio()
  262. })
  263. } else {
  264. httpApi.getCommonWordInfo({
  265. jieId: data.jieId,
  266. wordId: data.activeId
  267. }).then(res => {
  268. data.activeWord = res.data;
  269. data.wordList = res.data.danciList; // 单词组
  270. data.collectFlag = res.data.collectFlag; // 收藏状态
  271. if (data.wordList.length) {
  272. data.arrayList = chunkArray(data.wordList, 5); // 将1维单词数组转换为2维数组
  273. }
  274. handleCacheAudio()
  275. // autoPlayAudio()
  276. })
  277. }
  278. }
  279. // 新增自动播放功能
  280. const autoPlayAudio = async () => {
  281. // 等待数据加载完成
  282. await nextTick();
  283. // 检查是否有单词发音数据
  284. if (data.activeWord && data.activeWord.yinpin) {
  285. // 延迟500ms确保音频已缓存
  286. setTimeout(() => {
  287. handlePlayAudio({
  288. url: data.activeWord.yinpin,
  289. code: 'auto-play' // 特殊标记,区分自动播放
  290. });
  291. }, 500);
  292. }
  293. };
  294. function goXiangjie() {
  295. uni.redirectTo({
  296. url: `/pages/newEnglish/components/xiangjie?levelId=${data.levelId}&typeId=${data.typeId}&subjectId=${data.subjectId}&tipFlag=${data.tipFlag}&wordId=${data.activeId}&youkeZhangId=${data.youkeZhangId}&jieId=${data.jieId}`
  297. })
  298. }
  299. function handleCacheAudio() {
  300. const arr = []
  301. // yinpin
  302. arr.push(data.activeWord.yinpin);
  303. // yinjie
  304. data.activeWord.yinjie.forEach(item => {
  305. arr.push(item.yinpin)
  306. })
  307. // pindu
  308. data.activeWord.pindu.forEach(item => {
  309. arr.push(item.yinpin)
  310. })
  311. // 缓存音频
  312. arr.map(item => cacheAudio(item));
  313. // console.log('arr',arr)
  314. }
  315. async function handlePlayAudio({
  316. url,
  317. code
  318. }) {
  319. console.log('播放', url)
  320. console.log('播放', code)
  321. const cachedPath = await cacheAudio(url);
  322. if (cachedPath && cachedPath.includes('.mp3')) {
  323. // console.log('地址:', cachedPath)
  324. AudioP.play(cachedPath, code);
  325. } else {
  326. uni.showToast({
  327. title: '音频加载失败',
  328. icon: 'none'
  329. });
  330. }
  331. }
  332. </script>
  333. <style>
  334. </style>