This commit is contained in:
zyronon
2024-05-21 18:52:12 +08:00
parent 2d7e1cc86f
commit cee18aa246
13 changed files with 633 additions and 706 deletions

View File

@@ -64,7 +64,7 @@
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.2",
"unplugin-vue-define-options": "^1.4.1",
"vite": "^5.2.0",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5",
"xlsx": "^0.18.5"
},

987
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2321,7 +2321,7 @@ const germanExam: DictResource[] = [
]
// 英语文章
const enArticle: DictResource[] = [
export const enArticle: DictResource[] = [
{
id: 'article_nce2',
name: "新概念英语2-课文",

View File

@@ -117,7 +117,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
sentence.words[sentence.words.length - 1].nextSpace = false
let word2 = cloneDeep({
...DefaultArticleWord,
name: post,
word: post,
isSymbol: true,
nextSpace
});
@@ -146,7 +146,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
sentence.words[sentence.words.length - 1].nextSpace = true
let word3 = cloneDeep({
...DefaultArticleWord,
name: 'placeholder',
word: 'placeholder',
isSymbol: true,
nextSpace: false,
});
@@ -164,7 +164,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
sentence.words[sentence.words.length - 1].nextSpace = false
let word3 = cloneDeep({
...DefaultArticleWord,
name: post,
word: post,
isSymbol: true,
nextSpace: false,
});
@@ -182,7 +182,7 @@ export function splitEnArticle(text: string): { sections: Sentence[][], newText:
checkQuote(pre, index)
}
let word = cloneDeep({...DefaultArticleWord, name: v.text, nextSpace: true});
let word = cloneDeep({...DefaultArticleWord, word: v.text, nextSpace: true});
sentence.words.push(word)
let post: string = v.post

View File

@@ -6,6 +6,9 @@ import {ActivityCalendar} from "vue-activity-calendar";
import "vue-activity-calendar/style.css";
import {useRouter} from "vue-router";
import BaseIcon from "@/components/BaseIcon.vue";
import DictList from "@/pages/pc/components/list/DictList.vue";
import {enArticle} from "@/assets/dictionary.ts";
import {DictType} from "@/types.ts";
const base = useBaseStore()
const router = useRouter()
@@ -19,18 +22,24 @@ function clickEvent(e) {
<div class="word flex justify-center ">
<div class="w-5/10 pt-5">
<div class="flex gap-6">
<div class="card w-1/2 flex flex-col">
<div class="card w-1/4 flex flex-col">
<div class="title">
我的词典
</div>
<div class="grid flex-1 flex gap-5 mt-4">
<div class="p-4 flex-1 rounded-md bg-slate-200 relative" v-for="i in 3">
<div class="p-4 flex-1 rounded-md bg-slate-200 relative">
<span>收藏</span>
<div class="absolute bottom-4 right-4">333个词</div>
<div class="absolute bottom-4 right-4">3</div>
</div>
</div>
<div class="grid flex-1 flex gap-5 mt-4">
<div class="p-4 flex-1 rounded-md bg-slate-200 relative">
<span>添加</span>
<div class="absolute bottom-4 right-4">3</div>
</div>
</div>
</div>
<div class="w-1/2">
<div class="w-3/4">
<div class="card ">
<div class="flex justify-between items-center">
<div class="bg-slate-200 p-3 rounded-md cursor-pointer flex items-center">
@@ -39,39 +48,30 @@ function clickEvent(e) {
<Icon icon="uil:setting" class="text-2xl ml-2"/>
</div>
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
@click="router.push('/practice')">
@click="router.push('/learn-article')">
开始学习
</div>
</div>
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
<el-progress class="mt-1" percentage="80" :show-text="false"></el-progress>
<el-progress class="mt-1" :percentage="80" :show-text="false"></el-progress>
</div>
</div>
</div>
<div class="card">
<div class="flex justify-between">
<div class="title">
其他学习词典
</div>
<BaseIcon icon="ic:round-add" @click="router.push('/dict')"/>
</div>
<div class="grid grid-cols-2 gap-6 mt-5 ">
<div class=" p-4 rounded-md justify-between items-center bg-slate-200 " v-for="i in 3">
<div class="flex justify-between w-full">
<span>{{ base.currentDict.name }}</span>
<div class="text-2xl ml-2 flex gap-4">
<Icon icon="hugeicons:delete-02"/>
<Icon icon="nonicons:go-16"/>
</div>
<div class="mt-4">
<div class="title">文章</div>
<div class="mt-4 flex gap-4">
<div
class="bg-white rounded-md p-4 h-40 w-30 relative cursor-pointer"
v-for="dict in enArticle"
>
<div class="top">
<div class="name">{{ dict.name }}</div>
<div class="desc">{{ dict.description }}</div>
</div>
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
<el-progress class="mt-1" percentage="80" color="white" :show-text="false"></el-progress>
<div class="absolute bottom-4 right-4">{{ dict.length }}</div>
</div>
</div>
<div class="flex justify-center mt-2 text-2xl">
<Icon icon="mingcute:down-line"/>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,163 @@
<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/practice/Footer.vue";
import {useBaseStore} from "@/stores/base.ts";
import Statistics from "@/pages/pc/practice/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 practiceStore = usePracticeStore()
const store = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
const {toggleTheme} = useTheme()
const practiceRef: any = $ref()
watch(practiceStore, () => {
if (practiceStore.inputWordNumber < 1) {
return practiceStore.correctRate = -1
}
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
return practiceStore.correctRate = 0
}
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.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"/>
<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

@@ -1,5 +1,5 @@
<script lang="jsx">
import {nextTick, Teleport, Transition} from "vue";
import {nextTick} from "vue";
export default {
name: "PopConfirm",

View File

@@ -1,5 +1,4 @@
<script lang="jsx">
import {nextTick, Teleport, Transition} from "vue";
export default {
name: "Tooltip",
@@ -29,7 +28,7 @@ export default {
e.stopPropagation()
let rect = e.target.getBoundingClientRect()
this.show = true
nextTick(() => {
this.$nextTick(() => {
let tip = this.$refs?.tip?.getBoundingClientRect()
if (!tip) return
if (rect.top < 50) {

View File

@@ -9,6 +9,11 @@ import {cloneDeep} from "lodash-es";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import BaseIcon from "@/components/BaseIcon.vue";
import {useArticleOptions} from "@/hooks/dict.ts";
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import {Icon} from "@iconify/vue";
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
import VolumeSetting from "@/pages/pc/components/toolbar/VolumeSetting.vue";
interface IProps {
article: Article,
@@ -340,12 +345,30 @@ defineExpose({showSentence, play, del,hideSentence,nextSentence})
<div class="title word">{{ props.article.title }}</div>
<div class="titleTranslate" v-if="settingStore.translate">{{ props.article.titleTranslate }}</div>
<div class="options-wrapper">
<div class="flex gap10">
<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',props.article)"
/>
<BaseIcon
v-if="!isArticleCollect(props.article)"
class="collect"
@@ -359,7 +382,7 @@ defineExpose({showSentence, play, del,hideSentence,nextSentence})
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
icon="ph:star-fill"/>
<BaseIcon
:title="`跳过(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
icon="icon-park-outline:go-ahead"
@click="emit('over')"/>
</div>
@@ -450,28 +473,31 @@ $article-width: 1000px;
header {
word-wrap: break-word;
position: relative;
padding: 15rem 0;
padding: .9rem 0;
.title {
text-align: center;
font-weight: bold;
color: rgba(gray, .8);
font-size: 36rem;
font-size: 2.2rem;
font-family: var(--word-font-family);
}
.titleTranslate {
@extend .title;
font-size: 20rem;
font-size: 1.2rem;
font-family: unset;
}
.options-wrapper {
position: absolute;
right: 20rem;
//position: absolute;
right: 1.2rem;
top: 0;
display: flex;
gap: 10rem;
font-size: 18rem;
margin-top: 1rem;
justify-content: center;
gap: .6rem;
font-size: 1.1rem;
}
}
@@ -482,13 +508,13 @@ $article-width: 1000px;
article {
//height: 100%;
font-size: 24rem;
font-size: 1.6rem;
line-height: 2.5;
color: gray;
word-break: keep-all;
word-wrap: break-word;
white-space: pre-wrap;
padding-top: 20rem;
padding-top: 1.2rem;
.section {
font-family: var(--word-font-family);
@@ -498,11 +524,11 @@ $article-width: 1000px;
transition: all .3s;
&:first-child {
padding-left: 50rem;
padding-left: .2rem;
}
&.dictation {
letter-spacing: 3rem;
letter-spacing: .2rem;
}
}
@@ -519,10 +545,10 @@ $article-width: 1000px;
left: 0;
height: 100%;
width: 100%;
font-size: 18rem;
font-size: 1.1rem;
color: gray;
line-height: 3.5;
letter-spacing: 3rem;
letter-spacing: .2rem;
//display: none;
.row {
@@ -545,20 +571,20 @@ $article-width: 1000px;
&::after {
content: ' ';
position: absolute;
width: 1.5rem;
height: 4rem;
width: .1rem;
height: .25rem;
background: gray;
bottom: 2rem;
right: 2.5rem;
bottom: .12rem;
right: .25rem;
}
&::before {
content: ' ';
position: absolute;
width: 1.5rem;
height: 4rem;
width: .1rem;
height: .26rem;
background: gray;
bottom: 2rem;
bottom: .12rem;
left: 0;
}
}
@@ -591,10 +617,10 @@ $article-width: 1000px;
@keyframes underline {
0%, 100% {
border-left: 1.3rem solid black;
border-left: .1rem solid black;
}
50% {
border-left: 1.3rem solid transparent;
border-left: .1rem solid transparent;
}
}

View File

@@ -66,6 +66,8 @@ function init() {
if (!store.currentDict.articles.length) return
articleData.articles = cloneDeep(store.currentDict.articles)
getCurrentPractice()
console.log('inin', articleData.article)
}
function setArticle(val: Article) {
@@ -95,7 +97,7 @@ function getCurrentPractice() {
tabIndex = 0
articleData.article = cloneDeep(DefaultArticle)
let currentArticle = articleData.articles [store.currentDict.chapterIndex]
let currentArticle = articleData.articles[store.currentDict.chapterIndex]
let tempArticle = {...DefaultArticle, ...currentArticle}
// console.log('article', tempArticle)
if (tempArticle.sections.length) {

View File

@@ -44,7 +44,7 @@ function clickEvent(e) {
</div>
</div>
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
<el-progress class="mt-1" percentage="80" :show-text="false"></el-progress>
<el-progress class="mt-1" :percentage="80" :show-text="false"></el-progress>
</div>
<div class="card flex gap-3">
<div class="bg-slate-200 w-10 h-10 flex center text-2xl rounded">
@@ -60,7 +60,7 @@ function clickEvent(e) {
</div>
</div>
<div class="mt-2 text-xs">学习 50 个单词</div>
<el-progress class="flex-1 mt-1" percentage="80" :show-text="false"></el-progress>
<el-progress class="flex-1 mt-1" :percentage="80" :show-text="false"></el-progress>
</div>
</div>
</div>
@@ -83,7 +83,7 @@ function clickEvent(e) {
</div>
</div>
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
<el-progress class="mt-1" percentage="80" color="white" :show-text="false"></el-progress>
<el-progress class="mt-1" :percentage="80" color="white" :show-text="false"></el-progress>
</div>
</div>
<div class="flex justify-center mt-2 text-2xl">

View File

@@ -22,6 +22,7 @@ import PC from "@/pages/pc/index.vue";
import Dict2 from '@/pages/pc/dict2/index.vue'
import ArticleIndex from "@/pages/pc/article/ArticleIndex.vue";
import HomeIndex from "@/pages/pc/home/HomeIndex.vue";
import LearnArticle from "@/pages/pc/article/LearnArticle.vue";
export const routes: RouteRecordRaw[] = [
{
@@ -33,6 +34,7 @@ export const routes: RouteRecordRaw[] = [
{path: 'dict', component: Dict2},
{path: 'practice', component: Practice},
{path: 'article', component: ArticleIndex},
{path: 'learn-article', component: LearnArticle},
]
},
{path: '/pc/dict', component: Dict},

View File

@@ -59,20 +59,20 @@ export const DefaultBaseState = (): BaseState => ({
language: 'en',
type: DictType.word
},
// {
// ...cloneDeep(DefaultDict),
// id: 'article_nce2',
// name: "新概念英语2-课文",
// description: '新概念英语2-课文',
// category: '英语学习',
// tags: ['新概念英语'],
// url: 'NCE_2.json',
// translateLanguage: 'common',
// language: 'en',
// type: DictType.article,
// resourceId: 'article_nce2',
// length: 96
// },
{
...cloneDeep(DefaultDict),
id: 'article_nce2',
name: "新概念英语2-课文",
description: '新概念英语2-课文',
category: '英语学习',
tags: ['新概念英语'],
url: 'NCE_2.json',
translateLanguage: 'common',
language: 'en',
type: DictType.article,
resourceId: 'article_nce2',
length: 96
},
// {
// ...cloneDeep(DefaultDict),
// id: 'nce-new-2',
@@ -90,10 +90,10 @@ export const DefaultBaseState = (): BaseState => ({
],
collectDictIds: [],
current: {
index: 3,
index: 4,
// dictType: DictType.article,
// index: 0,
practiceType: DictType.word,
practiceType: DictType.article,
},
simpleWords: [
'a', 'an',
@@ -169,7 +169,7 @@ export const useBaseStore = defineStore('base', {
} else {
let configStr: string = await localforage.getItem(SAVE_DICT_KEY.key)
let data = checkAndUpgradeSaveDict(configStr)
this.setState(data)
// this.setState(data)
}
localforage.setItem(SAVE_DICT_KEY.key, JSON.stringify({val: this.$state, version: SAVE_DICT_KEY.version}))
} catch (e) {