feat:重构代码
This commit is contained in:
@@ -35,6 +35,10 @@ async function getBookDetail(val: DictResource) {
|
||||
}
|
||||
|
||||
async function getBookDetail2(val: Dict) {
|
||||
if (!val.name) {
|
||||
showSearchDialog = true
|
||||
return
|
||||
}
|
||||
runtimeStore.editDict = cloneDeep(val)
|
||||
nav('book-detail')
|
||||
}
|
||||
@@ -51,6 +55,14 @@ function addBook() {
|
||||
runtimeStore.editDict = getDefaultDict()
|
||||
nav('book-detail', {isAdd: true})
|
||||
}
|
||||
|
||||
function startStudy() {
|
||||
if (!base.currentBook.name) {
|
||||
showSearchDialog = true
|
||||
return
|
||||
}
|
||||
router.push('/learn-article')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -58,16 +70,21 @@ function addBook() {
|
||||
<div class="card ">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="bg-slate-200 p-3 gap-4 rounded-md cursor-pointer flex items-center">
|
||||
<span class="text-lg font-bold">{{ base.currentArticleDict.name }}</span>
|
||||
<BaseIcon @click="showSearchDialog = true" icon="gg:arrows-exchange"/>
|
||||
<span class="text-lg font-bold"
|
||||
@click="getBookDetail2(base.currentBook)">{{
|
||||
base.currentBook.name ?? '请选择书籍开始学习'
|
||||
}}</span>
|
||||
<BaseIcon @click="showSearchDialog = true"
|
||||
:icon="base.currentBook.name?'gg:arrows-exchange':'fluent:add-20-filled'"/>
|
||||
</div>
|
||||
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
|
||||
@click="router.push('/learn-article')">
|
||||
:class="base.currentBook.name??'opacity-70 cursor-not-allowed'"
|
||||
@click="startStudy">
|
||||
开始学习
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" :percentage="80" :show-text="false"></el-progress>
|
||||
<div class="mt-5 text-sm">已学习{{ base.currentBook.lastLearnIndex }}篇文章</div>
|
||||
<el-progress class="mt-1" :percentage="base.currentBookProgress" :show-text="false"></el-progress>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col">
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
import List from "@/pages/pc/components/list/List.vue";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useDisableEventListener, useWindowClick} from "@/hooks/event.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
@@ -24,7 +23,7 @@ const emit = defineEmits<{
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let article = $ref<Article>(getDefaultArticle())
|
||||
let show = $ref(false)
|
||||
let editArticleRef: any = $ref()
|
||||
let listEl: any = $ref()
|
||||
@@ -98,7 +97,7 @@ function checkDataChange() {
|
||||
async function add() {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +152,7 @@ useWindowClick(() => showExport = false)
|
||||
ref="listEl"
|
||||
v-model:list="runtimeStore.editDict.articles"
|
||||
:select-item="article"
|
||||
@del-select-item="article = cloneDeep(DefaultArticle)"
|
||||
@del-select-item="article = getDefaultArticle()"
|
||||
@select-item="selectArticle"
|
||||
>
|
||||
<template v-slot="{item,index}">
|
||||
|
||||
@@ -5,8 +5,7 @@ import BackIcon from "@/components/BackIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
@@ -21,7 +20,7 @@ const route = useRoute()
|
||||
let isEdit = $ref(false)
|
||||
let isAdd = $ref(false)
|
||||
|
||||
let article: Article = $ref(cloneDeep(DefaultArticle))
|
||||
let article: Article = $ref(getDefaultArticle())
|
||||
let chapterIndex = $ref(-1)
|
||||
|
||||
function handleCheckedChange(val) {
|
||||
@@ -89,7 +88,7 @@ function formClose() {
|
||||
</ArticleList>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
<div class="right flex-[3] shrink-0 pl-4 overflow-auto">
|
||||
<div class="right flex-[4] shrink-0 pl-4 overflow-auto">
|
||||
<div v-if="chapterIndex>-1">
|
||||
<div class="en-article-family title text-xl">
|
||||
<div class="text-center text-2xl">{{ article.title }}</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import PracticeArticle from "@/pages/pc/article/practice-article/index.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
@@ -142,4 +142,4 @@ useStartKeyboardEventListener()
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
type: 'single'
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ const TranslateEngineOptions = [
|
||||
{value: 'youdao', label: '有道'},
|
||||
]
|
||||
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
watch(() => props.article, val => {
|
||||
editArticle = cloneDeep(val)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, DefaultArticle, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import {Article, getDefaultArticle, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import EditAbleText from "@/pages/pc/components/EditAbleText.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
@@ -21,7 +21,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
type: 'single'
|
||||
})
|
||||
|
||||
@@ -39,13 +39,13 @@ const TranslateEngineOptions = [
|
||||
{value: 'youdao', label: '有道'},
|
||||
]
|
||||
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
watch(() => props.article, val => {
|
||||
editArticle = cloneDeep(val)
|
||||
progress = 0
|
||||
failCount = 0
|
||||
apply()
|
||||
apply(false)
|
||||
}, {immediate: true})
|
||||
|
||||
watch(() => editArticle.text, (s) => {
|
||||
@@ -54,7 +54,16 @@ watch(() => editArticle.text, (s) => {
|
||||
}
|
||||
})
|
||||
|
||||
function apply() {
|
||||
function apply(isHandle: boolean = true) {
|
||||
let text = editArticle.text.trim()
|
||||
if (!text && isHandle) {
|
||||
// text = "Last week I went to the theatre. I had a very good seat. The play was very interesting. I did not enjoy it. A young man and a young woman were sitting behind me. They were talking loudly. I got very angry. I could not hear the actors. I turned round. I looked at the man and the woman angrily. They did not pay any attention. In the end, I could not bear it. I turned round again. 'I can't hear a word!' I said angrily.\n\n 'It's none of your business,' the young man said rudely. 'This is a private conversation!'"
|
||||
// text = `While it is yet to be seen what direction the second Trump administration will take globally in its China policy, VOA traveled to the main island of Mahe in Seychelles to look at how China and the U.S. have impacted the country, and how each is fairing in that competition for influence there.`
|
||||
// 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!'"
|
||||
editArticle.sections = []
|
||||
ElMessage.error('请填写原文!')
|
||||
return
|
||||
}
|
||||
failCount = genArticleSectionData(editArticle)
|
||||
}
|
||||
|
||||
@@ -365,13 +374,13 @@ function setStartTime(val: Sentence, i: number, j: number) {
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button type="primary" @click="splitTranslateText">分句</el-button>
|
||||
<el-button type="primary" @click="apply">应用</el-button>
|
||||
<el-button type="primary" @click="apply(true)">应用</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex flex-col gap-2">
|
||||
<div class="title">结果</div>
|
||||
<div class="center">正文、译文与结果均可编辑,修改一处,另外两处会自动同步变动</div>
|
||||
<div class="center">正文、译文与结果均可编辑,编辑后点击应用按钮会自动同步</div>
|
||||
<div class="flex gap-2">
|
||||
<BaseButton>添加音频</BaseButton>
|
||||
<el-upload
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
@@ -23,7 +23,7 @@ const emit = defineEmits<{
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let article = $ref<Article>(getDefaultArticle())
|
||||
let show = $ref(false)
|
||||
let editArticleRef: any = $ref()
|
||||
let listEl: any = $ref()
|
||||
@@ -97,7 +97,7 @@ function checkDataChange() {
|
||||
async function add() {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ useWindowClick(() => showExport = false)
|
||||
ref="listEl"
|
||||
v-model:list="runtimeStore.editDict.articles"
|
||||
:select-item="article"
|
||||
@del-select-item="article = cloneDeep(DefaultArticle)"
|
||||
@del-select-item="article = getDefaultArticle()"
|
||||
@select-item="selectArticle"
|
||||
>
|
||||
<template v-slot="{item,index}">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import EditArticle2 from "@/pages/pc/article/components/EditArticle2.vue";
|
||||
@@ -12,7 +11,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
modelValue: false
|
||||
})
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, onMounted, onUnmounted, watch} from "vue"
|
||||
import {Article, ArticleWord, DefaultArticle, Sentence, Word} from "@/types.ts";
|
||||
import {computed, onMounted, onUnmounted, watch} from "vue"
|
||||
import {Article, ArticleWord, getDefaultArticle, Sentence, Word} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import jq from 'jquery'
|
||||
import {_nextTick} from "@/utils";
|
||||
@@ -25,7 +24,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
sectionIndex: 0,
|
||||
sentenceIndex: 0,
|
||||
wordIndex: 0,
|
||||
@@ -455,7 +454,7 @@ let showQuestions = $ref(false)
|
||||
|
||||
<div class="options flex justify-center" v-if="isEnd">
|
||||
<BaseButton
|
||||
v-if="store.currentArticleDict.lastLearnIndex < store.currentArticleDict.articles.length - 1"
|
||||
v-if="store.currentBook.lastLearnIndex < store.currentBook.articles.length - 1"
|
||||
@click="emitter.emit(EventKey.next)">下一章
|
||||
</BaseButton>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import TypingArticle from "./TypingArticle.vue";
|
||||
import {Article, ArticleItem, ArticleWord, DefaultArticle, DisplayStatistics, ShortcutKey, Word} from "@/types.ts";
|
||||
import {Article, ArticleItem, ArticleWord, DisplayStatistics, getDefaultArticle, ShortcutKey, Word} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import TypingWord from "@/pages/pc/components/TypingWord.vue";
|
||||
import Panel from "../../components/Panel.vue";
|
||||
@@ -33,7 +33,7 @@ let wordData = $ref({
|
||||
})
|
||||
let articleData = $ref({
|
||||
articles: [],
|
||||
article: cloneDeep(DefaultArticle),
|
||||
article: getDefaultArticle(),
|
||||
sectionIndex: 0,
|
||||
sentenceIndex: 0,
|
||||
wordIndex: 0,
|
||||
@@ -41,22 +41,22 @@ let articleData = $ref({
|
||||
})
|
||||
let showEditArticle = $ref(false)
|
||||
let typingArticleRef = $ref<any>()
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
let articleIsActive = $computed(() => tabIndex === 0)
|
||||
|
||||
function next() {
|
||||
if (!articleIsActive) return
|
||||
if (store.currentArticleDict.lastLearnIndex >= articleData.articles.length - 1) {
|
||||
store.currentArticleDict.lastLearnIndex = 0
|
||||
} else store.currentArticleDict.lastLearnIndex++
|
||||
if (store.currentBook.lastLearnIndex >= articleData.articles.length - 1) {
|
||||
store.currentBook.lastLearnIndex = 0
|
||||
} else store.currentBook.lastLearnIndex++
|
||||
|
||||
emitter.emit(EventKey.resetWord)
|
||||
getCurrentPractice()
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!store.currentArticleDict.articles.length) return
|
||||
articleData.articles = cloneDeep(store.currentArticleDict.articles)
|
||||
if (!store.currentBook.articles.length) return
|
||||
articleData.articles = cloneDeep(store.currentBook.articles)
|
||||
getCurrentPractice()
|
||||
console.log('inin', articleData.article)
|
||||
|
||||
@@ -64,7 +64,7 @@ function init() {
|
||||
|
||||
function setArticle(val: Article) {
|
||||
let tempVal = cloneDeep(val)
|
||||
articleData.articles[store.currentArticleDict.lastLearnIndex] = tempVal
|
||||
articleData.articles[store.currentBook.lastLearnIndex] = tempVal
|
||||
articleData.article = tempVal
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrong = 0
|
||||
@@ -82,13 +82,13 @@ function setArticle(val: Article) {
|
||||
}
|
||||
|
||||
function getCurrentPractice() {
|
||||
// console.log('store.currentArticleDict',store.currentArticleDict)
|
||||
// console.log('store.currentBook',store.currentBook)
|
||||
// return
|
||||
tabIndex = 0
|
||||
articleData.article = cloneDeep(DefaultArticle)
|
||||
articleData.article = getDefaultArticle()
|
||||
|
||||
let currentArticle = articleData.articles[store.currentArticleDict.lastLearnIndex]
|
||||
let tempArticle = {...DefaultArticle, ...currentArticle}
|
||||
let currentArticle = articleData.articles[store.currentBook.lastLearnIndex]
|
||||
let tempArticle = getDefaultArticle(currentArticle)
|
||||
// console.log('article', tempArticle)
|
||||
if (tempArticle.sections.length) {
|
||||
setArticle(tempArticle)
|
||||
@@ -102,9 +102,9 @@ function saveArticle(val: Article) {
|
||||
console.log('saveArticle', val, JSON.stringify(val.lrcPosition))
|
||||
console.log('saveArticle', val.textTranslate)
|
||||
showEditArticle = false
|
||||
let rIndex = store.currentArticleDict.articles.findIndex(v => v.id === val.id)
|
||||
let rIndex = store.currentBook.articles.findIndex(v => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
store.currentArticleDict.articles[rIndex] = cloneDeep(val)
|
||||
store.currentBook.articles[rIndex] = cloneDeep(val)
|
||||
}
|
||||
setArticle(val)
|
||||
}
|
||||
@@ -163,7 +163,7 @@ function nextWord(word: ArticleWord) {
|
||||
function handleChangeChapterIndex(val: ArticleItem) {
|
||||
let rIndex = articleData.articles.findIndex(v => v.id === val.item.id)
|
||||
if (rIndex > -1) {
|
||||
store.currentArticleDict.lastLearnIndex = rIndex
|
||||
store.currentBook.lastLearnIndex = rIndex
|
||||
getCurrentPractice()
|
||||
}
|
||||
}
|
||||
@@ -315,11 +315,11 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.currentArticleDict.name }}
|
||||
{{ store.currentBook.name }}
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentArticleDict.lastLearnIndex < articleData.articles.length - 1">
|
||||
v-if="store.currentBook.lastLearnIndex < articleData.articles.length - 1">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
|
||||
</IconWrapper>
|
||||
@@ -434,9 +434,6 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
icon="tabler:edit"
|
||||
@click="emitter.emit(ShortcutKey.EditArticle)"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
@@ -457,7 +454,6 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
@@ -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";
|
||||
|
||||
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 gap-2">
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
</div>
|
||||
<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 class="center flex-col">
|
||||
<div class="text-xl">A private conversation!</div>
|
||||
<div class="options-wrapper">
|
||||
<div class="flex gap-1">
|
||||
<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="emit('edit',)"
|
||||
/>
|
||||
|
||||
<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>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.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;
|
||||
|
||||
.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>
|
||||
@@ -1,38 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useRouter} from "vue-router";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
|
||||
const base = useBaseStore()
|
||||
const router = useRouter()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasePage>
|
||||
<article class="">
|
||||
<div class="text-align-center text-xl font-bold">One good turn deserves another</div>
|
||||
<div>
|
||||
I was having dinner at a restaurant when Tony Steele came in. Tony worked in a lawyer's office years ago, but he is now working at a bank. He gets a good salary, but he always borrows money from his friends and never pays it back. Tony saw me and came and sat at the same table. He has never borrowed money from me. While he was eating, I asked him to lend me twenty pounds. To my surprise, he gave me the money immediately. I have never borrowed any money from you, Tony said, so now you can pay for my dinner!
|
||||
</div>
|
||||
</article>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card {
|
||||
@apply rounded-xl bg-white p-4 mt-5;
|
||||
}
|
||||
|
||||
.center {
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
</style>
|
||||
@@ -1,164 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Toolbar from "@/pages/pc/components/toolbar/index.vue"
|
||||
import { onMounted, onUnmounted, watch } from "vue";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import Footer from "@/pages/pc/word/Footer.vue";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
|
||||
import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import { ShortcutKey } from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import { useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const { toggleTheme } = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (statisticsStore.wrong > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrong) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
repeat()
|
||||
}
|
||||
|
||||
//TODO 需要判断是否已忽略
|
||||
function repeat() {
|
||||
// console.log('repeat')
|
||||
emitter.emit(EventKey.resetWord)
|
||||
practiceRef.getCurrentPractice()
|
||||
}
|
||||
|
||||
function prev() {
|
||||
// console.log('next')
|
||||
if (store.currentDict.chapterIndex === 0) {
|
||||
ElMessage.warning('已经在第一章了~')
|
||||
} else {
|
||||
store.currentDict.chapterIndex--
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTranslate() {
|
||||
settingStore.translate = !settingStore.translate
|
||||
}
|
||||
|
||||
function toggleDictation() {
|
||||
settingStore.dictation = !settingStore.dictation
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
runtimeStore.showSettingModal = true
|
||||
}
|
||||
|
||||
function openDictDetail() {
|
||||
emitter.emit(EventKey.openDictModal, 'detail')
|
||||
}
|
||||
|
||||
function toggleConciseMode() {
|
||||
settingStore.showToolbar = !settingStore.showToolbar
|
||||
settingStore.showPanel = settingStore.showToolbar
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function jumpSpecifiedChapter(val: number) {
|
||||
store.currentDict.chapterIndex = val
|
||||
repeat()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.write, write)
|
||||
emitter.on(EventKey.repeat, repeat)
|
||||
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.on(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.on(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.on(ShortcutKey.DictationChapter, write)
|
||||
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.on(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.on(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.write, write)
|
||||
emitter.off(EventKey.repeat, repeat)
|
||||
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.off(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.off(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.off(ShortcutKey.DictationChapter, write)
|
||||
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.off(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<Toolbar />
|
||||
<PracticeArticle ref="practiceRef" />
|
||||
<Footer />
|
||||
</div>
|
||||
<DictModal />
|
||||
<Statistics />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,163 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted, onUnmounted, watch } from "vue";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
|
||||
import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import { ShortcutKey } from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import { useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import ArticleFooter from "@/pages/pc/article/ArticleFooter.vue";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const { toggleTheme } = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (statisticsStore.wrong > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrong) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
repeat()
|
||||
}
|
||||
|
||||
//TODO 需要判断是否已忽略
|
||||
function repeat() {
|
||||
// console.log('repeat')
|
||||
emitter.emit(EventKey.resetWord)
|
||||
practiceRef.getCurrentPractice()
|
||||
}
|
||||
|
||||
function prev() {
|
||||
// console.log('next')
|
||||
if (store.currentDict.chapterIndex === 0) {
|
||||
ElMessage.warning('已经在第一章了~')
|
||||
} else {
|
||||
store.currentDict.chapterIndex--
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTranslate() {
|
||||
settingStore.translate = !settingStore.translate
|
||||
}
|
||||
|
||||
function toggleDictation() {
|
||||
settingStore.dictation = !settingStore.dictation
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
runtimeStore.showSettingModal = true
|
||||
}
|
||||
|
||||
function openDictDetail() {
|
||||
emitter.emit(EventKey.openDictModal, 'detail')
|
||||
}
|
||||
|
||||
function toggleConciseMode() {
|
||||
settingStore.showToolbar = !settingStore.showToolbar
|
||||
settingStore.showPanel = settingStore.showToolbar
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function jumpSpecifiedChapter(val: number) {
|
||||
store.currentDict.chapterIndex = val
|
||||
repeat()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.write, write)
|
||||
emitter.on(EventKey.repeat, repeat)
|
||||
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.on(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.on(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.on(ShortcutKey.DictationChapter, write)
|
||||
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.on(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.on(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.write, write)
|
||||
emitter.off(EventKey.repeat, repeat)
|
||||
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.off(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.off(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.off(ShortcutKey.DictationChapter, write)
|
||||
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.off(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<PracticeArticle ref="practiceRef" />
|
||||
<ArticleFooter />
|
||||
</div>
|
||||
<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>
|
||||
@@ -46,7 +46,7 @@ watch(() => settingStore.load, (n) => {
|
||||
v-if="show">
|
||||
<div class="notice">
|
||||
坚持练习,提高外语能力。将
|
||||
<span class="active">「Typing Word」</span>
|
||||
<span class="active">「Type Words」</span>
|
||||
保存为书签,永不迷失!
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
@@ -190,4 +190,4 @@ watch(() => settingStore.load, (n) => {
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -47,7 +47,7 @@ function toggle() {
|
||||
/>
|
||||
<div class="options">
|
||||
<BaseButton @click="toggle">取消</BaseButton>
|
||||
<BaseButton @click="save">保存</BaseButton>
|
||||
<BaseButton @click="save">应用</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -76,4 +76,4 @@ function toggle() {
|
||||
font-size: 1.2rem;
|
||||
min-height: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -85,7 +85,7 @@ function changeCollect() {
|
||||
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">收藏</div>
|
||||
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{ store.simple.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.wrong.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 4 && 'active'" @click="tabIndex = 4">{{ store.master.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 4 && 'active'" @click="tabIndex = 4">{{ store.known.name }}</div>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`关闭(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
|
||||
@@ -447,7 +447,7 @@ function importData(e) {
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tabIndex === 5" class="about">
|
||||
<h1>Typing Word</h1>
|
||||
<h1>Type Words</h1>
|
||||
<p>
|
||||
本项目完全开源!好用请大家多多点Star!
|
||||
</p>
|
||||
|
||||
@@ -84,11 +84,11 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
.search {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0 var(--space);
|
||||
padding-right: var(--space);
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@ import BaseButton from "@/components/BaseButton.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, DefaultArticle, Dict, DictResource, DictType, getDefaultDict, Sort, TranslateType} from "@/types.ts";
|
||||
import {Article, Dict, DictResource, DictType, getDefaultArticle, getDefaultDict, Sort} from "@/types.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import EditBatchArticleModal from "@/pages/pc/article/components/EditBatchArticleModal.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
@@ -19,7 +19,6 @@ import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {syncMyDictList} from "@/hooks/dict.ts";
|
||||
import {useWindowClick} from "@/hooks/event.ts";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import * as copy from "copy-to-clipboard";
|
||||
import {getTranslateText} from "@/hooks/article.ts";
|
||||
import {_copy} from "@/utils";
|
||||
|
||||
@@ -31,7 +30,7 @@ const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
let chapterIndex = $ref(-1)
|
||||
let article: Article = $ref(cloneDeep(DefaultArticle))
|
||||
let article: Article = $ref(getDefaultArticle())
|
||||
let isEditDict = $ref(false)
|
||||
let showExport = $ref(false)
|
||||
let loading = $ref(false)
|
||||
@@ -104,7 +103,7 @@ function delArticle(index: number) {
|
||||
article = runtimeStore.editDict.articles[chapterIndex]
|
||||
}
|
||||
} else {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
chapterIndex = -1
|
||||
}
|
||||
syncMyDictList(runtimeStore.editDict)
|
||||
@@ -117,7 +116,7 @@ async function resetDict() {
|
||||
'提示',
|
||||
async () => {
|
||||
chapterIndex = -1
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
if (runtimeStore.editDict.url) {
|
||||
runtimeStore.editDict.sort = Sort.normal
|
||||
runtimeStore.editDict.chapterWordNumber = settingStore.chapterWordNumber
|
||||
@@ -150,15 +149,14 @@ function importData(e: any) {
|
||||
if (res.length) {
|
||||
let articles = res.map(v => {
|
||||
if (v['原文标题'] && v['原文正文']) {
|
||||
let article: Article = {
|
||||
...DefaultArticle,
|
||||
let article: Article = getDefaultArticle({
|
||||
id: nanoid(6),
|
||||
checked: false,
|
||||
title: String(v['原文标题']),
|
||||
text: String(v['原文正文']),
|
||||
titleTranslate: String(v['译文标题']),
|
||||
textTranslate: String(v['译文正文']),
|
||||
}
|
||||
})
|
||||
return article
|
||||
}
|
||||
}).filter(v => v)
|
||||
@@ -276,7 +274,7 @@ defineExpose({getDictDetail, add, editDict})
|
||||
</div>
|
||||
<template v-if="!isPinDict">
|
||||
<BaseIcon icon="tabler:edit" @click='editDict'/>
|
||||
<!-- <BaseIcon icon="ph:star" @click='no'/>-->
|
||||
<!-- <BaseIcon icon="ph:star" @click='no'/>-->
|
||||
<BaseButton size="small" v-if="runtimeStore.editDict.isCustom" @click="resetDict">恢复默认</BaseButton>
|
||||
</template>
|
||||
<div class="import hvr-grow">
|
||||
|
||||
@@ -40,14 +40,14 @@ const {toggleTheme} = useTheme()
|
||||
<Icon icon="ph:article-ny-times"/>
|
||||
<span v-if="settingStore.sideExpand">文章</span>
|
||||
</div>
|
||||
<div class="row" @click="router.push('/article2')">
|
||||
<Icon icon="healthicons:i-exam-multiple-choice-outline"/>
|
||||
<span v-if="settingStore.sideExpand">试卷</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<Icon icon="mdi-light:forum"/>
|
||||
<span v-if="settingStore.sideExpand">社区</span>
|
||||
</div>
|
||||
<!-- <div class="row" @click="router.push('/article2')">-->
|
||||
<!-- <Icon icon="healthicons:i-exam-multiple-choice-outline"/>-->
|
||||
<!-- <span v-if="settingStore.sideExpand">试卷</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <Icon icon="mdi-light:forum"/>-->
|
||||
<!-- <span v-if="settingStore.sideExpand">社区</span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div class="bottom flex justify-evenly ">
|
||||
<BaseIcon
|
||||
|
||||
@@ -162,21 +162,9 @@ function changePerDayStudyNumber() {
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-4 mt-4">
|
||||
<div class="book" @click="nav('edit-word-dict',{type:0})">
|
||||
<span>收藏</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.collectWord.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:1})">
|
||||
<span>错词本</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.wrong.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:2})">
|
||||
<span>简单词</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.simple.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:3})">
|
||||
<span>已掌握</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.master.words.length }}个词</div>
|
||||
<div class="book" v-for="item in store.word.bookList" @click="nav('edit-word-dict',{type:item.type})">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="absolute bottom-4 right-4">{{ item.words.length }}个词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -238,7 +226,7 @@ function changePerDayStudyNumber() {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.target-modal {
|
||||
.target-modal {
|
||||
width: 30rem;
|
||||
padding: var(--space);
|
||||
padding-top: 0;
|
||||
|
||||
Reference in New Issue
Block a user