feat: save

This commit is contained in:
王念超
2024-09-09 20:21:43 +08:00
parent 7ce9fdd682
commit 4dc0591b5b
6 changed files with 593 additions and 1 deletions

View File

@@ -0,0 +1,223 @@
<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">
@import "@/assets/css/variable";
.footer {
width: var(--article-width);
margin-bottom: .8rem;
transition: all var(--anim-time);
position: relative;
margin-top: 1rem;
&.hide {
margin-bottom: -6rem;
margin-top: 3rem;
.progress {
bottom: calc(100% + 1.8rem);
}
}
.bottom {
position: relative;
width: 100%;
box-sizing: border-box;
border-radius: .6rem;
background: var(--color-second-bg);
padding: .2rem var(--space) .4rem var(--space);
z-index: 2;
border: 1px solid var(--color-item-border);
box-shadow: var(--shadow);
.stat {
margin-top: .5rem;
display: flex;
justify-content: space-around;
.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>

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
import { useBaseStore } from "@/stores/base.ts";
import { Icon } from '@iconify/vue'
import "vue-activity-calendar/style.css";
import { useRouter } from "vue-router";
import { enArticle } from "@/assets/dictionary.ts";
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>

View File

@@ -0,0 +1,164 @@
<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>

View File

@@ -0,0 +1,163 @@
<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>

View File

@@ -41,7 +41,7 @@ let show = $ref(false)
<Icon icon="ph:article-ny-times"/>
<span>文章</span>
</div>
<div class="row">
<div class="row" @click="router.push('/article2')">
<Icon icon="healthicons:i-exam-multiple-choice-outline"/>
<span>试卷</span>
</div>

View File

@@ -19,6 +19,7 @@ import WordHome from "@/pages/pc/word/WordHome.vue";
import PC from "@/pages/pc/index.vue";
import Dict2 from '@/pages/pc/dict2/index.vue'
import ArticleIndex from "@/pages/pc/article/ArticleIndex.vue";
import Article2Index from "@/pages/pc/article2/ArticleIndex.vue";
import HomeIndex from "@/pages/pc/home/HomeIndex.vue";
import LearnArticle from "@/pages/pc/article/LearnArticle.vue";
import EditWordDict from "@/pages/pc/word/EditWordDict.vue";
@@ -35,6 +36,7 @@ export const routes: RouteRecordRaw[] = [
{path: 'edit-word-dict', component: EditWordDict},
{path: 'dict', component: Dict2},
{path: 'article', component: ArticleIndex},
{path: 'article2', component: Article2Index},
{path: 'learn-article', component: LearnArticle},
]
},