Optimize UI interface

This commit is contained in:
zyronon
2023-10-30 18:33:05 +08:00
parent 00d7a3ff0b
commit 1e201a429e
16 changed files with 322 additions and 414 deletions

2
components.d.ts vendored
View File

@@ -37,6 +37,7 @@ declare module 'vue' {
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
Empty: typeof import('./src/components/Empty.vue')['default']
FeedbackModal: typeof import('./src/components/Toolbar/FeedbackModal.vue')['default']
Fireworks: typeof import('./src/components/Fireworks.vue')['default']
Footer: typeof import('./src/components/Practice/Footer.vue')['default']
@@ -66,6 +67,5 @@ declare module 'vue' {
WordItem: typeof import('./src/components/WordItem.vue')['default']
WordList: typeof import('./src/components/WordList.vue')['default']
WordListModal: typeof import('./src/components/WordListModal.vue')['default']
WordPanel: typeof import('./src/components/Practice/PracticeWord/WordPanel.vue')['default']
}
}

View File

@@ -135,7 +135,7 @@ footer {
justify-content: center;
}
.current-practice-dict {
.panel-page-item {
display: flex;
flex-direction: column;
height: 100%;
@@ -143,18 +143,29 @@ footer {
box-sizing: border-box;
header {
padding: 8rem $space;
min-height: 50rem;
padding: 10rem $space;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14rem;
font-size: 16rem;
color: black;
.left {
flex: 1;
display: flex;
align-items: center;
gap: 10rem;
}
.title{
max-width: 70%;
}
.right{
word-break: keep-all;
}
}
.scroll {

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1698646880606" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1765" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M573.44 389.12V245.76H358.4c-28.16 0-51.2 23.04-51.2 51.2v460.8c0 28.16 23.04 51.2 51.2 51.2h307.2c28.16 0 51.2-23.04 51.2-51.2V389.12h-143.36z m43.52 101.376L502.784 629.76l-21.504-20.48-21.504 20.992v-41.984l133.12-85.504-147.456 71.68-38.4-35.84v-4.608l209.92-48.128v4.608z" fill="#4A68CC" opacity=".2" p-id="1766"></path><path d="M102.4 896a409.6 51.2 0 1 0 819.2 0 409.6 51.2 0 1 0-819.2 0Z" fill="#4A68CC" opacity=".1" p-id="1767"></path><path d="M96.256 525.312c0 8.704 6.656 15.36 15.36 15.36s15.36-6.656 15.36-15.36-6.656-15.36-15.36-15.36c-8.192 0-15.36 7.168-15.36 15.36zM147.456 822.272c0 8.704 6.656 15.36 15.36 15.36s15.36-6.656 15.36-15.36-6.656-15.36-15.36-15.36c-8.192 0-15.36 7.168-15.36 15.36zM926.72 847.872c-15.36 4.096-18.944 7.168-23.04 23.04-4.096-15.36-7.168-18.944-23.04-23.04 15.36-4.096 18.944-7.68 23.04-23.04 4.096 15.36 7.168 18.944 23.04 23.04zM199.68 467.456c-31.232 8.192-37.888 14.848-46.08 46.08-8.192-31.232-14.848-37.888-46.08-46.08 31.232-8.192 37.888-14.848 46.08-46.08 8.192 31.232 14.848 37.888 46.08 46.08zM821.76 214.528c-15.36 4.096-18.432 7.168-22.528 22.528-4.096-15.36-7.168-18.432-22.528-22.528 15.36-4.096 18.432-7.168 22.528-22.528 3.584 15.36 7.168 18.432 22.528 22.528zM882.688 135.68c-39.936 10.24-48.128 18.944-58.88 58.88-10.24-39.936-18.944-48.128-58.88-58.88 39.936-10.24 48.128-18.944 58.88-58.88 10.24 39.424 18.944 48.128 58.88 58.88zM783.36 337.408c0-1.024 0-2.048-0.512-2.56v-0.512c0-0.512-0.512-1.536-0.512-2.048 0-0.512 0-0.512-0.512-1.024s-0.512-1.024-1.024-1.536l-0.512-0.512c-0.512-0.512-1.024-1.536-1.536-2.048l-143.36-143.36c-0.512-0.512-1.024-1.024-2.048-1.536l-0.512-0.512c-0.512-0.512-1.024-0.512-1.536-1.024-0.512 0-0.512 0-1.024-0.512s-1.536-0.512-2.048-0.512h-0.512c-1.024 0-2.048-0.512-2.56-0.512H358.4c-65.024 0-117.76 52.736-117.76 117.76v460.8c0 65.024 52.736 117.76 117.76 117.76h307.2c65.024 0 117.76-52.736 117.76-117.76V337.408c0 0.512 0 0.512 0 0z m-143.36-105.984L731.136 322.56H640V231.424z m25.6 613.376H358.4c-48.128 0-87.04-38.912-87.04-87.04V296.96c0-48.128 38.912-87.04 87.04-87.04h250.88v128c0 8.704 6.656 15.36 15.36 15.36h128v404.48c0 48.128-38.912 87.04-87.04 87.04z" fill="#4A68CC" opacity=".5" p-id="1768"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -11,22 +11,19 @@ interface IProps {
type?: 'primary' | 'link'
}
const props = withDefaults(defineProps<IProps>(), {
withDefaults(defineProps<IProps>(), {
type: 'primary',
size: 'normal',
})
defineEmits(['click'])
function click() {
}
</script>
<template>
<Tooltip :disabled="!keyboard" :title="`快捷键: ${keyboard}`">
<div class="base-button"
@click="(!disabled && !loading) && $emit('click')"
@click="e => (!disabled && !loading) && $emit('click',e)"
:class="[
active && 'active',
size,
@@ -113,7 +110,8 @@ function click() {
&.link {
border-radius: 0;
border-bottom: 2px solid transparent;
&:hover{
&:hover {
border-bottom: 2px solid black;
}
}

29
src/components/Empty.vue Normal file
View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
defineProps<{
text?: string
}>()
</script>
<template>
<div class="empty">
<img src="@/assets/img/缺省页_空白页-通用.svg" alt="">
<span>{{ text ?? '空荡荡的~'}}</span>
</div>
</template>
<style scoped lang="scss">
.empty {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
font-size: 12rem;
gap: 20rem;
img {
width: 120rem;
}
}
</style>

View File

@@ -33,11 +33,18 @@ export default {
methods: {
showPop(e) {
if (this.disabled) return
e.stopPropagation()
e?.stopPropagation()
let rect = e.target.getBoundingClientRect()
this.show = true
nextTick(() => {
this.$refs.tip.style.top = rect.top + 'px'
let tip = this.$refs?.tip?.getBoundingClientRect()
console.log('rect', rect, tip)
if (!tip) return
if (rect.top < 150) {
this.$refs.tip.style.top = rect.top + rect.height + tip.height + 30 + 'px'
} else {
this.$refs.tip.style.top = rect.top - 10 + 'px'
}
this.$refs.tip.style.left = rect.left + rect.width / 2 - 50 + 'px'
})
},
@@ -98,8 +105,8 @@ $bg-color: rgb(226, 226, 226);
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10rem;
font-size: 10rem;
gap: 12rem;
font-size: 12rem;
div {
cursor: pointer;
@@ -108,8 +115,8 @@ $bg-color: rgb(226, 226, 226);
.main {
color: gray;
background: $bg-color;
padding: 3rem 8rem;
border-radius: 2rem;
padding: 3rem 10rem;
border-radius: 4rem;
}
}
}

View File

@@ -9,6 +9,8 @@ import PopConfirm from "@/components/PopConfirm.vue"
import BaseButton from "@/components/BaseButton.vue";
import {useSettingStore} from "@/stores/setting.ts";
import Close from "@/components/Close.vue";
import Empty from "@/components/Empty.vue";
import ArticleList from "@/components/Article/ArticleList.vue";
const store = useBaseStore()
const settingStore = useSettingStore()
@@ -31,10 +33,13 @@ watch(() => settingStore.showPanel, n => {
}
})
let practiceType = $ref(DictType.word)
function changeIndex(i: number, dict: Dict) {
store.changeDict(dict, dict.chapterIndex, i)
store.changeDict(dict, dict.chapterIndex, i,practiceType)
}
</script>
<template>
<Transition name="fade">
@@ -58,69 +63,84 @@ function changeIndex(i: number, dict: Dict) {
<slot></slot>
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ store.collect.words.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.collect)"
:isActive="settingStore.showPanel && tabIndex === 1"
:list="store.collect.words"
:activeIndex="-1"/>
<div class="panel-page-item">
<header>
<div class="left">
<el-radio-group v-model="practiceType">
<el-radio-button border :label="DictType.word">单词</el-radio-button>
<el-radio-button border :label="DictType.article">文章</el-radio-button>
</el-radio-group>
<div class="dict-name" v-if="practiceType === DictType.word">{{ store.collect.words.length }}个单词</div>
<div class="dict-name" v-else> {{ store.collect.articles.length }}篇文章</div>
</div>
<template v-if="store.current.dictType !== DictType.collect &&
(
( practiceType === DictType.word && store.collect.words.length) ||
( practiceType === DictType.article && store.collect.articles.length)
)">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.collect)"
>
<BaseButton size="small">切换</BaseButton>
</PopConfirm>
</template>
</header>
<template v-if="practiceType === DictType.word">
<WordList
v-if="store.collect.words.length"
class="word-list"
:list="store.collect.words"/>
<Empty v-else/>
</template>
<template v-else>
<ArticleList
v-if="store.collect.articles.length"
style="padding: 0 20rem;"
:select-item="{id: ''} as any"
v-model:list="store.collect.articles"/>
<Empty v-else/>
</template>
</div>
<footer v-if="store.current.dictType !== DictType.collect && store.collect.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.collect)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
</div>
<div class="slide-item">
<header>
<a href="" target="_blank"></a>
<div class="dict-name">总词数{{ store.wrong.words.length }}</div>
</header>
<div class="content">
<div class="panel-page-item" v-if="store.wrong.words.length">
<header>
<div class="dict-name">总词数:{{ store.wrong.words.length }}</div>
<template
v-if="store.current.dictType !== DictType.wrong && store.wrong.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.wrong)"
>
<BaseButton size="small">切换</BaseButton>
</PopConfirm>
</template>
</header>
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.wrong)"
:isActive="settingStore.showPanel && tabIndex === 2"
:list="store.wrong.words"
:activeIndex="-1"/>
:list="store.wrong.words"/>
</div>
<footer
v-if="store.current.dictType !== DictType.wrong && store.wrong.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.wrong)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
<Empty v-else/>
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ store.skip.words.length }}</div>
</header>
<div class="content">
<div class="panel-page-item" v-if="store.skip.words.length">
<header>
<div class="dict-name">总词数:{{ store.skip.words.length }}</div>
<template v-if="store.current.dictType !== DictType.skip && store.skip.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.skip)"
>
<BaseButton size="small">切换</BaseButton>
</PopConfirm>
</template>
</header>
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.skip)"
:isActive="settingStore.showPanel && tabIndex === 3"
:list="store.skip.words"
:activeIndex="-1"/>
:list="store.skip.words"/>
</div>
<footer v-if="store.current.dictType !== DictType.skip && store.skip.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.skip)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
<Empty v-else/>
</div>
</div>
</div>
@@ -158,7 +178,7 @@ $header-height: 50rem;
align-items: center;
justify-content: flex-end;
gap: 10rem;
font-size: 18rem;
font-size: 16rem;
color: black;
}
@@ -170,7 +190,7 @@ $header-height: 50rem;
footer {
padding-right: $space;
height: 50rem;
margin-bottom: 10rem;
align-items: center;
}
}

View File

@@ -16,16 +16,17 @@ import ArticleList from "@/components/Article/ArticleList.vue";
import IconWrapper from "@/components/IconWrapper.vue";
import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
const store = useBaseStore()
const practiceStore = usePracticeStore()
const runtimeStore = useRuntimeStore()
let tabIndex = $ref(0)
let wordData = $ref({
words: [],
index: -1
})
let index = $ref(0)
let articleData = $ref({
article: cloneDeep(DefaultArticle),
sectionIndex: 0,
@@ -235,20 +236,20 @@ function nextWord(word: ArticleWord) {
<div class="panel-wrapper">
<Panel
v-if="tabIndex === 0">
<div class="current-practice-dict">
<div class="panel-page-item">
<header>
<div class="left">
<Tooltip title="切换词典">
<IconWrapper>
<Icon icon="basil:exchange-outline"/>
<Icon @click="runtimeStore.showDictModal = true" icon="basil:exchange-outline"/>
</IconWrapper>
</Tooltip>
<div class="title">
{{ store.currentDict.name + `${store.currentDict.chapterIndex + 1}` }}
{{ store.dictTitle }}
</div>
</div>
<div class="right">
{{ store.currentDict.articles.length }}
{{ store.currentDict.articles.length }}篇文
</div>
</header>
<ArticleList :select-item="articleData.article"

View File

@@ -1,6 +1,5 @@
<script setup lang="ts">
import WordPanel from "@/components/Practice/PracticeWord/WordPanel.vue";
import TypingWord from "@/components/Practice/PracticeWord/TypingWord.vue";
import {$ref} from "vue/macros";
import {cloneDeep} from "lodash-es";

View File

@@ -12,11 +12,10 @@ import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";
import Options from "@/components/Practice/Options.vue";
import Typing from "@/components/Practice/PracticeWord/Typing.vue";
import WordPanel from "@/components/Practice/PracticeWord/WordPanel.vue";
import ArticleList from "@/components/Article/ArticleList.vue";
import Panel from "@/components/Practice/Panel.vue";
import IconWrapper from "@/components/IconWrapper.vue";
import WordList from "@/components/WordList.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
interface IProps {
words: Word[],
@@ -37,6 +36,7 @@ let data = $ref({
let typingRef: any = $ref()
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const practiceStore = usePracticeStore()
const settingStore = useSettingStore()
@@ -200,7 +200,6 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
</Tooltip>
</div>
<Typing
v-if="false"
ref="typingRef"
:word="word"
@wrong="wordWrong"
@@ -213,25 +212,26 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
/>
<div class="word-panel-wrapper">
<Panel>
<div class="current-practice-dict">
<div class="panel-page-item">
<header>
<div class="left">
<Tooltip title="切换词典">
<IconWrapper>
<Icon icon="basil:exchange-outline"/>
<Icon @click="runtimeStore.showDictModal = true" icon="basil:exchange-outline"/>
</IconWrapper>
</Tooltip>
<div class="title">
{{ store.currentDict.name + `${store.currentDict.chapterIndex + 1}` }}
{{ store.dictTitle }}
</div>
</div>
<div class="right">
{{ data.words.length }}
{{ data.words.length }}个单
</div>
</header>
<WordList
class="word-list"
:is-active="true"
@change="(i:number) => data.index = i"
:list="data.words"
:activeIndex="data.index"/>
</div>

View File

@@ -1,312 +0,0 @@
<script setup lang="ts">
import {useBaseStore} from "@/stores/base.ts"
import WordList from "@/components/WordList.vue"
import {$computed, $ref} from "vue/macros"
import {computed, provide, watch} from "vue"
import {Dict, DictType, Word} from "@/types.ts"
import PopConfirm from "@/components/PopConfirm.vue"
import BaseButton from "@/components/BaseButton.vue";
import {useSettingStore} from "@/stores/setting.ts";
import Close from "@/components/Close.vue";
const props = defineProps<{
list?: Word[],
index: number
}>()
const emit = defineEmits<{
'update:index': [i: number]
}>()
const store = useBaseStore()
const settingStore = useSettingStore()
let tabIndex = $ref(0)
provide('tabIndex', computed(() => tabIndex))
watch(() => settingStore.showPanel, n => {
if (n) {
switch (store.current.dictType) {
case DictType.collect:
return tabIndex = 1;
case DictType.skip:
return tabIndex = 3;
case DictType.wrong:
return tabIndex = 2;
case DictType.word:
case DictType.customWord:
return tabIndex = 0;
}
}
})
const currentDict: Dict = $computed(() => {
return store.myDicts[store.current.index]
})
const currentData = $computed(() => {
if (store.current.dictType !== currentDict.type) return {
list: currentDict.chapterWords[currentDict.chapterIndex] ?? [],
index: -1
}
else return props
})
const newData = $computed(() => {
if (store.current.dictType !== DictType.collect) return {list: store.collect.words ?? [], index: -1}
else return props
})
const wrongData = $computed(() => {
if (store.current.dictType !== DictType.wrong) return {list: store.wrong.words ?? [], index: -1}
else return props
})
const skipData = $computed(() => {
if (store.current.dictType !== DictType.skip) return {list: store.skip.words ?? [], index: -1}
else return props
})
function changeIndex(i: number, dict: Dict) {
dict.wordIndex = i
console.log('i', i, dict.type)
if (store.current.dictType === dict.type) {
emit('update:index', i)
} else {
store.changeDict(dict, dict.chapterIndex, i)
}
}
</script>
<template>
<Teleport to="body">
<Transition name="fade">
<div class="panel" v-if="settingStore.showPanel">
<header>
<Transition name="fade">
<Close
@click="settingStore.showPanel = false"
v-if="!settingStore.showToolbar"/>
</Transition>
<div class="tabs">
<div class="tab current" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
{{ currentDict.name + ` ${currentDict.chapterIndex + 1}` }}
</div>
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.collect.name }}</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">
{{ store.wrong.name }}
</div>
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.skip.name }}</div>
</div>
</header>
<div class="slide">
<div class="slide-list" :class="`step${tabIndex}`">
<div class="slide-item">
<header>
<div class="dict-name">词数{{ currentData.list.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,currentDict)"
:isActive="settingStore.showPanel && tabIndex === 0"
:list="currentData.list"
:activeIndex="currentData.index"/>
</div>
<footer v-if="![DictType.customWord,DictType.word].includes(store.current.dictType)">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,currentDict)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ newData.list.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.collect)"
:isActive="settingStore.showPanel && tabIndex === 1"
:list="newData.list"
:activeIndex="newData.index"/>
</div>
<footer v-if="store.current.dictType !== DictType.collect && newData.list.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.collect)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
</div>
<div class="slide-item">
<header>
<a href="" target="_blank"></a>
<div class="dict-name">总词数{{ wrongData.list.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.wrong)"
:isActive="settingStore.showPanel && tabIndex === 2"
:list="wrongData.list"
:activeIndex="wrongData.index"/>
</div>
<footer
v-if="store.current.dictType !== DictType.wrong && wrongData.list.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.wrong)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ skipData.list.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.skip)"
:isActive="settingStore.showPanel && tabIndex === 3"
:list="skipData.list"
:activeIndex="skipData.index"/>
</div>
<footer v-if="store.current.dictType !== DictType.skip && skipData.list.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.skip)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
</div>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped lang="scss">
@import "@/assets/css/variable.scss";
$width: 20vw;
$header-height: 50rem;
.slide {
width: 100%;
flex: 1;
overflow: hidden;
.slide-list {
width: 400%;
height: 100%;
display: flex;
transition: all .5s;
.slide-item {
width: $width;
height: 100%;
display: flex;
flex-direction: column;
> header {
padding: 0 $space;
height: $header-height;
position: relative;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rem;
font-size: 18rem;
color: black;
}
.content {
flex: 1;
overflow: auto;
padding-bottom: $space;
}
footer {
padding-right: $space;
height: 50rem;
align-items: center;
}
}
}
.step1 {
transform: translate3d(-25%, 0, 0);
}
.step2 {
transform: translate3d(-50%, 0, 0);
}
.step3 {
transform: translate3d(-75%, 0, 0);
}
}
.panel {
position: fixed;
left: 0;
top: 10rem;
border-radius: 8rem;
margin-left: calc(50% + (var(--toolbar-width) / 2) + $space);
width: $width;
background: var(--color-second-bg);
height: calc(100% - 20rem);
display: flex;
flex-direction: column;
transition: all .3s;
z-index: 1;
& > header {
min-height: 50rem;
box-sizing: border-box;
position: relative;
display: flex;
align-items: center;
padding: 10rem 15rem;
border-bottom: 1px solid #e1e1e1;
gap: 15rem;
.close {
cursor: pointer;
}
.tabs {
justify-content: flex-end;
width: 100%;
display: flex;
align-items: center;
gap: 15rem;
font-size: 14rem;
color: gray;
.tab {
cursor: pointer;
word-break: keep-all;
font-size: 16rem;
&.active {
color: rgb(36, 127, 255);
font-weight: bold;
}
}
.current {
word-break: break-word;
}
}
}
}
</style>

View File

@@ -179,7 +179,7 @@ const dictIsArticle = $computed(() => {
<div class="translate">
<span>翻译</span>
<el-radio-group v-model="currentTranslateLanguage">
<el-radio border v-for="i in translateLanguageList" :label="i">{{ i }}</el-radio>
<el-radio-button border v-for="i in translateLanguageList" :label="i">{{ i }}</el-radio-button>
</el-radio-group>
</div>
<DictGroup
@@ -416,6 +416,8 @@ $header-height: 60rem;
padding-right: $space;
.translate {
display: flex;
align-items: center;
color: black;
margin-bottom: 30rem;

View File

@@ -17,15 +17,16 @@ import TranslateSetting from "@/components/Toolbar/TranslateSetting.vue";
import Add from "@/components/Toolbar/Add.vue";
import {useSettingStore} from "@/stores/setting.ts";
import {usePracticeStore} from "@/stores/practice.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
const {toggle} = useTheme()
const store = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
const practiceStore = usePracticeStore()
const showFeedbackModal = $ref(false)
const showSettingModal = $ref(false)
const showDictModal = $ref(false)
const headerRef = $ref<HTMLDivElement>(null)
watch(() => settingStore.showToolbar, n => {
@@ -43,7 +44,7 @@ watch(() => settingStore.showToolbar, n => {
<template>
<header ref="headerRef">
<div class="content">
<div class="info" @click="showDictModal = true">
<div class="info" @click="runtimeStore.showDictModal = true">
{{ store.dictTitle }} {{ practiceStore.repeatNumber ? ' 复习错词' : '' }}
</div>
<div class="options">
@@ -112,7 +113,7 @@ watch(() => settingStore.showToolbar, n => {
color="#999"/>
</Tooltip>
</header>
<DictModal :model-value="showDictModal" @close="showDictModal = false"/>
<DictModal :model-value="runtimeStore.showDictModal" @close="runtimeStore.showDictModal = false"/>
<SettingModal v-if="showSettingModal" @close="showSettingModal = false"/>
<FeedbackModal v-if="showFeedbackModal" @close="showFeedbackModal = false"/>
</template>

View File

@@ -9,11 +9,14 @@ const emit = defineEmits<{
del: [i: number],
change: [i: number]
}>()
const props = defineProps<{
const props = withDefaults(defineProps<{
list: Word[],
activeIndex: number,
isActive: boolean
}>()
activeIndex?: number,
isActive?: boolean
}>(), {
activeIndex: -1,
isActive: false
})
const listRef: HTMLElement = $ref(null as any)

View File

@@ -1,5 +1,5 @@
import {defineStore} from 'pinia'
import {DefaultDict, Dict, DictType, DisplayStatistics, SaveDictKey, Sort, Statistics, Word} from "../types.ts"
import {DefaultDict, Dict, DictType, DisplayStatistics, SaveDictKey, Word} from "../types.ts"
import {chunk, cloneDeep} from "lodash-es";
import {emitter, EventKey} from "@/utils/eventBus.ts"
import {v4 as uuidv4} from 'uuid';
@@ -13,8 +13,7 @@ export interface State {
current: {
dictType: DictType,
index: number,
editIndex: number,
repeatNumber: number,
practiceType: DictType,//练习类型目前仅词典为collect时判断是练单词还是文章使用
},
simpleWords: string[],
load: boolean
@@ -25,18 +24,150 @@ export const useBaseStore = defineStore('base', {
return {
collect: {
...cloneDeep(DefaultDict),
words: [
{
"name": "cancel",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "prepare",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "half",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "spacious",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "analyse",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "costing",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "nowadays",
"trans": [],
"usphone": "",
"ukphone": ""
},
],
id: 'collect',
name: '收藏',
type: DictType.collect,
},
skip: {
...cloneDeep(DefaultDict),
words: [
{
"name": "cancel",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "prepare",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "half",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "spacious",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "analyse",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "costing",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "nowadays",
"trans": [],
"usphone": "",
"ukphone": ""
},
],
id: 'skip',
name: '简单词',
type: DictType.skip,
},
wrong: {
...cloneDeep(DefaultDict),
words: [
{
"name": "cancel",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "prepare",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "half",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "spacious",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "analyse",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "costing",
"trans": [],
"usphone": "",
"ukphone": ""
},
{
"name": "nowadays",
"trans": [],
"usphone": "",
"ukphone": ""
},
],
id: 'wrong',
name: '错词本',
type: DictType.wrong,
@@ -67,8 +198,7 @@ export const useBaseStore = defineStore('base', {
index: 1,
// dictType: DictType.article,
// index: 0,
editIndex: 0,
repeatNumber: 0,
practiceType: DictType.word,
},
simpleWords: [
'a', 'an',
@@ -89,6 +219,10 @@ export const useBaseStore = defineStore('base', {
return state.skip.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
},
isArticle(state: State): boolean {
//如果是收藏时,特殊判断
if (state.current.dictType === DictType.collect) {
return state.current.practiceType === DictType.article
}
return [
DictType.article,
DictType.customArticle
@@ -124,8 +258,18 @@ export const useBaseStore = defineStore('base', {
},
dictTitle(state: State) {
let title = this.currentDict.name
if ([DictType.word, DictType.customWord].includes(this.current.dictType)) {
title += `${this.currentDict.chapterIndex + 1}`
switch (state.current.dictType) {
case DictType.collect:
if (state.current.dictType === DictType.collect) {
title += `${this.currentDict.chapterIndex + 1}`
}
break
case DictType.word:
case DictType.article:
case DictType.customWord:
case DictType.customArticle:
title += `${this.currentDict.chapterIndex + 1}`
break
}
return title
}
@@ -193,7 +337,7 @@ export const useBaseStore = defineStore('base', {
DictType.article,
DictType.customArticle,
].includes(this.current.dictType)) {
console.log(1,this.currentDict)
console.log(1, this.currentDict)
if (!this.currentDict.articles.length) {
console.log(2)
let r = await fetch(`./dicts/${this.currentDict.language}/${this.currentDict.type}/${this.currentDict.translateLanguage}/${this.currentDict.url}`)
@@ -215,16 +359,18 @@ export const useBaseStore = defineStore('base', {
this.currentDict.statistics.push(statistics)
}
},
async changeDict(dict: Dict, chapterIndex: number = dict.chapterIndex, chapterWordIndex: number = dict.chapterWordNumber) {
async changeDict(dict: Dict, chapterIndex: number = dict.chapterIndex, chapterWordIndex: number = dict.chapterWordNumber, practiceType: DictType) {
//TODO 保存统计
// this.saveStatistics()
console.log('changeDict', cloneDeep(dict), chapterIndex, chapterWordIndex)
this.current.dictType = dict.type
this.current.practiceType = practiceType
if ([DictType.collect,
DictType.skip,
DictType.wrong].includes(dict.type)) {
this[dict.type].chapterIndex = chapterIndex
this[dict.type].chapterIndex = 0
this[dict.type].chapterWordIndex = chapterWordIndex
this[dict.type].chapterWords = [this[dict.type].words]
} else {
let rIndex = this.myDicts.findIndex((v: Dict) => v.name === dict.name)
if (rIndex > -1) {

View File

@@ -6,6 +6,7 @@ export interface RuntimeState {
modalList: Array<{ id: string | number, close: Function }>
editDict: Dict
translateWordList: Word[]
showDictModal: boolean
}
export const useRuntimeStore = defineStore('runtime', {
@@ -15,6 +16,7 @@ export const useRuntimeStore = defineStore('runtime', {
modalList: [],
editDict: {...DefaultDict},
translateWordList: [],
showDictModal: false,
}
},
})