From 2d84928eb05f0f16ce9328b0c8722d202b33da77 Mon Sep 17 00:00:00 2001 From: zyronon Date: Mon, 19 May 2025 03:48:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=9F=B3=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/dicts/en/article/common/NCE_2.json | 6 +- src/hooks/article.ts | 32 ++++++++- .../pc/components/article/EditArticle.vue | 70 +++++++++++++------ .../practice-article/TypingArticle.vue | 41 ++++------- .../pc/practice/practice-article/index.vue | 12 +++- src/utils/index.ts | 2 +- 6 files changed, 108 insertions(+), 55 deletions(-) diff --git a/public/dicts/en/article/common/NCE_2.json b/public/dicts/en/article/common/NCE_2.json index 4830d25f..c9deee6d 100644 --- a/public/dicts/en/article/common/NCE_2.json +++ b/public/dicts/en/article/common/NCE_2.json @@ -10,7 +10,7 @@ "useTranslateType": "custom", "id": "HmlGhw", "audioSrc": "/public/sound/article/nce2-1/1.mp3", - "lrcPosition": [[14.6,19.15],[19.15,22.03],[22.03,24.59],[24.59,27.26],[27.26,31.65],[31.65,34.43],[34.43,36.98],[36.98,40.36],[40.7,42.47],[40.36,46.59],[46.59,50.65],[50.65,54.57],[55.03,56.84],[57.17,63],[62.98,68.85],[68.85,-1]] + "lrcPosition": [[15.6,19.11],[19.15,22.03],[22.03,24.59],[24.59,27.26],[27.26,31.65],[31.65,34.43],[34.43,36.98],[36.98,40.36],[40.7,42.47],[42.47,46.59],[46.59,50.65],[50.65,54.57],[55.03,56.84],[57.17,63],[62.98,68.85],[68.85,72.54]] }, { "title": "Breakfast or lunch?", @@ -22,7 +22,7 @@ "useTranslateType": "custom", "newWords": [], "audioSrc": "/public/sound/article/nce2-1/2.mp3", - "lrcPosition": [], + "lrcPosition": [[15.9,17.48],[17.75,21.51],[21.54,26.13],[26.58,30.47],[30.86,33.34],[33.34,35.68],[35.68,39.41],[39.41,45.64],[45.64,48.45],[48.45,53.01],[53.01,55.3],[55.3,60.11],[60.11,63.68],[63.4,67.15],[67.3,70.19],[69.98,75.54]], "id": "1ao0Qx" }, { @@ -34,6 +34,8 @@ "textCustomTranslateIsFormat": true, "useTranslateType": "custom", "newWords": [], + "audioSrc": "/public/sound/article/nce2-1/3.mp3", + "lrcPosition": [[16.07,19.99],[19.99,24.13],[24.13,28.69],[28.53,33.01],[33.01,35.69],[35.77,41.15],[41.67,45.43],[45.48,51.95],[52.55,57.01],[57.01,62.48],[62.48,66.62],[66.32,-1]], "id": "x1VwGU" }, { diff --git a/src/hooks/article.ts b/src/hooks/article.ts index 93ff1bde..a907fef0 100644 --- a/src/hooks/article.ts +++ b/src/hooks/article.ts @@ -2,6 +2,7 @@ import {Article, ArticleWord, DefaultArticleWord, DictType, Sentence, TranslateT import {cloneDeep} from "lodash-es"; import nlp from "compromise/one"; import {split} from "sentence-splitter"; +import {usePlayWordAudio} from "@/hooks/sound.ts"; interface KeyboardMap { Period: string, @@ -242,7 +243,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText: // console.log(sections) - text= sections.map(v => v.map(s => s.text.trim()).join('\n')).join('\n\n'); + text = sections.map(v => v.map(s => s.text.trim()).join('\n')).join('\n\n'); // console.log('s',text) return { //s.text.trim()的trim()不能去掉,因为这个方法会重复执行,要保证句子后面只有一个\n,不trim() \n就会累加 @@ -321,4 +322,33 @@ export function getTranslateText(article: Article) { } else { return [] } +} + +export function usePlaySentenceAudio() { + const playWordAudio = usePlayWordAudio() + let timer = $ref(0) + + function playSentenceAudio(sentence: Sentence, ref?: HTMLAudioElement, article?: Article) { + if (sentence.audioPosition?.length && article.audioSrc && ref) { + clearTimeout(timer) + if (ref.played) { + ref.pause() + } + let start = sentence.audioPosition[0]; + ref.currentTime = start + ref.play() + let end = sentence.audioPosition?.[1] + if (end && end !== -1) { + timer = setTimeout(() => { + ref.pause() + }, (end - start) * 1000) + } + } else { + playWordAudio(sentence.text) + } + } + + return { + playSentenceAudio + } } \ No newline at end of file diff --git a/src/pages/pc/components/article/EditArticle.vue b/src/pages/pc/components/article/EditArticle.vue index b9258a1b..f6107256 100644 --- a/src/pages/pc/components/article/EditArticle.vue +++ b/src/pages/pc/components/article/EditArticle.vue @@ -15,10 +15,10 @@ import { import {MessageBox} from "@/utils/MessageBox.tsx"; import {getSplitTranslateText} from "@/hooks/article.ts"; import {cloneDeep, last} from "lodash-es"; -import {watch, ref} from "vue"; +import {watch, ref, nextTick} from "vue"; import Empty from "@/components/Empty.vue"; import {UploadProps, UploadUserFile} from "element-plus"; -import {_copy, _parseLRC} from "@/utils"; +import {_copy, _nextTick, _parseLRC} from "@/utils"; import * as Comparison from "string-comparison" import audio from '/public/sound/article/nce2-1/1.mp3' import BaseIcon from "@/components/BaseIcon.vue"; @@ -274,6 +274,7 @@ const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => { let currentSentence = $ref({} as any) let editSentence = $ref({} as any) +let preSentence = $ref({} as any) let showEditAudioDialog = $ref(false) let sentenceAudioRef = $ref() let audioRef = $ref() @@ -282,20 +283,23 @@ function handleShowEditAudioDialog(val: Sentence, i: number, j: number) { showEditAudioDialog = true currentSentence = val editSentence = cloneDeep(val) - let pre = null + preSentence = null if (j == 0) { if (i != 0) { - pre = last(editArticle.sections[i - 1]) + preSentence = last(editArticle.sections[i - 1]) } } else { - pre = editArticle.sections[i][j - 1] + preSentence = editArticle.sections[i][j - 1] } if (!editSentence.audioPosition?.length) { editSentence.audioPosition = [0, 0] - if (pre) { - editSentence.audioPosition = [pre.audioPosition[1] ?? 0, 0] + if (preSentence) { + editSentence.audioPosition = [preSentence.audioPosition[1] ?? 0, 0] } } + _nextTick(() => { + sentenceAudioRef.currentTime = editSentence.audioPosition[0] + }) } function recordStart() { @@ -337,6 +341,16 @@ function saveLrcPosition() { currentSentence.audioPosition = cloneDeep(editSentence.audioPosition) editArticle.lrcPosition = editArticle.sections.map((v, i) => v.map((w, j) => (w.audioPosition ?? []))).flat() } + +function jumpAudio(time: number) { + sentenceAudioRef.currentTime = time +} + +function setPreEndTimeToCurrentStartTime() { + if (preSentence) { + editSentence.audioPosition[0] = preSentence.audioPosition[1] + } +}