feat: 修改 splitEnArticle 方法,修复'Do you always get up so late? It's one o'clock!' 断成两句,然后合并之后,后面的那句为空,但未被删除掉
This commit is contained in:
@@ -8,17 +8,21 @@
|
||||
"newWords": [],
|
||||
"textCustomTranslateIsFormat": true,
|
||||
"useTranslateType": "custom",
|
||||
"id": "HmlGhw"
|
||||
"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]]
|
||||
},
|
||||
{
|
||||
"title": "Breakfast or lunch?",
|
||||
"titleTranslate": "早餐还是午餐?",
|
||||
"text": "It was Sunday. I never get up early on Sundays. I sometimes stay in bed until lunchtime. Last Sunday I got up very late. I looked out of the window. It was dark outside. 'What a day!' I thought. 'It's raining again.' Just then, the telephone rang. It was my aunt Lucy. 'I've just arrived by train,' she said. 'I'm coming to see you.'\n 'But I'm still having breakfast,' I said.\n 'What are you doing?' she asked.\n 'I'm having breakfast,' I repeated.\n 'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'",
|
||||
"textCustomTranslate": "那是个星期天,\n而在星期天我是从来不早起的,\n有时我要一直躺到吃午饭的时候。\n上个星期天,我起得很晚。\n我望望窗外,外面一片昏暗。\n“鬼天气!”\n我想,“又下雨了。”\n正在这时,电话铃响了。\n是我姑母露西打来的。\n“我刚下火车,”她说,\n“我这就来看你。”\n\n“但我还在吃早饭,”我说。\n\n“你在干什么?”她问道。\n\n“我正在吃早饭,”我又说了一遍。\n\n“天啊,”她说,\n“你总是起得这么晚吗?",
|
||||
"text": "It was Sunday. I never get up early on Sundays. I sometimes stay in bed until lunchtime. Last Sunday I got up very late. I looked out of the window. It was dark outside. 'What a day!' I thought. 'It's raining again.' Just then, the telephone rang. It was my aunt Lucy. 'I've just arrived by train,' she said. 'I'm coming to see you.'\n\n 'But I'm still having breakfast,' I said.\n\n 'What are you doing?' she asked.\n\n 'I'm having breakfast,' I repeated.\n\n 'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'",
|
||||
"textCustomTranslate": "那是个星期天,\n而在星期天我是从来不早起的,\n有时我要一直躺到吃午饭的时候。\n上个星期天,我起得很晚。\n我望望窗外,\n外面一片昏暗。\n“鬼天气!”我想,\n“又下雨了。”正在这时,电话铃响了。\n是我姑母露西打来的。\n“我刚下火车,”她说,\n“我这就来看你。”\n\n“但我还在吃早饭,”我说。\n\n“你在干什么?”她问道。\n\n“我正在吃早饭,”我又说了一遍。\n\n“天啊,”她说,\n“你总是起得这么晚吗?现在已经1点钟了!",
|
||||
"textNetworkTranslate": "",
|
||||
"textCustomTranslateIsFormat": true,
|
||||
"useTranslateType": "custom",
|
||||
"newWords": [],
|
||||
"audioSrc": "/public/sound/article/nce2-1/2.mp3",
|
||||
"lrcPosition": [],
|
||||
"id": "1ao0Qx"
|
||||
},
|
||||
{
|
||||
|
||||
18
public/sound/article/nce2-1/2.lrc
Normal file
18
public/sound/article/nce2-1/2.lrc
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
[ti:<3A>¸<EFBFBD><C2B8><EFBFBD>Ӣ<EFBFBD><D3A2><EFBFBD>ڶ<EFBFBD><DAB6><EFBFBD>]
|
||||
[by:http://www.TingClass.com]
|
||||
[00:02.77]Lesson 2
|
||||
[00:04.08] Breakfast or lunch?
|
||||
[00:06.06]First listen and then answer the question:
|
||||
[00:10.85]Why was the writer's aunt surprised?
|
||||
[00:16.35]It was Sunday.I never get up early on Sundays.
|
||||
[00:21.54]I sometimes stay in bed until lunchtime.
|
||||
[00:27.48]Last Sunday I got up very late.
|
||||
[00:31.76]I looked out of the window. It was dark outside.
|
||||
[00:37.01]'What a day!'I thought. 'It's raining again.'
|
||||
[00:43.20]Just then,the telephone rang.It was my aunt Lucy.
|
||||
[00:49.33]'I've just arrived by train,' she said.'I'm coming to see you.'
|
||||
[00:56.43]'But I'm still having breakfast,'I said. 'What are you doing? she asked.
|
||||
[01:04.20]'I'm having breakfast,'I repeated.
|
||||
[01:08.00]'Dear me,'she said.
|
||||
[01:10.88] 'Do you always get up so late? It's one o'clock!'
|
||||
BIN
public/sound/article/nce2-1/2.mp3
Normal file
BIN
public/sound/article/nce2-1/2.mp3
Normal file
Binary file not shown.
@@ -50,6 +50,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
|
||||
let sentenceNlpList = []
|
||||
// console.log('ss', sentenceNlpList)
|
||||
doc.json().map(item => {
|
||||
|
||||
//如果整句大于15个单词以上,检测是否有 逗号子句
|
||||
if (item.terms.length > 15) {
|
||||
//正则匹配“逗号加and|but|so|because"
|
||||
@@ -71,13 +72,14 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
|
||||
})
|
||||
|
||||
sentenceNlpList.map(item => {
|
||||
let sentence: Sentence = {
|
||||
let sentence: Sentence = cloneDeep({
|
||||
//他没有空格,导致修改一行一行的数据时,汇总时全没有空格了,库无法正常断句
|
||||
text: item.text + ' ',
|
||||
// text: '',
|
||||
translate: '',
|
||||
words: [],
|
||||
}
|
||||
audioPosition: [],
|
||||
})
|
||||
section.push(sentence)
|
||||
|
||||
const checkQuote = (pre: string, index?: number) => {
|
||||
@@ -126,6 +128,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
|
||||
lastSentence.words = lastSentence.words.concat(sentence.words)
|
||||
lastSentence.words.push(word3)
|
||||
sentence.words = []
|
||||
//这里还不能直接删除sentence,因为后面还有一个 sentence.words = sentence.words.filter(v => v.word !== 'placeholder') 的判断
|
||||
// section.pop()
|
||||
}
|
||||
}
|
||||
@@ -218,6 +221,10 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
|
||||
|
||||
//去除空格占位符
|
||||
sentence.words = sentence.words.filter(v => v.word !== 'placeholder')
|
||||
//如果是空的,直接去掉
|
||||
if (!sentence.words.length) {
|
||||
section.pop()
|
||||
}
|
||||
})
|
||||
|
||||
// console.log(sentenceNlpList)
|
||||
|
||||
@@ -197,4 +197,11 @@ export function renewSectionTexts(article: Article) {
|
||||
let {newText, sections} = splitEnArticle(article.text)
|
||||
article.text = newText
|
||||
article.sections = sections
|
||||
if (article.lrcPosition.length) {
|
||||
article.sections.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
w.audioPosition = article.lrcPosition[(i * (article.sections[i - 1]||[]).length) + j]
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, onUnmounted} from "vue"
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {Article, ArticleWord, ShortcutKey, Word} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import VolumeSetting from "@/pages/pc/components/toolbar/VolumeSetting.vue";
|
||||
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
|
||||
import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
|
||||
import {useArticleOptions} from "@/hooks/dict.ts";
|
||||
import audio from '/public/sound/article/nce2-1/1.mp3'
|
||||
import {emitter} from "@/utils/eventBus.ts";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const {
|
||||
isArticleCollect,
|
||||
toggleArticleCollect
|
||||
} = useArticleOptions()
|
||||
|
||||
const emit = defineEmits<{
|
||||
ignore: [],
|
||||
wrong: [val: Word],
|
||||
nextWord: [val: ArticleWord],
|
||||
over: [],
|
||||
edit: [val: Article]
|
||||
}>()
|
||||
|
||||
function format(val: number, suffix: string = '', check: number = -1) {
|
||||
return val === check ? '-' : (val + suffix)
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!statisticsStore.total) return 0
|
||||
if (statisticsStore.index > statisticsStore.total) return 100
|
||||
return ((statisticsStore.index / statisticsStore.total) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="footer " :class="!settingStore.showToolbar && 'hide'">
|
||||
<div class="bottom">
|
||||
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.wrong, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<audio :src="audio" controls></audio>
|
||||
<div class="flex gap-3 center">
|
||||
<Tooltip
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="majesticons:eye-off-line"
|
||||
v-if="settingStore.dictation"
|
||||
@click="settingStore.dictation = false"/>
|
||||
<Icon icon="mdi:eye-outline"
|
||||
v-else
|
||||
@click="settingStore.dictation = true"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
<TranslateSetting/>
|
||||
|
||||
<VolumeSetting/>
|
||||
|
||||
<BaseIcon
|
||||
:title="`编辑(${settingStore.shortcutKeyMap[ShortcutKey.EditArticle]})`"
|
||||
icon="tabler:edit"
|
||||
@click="emitter.emit(ShortcutKey.EditArticle)"
|
||||
/>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isArticleCollect()"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star-fill"/>
|
||||
<BaseIcon
|
||||
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
icon="icon-park-outline:go-ahead"
|
||||
@click="emit('over')"/>
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
.footer {
|
||||
width: var(--article-width);
|
||||
margin-bottom: .8rem;
|
||||
transition: all var(--anim-time);
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -6rem;
|
||||
margin-top: 3rem;
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 1.8rem);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
padding: .2rem var(--space) .4rem var(--space);
|
||||
z-index: 2;
|
||||
border: 1px solid var(--color-item-border);
|
||||
box-shadow: var(--shadow);
|
||||
|
||||
.stat {
|
||||
margin-top: .5rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 2rem;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
width: 5rem;
|
||||
color: gray;
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: var(--color-sub-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
transition: all .3s;
|
||||
padding: 0 .6rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.el-progress-bar__inner) {
|
||||
background: var(--color-scrollbar);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -137,27 +137,10 @@ useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<PracticeArticle ref="practiceRef"/>
|
||||
<ArticleFooter/>
|
||||
</div>
|
||||
<PracticeArticle ref="practiceRef"/>
|
||||
<DictModal/>
|
||||
<Statistics/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {getSplitTranslateText} from "@/hooks/article.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {cloneDeep, last} from "lodash-es";
|
||||
import {watch, ref} from "vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {UploadProps, UploadUserFile} from "element-plus";
|
||||
@@ -72,7 +72,7 @@ watch(() => props.article, val => {
|
||||
}
|
||||
}
|
||||
renewSections()
|
||||
// console.log('ar', article)
|
||||
console.log('ar', editArticle)
|
||||
}, {immediate: true})
|
||||
|
||||
watch(() => editArticle.text, (s) => {
|
||||
@@ -238,86 +238,63 @@ function save(option: 'save' | 'saveAndNext') {
|
||||
//不知道为什么直接用editArticle,取到是空的默认值
|
||||
defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
|
||||
const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`The limit is 3, you selected ${files.length} files this time, add up to ${
|
||||
files.length + uploadFiles.length
|
||||
} totally`
|
||||
)
|
||||
}
|
||||
|
||||
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
|
||||
console.log(1)
|
||||
fileList.value.push({
|
||||
name: uploadFile.name,
|
||||
url: uploadFile.url,
|
||||
})
|
||||
}
|
||||
|
||||
function test() {
|
||||
let lrc = `[00:00.00]Lesson 1 A Private Conversation
|
||||
[00:04.35]First listen and then answer the question.
|
||||
[00:09.26]Why did the writer complain to the people behind him?
|
||||
[00:14.60]Last week I went to the theatre.
|
||||
[00:19.15]I had a very good seat.
|
||||
[00:22.03]The play was very interesting.
|
||||
[00:24.59]I did not enjoy it.
|
||||
[00:27.26]A young man and a young woman were sitting behind me.
|
||||
[00:31.65]They were talking loudly.
|
||||
[00:34.43]I got very angry.
|
||||
[00:36.98]I could not hear the actors.
|
||||
[00:40.36]I turned round.I looked at the man and the woman angrily.
|
||||
[00:46.59]They did not pay any attention.
|
||||
[00:50.65]In the end,I could not bear it.
|
||||
[00:54.57]I turned round again 'I can't hear a word!'I said angrily
|
||||
[01:02.98]'It's none of your business,'the young man said rudely.
|
||||
[01:08.85]'This is a private conversation!'
|
||||
`
|
||||
let lrcList = _parseLRC(lrc)
|
||||
console.log(lrcList)
|
||||
editArticle.sections.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
console.log(w)
|
||||
for (let k = 0; k < lrcList.length; k++) {
|
||||
let s = lrcList[k]
|
||||
let d = Comparison.default.cosine.similarity(w.text, s.text)
|
||||
d = Comparison.default.levenshtein.similarity(w.text, s.text)
|
||||
d = Comparison.default.longestCommonSubsequence.similarity(w.text, s.text)
|
||||
// d = Comparison.default.metricLcs.similarity(w.text, s.text)
|
||||
console.log(w.text, s.text, d)
|
||||
if (d >= 0.8) {
|
||||
w.audioPosition = [s.start, s.end ?? -1]
|
||||
w.test = s.text
|
||||
break
|
||||
}
|
||||
console.log(uploadFile)
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(uploadFile.raw, 'UTF-8');
|
||||
reader.onload = function (e) {
|
||||
let lrc: string = e.target.result as string;
|
||||
console.log(lrc)
|
||||
if (lrc.trim()) {
|
||||
let lrcList = _parseLRC(lrc)
|
||||
console.log('lrcList', lrcList)
|
||||
if (lrcList.length) {
|
||||
editArticle.lrcPosition = editArticle.sections.map((v, i) => {
|
||||
return v.map((w, j) => {
|
||||
for (let k = 0; k < lrcList.length; k++) {
|
||||
let s = lrcList[k]
|
||||
let d = Comparison.default.cosine.similarity(w.text, s.text)
|
||||
d = Comparison.default.levenshtein.similarity(w.text, s.text)
|
||||
d = Comparison.default.longestCommonSubsequence.similarity(w.text, s.text)
|
||||
// d = Comparison.default.metricLcs.similarity(w.text, s.text)
|
||||
// console.log(w.text, s.text, d)
|
||||
if (d >= 0.8) {
|
||||
w.audioPosition = [s.start, s.end ?? -1]
|
||||
break
|
||||
}
|
||||
}
|
||||
return w.audioPosition ?? []
|
||||
})
|
||||
}).flat()
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(editArticle.sections.flat())
|
||||
// console.log(cosine.similarity('Thanos', 'Rival'))
|
||||
}
|
||||
|
||||
|
||||
function s() {
|
||||
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let currentSentence = $ref<Sentence>({} as any)
|
||||
let editSentence = $ref<Sentence>({} as any)
|
||||
let showEditAudioDialog = $ref(false)
|
||||
let sentenceAudioRef = $ref<HTMLAudioElement>()
|
||||
let audioRef = $ref<HTMLAudioElement>()
|
||||
|
||||
function handleShowEditAudioDialog(val: Sentence) {
|
||||
function handleShowEditAudioDialog(val: Sentence, i: number, j: number) {
|
||||
showEditAudioDialog = true
|
||||
currentSentence = val
|
||||
if (!currentSentence.audioPosition?.length) {
|
||||
currentSentence.audioPosition = [0, 0]
|
||||
editSentence = cloneDeep(val)
|
||||
let pre = null
|
||||
if (j == 0) {
|
||||
if (i != 0) {
|
||||
pre = last(editArticle.sections[i - 1])
|
||||
}
|
||||
} else {
|
||||
pre = editArticle.sections[i][j - 1]
|
||||
}
|
||||
if (!editSentence.audioPosition?.length) {
|
||||
editSentence.audioPosition = [0, 0]
|
||||
if (pre) {
|
||||
editSentence.audioPosition = [pre.audioPosition[1] ?? 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,14 +302,14 @@ function recordStart() {
|
||||
if (sentenceAudioRef.paused) {
|
||||
sentenceAudioRef.play()
|
||||
}
|
||||
currentSentence.audioPosition[0] = Number(sentenceAudioRef.currentTime.toFixed(2))
|
||||
editSentence.audioPosition[0] = Number(sentenceAudioRef.currentTime.toFixed(2))
|
||||
}
|
||||
|
||||
function recordEnd() {
|
||||
if (!sentenceAudioRef.paused) {
|
||||
sentenceAudioRef.pause()
|
||||
}
|
||||
currentSentence.audioPosition[1] = Number(sentenceAudioRef.currentTime.toFixed(2))
|
||||
editSentence.audioPosition[1] = Number(sentenceAudioRef.currentTime.toFixed(2))
|
||||
}
|
||||
|
||||
let timer = -1
|
||||
@@ -354,6 +331,12 @@ function playSentenceAudio(sentence: Sentence, ref?: HTMLAudioElement) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveLrcPosition() {
|
||||
// showEditAudioDialog = false
|
||||
currentSentence.audioPosition = cloneDeep(editSentence.audioPosition)
|
||||
editArticle.lrcPosition = editArticle.sections.map((v, i) => v.map((w, j) => (w.audioPosition ?? []))).flat()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -455,21 +438,15 @@ function playSentenceAudio(sentence: Sentence, ref?: HTMLAudioElement) {
|
||||
<div class="center">正文、译文与结果均可编辑,修改一处,另外两处会自动同步变动</div>
|
||||
<div class="flex gap-2">
|
||||
<BaseButton>添加音频</BaseButton>
|
||||
<BaseButton @click="test">添加音频LRC</BaseButton>
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="upload-demo"
|
||||
:limit="1"
|
||||
:on-change="handleChange"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<el-button type="primary">Click to upload</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
jpg/png files with a size less than 500KB.
|
||||
</div>
|
||||
</template>
|
||||
<el-button type="primary">添加音频LRC文件</el-button>
|
||||
</el-upload>
|
||||
<audio ref="audioRef" :src="audio" controls></audio>
|
||||
<audio ref="audioRef" :src="editArticle.audioSrc" controls></audio>
|
||||
</div>
|
||||
<template v-if="editArticle.sections.length">
|
||||
<div class="flex-1 overflow-auto flex flex-col">
|
||||
@@ -499,10 +476,11 @@ function playSentenceAudio(sentence: Sentence, ref?: HTMLAudioElement) {
|
||||
<div class="text-base" v-if="sentence.audioPosition?.length">
|
||||
<span>{{ sentence.audioPosition?.[0] }}s</span>
|
||||
<span v-if="sentence.audioPosition?.[1] !== -1"> - {{ sentence.audioPosition?.[1] }}s</span>
|
||||
<span v-else> - 结束</span>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon :icon="sentence.audioPosition?.length ? 'basil:edit-outline' : 'basil:add-outline'"
|
||||
@click="handleShowEditAudioDialog(sentence)"/>
|
||||
@click="handleShowEditAudioDialog(sentence,indexI,indexJ)"/>
|
||||
<BaseIcon v-if="sentence.audioPosition?.length" icon="hugeicons:play"
|
||||
@click="playSentenceAudio(sentence,audioRef)"/>
|
||||
</div>
|
||||
@@ -532,43 +510,58 @@ function playSentenceAudio(sentence: Sentence, ref?: HTMLAudioElement) {
|
||||
</template>
|
||||
<Empty v-else text="没有译文对照~"/>
|
||||
</div>
|
||||
<Dialog title="音频"
|
||||
<Dialog title="设置音频与句子的对应位置(LRC)"
|
||||
v-model="showEditAudioDialog"
|
||||
:footer="true"
|
||||
@close="showEditAudioDialog = false"
|
||||
@ok="saveLrcPosition"
|
||||
>
|
||||
<div class="p-4 pt-0 color-black w-150 flex flex-col gap-2">
|
||||
<div>
|
||||
句子:{{ currentSentence.text }}
|
||||
句子:{{ editSentence.text }}
|
||||
</div>
|
||||
<audio ref="sentenceAudioRef" :src="audio" controls class="w-full"></audio>
|
||||
<audio ref="sentenceAudioRef" :src="editArticle.audioSrc" controls class="w-full"></audio>
|
||||
<div class="">
|
||||
使用方法:点击上方播放按钮,音频播放到句子开始时,点击 记录开始时间 按钮,播放到句子结束时,点击 记录开始时间 按钮
|
||||
使用方法:点击上方播放按钮,当音频播放到当前句子开始时,点击开始时间的 <span class="color-red">记录</span>
|
||||
按钮;当播放到句子结束时,点击结束时间的 <span class="color-red">记录</span> 按钮
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2 items-center">
|
||||
<BaseButton @click="recordStart">记录开始时间</BaseButton>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2 items-center">
|
||||
<div>开始时间:</div>
|
||||
<div class="flex space-between flex-1">
|
||||
<div class="flex items-center gap-1">
|
||||
<el-input-number v-model="currentSentence.audioPosition[0]" :precision="2" :step="0.1">
|
||||
<template #suffix>
|
||||
<span>s</span>
|
||||
</template>
|
||||
</el-input-number>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<BaseButton @click="recordEnd">记录结束时间</BaseButton>
|
||||
<div class="flex items-center gap-1">
|
||||
<el-input-number v-model="currentSentence.audioPosition[1]" :precision="2" :step="0.1">
|
||||
<el-input-number v-model="editSentence.audioPosition[0]" :precision="2" :step="0.1">
|
||||
<template #suffix>
|
||||
<span>s</span>
|
||||
</template>
|
||||
</el-input-number>
|
||||
</div>
|
||||
<BaseButton @click="recordStart">记录</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<BaseButton @click="playSentenceAudio(currentSentence,audioRef)">播放记录时间</BaseButton>
|
||||
<div class="flex gap-2 items-center">
|
||||
<div>结束时间:</div>
|
||||
<div class="flex space-between flex-1">
|
||||
<div class="flex items-center gap-1">
|
||||
<el-input-number v-model="editSentence.audioPosition[1]" :precision="2" :step="0.1">
|
||||
<template #suffix>
|
||||
<span>s</span>
|
||||
</template>
|
||||
</el-input-number>
|
||||
<span>或</span>
|
||||
<BaseButton size="small" @click="editSentence.audioPosition[1] = -1">结束</BaseButton>
|
||||
</div>
|
||||
<BaseButton @click="recordEnd">记录</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2" v-if="editSentence.audioPosition?.length">
|
||||
<div>
|
||||
lrc:<span>{{ editSentence.audioPosition?.[0] }}s</span>
|
||||
<span v-if="editSentence.audioPosition?.[1] !== -1"> - {{ editSentence.audioPosition?.[1] }}s</span>
|
||||
<span v-else> - 结束</span>
|
||||
</div>
|
||||
<BaseButton @click="playSentenceAudio(editSentence,audioRef)">试听</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
@@ -464,7 +464,7 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
<div class="text-2xl center">{{ props.article.titleTranslate }}</div>
|
||||
</header>
|
||||
<template v-if="getTranslateText(article).length">
|
||||
<div class="text-xl" v-for="t in getTranslateText(article)">{{ t }}</div>
|
||||
<div class="text-xl mb-4" v-for="t in getTranslateText(article)">{{ t }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -510,7 +510,7 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
|
||||
article {
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.5;
|
||||
line-height: 1.3;
|
||||
word-break: keep-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import TypingWord from "@/pages/pc/components/TypingWord.vue";
|
||||
import Panel from "../../components/Panel.vue";
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {renewSectionTexts, renewSectionTranslates} from "@/hooks/translate.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
@@ -26,9 +26,11 @@ import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {syncMyDictList, useArticleOptions} from "@/hooks/dict.ts";
|
||||
import {useArticleOptions} from "@/hooks/dict.ts";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import {useOnKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import VolumeSetting from "@/pages/pc/components/toolbar/VolumeSetting.vue";
|
||||
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
|
||||
|
||||
const store = useBaseStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
@@ -161,7 +163,7 @@ function getCurrentPractice() {
|
||||
}
|
||||
|
||||
function saveArticle(val: Article) {
|
||||
console.log('saveArticle', val)
|
||||
console.log('saveArticle', val, JSON.stringify(val.lrcPosition))
|
||||
showEditArticle = false
|
||||
let rIndex = store.currentArticleDict.articles.findIndex(v => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
@@ -300,101 +302,241 @@ useEvents([
|
||||
|
||||
defineExpose({getCurrentPractice})
|
||||
|
||||
const emit = defineEmits<{
|
||||
ignore: [],
|
||||
wrong: [val: Word],
|
||||
nextWord: [val: ArticleWord],
|
||||
over: [],
|
||||
edit: [val: Article]
|
||||
}>()
|
||||
|
||||
function format(val: number, suffix: string = '', check: number = -1) {
|
||||
return val === check ? '-' : (val + suffix)
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!statisticsStore.total) return 0
|
||||
if (statisticsStore.index > statisticsStore.total) return 100
|
||||
return ((statisticsStore.index / statisticsStore.total) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="practice-article">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-list" :class="`step${tabIndex}`">
|
||||
<div class="swiper-item">
|
||||
<TypingArticle
|
||||
ref="typingArticleRef"
|
||||
:active="tabIndex === 0"
|
||||
@edit="edit"
|
||||
@wrong="wrong"
|
||||
@over="skip"
|
||||
@nextWord="nextWord"
|
||||
:article="articleData.article"
|
||||
/>
|
||||
</div>
|
||||
<div class="swiper-item">
|
||||
<div class="typing-word-wrapper">
|
||||
<TypingWord
|
||||
@sort="sort"
|
||||
:words="wordData.words"
|
||||
:index="wordData.index"
|
||||
v-if="tabIndex === 1"
|
||||
<div class="practice-wrapper">
|
||||
<div class="practice-article">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-list" :class="`step${tabIndex}`">
|
||||
<div class="swiper-item">
|
||||
<TypingArticle
|
||||
ref="typingArticleRef"
|
||||
:active="tabIndex === 0"
|
||||
@edit="edit"
|
||||
@wrong="wrong"
|
||||
@over="skip"
|
||||
@nextWord="nextWord"
|
||||
:article="articleData.article"
|
||||
/>
|
||||
</div>
|
||||
<div class="swiper-item">
|
||||
<div class="typing-word-wrapper">
|
||||
<TypingWord
|
||||
@sort="sort"
|
||||
:words="wordData.words"
|
||||
:index="wordData.index"
|
||||
v-if="tabIndex === 1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div class="panel-wrapper">
|
||||
<Panel v-if="tabIndex === 0">
|
||||
<template v-slot="{active}">
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<BaseIcon title="切换词典"
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.currentArticleDict.name }}
|
||||
<Teleport to="body">
|
||||
<div class="panel-wrapper">
|
||||
<Panel v-if="tabIndex === 0">
|
||||
<template v-slot="{active}">
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<BaseIcon title="切换词典"
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.currentArticleDict.name }}
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentArticleDict.lastLearnIndex < articleData.articles.length - 1">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="right">
|
||||
{{ articleData.articles.length }}篇文章
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentArticleDict.lastLearnIndex < articleData.articles.length - 1">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="right">
|
||||
{{ articleData.articles.length }}篇文章
|
||||
</div>
|
||||
|
||||
<ArticleList
|
||||
:isActive="active"
|
||||
:static="false"
|
||||
:show-border="true"
|
||||
:show-translate="settingStore.translate"
|
||||
@title="e => emitter.emit(EventKey.openArticleContentModal,e.item)"
|
||||
@click="handleChangeChapterIndex"
|
||||
:active-id="articleData.article.id"
|
||||
:list="articleData.articles ">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
v-if="!isArticleCollect(item)"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect(item)"
|
||||
title="收藏" icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect(item)"
|
||||
title="取消收藏" icon="ph:star-fill"/>
|
||||
</template>
|
||||
</ArticleList>
|
||||
</div>
|
||||
</template>
|
||||
</Panel>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<ArticleList
|
||||
:isActive="active"
|
||||
:static="false"
|
||||
:show-border="true"
|
||||
:show-translate="settingStore.translate"
|
||||
@title="e => emitter.emit(EventKey.openArticleContentModal,e.item)"
|
||||
@click="handleChangeChapterIndex"
|
||||
:active-id="articleData.article.id"
|
||||
:list="articleData.articles ">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
v-if="!isArticleCollect(item)"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect(item)"
|
||||
title="收藏" icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect(item)"
|
||||
title="取消收藏" icon="ph:star-fill"/>
|
||||
</template>
|
||||
</ArticleList>
|
||||
<EditSingleArticleModal
|
||||
v-model="showEditArticle"
|
||||
:article="editArticle"
|
||||
@save="saveArticle"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer " :class="!settingStore.showToolbar && 'hide'">
|
||||
<div class="bottom">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.wrong, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Panel>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
<div>
|
||||
<audio :src="articleData.article.audioSrc" controls></audio>
|
||||
<div class="flex gap-3 center">
|
||||
<Tooltip
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="majesticons:eye-off-line"
|
||||
v-if="settingStore.dictation"
|
||||
@click="settingStore.dictation = false"/>
|
||||
<Icon icon="mdi:eye-outline"
|
||||
v-else
|
||||
@click="settingStore.dictation = true"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
<EditSingleArticleModal
|
||||
v-model="showEditArticle"
|
||||
:article="editArticle"
|
||||
@save="saveArticle"
|
||||
/>
|
||||
<TranslateSetting/>
|
||||
|
||||
<VolumeSetting/>
|
||||
|
||||
<BaseIcon
|
||||
:title="`编辑(${settingStore.shortcutKeyMap[ShortcutKey.EditArticle]})`"
|
||||
icon="tabler:edit"
|
||||
@click="emitter.emit(ShortcutKey.EditArticle)"
|
||||
/>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isArticleCollect()"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star-fill"/>
|
||||
<BaseIcon
|
||||
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
icon="icon-park-outline:go-ahead"
|
||||
@click="emit('over')"/>
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
.swiper-wrapper {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
@@ -435,4 +577,69 @@ defineExpose({getCurrentPractice})
|
||||
height: calc(100% - 1.2rem);
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: var(--article-width);
|
||||
margin-bottom: .8rem;
|
||||
transition: all var(--anim-time);
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -6rem;
|
||||
margin-top: 3rem;
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 1.8rem);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
padding: .2rem var(--space) .4rem var(--space);
|
||||
z-index: 2;
|
||||
border: 1px solid var(--color-item-border);
|
||||
box-shadow: var(--shadow);
|
||||
|
||||
.stat {
|
||||
margin-top: .5rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 2rem;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
width: 5rem;
|
||||
color: gray;
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: var(--color-sub-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
transition: all .3s;
|
||||
padding: 0 .6rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.el-progress-bar__inner) {
|
||||
background: var(--color-scrollbar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -118,7 +118,6 @@ export const DefaultBaseState = (): BaseState => ({
|
||||
index: 5, name: '已掌握', type: DictType.master, words: [], statistics: []
|
||||
},
|
||||
],
|
||||
|
||||
articleDictList: [
|
||||
{
|
||||
...getDefaultDict(),
|
||||
@@ -165,7 +164,6 @@ export const DefaultBaseState = (): BaseState => ({
|
||||
dictIndex: 0,
|
||||
},
|
||||
},
|
||||
|
||||
myDictList: [
|
||||
{
|
||||
...getDefaultDict(),
|
||||
@@ -337,7 +335,6 @@ export const useBaseStore = defineStore('base', {
|
||||
}))
|
||||
}
|
||||
console.log('this.currentArticleDict', this.currentArticleDict.articles[0])
|
||||
|
||||
}
|
||||
emitter.emit(EventKey.changeDict)
|
||||
resolve(true)
|
||||
|
||||
@@ -115,7 +115,9 @@ export interface Article {
|
||||
newWords: Word[],
|
||||
textAllWords: string[],
|
||||
sections: Sentence[][],
|
||||
useTranslateType: TranslateType
|
||||
useTranslateType: TranslateType,
|
||||
audioSrc: string,
|
||||
lrcPosition: number[][],
|
||||
}
|
||||
|
||||
export const DefaultArticle: Article = {
|
||||
@@ -130,7 +132,9 @@ export const DefaultArticle: Article = {
|
||||
newWords: [],
|
||||
textAllWords: [],
|
||||
sections: [],
|
||||
useTranslateType: TranslateType.custom
|
||||
useTranslateType: TranslateType.custom,
|
||||
audioSrc: '',
|
||||
lrcPosition: [],
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
|
||||
Reference in New Issue
Block a user