This commit is contained in:
zyronon
2025-09-19 01:38:40 +08:00
parent 107a9fed45
commit 8069c9e14c
37 changed files with 20171 additions and 311 deletions

View File

@@ -11,7 +11,7 @@ import BaseButton from "@/components/BaseButton.vue";
import {useRoute, useRouter} from "vue-router";
import EditBook from "@/pages/pc/article/components/EditBook.vue";
import {computed, onMounted} from "vue";
import {_getDictDataByUrl, cloneDeep, useNav} from "@/utils";
import {_dateFormat, _getDictDataByUrl, cloneDeep, msToHourMinute, total, useNav} from "@/utils";
import BaseIcon from "@/components/BaseIcon.vue";
import {useArticleOptions} from "@/hooks/dict.ts";
import {getDefaultArticle, getDefaultDict} from "@/types/func.ts";
@@ -102,7 +102,6 @@ function reset() {
'继续此操作会重置所有文章,并从官方书籍获取最新文章列表,学习记录不会被重置。确认恢复默认吗?',
'恢复默认',
async () => {
debugger
let dict = book_list.flat().find(v => v.url === runtimeStore.editDict.url) as Dict
if (dict && dict.id) {
dict = await _getDictDataByUrl(dict, DictType.article)
@@ -124,6 +123,21 @@ function reset() {
}
)
}
const currentPractice = $computed(() => {
if (runtimeStore.editDict.statistics?.length) {
return runtimeStore.editDict.statistics.filter(v => v.title === selectArticle.title)
}
return []
})
const totalSpend = $computed(() => {
if (runtimeStore.editDict.statistics?.length) {
return msToHourMinute(total(runtimeStore.editDict.statistics,'spend'))
}
return 0
})
</script>
<template>
@@ -142,6 +156,8 @@ function reset() {
</div>
</div>
<div class="text-lg ">介绍{{ runtimeStore.editDict.description }}</div>
<div class="text-base " v-if="totalSpend">总学习时长{{ totalSpend }}</div>
<div class="line my-3"></div>
<div class="flex flex-1 overflow-hidden">
@@ -166,6 +182,12 @@ function reset() {
</div>
<div class="right flex-[4] shrink-0 pl-4 overflow-auto">
<div v-if="selectArticle.id">
<div class="font-family text-base mb-4" v-if="currentPractice.length">
<div>学习记录{{ msToHourMinute(total(currentPractice, 'spend'))}}</div>
<div class="item" v-for="i in currentPractice">
{{_dateFormat(i.startDate,'YYYY-MM-DD HH-mm')}}: {{ msToHourMinute(i.spend) }}
</div>
</div>
<div class="en-article-family title text-xl">
<div class="text-center text-2xl my-2">
<ArticleAudio :article="selectArticle"></ArticleAudio>

View File

@@ -8,7 +8,7 @@ import {Article, ArticleItem, ArticleWord, Dict, DictType, ShortcutKey, Statisti
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
import {_getDictDataByUrl, cloneDeep} from "@/utils";
import {_getDictDataByUrl, cloneDeep, msToHourMinute, msToMinute, total} from "@/utils";
import {usePracticeStore} from "@/stores/practice.ts";
import {useArticleOptions} from "@/hooks/dict.ts";
import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
@@ -176,6 +176,10 @@ function complete() {
window.umami?.track('studyWordArticle', reportData)
store.sbook.statistics.push(data as any)
console.log(data, reportData)
//重置
statStore.wrong = 0
statStore.startDate = Date.now()
}
function getCurrentPractice() {
@@ -308,6 +312,12 @@ function play2(e) {
}
}
const currentPractice = $computed(() => {
if (store.sbook.statistics?.length) {
return store.sbook.statistics.filter(v => v.title === articleData.article.title)
}
return []
})
</script>
<template>
<PracticeLayout
@@ -334,7 +344,7 @@ function play2(e) {
</template>
<div class="panel-page-item pl-4">
<ArticleList
:isActive="true"
:isActive="settingStore.showPanel"
:static="false"
:show-translate="settingStore.translate"
@click="changeArticle"
@@ -365,6 +375,11 @@ function play2(e) {
<div class="bottom">
<div class="flex justify-between items-center gap-2">
<div class="stat">
<div class="row">
<div class="num">{{ currentPractice.length }}/{{ msToMinute(total(currentPractice, 'spend'))}}</div>
<div class="line"></div>
<div class="name">记录</div>
</div>
<div class="row">
<div class="num">{{ speedMinute }}分钟</div>
<div class="line"></div>
@@ -473,7 +488,7 @@ function play2(e) {
flex-direction: column;
align-items: center;
gap: .3rem;
width: 5rem;
width: 6rem;
color: gray;
.line {

View File

@@ -343,17 +343,33 @@ function onTyping(e: KeyboardEvent) {
}
// 检查是否是句子的最后一个单词
const isLastWordInSentence = wordIndex + 1 >= currentSentence.words.length;
// const isLastWordInSentence = wordIndex + 1 >= currentSentence.words.length;
// if (isLastWordInSentence) {
// // 如果是句子的最后一个单词,自动跳转到下一句,不用再输入空格
// nextSentence();
// } else if (currentWord.nextSpace) {
// // 如果不是最后一个单词,且需要空格,设置等待空格输入
// isSpace = true;
// } else {
// // 如果不需要空格,直接移动到下一个单词
// nextWord();
// }
if (isLastWordInSentence) {
// 如果是句子的最后一个单词,自动跳转到下一句,不用再输入空格
nextSentence();
} else if (currentWord.nextSpace) {
// 如果不是最后一个单词,且需要空格,设置等待空格输入
isSpace = true;
//换句不打空格不符合习惯
if (currentWord.nextSpace) {
if (
sectionIndex === props.article.sections.length - 1 &&
sentenceIndex === currentSection.length - 1 &&
wordIndex === currentSentence.words.length - 1
) {
console.log('打完了')
isEnd = true
emit('complete')
} else {
isSpace = true
}
} else {
// 如果不需要空格,直接移动到下一个单词
nextWord();
nextWord()
}
}

View File

@@ -3,6 +3,8 @@
import Input from "@/pages/pc/components/Input.vue";
import {Article} from "@/types/types.ts";
import BaseList from "@/pages/pc/components/list/BaseList.vue";
import * as sea from "node:sea";
import {watch, watchEffect} from "vue";
const props = withDefaults(defineProps<{
list: Article[],
@@ -20,12 +22,31 @@ const emit = defineEmits<{
let searchKey = $ref('')
let localList = $computed(() => {
if (searchKey) {
return props.list.filter((item: Article) => {
//把搜索内容,分词之后,判断是否有这个词,比单纯遍历包含体验更好
return searchKey.toLowerCase().split(' ').filter(v => v).some(value => {
//把搜索内容,分词之后,判断是否有这个词,比单纯遍历包含体验更好
let t = searchKey.toLowerCase()
let strings = t.split(' ').filter(v => v);
let res = props.list.filter((item: Article) => {
return strings.some(value => {
return item.title.toLowerCase().includes(value) || item.titleTranslate.toLowerCase().includes(value)
})
})
try {
let d = Number(t)
//如果是纯数字,把那一条加进去
if (!isNaN(d)) {
res.push(props.list[d])
}
} catch (err) {
}
return res.sort((a: Article, b: Article) => {
//使完整包含的条目更靠前
const aMatch = a.title.toLowerCase().includes(t);
const bMatch = b.title.toLowerCase().includes(t);
if (aMatch && !bMatch) return -1; // a 靠前
if (!aMatch && bMatch) return 1; // b 靠前
return 0; // 都匹配或都不匹配,保持原顺序
})
} else {
return props.list
}

View File

@@ -146,8 +146,8 @@ defineExpose({scrollToBottom, scrollToItem})
style="overflow: auto;"
ref="listRef">
<div class="list-item-wrapper"
v-for="(item,index) in list"
:key="item.id"
v-for="(item,index) in props.list"
:key="item.title"
>
<div class="common-list-item"
:class="{

View File

@@ -431,7 +431,7 @@ useEvents([
<div class="panel-page-item pl-4">
<WordList
v-if="data.words.length"
:is-active="true"
:is-active="settingStore.showPanel"
:static="false"
:show-word="!settingStore.dictation"
:show-translate="settingStore.translate"