Merge branch 'dev'
This commit is contained in:
4
Note.md
4
Note.md
@@ -41,8 +41,6 @@ A cold welcome 有bug
|
||||
|
||||
[EditAbleText.vue](src%2Fcomponents%2FEditAbleText.vue) 不能自动聚焦
|
||||
|
||||
在文章模式下,背单词时不能调出面板
|
||||
|
||||
单词发音,点击第二遍时减速
|
||||
|
||||
http://enpuz.com/ 语法分析工具
|
||||
@@ -51,8 +49,6 @@ http://enpuz.com/ 语法分析工具
|
||||
|
||||
加载单词列表时需要loading
|
||||
|
||||
背单词页面div,位置应该恒定,不应该随翻译内容变动而跳动
|
||||
|
||||
点击句子播放的音乐,需要可暂停
|
||||
|
||||
footer 的输入数统计有问题,当在列表点一个,然后输入错误之后,不会统计到输入数里面(单词和文章的都有问题)
|
||||
|
||||
4
components.d.ts
vendored
4
components.d.ts
vendored
@@ -66,5 +66,9 @@ declare module 'vue' {
|
||||
VolumeSetting: typeof import('./src/components/Toolbar/VolumeSetting.vue')['default']
|
||||
WordList: typeof import('./src/components/WordList.vue')['default']
|
||||
WordListModal: typeof import('./src/components/WordListModal.vue')['default']
|
||||
WordListModal2: typeof import('./src/components/WordListModal2.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,14 +40,12 @@ html.dark {
|
||||
--color-main-bg: rgba(14, 18, 23, 1);
|
||||
//--color-main-bg: rgba(30,31,34, 1);
|
||||
--color-second-bg: rgb(43,45,48);
|
||||
--color-second-bg: rgb(30,31,34);
|
||||
|
||||
--color-item-bg: rgb(51, 51, 51);
|
||||
--color-item-hover: #5e5e5e;
|
||||
--color-item-bg: rgb(43,45,48);
|
||||
--color-item-hover: rgb(67,69,74);
|
||||
//--color-item-active: rgb(75, 110, 175);
|
||||
//--color-item-active: rgb(103, 103, 103);
|
||||
--color-item-active: rgb(67,69,74);
|
||||
--color-item-border: rgb(73, 73, 73);
|
||||
--color-item-active: rgb(84,84,84);
|
||||
--color-item-border: rgb(41, 41, 41);
|
||||
|
||||
--color-header-bg: rgb(51, 51, 51);
|
||||
--color-tooltip-bg: #252525;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {onMounted} from "vue"
|
||||
const canvas = $ref<HTMLCanvasElement>()
|
||||
|
||||
onMounted(() => {
|
||||
console.log('canvas;', canvas)
|
||||
// console.log('canvas;', canvas)
|
||||
let ctx = canvas.getContext("2d");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
@@ -24,17 +24,11 @@ const list: any[] = $computed(() => {
|
||||
|
||||
|
||||
function showWordListModal(index: number, item: Word[]) {
|
||||
if (runtimeStore.editDict.translateLanguage === 'common') {
|
||||
console.time()
|
||||
item.map((w: Word) => {
|
||||
if (!w.trans.length) {
|
||||
let res = runtimeStore.translateWordList.find(a => a.name === w.name)
|
||||
if (res) w = Object.assign(w, res)
|
||||
}
|
||||
})
|
||||
console.timeEnd()
|
||||
}
|
||||
emitter.emit(EventKey.openWordListModal, {title: `第${index + 1}章`, list: item})
|
||||
emitter.emit(EventKey.openWordListModal, {
|
||||
title: `第${index + 1}章`,
|
||||
translateLanguage: runtimeStore.editDict.translateLanguage,
|
||||
list: item
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -18,10 +18,8 @@ let wordData = $ref({
|
||||
|
||||
watch([
|
||||
() => store.load,
|
||||
() => store.current.index,
|
||||
() => store.current.dictType,
|
||||
() => store.currentDict.chapterIndex,
|
||||
() => store.currentDict.chapterWordNumber,
|
||||
() => store.currentDict.words,
|
||||
], n => {
|
||||
getCurrentPractice()
|
||||
})
|
||||
@@ -36,7 +34,7 @@ function getCurrentPractice() {
|
||||
}
|
||||
wordData.words = cloneDeep(store.chapter)
|
||||
wordData.index = 0
|
||||
console.log('wordData', wordData)
|
||||
// console.log('wordData', wordData)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -237,6 +237,8 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
class="word-list"
|
||||
:is-active="active"
|
||||
@change="(i:number) => data.index = i"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"/>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import {dictionaryResources} from '@/assets/dictionary.ts'
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {watch} from "vue"
|
||||
import {DefaultDict, Dict, DictResource, DictType, languageCategoryOptions} from "@/types.ts"
|
||||
import {chunk, cloneDeep, groupBy} from "lodash-es";
|
||||
import {onUnmounted, watch} from "vue"
|
||||
import {DefaultDict, Dict, DictResource, DictType, languageCategoryOptions, Sort} from "@/types.ts"
|
||||
import {chunk, cloneDeep, groupBy, reverse, shuffle} from "lodash-es";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import Modal from "@/components/Modal/Modal.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
@@ -17,6 +17,7 @@ import WordListModal from "@/components/WordListModal.vue";
|
||||
import {isArticle} from "@/hooks/article.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
|
||||
interface IProps {
|
||||
modelValue?: boolean,
|
||||
@@ -98,9 +99,6 @@ function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function resetChapterList() {
|
||||
runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, runtimeStore.editDict.chapterWordNumber)
|
||||
}
|
||||
|
||||
function groupByDictTags(dictList: DictResource[]) {
|
||||
return dictList.reduce<Record<string, DictResource[]>>((result, dict) => {
|
||||
@@ -148,6 +146,32 @@ const dictIsArticle = $computed(() => {
|
||||
return isArticle(runtimeStore.editDict.type)
|
||||
})
|
||||
|
||||
function showAllWordModal() {
|
||||
emitter.emit(EventKey.openWordListModal, {
|
||||
title: runtimeStore.editDict.name,
|
||||
translateLanguage: runtimeStore.editDict.translateLanguage,
|
||||
list: runtimeStore.editDict.words
|
||||
})
|
||||
}
|
||||
|
||||
function resetChapterList() {
|
||||
runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, runtimeStore.editDict.chapterWordNumber)
|
||||
}
|
||||
|
||||
function changeSort(v) {
|
||||
if (v === Sort.normal) {
|
||||
runtimeStore.editDict.words = cloneDeep(runtimeStore.editDict.originWords)
|
||||
} else if (v === Sort.random) {
|
||||
runtimeStore.editDict.words = shuffle(cloneDeep(runtimeStore.editDict.originWords))
|
||||
} else {
|
||||
runtimeStore.editDict.words = reverse(cloneDeep(runtimeStore.editDict.originWords))
|
||||
}
|
||||
resetChapterList()
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -215,7 +239,9 @@ const dictIsArticle = $computed(() => {
|
||||
>总文章:{{ runtimeStore.editDict.articles.length }}篇
|
||||
</div>
|
||||
<div class="num" v-else>
|
||||
总词汇:<span>{{ runtimeStore.editDict.originWords.length }}词</span>
|
||||
总词汇:<span class="count"
|
||||
@click="showAllWordModal"
|
||||
>{{ runtimeStore.editDict.originWords.length }}词</span>
|
||||
</div>
|
||||
<div class="num">开始日期:-</div>
|
||||
<div class="num">花费时间:-</div>
|
||||
@@ -240,10 +266,22 @@ const dictIsArticle = $computed(() => {
|
||||
<span>{{ runtimeStore.editDict.chapterWordNumber }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">单词顺序</div>
|
||||
<div class="option">
|
||||
<el-radio-group v-model="runtimeStore.editDict.sort"
|
||||
@change="changeSort"
|
||||
>
|
||||
<el-radio :label="Sort.normal" size="large">默认</el-radio>
|
||||
<el-radio :label="Sort.random" size="large">随机</el-radio>
|
||||
<el-radio :label="Sort.reverse" size="large">反转</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">学习模式</div>
|
||||
<div class="option">
|
||||
<el-radio-group v-model="settingStore.dictation" class="ml-4">
|
||||
<el-radio-group v-model="settingStore.dictation">
|
||||
<el-radio :label="false" size="large">再认</el-radio>
|
||||
<el-radio :label="true" size="large">拼写</el-radio>
|
||||
</el-radio-group>
|
||||
@@ -252,21 +290,12 @@ const dictIsArticle = $computed(() => {
|
||||
<div class="row">
|
||||
<div class="label">单词发音</div>
|
||||
<div class="option">
|
||||
<el-radio-group v-model="settingStore.wordSoundType" class="ml-4">
|
||||
<el-radio-group v-model="settingStore.wordSoundType">
|
||||
<el-radio label="us" size="large">美音</el-radio>
|
||||
<el-radio label="uk" size="large">英音</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="label">词序</div>-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <el-radio-group v-model="radio1" class="ml-4">-->
|
||||
<!-- <el-radio label="1" size="large">随机</el-radio>-->
|
||||
<!-- <el-radio label="2" size="large">正常</el-radio>-->
|
||||
<!-- </el-radio-group>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="row">
|
||||
<div class="label">单词自动发音</div>
|
||||
<div class="option">
|
||||
@@ -514,6 +543,10 @@ $header-height: 60rem;
|
||||
height: 40rem;
|
||||
word-break: keep-all;
|
||||
gap: 10rem;
|
||||
|
||||
.el-radio {
|
||||
margin-right: 10rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ watch(() => store.load, n => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="with-bg">
|
||||
<div class="with-bg anim">
|
||||
<Tooltip title="设置">
|
||||
<IconWrapper>
|
||||
<Icon icon="uil:setting" @click="showSettingModal = true"/>
|
||||
|
||||
@@ -13,10 +13,14 @@ const props = withDefaults(defineProps<{
|
||||
activeIndex?: number,
|
||||
showDel?: boolean,
|
||||
isActive?: boolean
|
||||
showTranslate?: boolean
|
||||
showWord?: boolean
|
||||
}>(), {
|
||||
activeIndex: -1,
|
||||
isActive: false,
|
||||
showDel: false
|
||||
showDel: false,
|
||||
showTranslate: true,
|
||||
showWord: true
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -77,11 +81,11 @@ const {
|
||||
@del="delWrongWord(word)"
|
||||
>
|
||||
<div class="item-title">
|
||||
<span class="word" :class="settingStore.dictation && 'text-shadow'">{{ word.name }}</span>
|
||||
<span class="word" :class="!showWord && 'text-shadow'">{{ word.name }}</span>
|
||||
<span class="phonetic">{{ word.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(word.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="word.trans.length && settingStore.translate">
|
||||
<div class="item-sub-title" v-if="word.trans.length && showTranslate">
|
||||
<div v-for="item in word.trans">{{ item }}</div>
|
||||
</div>
|
||||
</ListItem>
|
||||
|
||||
@@ -1,30 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import Modal from "@/components/Modal/Modal.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import {Word} from "@/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
let show = $ref(false)
|
||||
let loading = $ref(false)
|
||||
let list = $ref([])
|
||||
let title = $ref('')
|
||||
let progress = $ref(0)
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openWordListModal, (val: any) => {
|
||||
show = true
|
||||
list = val.list
|
||||
title = val.title
|
||||
show = true
|
||||
let count = 0
|
||||
if (val.translateLanguage === 'common') {
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
let w = list[index]
|
||||
if (!w.trans.length) {
|
||||
requestIdleCallback(() => {
|
||||
if (list.length) {
|
||||
let res = runtimeStore.translateWordList.find(a => a.name === w.name)
|
||||
if (res) w = Object.assign(w, res)
|
||||
count++
|
||||
if (count === list.length) {
|
||||
progress = 100
|
||||
} else {
|
||||
if (count % 30 === 0) progress = (count / list.length) * 100
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
count++
|
||||
if (count === list.length) {
|
||||
progress = 100
|
||||
} else {
|
||||
if (count % 30 === 0) progress = (count / list.length) * 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => show, v => {
|
||||
if (!v) {
|
||||
list = []
|
||||
progress = 0
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.openWordListModal)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -32,6 +71,14 @@ onUnmounted(() => {
|
||||
:title="title"
|
||||
v-model="show">
|
||||
<div class="all-word">
|
||||
<div class="progress-wrapper" v-if="progress !== 100 && list.length > 1000">
|
||||
<span>词典加载进度:</span>
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:duration="0"
|
||||
:indeterminate="false"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
<virtual-list class="virtual-list"
|
||||
:keeps="20"
|
||||
data-key="name"
|
||||
@@ -64,6 +111,19 @@ onUnmounted(() => {
|
||||
padding-top: 0;
|
||||
width: 400rem;
|
||||
height: 75vh;
|
||||
|
||||
.progress-wrapper {
|
||||
height: 45rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
font-size: 14rem;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.el-progress {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
104
src/components/WordListModal2.vue
Normal file
104
src/components/WordListModal2.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import Modal from "@/components/Modal/Modal.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import {Word} from "@/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
|
||||
let show = $ref(false)
|
||||
let loading = $ref(false)
|
||||
let list = $ref([])
|
||||
let title = $ref('')
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openWordListModal, (val: any) => {
|
||||
loading = true
|
||||
show = true
|
||||
list = cloneDeep(val.list)
|
||||
title = val.title
|
||||
setTimeout(() => {
|
||||
if (val.translateLanguage === 'common') {
|
||||
console.time()
|
||||
let tempList = cloneDeep(val.list)
|
||||
tempList.map((w: Word) => {
|
||||
if (!w.trans.length) {
|
||||
let res = runtimeStore.translateWordList.find(a => a.name === w.name)
|
||||
if (res) w = Object.assign(w, res)
|
||||
}
|
||||
})
|
||||
list = cloneDeep(tempList)
|
||||
console.timeEnd()
|
||||
}
|
||||
// loading = false
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.openWordListModal)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
:title="title"
|
||||
v-model="show">
|
||||
<div class="all-word">
|
||||
<virtual-list class="virtual-list"
|
||||
:keeps="20"
|
||||
data-key="name"
|
||||
v-loading="loading"
|
||||
:data-sources="list"
|
||||
:estimate-size="85"
|
||||
item-class="dict-virtual-item"
|
||||
>
|
||||
<template #={source}>
|
||||
<ListItem
|
||||
class="common-list-item"
|
||||
:show-volume="true">
|
||||
<div class="item-title">
|
||||
<span class="word">{{ source.name }}</span>
|
||||
<span class="phonetic">{{ source.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(source.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="source.trans.length">{{ source.trans.join(';') }}</div>
|
||||
</ListItem>
|
||||
</template>
|
||||
</virtual-list>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.all-word {
|
||||
padding: $space;
|
||||
padding-top: 0;
|
||||
width: 400rem;
|
||||
height: 75vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/variable.scss";
|
||||
|
||||
.virtual-list {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dict-virtual-item {
|
||||
margin-bottom: 15rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -285,6 +285,15 @@ export const useBaseStore = defineStore('base', {
|
||||
this[dict.type].chapterWordIndex = chapterWordIndex
|
||||
this[dict.type].chapterWords = [this[dict.type].words]
|
||||
} else {
|
||||
if (dict.type === DictType.article || dict.type === DictType.customArticle) {
|
||||
if (chapterIndex > dict.articles.length) {
|
||||
dict.chapterIndex = 0
|
||||
}
|
||||
} else {
|
||||
if (chapterIndex > dict.chapterWords.length) {
|
||||
dict.chapterIndex = 0
|
||||
}
|
||||
}
|
||||
let rIndex = this.myDicts.findIndex((v: Dict) => v.name === dict.name)
|
||||
if (rIndex > -1) {
|
||||
this.myDicts[rIndex] = dict
|
||||
|
||||
@@ -48,6 +48,7 @@ export type DictResource = {
|
||||
export interface Dict {
|
||||
id: string,
|
||||
name: string,
|
||||
description: string,
|
||||
sort: Sort,
|
||||
originWords: Word[],//原始单词
|
||||
words: Word[],
|
||||
@@ -199,6 +200,7 @@ export const languageCategoryOptions = [
|
||||
export const DefaultDict: Dict = {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
sort: Sort.normal,
|
||||
originWords: [],//原始单词
|
||||
words: [],
|
||||
|
||||
Reference in New Issue
Block a user