feat:fix dict list
This commit is contained in:
@@ -79,7 +79,7 @@
|
||||
<!-- <div class="tab" :class="index === 0 && 'active'" @click="index = 0">当前</div>-->
|
||||
<!-- <div class="tab" :class="index === 1 && 'active'" @click="index = 1">收藏</div>-->
|
||||
<!-- <div class="tab" :class="index === 2 && 'active'" @click="index = 2">错词本</div>-->
|
||||
<!-- <div class="tab" :class="index === 3 && 'active'" @click="index = 3">简单词</div>-->
|
||||
<!-- <div class="tab" :class="index === 3 && 'active'" @click="index = 3">已掌握</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="indicator" :style="{left:index * 25 + '%'}"></div>-->
|
||||
<!-- </div>-->
|
||||
@@ -255,4 +255,4 @@
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
<!--</style>-->
|
||||
<!--</style>-->
|
||||
|
||||
@@ -26,9 +26,9 @@ import {APP_NAME} from "../../../utils/const.ts";
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title">生词本、错词本、简单词</div>
|
||||
<div class="title">生词本、错词本、已掌握</div>
|
||||
<div class="txt">
|
||||
默写单词时输入错误会自动添加到错词本,以便后续复习。也可以添加到简单词,之后再遇到这个词便会自动跳过,同时也可以将其添加到生词本中,以便巩固复习
|
||||
默写单词时输入错误会自动添加到错词本,以便后续复习。也可以添加到已掌握,之后再遇到这个词便会自动跳过,同时也可以将其添加到生词本中,以便巩固复习
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
@@ -78,4 +78,4 @@ import {APP_NAME} from "../../../utils/const.ts";
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -74,7 +74,7 @@ function $no() {
|
||||
</div>
|
||||
<div class="item" @click="router.push('/mobile/simple')">
|
||||
<img src="../../../assets/img/complete.png" alt="">
|
||||
<span>简单词</span>
|
||||
<span>已掌握</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-list">
|
||||
|
||||
@@ -7,11 +7,11 @@ import SimpleList from "@/pages/mobile/components/SimpleList.vue";
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="简单词"/>
|
||||
<NavBar title="已掌握"/>
|
||||
<SimpleList/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -373,13 +373,13 @@ onMounted(() => {
|
||||
v-if="!isWordSimple(item)"
|
||||
class="easy"
|
||||
@click="toggleWordSimple(item)"
|
||||
title="标记为简单词"
|
||||
title="标记为已掌握"
|
||||
icon="material-symbols:check-circle-outline-rounded"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleWordSimple(item)"
|
||||
title="取消标记简单词"
|
||||
title="取消标记已掌握"
|
||||
icon="material-symbols:check-circle-rounded"/>
|
||||
</template>
|
||||
</WordList>
|
||||
|
||||
@@ -61,7 +61,7 @@ function startStudy() {
|
||||
showSearchDialog = true
|
||||
return
|
||||
}
|
||||
router.push('/learn-article')
|
||||
router.push('/study-article')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {onMounted, onUnmounted} from "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/article/practice-article/index.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
@@ -35,7 +35,6 @@ const emit = defineEmits<{
|
||||
}],
|
||||
}>()
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
let listRef: any = $ref()
|
||||
|
||||
function scrollToBottom() {
|
||||
@@ -73,11 +72,11 @@ let selectAll = $computed(() => {
|
||||
})
|
||||
|
||||
function toggleSelect(item) {
|
||||
let rIndex = selectIds.findIndex(v => v === item.id)
|
||||
let rIndex = selectIds.findIndex(v => v === item.word)
|
||||
if (rIndex > -1) {
|
||||
selectIds.splice(rIndex, 1)
|
||||
} else {
|
||||
selectIds.push(item.id)
|
||||
selectIds.push(item.word)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ function toggleSelectAll() {
|
||||
if (selectAll) {
|
||||
selectIds = []
|
||||
} else {
|
||||
selectIds = currentList.map(v => v.id)
|
||||
selectIds = currentList.map(v => v.word)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +118,7 @@ const s = useSlots()
|
||||
defineRender(
|
||||
() => {
|
||||
const d = (item) => <el-checkbox
|
||||
modelValue={selectIds.includes(item.id)}
|
||||
modelValue={selectIds.includes(item.word)}
|
||||
onChange={() => toggleSelect(item)}
|
||||
size="large"/>
|
||||
|
||||
@@ -211,7 +210,7 @@ defineRender(
|
||||
{currentList.map((item) => {
|
||||
return (
|
||||
<div class="list-item-wrapper"
|
||||
key={item.id}
|
||||
key={item.word}
|
||||
>
|
||||
{s.default({checkbox: d, item})}
|
||||
</div>
|
||||
@@ -238,4 +237,4 @@ defineRender(
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,12 @@ import {dictionaryResources} from "@/assets/dictionary.ts";
|
||||
import {groupBy} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictGroup from "@/pages/pc/components/list/DictGroup.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Input from "@/pages/pc/components/Input.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {computed} from "vue";
|
||||
import DictList from "@/pages/pc/components/list/DictList.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [],
|
||||
@@ -12,14 +18,11 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
const store = useBaseStore()
|
||||
|
||||
let currentLanguage = $ref('en')
|
||||
let currentTranslateLanguage = $ref('common')
|
||||
let groupByLanguage = groupBy(dictionaryResources, 'language')
|
||||
|
||||
function groupByDictTags(dictList: DictResource[]) {
|
||||
return dictList.reduce<Record<string, DictResource[]>>((result, dict) => {
|
||||
dict.tags.forEach((tag) => {
|
||||
if (Object.prototype.hasOwnProperty.call(result, tag)) {
|
||||
if (result[tag]) {
|
||||
result[tag].push(dict)
|
||||
} else {
|
||||
result[tag] = [dict]
|
||||
@@ -29,34 +32,64 @@ function groupByDictTags(dictList: DictResource[]) {
|
||||
}, {})
|
||||
}
|
||||
|
||||
const groupByTranslateLanguage = $computed(() => {
|
||||
return groupBy(groupByLanguage[currentLanguage], 'translateLanguage')
|
||||
})
|
||||
|
||||
const groupedByCategoryAndTag = $computed(() => {
|
||||
const currentTranslateLanguageDictList = groupByTranslateLanguage[currentTranslateLanguage]
|
||||
const groupByCategory = groupBy(currentTranslateLanguageDictList, 'category')
|
||||
|
||||
const groupByCategory = groupBy(dictionaryResources, 'category')
|
||||
let data = []
|
||||
for (const [key, value] of Object.entries(groupByCategory)) {
|
||||
data.push([key, groupByDictTags(value)])
|
||||
}
|
||||
// console.log('groupedByCategoryAndTag', data)
|
||||
return data
|
||||
})
|
||||
|
||||
let showSearchInput = $ref(false)
|
||||
let searchKey = $ref('')
|
||||
|
||||
const searchList = computed(() => {
|
||||
if (searchKey) {
|
||||
let s = searchKey.toLowerCase()
|
||||
return dictionaryResources.filter((item) => {
|
||||
return item.name.toLowerCase().includes(s)
|
||||
|| item.category.toLowerCase().includes(s)
|
||||
|| item.tags.join('').replace('所有', '').toLowerCase().includes(s)
|
||||
|| item?.url?.toLowerCase?.().includes?.(s)
|
||||
})
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
defineExpose({startSearch: () => showSearchInput = true})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<DictGroup
|
||||
v-for="item in groupedByCategoryAndTag"
|
||||
:select-id="store.currentStudyWordDict.id"
|
||||
@selectDict="e => emit('selectDict',e)"
|
||||
:groupByTag="item[1]"
|
||||
:category="item[0]"
|
||||
/>
|
||||
<div class="card">
|
||||
<div class="flex gap-4" v-if="showSearchInput">
|
||||
<Input placeholder="请输入词典名称/缩写/类别" v-model="searchKey" class="flex-1" autofocus/>
|
||||
<BaseButton @click="showSearchInput = false, searchKey = ''">取消</BaseButton>
|
||||
</div>
|
||||
<div class="title flex justify-between" v-else>
|
||||
<span>词典列表</span>
|
||||
<BaseIcon @click="showSearchInput = true" icon="fluent:search-24-regular"/>
|
||||
</div>
|
||||
<div class="mt-4" v-if="searchKey">
|
||||
<DictList
|
||||
v-if="searchList.length "
|
||||
@selectDict="e => emit('selectDict',e)"
|
||||
:list="searchList"
|
||||
:select-id="'-1'"/>
|
||||
<Empty v-else text="没有相关词典"/>
|
||||
</div>
|
||||
<div class="w-full" v-else>
|
||||
<DictGroup
|
||||
v-for="item in groupedByCategoryAndTag"
|
||||
:select-id="store.currentStudyWordDict.id"
|
||||
@selectDict="e => emit('selectDict',e)"
|
||||
:groupByTag="item[1]"
|
||||
:category="item[0]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -6,6 +6,7 @@ import {useDisableEventListener, useWindowClick} from "@/hooks/event.ts";
|
||||
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
placeholder: string
|
||||
autofocus?: boolean
|
||||
}>()
|
||||
|
||||
@@ -40,6 +41,7 @@ const vFocus = {
|
||||
<input type="text"
|
||||
:value="modelValue"
|
||||
v-focus="autofocus"
|
||||
:placeholder="placeholder"
|
||||
@input="e=>$emit('update:modelValue',e.target.value)"
|
||||
>
|
||||
<transition name="fade">
|
||||
@@ -52,7 +54,7 @@ const vFocus = {
|
||||
|
||||
|
||||
.base-input {
|
||||
border: 1px solid var(--color-second-bg);
|
||||
border: 1px solid var(--color-input-border);
|
||||
border-radius: .4rem;
|
||||
overflow: hidden;
|
||||
padding: .2rem .3rem;
|
||||
|
||||
@@ -158,10 +158,12 @@ let tab = $ref(2)
|
||||
<div class="flex gap-1 mt-26">
|
||||
<div class="phonetic" v-if="settingStore.wordSoundType === 'us' && word.phonetic0">[{{
|
||||
(settingStore.dictation && !showFullWord) ? '_'.repeat(word.phonetic0.length) : word.phonetic0
|
||||
}}]</div>
|
||||
}}]
|
||||
</div>
|
||||
<div class="phonetic" v-if="settingStore.wordSoundType === 'uk' && word.phonetic1">[{{
|
||||
(settingStore.dictation && !showFullWord) ? '_'.repeat(word.phonetic1.length) : word.phonetic1
|
||||
}}]</div>
|
||||
}}]
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
@@ -186,10 +188,10 @@ let tab = $ref(2)
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
|
||||
<div class="translate"
|
||||
<div class="translate anim"
|
||||
v-opacity="settingStore.translate"
|
||||
:style="{
|
||||
fontSize: settingStore.fontSize.wordTranslateFontSize +'px',
|
||||
opacity: settingStore.translate ? 1 : 0
|
||||
}"
|
||||
>
|
||||
<div class="my-2 flex" v-for="(v,i) in word.trans">
|
||||
@@ -199,13 +201,17 @@ let tab = $ref(2)
|
||||
</div>
|
||||
</div>
|
||||
<div class="other">
|
||||
<div class="line-white my-4"></div>
|
||||
<div class="sentences" v-if="word.sentences && word.sentences.length">
|
||||
<div class="sentence my-2" v-for="item in word.sentences">
|
||||
<SentenceHightLightWord class="text-lg" :text="item.c" :word="word.word" :dictation="settingStore.dictation"/>
|
||||
<div class="text-md">{{ item.cn }}</div>
|
||||
<template v-if="word.sentences && word.sentences.length">
|
||||
<div class="line-white my-4"></div>
|
||||
<div class="sentences">
|
||||
<div class="sentence my-2" v-for="item in word.sentences">
|
||||
<SentenceHightLightWord class="text-lg" :text="item.c" :word="word.word"
|
||||
:dictation="settingStore.dictation"/>
|
||||
<div class="text-md anim" v-opacity="settingStore.translate">{{ item.cn }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="line-white my-4"></div>
|
||||
<div class="tabs">
|
||||
<div @click="tab = 0" class="tab" :class="tab === 0 && 'active'">短语</div>
|
||||
@@ -215,8 +221,9 @@ let tab = $ref(2)
|
||||
</div>
|
||||
<template v-if="tab === 0">
|
||||
<div class="my-2" v-for="item in word.phrases">
|
||||
<SentenceHightLightWord class="text-lg" :text="item.c" :word="word.word" :high-light="false" :dictation="settingStore.dictation"/>
|
||||
<div class="text-md">{{ item.cn }}</div>
|
||||
<SentenceHightLightWord class="text-lg" :text="item.c" :word="word.word" :high-light="false"
|
||||
:dictation="settingStore.dictation"/>
|
||||
<div class="text-md anim" v-opacity="settingStore.translate">{{ item.cn }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="tab === 1">
|
||||
@@ -230,7 +237,7 @@ let tab = $ref(2)
|
||||
</template>
|
||||
<template v-if="tab === 2">
|
||||
<div class="mt-2">
|
||||
<div>
|
||||
<div v-if="word.relWords.root">
|
||||
词根:{{ word.relWords.root }}
|
||||
</div>
|
||||
<div class="flex my-2" v-for="item in word.relWords.rels">
|
||||
@@ -264,7 +271,6 @@ let tab = $ref(2)
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 1.2rem;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
|
||||
@@ -4,7 +4,6 @@ import {Word} from "@/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
item: Word,
|
||||
showTranslate?: boolean
|
||||
@@ -32,8 +31,20 @@ const playWordAudio = usePlayWordAudio()
|
||||
<span class="phonetic">{{ item.phonetic0 }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(item.word)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="item.trans.length && showTranslate">
|
||||
<div v-for="v in item.trans">{{ (v.pos ? v.pos + '.' : '') + (v.tran ) }}</div>
|
||||
<div class="item-sub-title flex flex-col gap-2" v-if="item.trans.length && showTranslate">
|
||||
<div v-for="v in item.trans">
|
||||
<el-popover
|
||||
v-if="v.cn.length > 30"
|
||||
width="300"
|
||||
:content="v.pos + ' ' + v.cn"
|
||||
placement="top"
|
||||
>
|
||||
<template #reference>
|
||||
<span>{{ v.pos + ' ' + v.cn.slice(0, 30) + '...' }}</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
<span v-else>{{ v.pos + ' ' + v.cn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {DictResource} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
dict?: Dict,
|
||||
dict?: DictResource,
|
||||
active?: boolean
|
||||
showDel?: boolean
|
||||
}>()
|
||||
@@ -28,7 +28,7 @@ const emit = defineEmits<{
|
||||
<div class="desc" :style="{opacity:dict.name !== dict.description?1:0}">{{ dict.description }}</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="num">{{ dict.length + '词' }}</div>
|
||||
<div class="num text-align-right">{{ dict.length + '词' }}</div>
|
||||
</div>
|
||||
<div class="del" v-if="dict.showDel && !active">
|
||||
<BaseIcon icon="solar:trash-bin-minimalistic-linear" @click="emit('del')"/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {Dict} from "@/types.ts";
|
||||
import {Dict, DictResource} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import DictItem from "@/pages/pc/components/list/DictItem.vue";
|
||||
|
||||
defineProps<{
|
||||
list?: Dict[],
|
||||
list?: DictResource[],
|
||||
selectId?: string
|
||||
}>()
|
||||
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {assign, cloneDeep} from "lodash-es";
|
||||
import {nanoid} from "nanoid";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {_checkDictWords, useNav} from "@/utils";
|
||||
import {useNav} from "@/utils";
|
||||
import BaseTable from "@/pages/pc/components/BaseTable.vue";
|
||||
import WordItem from "@/pages/pc/components/WordItem.vue";
|
||||
import type {Word} from "@/types.ts";
|
||||
@@ -16,8 +14,6 @@ import type {FormInstance, FormRules} from "element-plus";
|
||||
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
|
||||
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const store = useBaseStore()
|
||||
const route = useRoute()
|
||||
let loading = $ref(false)
|
||||
|
||||
let list = $computed({
|
||||
@@ -30,31 +26,13 @@ let list = $computed({
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
switch (Number(route.query.type)) {
|
||||
case -1:
|
||||
if (runtimeStore.routeData) {
|
||||
loading = true
|
||||
runtimeStore.editDict = cloneDeep(runtimeStore.routeData)
|
||||
await _checkDictWords(runtimeStore.editDict)
|
||||
setTimeout(() => {
|
||||
loading = false
|
||||
}, 300)
|
||||
}
|
||||
break
|
||||
case 0:
|
||||
runtimeStore.editDict = cloneDeep(store.collectWord)
|
||||
break
|
||||
case 1:
|
||||
runtimeStore.editDict = cloneDeep(store.wrong)
|
||||
break
|
||||
case 2:
|
||||
runtimeStore.editDict = cloneDeep(store.simple)
|
||||
break
|
||||
case 3:
|
||||
runtimeStore.editDict = cloneDeep(store.master)
|
||||
break
|
||||
default:
|
||||
break
|
||||
if (runtimeStore.routeData) {
|
||||
loading = true
|
||||
runtimeStore.editDict = cloneDeep(runtimeStore.routeData)
|
||||
// await _checkDictWords(runtimeStore.editDict)
|
||||
setTimeout(() => {
|
||||
loading = false
|
||||
}, 300)
|
||||
}
|
||||
})
|
||||
const {back} = useNav()
|
||||
@@ -161,109 +139,111 @@ function s(ss) {
|
||||
defineRender(() => {
|
||||
return (
|
||||
<BasePage>
|
||||
<header class="flex gap-4 mb-2 items-center">
|
||||
<BaseIcon onClick={back} icon="octicon:arrow-left-24" width={20}/>
|
||||
<div class="left">
|
||||
<div class="top">
|
||||
<div class="text-xl">
|
||||
{runtimeStore.editDict.name}
|
||||
<div className="card">
|
||||
<header class="flex gap-4 mb-2 items-center">
|
||||
<BaseIcon onClick={back} icon="octicon:arrow-left-24" width={20}/>
|
||||
<div class="left">
|
||||
<div class="top">
|
||||
<div class="text-xl">
|
||||
{runtimeStore.editDict.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
runtimeStore.editDict.description ?
|
||||
<div class="desc">{runtimeStore.editDict.description}</div> : null
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
<div class="flex" style="height:calc(100vh - 8rem)">
|
||||
<div class="w-1/2">
|
||||
<BaseTable
|
||||
class="h-full"
|
||||
list={list}
|
||||
loading={loading}
|
||||
onUpdate:list={e => list = e}
|
||||
del={delWord}
|
||||
batchDel={batchDel}
|
||||
add={addWord}
|
||||
>
|
||||
{
|
||||
(val) =>
|
||||
<WordItem
|
||||
item={val.item}>
|
||||
{{
|
||||
prefix: () => val.checkbox(val.item),
|
||||
suffix: () => (
|
||||
<div class='flex flex-col'>
|
||||
<BaseIcon
|
||||
class="option-icon"
|
||||
onClick={() => editWord(val.item)}
|
||||
title="编辑"
|
||||
icon="tabler:edit"/>
|
||||
<PopConfirm title="确认删除?"
|
||||
onConfirm={() => delWord(val.item.id)}
|
||||
>
|
||||
runtimeStore.editDict.description ?
|
||||
<div class="desc">{runtimeStore.editDict.description}</div> : null
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
<div class="flex" style="height:calc(100vh - 8rem)">
|
||||
<div class="w-1/2">
|
||||
<BaseTable
|
||||
class="h-full"
|
||||
list={list}
|
||||
loading={loading}
|
||||
onUpdate:list={e => list = e}
|
||||
del={delWord}
|
||||
batchDel={batchDel}
|
||||
add={addWord}
|
||||
>
|
||||
{
|
||||
(val) =>
|
||||
<WordItem
|
||||
item={val.item}>
|
||||
{{
|
||||
prefix: () => val.checkbox(val.item),
|
||||
suffix: () => (
|
||||
<div class='flex flex-col'>
|
||||
<BaseIcon
|
||||
class="option-icon"
|
||||
title="删除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</PopConfirm>
|
||||
onClick={() => editWord(val.item)}
|
||||
title="编辑"
|
||||
icon="tabler:edit"/>
|
||||
<PopConfirm title="确认删除?"
|
||||
onConfirm={() => delWord(val.item.id)}
|
||||
>
|
||||
<BaseIcon
|
||||
class="option-icon"
|
||||
title="删除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</PopConfirm>
|
||||
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</WordItem>
|
||||
}
|
||||
</BaseTable>
|
||||
</div>
|
||||
{
|
||||
wordFormData.type ? (
|
||||
<div class="flex-1 ml-4">
|
||||
<div class="common-title">
|
||||
{wordFormData.type === FormMode.Add ? '添加' : '修改'}单词
|
||||
</div>
|
||||
<el-form
|
||||
className="form"
|
||||
ref="wordFormRef"
|
||||
rules={wordRules}
|
||||
model={wordForm}
|
||||
label-width="7rem">
|
||||
<el-form-item label="单词" prop="word">
|
||||
<el-input
|
||||
modelValue={wordForm.word}
|
||||
onUpdate:model-value={e => wordForm.word = e}
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="翻译">
|
||||
<el-input
|
||||
modelValue={wordForm.trans}
|
||||
onUpdate:model-value={e => wordForm.trans = e}
|
||||
placeholder="多个翻译请换行"
|
||||
autosize={{minRows: 6, maxRows: 10}}
|
||||
type="textarea"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="音标/发音①">
|
||||
<el-input
|
||||
modelValue={wordForm.phonetic0}
|
||||
onUpdate:model-value={e => wordForm.phonetic0 = e}
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="音标/发音②">
|
||||
<el-input
|
||||
modelValue={wordForm.phonetic1}
|
||||
onUpdate:model-value={e => wordForm.phonetic1 = e}/>
|
||||
</el-form-item>
|
||||
<div class="center">
|
||||
<el-button
|
||||
onClick={closeWordForm}>关闭
|
||||
</el-button>
|
||||
<el-button type="primary"
|
||||
onClick={onSubmitWord}>保存
|
||||
</el-button>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</WordItem>
|
||||
}
|
||||
</BaseTable>
|
||||
</div>
|
||||
{
|
||||
wordFormData.type ? (
|
||||
<div class="flex-1 ml-4">
|
||||
<div class="common-title">
|
||||
{wordFormData.type === FormMode.Add ? '添加' : '修改'}单词
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
<el-form
|
||||
className="form"
|
||||
ref="wordFormRef"
|
||||
rules={wordRules}
|
||||
model={wordForm}
|
||||
label-width="7rem">
|
||||
<el-form-item label="单词" prop="word">
|
||||
<el-input
|
||||
modelValue={wordForm.word}
|
||||
onUpdate:model-value={e => wordForm.word = e}
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="翻译">
|
||||
<el-input
|
||||
modelValue={wordForm.trans}
|
||||
onUpdate:model-value={e => wordForm.trans = e}
|
||||
placeholder="多个翻译请换行"
|
||||
autosize={{minRows: 6, maxRows: 10}}
|
||||
type="textarea"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="音标/发音①">
|
||||
<el-input
|
||||
modelValue={wordForm.phonetic0}
|
||||
onUpdate:model-value={e => wordForm.phonetic0 = e}
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="音标/发音②">
|
||||
<el-input
|
||||
modelValue={wordForm.phonetic1}
|
||||
onUpdate:model-value={e => wordForm.phonetic1 = e}/>
|
||||
</el-form-item>
|
||||
<div class="center">
|
||||
<el-button
|
||||
onClick={closeWordForm}>关闭
|
||||
</el-button>
|
||||
<el-button type="primary"
|
||||
onClick={onSubmitWord}>保存
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, onUnmounted} from "vue"
|
||||
import {inject, onMounted, onUnmounted, provide} from "vue"
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import {ShortcutKey, StudyData} from "@/types.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
|
||||
@@ -32,12 +32,6 @@ 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(() => {
|
||||
@@ -50,6 +44,30 @@ onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
const statStore = usePracticeStore()
|
||||
let studyData = inject<StudyData>('studyData')
|
||||
|
||||
const status = $computed(() => {
|
||||
let str = '正在'
|
||||
switch (statStore.step) {
|
||||
case 0:
|
||||
str += `学习新词`
|
||||
break
|
||||
case 1:
|
||||
str += `复习`
|
||||
break
|
||||
case 2:
|
||||
str += '默写'
|
||||
break
|
||||
}
|
||||
return str
|
||||
})
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!studyData.words.length) return 0
|
||||
return ((studyData.index / studyData.words.length) * 100)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -71,9 +89,9 @@ onUnmounted(() => {
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="stat gap-6">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="num">{{ `${studyData.index}/${studyData.words.length}` }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
<div class="name">{{ status }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
@@ -96,13 +114,13 @@ onUnmounted(() => {
|
||||
v-if="!isSimple"
|
||||
class="collect"
|
||||
@click="$emit('toggleSimple')"
|
||||
:title="`标记为简单词(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
|
||||
:title="`标记为已掌握(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
|
||||
icon="material-symbols:check-circle-outline-rounded"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="$emit('toggleSimple')"
|
||||
:title="`取消标记简单词(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
|
||||
:title="`取消标记已掌握(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
|
||||
icon="material-symbols:check-circle-rounded"/>
|
||||
|
||||
<BaseIcon
|
||||
@@ -202,7 +220,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
width: 5rem;
|
||||
width: 6rem;
|
||||
color: gray;
|
||||
|
||||
.line {
|
||||
|
||||
@@ -8,28 +8,26 @@ import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {_getAccomplishDate, _getAccomplishDays, useNav} from "@/utils";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {getDefaultDict} from "@/types.ts";
|
||||
import {Dict, DictResource, getDefaultDict} from "@/types.ts";
|
||||
import {onMounted} from "vue";
|
||||
import {getCurrentStudyWord} from "@/hooks/dict.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {EventKey, useEvent} from "@/utils/eventBus.ts";
|
||||
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
|
||||
import DictGroup from "@/pages/pc/components/list/DictGroup.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {getArticleBookDataByUrl} from "@/utils/article.ts";
|
||||
|
||||
const store = useBaseStore()
|
||||
const router = useRouter()
|
||||
const {nav} = useNav()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
|
||||
let showMore = $ref(false)
|
||||
const otherWordDictList = $computed(() => {
|
||||
if (showMore) return store.otherWordDictList
|
||||
else return store.otherWordDictList.slice(0, 4)
|
||||
})
|
||||
|
||||
let currentStudy = $ref({
|
||||
new: [],
|
||||
review: [],
|
||||
@@ -61,6 +59,30 @@ function changePerDayStudyNumber() {
|
||||
function selectDict(e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
async function goDictDetail2(val: Dict) {
|
||||
runtimeStore.editDict = cloneDeep(val)
|
||||
nav('edit-word-dict')
|
||||
}
|
||||
|
||||
async function getBookDetail(val: DictResource) {
|
||||
let r = await getArticleBookDataByUrl(val)
|
||||
runtimeStore.editDict = cloneDeep(r)
|
||||
nav('book-detail')
|
||||
}
|
||||
|
||||
let showAddChooseDialog = $ref(false)
|
||||
let dictListRef = $ref<any>()
|
||||
|
||||
function goChooseDict() {
|
||||
showAddChooseDialog = false
|
||||
dictListRef.startSearch()
|
||||
}
|
||||
|
||||
function addDict() {
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -131,55 +153,29 @@ function selectDict(e) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" v-if="otherWordDictList.length">
|
||||
<div class="flex justify-between">
|
||||
<div class="title">
|
||||
其他学习词典
|
||||
</div>
|
||||
<BaseIcon icon="ic:round-add"
|
||||
title="切换词典"
|
||||
@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 otherWordDictList">
|
||||
<div class="flex justify-between w-full">
|
||||
<span>{{ i.name }}</span>
|
||||
<div class="text-2xl ml-2 flex gap-4">
|
||||
<BaseIcon title="删除" icon="hugeicons:delete-02" @click="store.delWordDict(i)"/>
|
||||
<BaseIcon title="学习" icon="nonicons:go-16" @click="store.changeWordDict(getDefaultDict(i))"/>
|
||||
<BaseIcon title="修改" icon="nonicons:go-16" @click="nav('edit-word-dict',{type:-1},i)"/>
|
||||
</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>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 text-2xl" v-if="store.otherWordDictList.length > 4">
|
||||
<BaseIcon @click="showMore = !showMore" v-if="showMore" icon="mingcute:up-line"/>
|
||||
<BaseIcon @click="showMore = !showMore" v-else icon="mingcute:down-line"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col">
|
||||
<div class="title">
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-4 mt-4">
|
||||
<div class="book" v-for="item in store.word.bookList" @click="nav('edit-word-dict',{type:item.type})">
|
||||
<div class="book" v-for="item in store.word.bookList" @click="goDictDetail2(item)">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="absolute bottom-4 right-4">{{ item.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="showAddChooseDialog = true">
|
||||
<div class="center h-full">
|
||||
<Icon
|
||||
width="40px"
|
||||
icon="fluent:add-20-filled"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
所有词典
|
||||
</div>
|
||||
<DictListPanel
|
||||
@selectDict="selectDict"
|
||||
/>
|
||||
</div>
|
||||
<DictListPanel
|
||||
ref="dictListRef"
|
||||
@selectDict="selectDict"
|
||||
/>
|
||||
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
@@ -234,6 +230,16 @@ function selectDict(e) {
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model="showAddChooseDialog" title="选项">
|
||||
<div class="color-black px-6 w-100">
|
||||
<div class="cursor-pointer hover:bg-black/10 p-2 rounded"
|
||||
@click="goChooseDict">选择一本词典
|
||||
</div>
|
||||
<p class="cursor-pointer hover:bg-black/10 p-2 rounded" @click="addDict">创建自己的词典</p>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import {watch} from "vue"
|
||||
import {provide, watch} from "vue"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {getDefaultWord, ShortcutKey, Word} from "@/types.ts";
|
||||
import {getDefaultWord, ShortcutKey, StudyData, Word} from "@/types.ts";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts"
|
||||
import {cloneDeep, shuffle} from "lodash-es"
|
||||
import {usePracticeStore} from "@/stores/practice.ts"
|
||||
@@ -11,7 +11,6 @@ import {Icon} from "@iconify/vue";
|
||||
import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
import Typing from "@/pages/pc/components/Typing.vue";
|
||||
import Panel from "@/pages/pc/components/Panel.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import WordList from "@/pages/pc/components/list/WordList.vue";
|
||||
@@ -42,7 +41,6 @@ const emit = defineEmits<{
|
||||
|
||||
const typingRef: any = $ref()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const statStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
@@ -54,16 +52,19 @@ const {
|
||||
} = useWordOptions()
|
||||
|
||||
let allWrongWords = []
|
||||
let current = $ref({
|
||||
|
||||
let data = $ref<StudyData>({
|
||||
index: 0,
|
||||
words: [],
|
||||
wrongWords: [],
|
||||
})
|
||||
|
||||
provide('studyData', data)
|
||||
|
||||
watch(() => props.data, () => {
|
||||
current.words = props.data.new
|
||||
current.index = 0
|
||||
current.wrongWords = []
|
||||
data.words = props.data.new
|
||||
data.index = 0
|
||||
data.wrongWords = []
|
||||
allWrongWords = []
|
||||
|
||||
statStore.step = 0
|
||||
@@ -76,26 +77,26 @@ watch(() => props.data, () => {
|
||||
}, {immediate: true, deep: true})
|
||||
|
||||
const word = $computed(() => {
|
||||
return current.words[current.index] ?? getDefaultWord()
|
||||
return data.words[data.index] ?? getDefaultWord()
|
||||
})
|
||||
|
||||
const prevWord: Word = $computed(() => {
|
||||
return current.words?.[current.index - 1] ?? undefined
|
||||
return data.words?.[data.index - 1] ?? undefined
|
||||
})
|
||||
|
||||
const nextWord: Word = $computed(() => {
|
||||
return current.words?.[current.index + 1] ?? undefined
|
||||
return data.words?.[data.index + 1] ?? undefined
|
||||
})
|
||||
|
||||
function next(isTyping: boolean = true) {
|
||||
if (current.index === current.words.length - 1) {
|
||||
if (current.wrongWords.length) {
|
||||
if (data.index === data.words.length - 1) {
|
||||
if (data.wrongWords.length) {
|
||||
console.log('学完了,但还有错词')
|
||||
current.words = shuffle(cloneDeep(current.wrongWords))
|
||||
current.index = 0
|
||||
current.wrongWords = []
|
||||
data.words = shuffle(cloneDeep(data.wrongWords))
|
||||
data.index = 0
|
||||
data.wrongWords = []
|
||||
} else {
|
||||
console.log('学完了,没错词', statStore.total, statStore.step, current.index)
|
||||
console.log('学完了,没错词', statStore.total, statStore.step, data.index)
|
||||
isTyping && statStore.inputWordNumber++
|
||||
statStore.speed = Date.now() - statStore.startDate
|
||||
|
||||
@@ -107,24 +108,24 @@ function next(isTyping: boolean = true) {
|
||||
|
||||
if (statStore.step === 1) {
|
||||
settingStore.dictation = true
|
||||
current.words = shuffle(props.data.write.concat(props.data.new).concat(props.data.review))
|
||||
data.words = shuffle(props.data.write.concat(props.data.new).concat(props.data.review))
|
||||
statStore.step++
|
||||
current.index = 0
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
if (statStore.step === 0) {
|
||||
statStore.step++
|
||||
if (props.data.review.length) {
|
||||
current.words = shuffle(props.data.review)
|
||||
data.words = shuffle(props.data.review)
|
||||
settingStore.dictation = false
|
||||
current.index = 0
|
||||
data.index = 0
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current.index++
|
||||
data.index++
|
||||
isTyping && statStore.inputWordNumber++
|
||||
// console.log('这个词完了')
|
||||
}
|
||||
@@ -134,8 +135,8 @@ function wordWrong() {
|
||||
if (!store.wrong.words.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
store.wrong.words.push(word)
|
||||
}
|
||||
if (!current.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
current.wrongWords.push(word)
|
||||
if (!data.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
data.wrongWords.push(word)
|
||||
}
|
||||
if (!allWrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
allWrongWords.push(word)
|
||||
@@ -160,10 +161,10 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
//TODO 略过忽略的单词上
|
||||
function prev() {
|
||||
if (current.index === 0) {
|
||||
if (data.index === 0) {
|
||||
ElMessage.warning('已经是第一个了~')
|
||||
} else {
|
||||
current.index--
|
||||
data.index--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,28 +204,13 @@ useEvents([
|
||||
[ShortcutKey.PlayWordPronunciation, play],
|
||||
])
|
||||
|
||||
const status = $computed(() => {
|
||||
let str = '正在'
|
||||
switch (statStore.step) {
|
||||
case 0:
|
||||
str += `学习新单词`
|
||||
break
|
||||
case 1:
|
||||
str += '复习'
|
||||
break
|
||||
case 2:
|
||||
str += '默写'
|
||||
break
|
||||
}
|
||||
return str
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<div class="practice-word">
|
||||
<div class="near-word" v-if="settingStore.showNearWord">
|
||||
<div class="prev"
|
||||
<div class="absolute z-1 top-4 w-full" v-if="settingStore.showNearWord">
|
||||
<div class="center gap-2 cursor-pointer float-left"
|
||||
@click="prev"
|
||||
v-if="prevWord">
|
||||
<Icon class="arrow" icon="bi:arrow-left" width="22"/>
|
||||
@@ -234,7 +220,7 @@ const status = $computed(() => {
|
||||
<div class="word">{{ prevWord.word }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="next"
|
||||
<div class="center gap-2 cursor-pointer float-right "
|
||||
@click="next(false)"
|
||||
v-if="nextWord">
|
||||
<Tooltip
|
||||
@@ -267,24 +253,15 @@ const status = $computed(() => {
|
||||
<div class="panel-page-item pl-4"
|
||||
v-loading="!store.load"
|
||||
>
|
||||
<div class="list-header">
|
||||
<div class="flex items-center gap-1">
|
||||
<Icon icon="material-symbols:hourglass-empty-rounded"/>
|
||||
<span class="text-sm"> {{ status }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span> {{ current.index }} / {{ current.words.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="current.words.length"
|
||||
v-if="data.words.length"
|
||||
:is-active="active"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="current.words"
|
||||
:activeIndex="current.index"
|
||||
@click="(val:any) => current.index = val.index"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
>
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
@@ -301,13 +278,13 @@ const status = $computed(() => {
|
||||
v-if="!isWordSimple(item)"
|
||||
class="easy"
|
||||
@click="toggleWordSimple(item)"
|
||||
title="标记为简单词"
|
||||
title="标记为已掌握"
|
||||
icon="material-symbols:check-circle-outline-rounded"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleWordSimple(item)"
|
||||
title="取消标记简单词"
|
||||
title="取消标记已掌握"
|
||||
icon="material-symbols:check-circle-rounded"/>
|
||||
</template>
|
||||
</WordList>
|
||||
@@ -342,48 +319,6 @@ const status = $computed(() => {
|
||||
gap: .4rem;
|
||||
position: relative;
|
||||
width: var(--toolbar-width);
|
||||
|
||||
.near-word {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
& > div {
|
||||
width: 45%;
|
||||
align-items: center;
|
||||
|
||||
.arrow {
|
||||
font-size: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.word {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: .2rem;
|
||||
font-family: var(--word-font-family);
|
||||
}
|
||||
|
||||
.prev {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
float: left;
|
||||
gap: .8rem;
|
||||
}
|
||||
|
||||
.next {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: .8rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.options-wrapper {
|
||||
position: absolute;
|
||||
margin-top: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.word-panel-wrapper {
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
},
|
||||
{
|
||||
"id": "skip",
|
||||
"name": "简单词",
|
||||
"name": "已掌握",
|
||||
"description": "",
|
||||
"sort": 0,
|
||||
"originWords": [],
|
||||
@@ -115323,4 +115323,4 @@
|
||||
"load": true
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user