Optimize UI interface zyronon Today 11:24
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -64,7 +64,6 @@ declare module 'vue' {
|
||||
TypingWord: typeof import('./src/components/Practice/PracticeWord/TypingWord.vue')['default']
|
||||
VolumeIcon: typeof import('./src/components/VolumeIcon.vue')['default']
|
||||
VolumeSetting: typeof import('./src/components/Toolbar/VolumeSetting.vue')['default']
|
||||
WordItem: typeof import('./src/components/WordItem.vue')['default']
|
||||
WordList: typeof import('./src/components/WordList.vue')['default']
|
||||
WordListModal: typeof import('./src/components/WordListModal.vue')['default']
|
||||
}
|
||||
|
||||
16
src/App.vue
16
src/App.vue
@@ -8,7 +8,7 @@ import {useEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts"
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {dictionaryResources} from "@/assets/dictionary.ts";
|
||||
import {groupBy} from "lodash-es";
|
||||
import {cloneDeep, groupBy} from "lodash-es";
|
||||
import {$ref} from "vue/macros";
|
||||
|
||||
const store = useBaseStore()
|
||||
@@ -31,6 +31,20 @@ watch(store.$state, (n) => {
|
||||
localStorage.setItem(SaveDictKey, JSON.stringify(n))
|
||||
})
|
||||
|
||||
//检测几个特定词典
|
||||
watch(store.collect.originWords, (n) => {
|
||||
store.collect.words = cloneDeep(n)
|
||||
store.collect.chapterWords = [store.collect.words]
|
||||
})
|
||||
watch(store.simple.originWords, (n) => {
|
||||
store.simple.words = cloneDeep(n)
|
||||
store.simple.chapterWords = [store.simple.words]
|
||||
})
|
||||
watch(store.wrong.originWords, (n) => {
|
||||
store.wrong.words = cloneDeep(n)
|
||||
store.wrong.chapterWords = [store.wrong.words]
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -161,11 +161,11 @@ footer {
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
.title{
|
||||
.title {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.right{
|
||||
.right {
|
||||
word-break: keep-all;
|
||||
}
|
||||
}
|
||||
@@ -175,4 +175,46 @@ footer {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.word-item {
|
||||
.volume {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.volume {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.phonetic {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.volume {
|
||||
color: #E6A23C;
|
||||
}
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
|
||||
.word {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-translate {
|
||||
font-size: 16rem;
|
||||
}
|
||||
@@ -4,7 +4,8 @@ $dark-second-bg: rgb(60, 63, 65);
|
||||
|
||||
$font-color: rgb(187, 187, 187);
|
||||
|
||||
$main: rgb(12, 140, 233);
|
||||
$main: rgb(64,158,255);
|
||||
//$main: rgb(121,187,255);
|
||||
$second: rgb(75, 110, 175);
|
||||
$item-hover: rgb(75, 75, 75);
|
||||
$space: 24rem;
|
||||
|
||||
@@ -83,7 +83,7 @@ watch(() => props.list, () => {
|
||||
:active="activeIndex === i"
|
||||
:key="item.id">
|
||||
<div class="name"> {{ `${i + 1}. ${item.title}` }}</div>
|
||||
<div class="translate"> {{ ` ${item.titleTranslate}` }}</div>
|
||||
<div class="item-translate"> {{ ` ${item.titleTranslate}` }}</div>
|
||||
</ListItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {Icon} from "@iconify/vue";
|
||||
defineProps<{
|
||||
title?: string,
|
||||
icon: string,
|
||||
className: string
|
||||
}>()
|
||||
|
||||
defineEmits(['click'])
|
||||
@@ -14,7 +15,7 @@ defineEmits(['click'])
|
||||
|
||||
<template>
|
||||
<Tooltip :title="title">
|
||||
<IconWrapper>
|
||||
<IconWrapper :class="className">
|
||||
<Icon @click.stop="$emit('click')" :icon="icon"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<script lang="jsx">
|
||||
export default {
|
||||
render() {
|
||||
let Vnode = this.$slots.default()[0]
|
||||
return (
|
||||
<div class="icon-wrapper ">
|
||||
<Vnode
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="icon-wrapper">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable.scss";
|
||||
|
||||
@@ -5,13 +5,16 @@ import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
|
||||
defineProps<{
|
||||
showVolume?: boolean,
|
||||
showDel?: boolean,
|
||||
active?: boolean
|
||||
isCollect: boolean
|
||||
isSimple: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
toggleDel: [val: any],
|
||||
toggleCollect: [val: any],
|
||||
play: [],
|
||||
toggleSimple: [],
|
||||
toggleCollect: [],
|
||||
del: [],
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -24,12 +27,38 @@ defineEmits<{
|
||||
</div>
|
||||
<div class="right">
|
||||
<BaseIcon
|
||||
@click="$emit('toggleDel')"
|
||||
title="收藏" icon="ph:star"/>
|
||||
<BaseIcon
|
||||
@click="$emit('toggleCollect')"
|
||||
title="删除" icon="fluent:delete-24-regular"/>
|
||||
v-if="showDel"
|
||||
class-name="del"
|
||||
@click="$emit('del')"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
<template v-else>
|
||||
<BaseIcon
|
||||
v-if="!isCollect"
|
||||
class-name="collect"
|
||||
@click="$emit('toggleCollect')"
|
||||
title="收藏" icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class-name="fill"
|
||||
@click="$emit('toggleCollect')"
|
||||
title="取消收藏" icon="ph:star-fill"/>
|
||||
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isSimple"
|
||||
class-name="easy"
|
||||
@click="$emit('toggleSimple')"
|
||||
title="忽略(快捷键:`)"
|
||||
icon="material-symbols:check-circle-outline-rounded"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class-name="fill"
|
||||
@click="$emit('toggleSimple')"
|
||||
title="取消忽略(快捷键:`)"
|
||||
icon="material-symbols:check-circle-rounded"/>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -60,13 +89,24 @@ defineEmits<{
|
||||
flex-direction: column;
|
||||
gap: 5rem;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
:deep(.collect) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
:deep(.easy) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgb(226, 226, 226);
|
||||
|
||||
.right {
|
||||
:deep(.collect) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:deep(.easy) {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -74,6 +114,20 @@ defineEmits<{
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: white !important;
|
||||
|
||||
$c: #E6A23C;
|
||||
|
||||
:deep(.collect) {
|
||||
color: $c;
|
||||
}
|
||||
|
||||
:deep(.fill) {
|
||||
color: $c;
|
||||
}
|
||||
|
||||
:deep(.easy) {
|
||||
color: $c;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -28,7 +28,7 @@ withDefaults(defineProps<IProps>(), {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: $space;
|
||||
color: black;
|
||||
color: var(--color-font-1);
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ withDefaults(defineProps<IProps>(), {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
width: 180rem;
|
||||
background: white;
|
||||
background: var(--color-second-bg);
|
||||
border-radius: 8rem;
|
||||
box-shadow: 1px 1px 6px #bbbbbb;
|
||||
padding: 10rem $space;
|
||||
|
||||
@@ -246,7 +246,7 @@ $header-height: 60rem;
|
||||
|
||||
.modal {
|
||||
position: relative;
|
||||
background: var(--color-main-bg);
|
||||
background: var(--color-second-bg);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -39,7 +39,8 @@ onUnmounted(() => {
|
||||
<template>
|
||||
<div class="footer" :class="!settingStore.showToolbar && 'hide'">
|
||||
<div class="bottom">
|
||||
<el-progress :percentage="progress"
|
||||
<el-progress
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="stat">
|
||||
|
||||
@@ -3,39 +3,59 @@
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import IconWrapper from "@/components/IconWrapper.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
|
||||
defineProps<{
|
||||
showEdit?: boolean
|
||||
showEdit?: boolean,
|
||||
isCollect: boolean,
|
||||
isSimple: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
remove: [],
|
||||
collect: [],
|
||||
skip: [],
|
||||
toggleCollect: [],
|
||||
toggleSimple: [],
|
||||
edit: [],
|
||||
skip: [],
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="options">
|
||||
<Tooltip v-if="showEdit" title="编辑(快捷键:Ctrl + E)">
|
||||
<Tooltip v-if="showEdit" title="编辑(快捷键:Ctrl + E)">
|
||||
<IconWrapper>
|
||||
<Icon icon="tabler:edit" class="menu"
|
||||
@click="emit('edit')"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<Tooltip title="忽略(快捷键:`)">
|
||||
<IconWrapper>
|
||||
<Icon icon="fluent:delete-20-regular" class="menu"
|
||||
@click="emit('remove')"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<Tooltip title="收藏(快捷键:Enter)">
|
||||
<IconWrapper>
|
||||
<Icon icon="ph:star" class="menu"
|
||||
@click="emit('collect')"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isSimple"
|
||||
class-name="collect"
|
||||
@click="$emit('toggleSimple')"
|
||||
title="忽略(快捷键:`)"
|
||||
icon="material-symbols:check-circle-outline-rounded"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class-name="fill"
|
||||
@click="$emit('toggleSimple')"
|
||||
title="已忽略(快捷键:`)"
|
||||
icon="material-symbols:check-circle-rounded"/>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isCollect"
|
||||
class-name="collect"
|
||||
@click="$emit('toggleCollect')"
|
||||
title="收藏(快捷键:Enter)"
|
||||
icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class-name="fill"
|
||||
@click="$emit('toggleCollect')"
|
||||
title="已收藏(快捷键:Enter)"
|
||||
icon="ph:star-fill"/>
|
||||
|
||||
<Tooltip title="跳过(快捷键:Tab)">
|
||||
<IconWrapper>
|
||||
<Icon icon="icon-park-outline:go-ahead" class="menu"
|
||||
|
||||
@@ -46,7 +46,7 @@ function changeIndex(i: number, dict: Dict) {
|
||||
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">当前</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 class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.simple.name }}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="slide">
|
||||
@@ -114,18 +114,19 @@ function changeIndex(i: number, dict: Dict) {
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
:show-del="true"
|
||||
:list="store.wrong.words"/>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<div class="panel-page-item" v-if="store.skip.words.length">
|
||||
<div class="panel-page-item" v-if="store.simple.words.length">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.skip.words.length }}</div>
|
||||
<template v-if="store.current.dictType !== DictType.skip && store.skip.words.length">
|
||||
<div class="dict-name">总词数:{{ store.simple.words.length }}</div>
|
||||
<template v-if="store.current.dictType !== DictType.simple && store.simple.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex(0,store.skip)"
|
||||
@confirm="changeIndex(0,store.simple)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
@@ -133,7 +134,7 @@ function changeIndex(i: number, dict: Dict) {
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
:list="store.skip.words"/>
|
||||
:list="store.simple.words"/>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
|
||||
@@ -167,8 +167,6 @@ function wrong(word: Word) {
|
||||
let lowerName = word.name.toLowerCase();
|
||||
if (!store.wrong.originWords.find((v: Word) => v.name.toLowerCase() === lowerName)) {
|
||||
store.wrong.originWords.push(word)
|
||||
store.wrong.words.push(word)
|
||||
store.wrong.chapterWords = [store.wrong.words]
|
||||
}
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(lowerName)) {
|
||||
if (!practiceStore.wrongWords.find((v) => v.name.toLowerCase() === lowerName)) {
|
||||
|
||||
@@ -16,6 +16,7 @@ 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";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
|
||||
interface IProps {
|
||||
words: Word[],
|
||||
@@ -40,6 +41,13 @@ const runtimeStore = useRuntimeStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const {
|
||||
isWordCollect,
|
||||
toggleWordCollect,
|
||||
isWordSimple,
|
||||
toggleWordSimple
|
||||
} = useWordOptions()
|
||||
|
||||
watch(() => props.words, () => {
|
||||
data.words = props.words
|
||||
data.index = props.index
|
||||
@@ -119,27 +127,15 @@ function prev() {
|
||||
data.index--
|
||||
}
|
||||
|
||||
function skip() {
|
||||
next(false)
|
||||
}
|
||||
|
||||
function collect() {
|
||||
if (!store.collect.originWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
|
||||
store.collect.originWords.push(word)
|
||||
store.collect.words.push(word)
|
||||
store.collect.chapterWords = [store.collect.words]
|
||||
function toggleWordSimpleWrapper() {
|
||||
if (!isWordSimple(word)) {
|
||||
toggleWordSimple(word)
|
||||
next(false)
|
||||
} else {
|
||||
toggleWordSimple(word)
|
||||
}
|
||||
}
|
||||
|
||||
function remove() {
|
||||
if (!store.skipWordNames.includes(word.name.toLowerCase())) {
|
||||
store.skip.originWords.push(word)
|
||||
store.skip.words.push(word)
|
||||
store.skip.chapterWords = [store.skip.words]
|
||||
}
|
||||
next(false)
|
||||
}
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
typingRef.hideWord()
|
||||
}
|
||||
@@ -147,8 +143,6 @@ function onKeyUp(e: KeyboardEvent) {
|
||||
function wordWrong() {
|
||||
if (!store.wrong.originWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
|
||||
store.wrong.originWords.push(word)
|
||||
store.wrong.words.push(word)
|
||||
store.wrong.chapterWords = [store.wrong.words]
|
||||
}
|
||||
if (!data.wrongWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
|
||||
data.wrongWords.push(word)
|
||||
@@ -163,13 +157,13 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
typingRef.del()
|
||||
break
|
||||
case ShortKeyMap.Collect:
|
||||
collect()
|
||||
toggleWordCollect(word)
|
||||
break
|
||||
case ShortKeyMap.Remove:
|
||||
remove()
|
||||
toggleWordSimpleWrapper()
|
||||
break
|
||||
case ShortKeyMap.Ignore:
|
||||
skip()
|
||||
next(false)
|
||||
e.preventDefault()
|
||||
break
|
||||
case ShortKeyMap.Show:
|
||||
@@ -208,9 +202,11 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
/>
|
||||
<div class="options-wrapper">
|
||||
<Options
|
||||
@remove="remove"
|
||||
@skip="skip"
|
||||
@collect="collect"
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimpleWrapper"
|
||||
:is-collect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
@skip="next(false)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ const emit = defineEmits([
|
||||
//height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
background: var(--color-second-bg);
|
||||
align-items: center;
|
||||
padding: $space;
|
||||
//justify-content: center;
|
||||
|
||||
@@ -68,7 +68,7 @@ defineExpose({play})
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
$w: 26rem;
|
||||
$w: 22rem;
|
||||
|
||||
:deep(svg) {
|
||||
width: $w;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {Word} from "@/types.ts";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
defineProps<{
|
||||
word: Word,
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
del: []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ListItem
|
||||
class="item"
|
||||
:class="{active}"
|
||||
:show-volume="true"
|
||||
@play="playWordAudio(word.name)">
|
||||
<div class="title">
|
||||
<span class="word">{{ word.name }}</span>
|
||||
<span class="phonetic">{{ word.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(word.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="translate" v-if="word.trans.length">{{ word.trans.join(';') }}</div>
|
||||
</ListItem>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable.scss";
|
||||
|
||||
.item {
|
||||
.volume {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.volume {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
.phonetic {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
|
||||
.word {
|
||||
font-size: 18rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 16rem;
|
||||
}
|
||||
</style>
|
||||
@@ -2,19 +2,21 @@
|
||||
import {Word} from "../types";
|
||||
import {watch} from "vue"
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import WordItem from "@/components/WordItem.vue";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
list: Word[],
|
||||
activeIndex?: number,
|
||||
showDel?: boolean,
|
||||
isActive?: boolean
|
||||
}>(), {
|
||||
activeIndex: -1,
|
||||
isActive: false
|
||||
isActive: false,
|
||||
showDel: false
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -48,6 +50,13 @@ watch(() => props.list, () => {
|
||||
})
|
||||
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
const {
|
||||
isWordCollect,
|
||||
toggleWordCollect,
|
||||
isWordSimple,
|
||||
toggleWordSimple,
|
||||
delWrongWord
|
||||
} = useWordOptions()
|
||||
|
||||
</script>
|
||||
|
||||
@@ -55,18 +64,24 @@ const playWordAudio = usePlayWordAudio()
|
||||
<div class="list" ref="listRef">
|
||||
<ListItem
|
||||
v-for="(word,i) in list" :key="i"
|
||||
@click="emit('change',i)"
|
||||
class="word-item"
|
||||
:active="activeIndex === i"
|
||||
class="item"
|
||||
:class="{active:activeIndex === i}"
|
||||
:show-volume="true"
|
||||
@play="playWordAudio(word.name)">
|
||||
<div class="title">
|
||||
@click="emit('change',i)"
|
||||
:isCollect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimple(word)"
|
||||
:show-del="showDel"
|
||||
@del="delWrongWord(word)"
|
||||
>
|
||||
<div class="word-wrapper">
|
||||
<span class="word">{{ word.name }}</span>
|
||||
<span class="phonetic">{{ word.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(word.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="translate" v-if="word.trans.length">{{ word.trans.join(';') }}</div>
|
||||
<div class="item-translate" v-if="word.trans.length">{{ word.trans.join(';') }}</div>
|
||||
</ListItem>
|
||||
</div>
|
||||
</template>
|
||||
@@ -82,43 +97,5 @@ const playWordAudio = usePlayWordAudio()
|
||||
flex: 1;
|
||||
overflow: overlay;
|
||||
padding: 0 $space;
|
||||
|
||||
.item {
|
||||
.volume {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.volume {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.phonetic {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
|
||||
.word {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 16rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {$ref} from "vue/macros";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import WordItem from "@/components/WordItem.vue";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
|
||||
let show = $ref(false)
|
||||
let list = $ref([])
|
||||
@@ -15,7 +15,7 @@ let title = $ref('')
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openWordListModal, (val) => {
|
||||
emitter.on(EventKey.openWordListModal, (val: any) => {
|
||||
list = val.list
|
||||
title = val.title
|
||||
show = true
|
||||
@@ -40,19 +40,16 @@ onUnmounted(() => {
|
||||
item-class="dict-virtual-item"
|
||||
>
|
||||
<template #={source}>
|
||||
<div class="left">
|
||||
<div class="title">
|
||||
<ListItem
|
||||
class="word-item"
|
||||
:show-volume="true">
|
||||
<div class="word-wrapper">
|
||||
<span class="word">{{ source.name }}</span>
|
||||
<span class="phonetic">{{ source.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(source.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="translate">{{ source.trans.join(';') }}</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<VolumeIcon @click="playWordAudio(source.name)"></VolumeIcon>
|
||||
<Icon icon="fluent:delete-28-regular" width="20" color="#929596"/>
|
||||
</div>
|
||||
<!-- <WordItem-->
|
||||
<!-- :word="source"/>-->
|
||||
<div class="item-translate" v-if="source.trans.length">{{ source.trans.join(';') }}</div>
|
||||
</ListItem>
|
||||
</template>
|
||||
</virtual-list>
|
||||
</div>
|
||||
@@ -81,53 +78,11 @@ onUnmounted(() => {
|
||||
.dict-virtual-item {
|
||||
background: var(--color-header-bg);
|
||||
border-radius: 6rem;
|
||||
padding: 8rem 12rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
color: var(--color-font-1);
|
||||
margin-bottom: 10rem;
|
||||
|
||||
&.active {
|
||||
background: $second;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
//background: $dark-main-bg;
|
||||
//background: $item-hover;
|
||||
background: rgb(226, 226, 226);
|
||||
}
|
||||
|
||||
.left {
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
|
||||
.word {
|
||||
font-size: 24rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import {Word} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
|
||||
export function useWordOptions() {
|
||||
const store = useBaseStore()
|
||||
|
||||
function isWordCollect(val: Word) {
|
||||
return !!store.collect.originWords.find(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
}
|
||||
|
||||
function toggleWordCollect(val: Word) {
|
||||
let rIndex = store.collect.originWords.findIndex(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
if (rIndex > -1) {
|
||||
store.collect.originWords.splice(rIndex, 1)
|
||||
} else {
|
||||
let rIndex = store.simple.originWords.findIndex(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
if (rIndex > -1) {
|
||||
store.simple.originWords.splice(rIndex, 1)
|
||||
}
|
||||
store.collect.originWords.push(val)
|
||||
}
|
||||
}
|
||||
|
||||
function isWordSimple(val: Word) {
|
||||
return !!store.simple.originWords.find(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
}
|
||||
|
||||
function toggleWordSimple(val: Word) {
|
||||
let rIndex = store.simple.originWords.findIndex(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
if (rIndex > -1) {
|
||||
store.simple.originWords.splice(rIndex, 1)
|
||||
} else {
|
||||
let rIndex = store.collect.originWords.findIndex(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
if (rIndex > -1) {
|
||||
store.collect.originWords.splice(rIndex, 1)
|
||||
}
|
||||
store.simple.originWords.push(val)
|
||||
}
|
||||
}
|
||||
|
||||
function delWrongWord(val: Word) {
|
||||
let rIndex = store.wrong.originWords.findIndex(v => v.name.toLowerCase() === val.name.toLowerCase())
|
||||
if (rIndex > -1) {
|
||||
store.wrong.originWords.splice(rIndex, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isWordCollect,
|
||||
toggleWordCollect,
|
||||
isWordSimple,
|
||||
toggleWordSimple,
|
||||
delWrongWord
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export function useStartKeyboardEventListener() {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
// console.log('e',e.keyCode,e.code)
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
//非英文模式下,输入区域的 keyCode 均为 229时,
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90)
|
||||
@@ -36,7 +37,7 @@ export function useStartKeyboardEventListener() {
|
||||
|| e.code === 'Minus'
|
||||
|| e.code === 'Equal'
|
||||
|| e.code === 'Semicolon'
|
||||
|| e.code === 'Backquote'
|
||||
// || e.code === 'Backquote'
|
||||
|| e.keyCode === 229
|
||||
) {
|
||||
emitter.emit(EventKey.onTyping, e)
|
||||
|
||||
@@ -7,7 +7,7 @@ import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
export interface State {
|
||||
collect: Dict,
|
||||
skip: Dict,
|
||||
simple: Dict,
|
||||
wrong: Dict,
|
||||
myDicts: Dict[],
|
||||
current: {
|
||||
@@ -19,155 +19,68 @@ export interface State {
|
||||
load: boolean
|
||||
}
|
||||
|
||||
// words: [
|
||||
// {
|
||||
// "name": "cancelcancelcancelcancelcancelcancelcancelcancel",
|
||||
// "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": ""
|
||||
// },
|
||||
// ],
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: (): State => {
|
||||
return {
|
||||
collect: {
|
||||
...cloneDeep(DefaultDict),
|
||||
words: [
|
||||
{
|
||||
"name": "cancelcancelcancelcancelcancelcancelcancelcancel",
|
||||
"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: {
|
||||
simple: {
|
||||
...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,
|
||||
type: DictType.simple,
|
||||
},
|
||||
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,
|
||||
@@ -213,10 +126,10 @@ export const useBaseStore = defineStore('base', {
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skip.originWords.map(v => v.name.toLowerCase())
|
||||
return state.simple.originWords.map(v => v.name.toLowerCase())
|
||||
},
|
||||
skipWordNamesWithSimpleWords: (state: State) => {
|
||||
return state.skip.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
|
||||
return state.simple.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
|
||||
},
|
||||
isArticle(state: State): boolean {
|
||||
//如果是收藏时,特殊判断
|
||||
@@ -232,8 +145,8 @@ export const useBaseStore = defineStore('base', {
|
||||
switch (state.current.dictType) {
|
||||
case DictType.collect:
|
||||
return state.collect
|
||||
case DictType.skip:
|
||||
return state.skip
|
||||
case DictType.simple:
|
||||
return state.simple
|
||||
case DictType.wrong:
|
||||
return state.wrong
|
||||
case DictType.word:
|
||||
@@ -291,7 +204,7 @@ export const useBaseStore = defineStore('base', {
|
||||
if ([
|
||||
DictType.collect,
|
||||
DictType.wrong,
|
||||
DictType.skip,
|
||||
DictType.simple,
|
||||
].includes(this.current.dictType)) {
|
||||
|
||||
} else {
|
||||
@@ -366,7 +279,7 @@ export const useBaseStore = defineStore('base', {
|
||||
this.current.dictType = dict.type
|
||||
this.current.practiceType = practiceType
|
||||
if ([DictType.collect,
|
||||
DictType.skip,
|
||||
DictType.simple,
|
||||
DictType.wrong].includes(dict.type)) {
|
||||
this[dict.type].chapterIndex = 0
|
||||
this[dict.type].chapterWordIndex = chapterWordIndex
|
||||
|
||||
@@ -61,7 +61,7 @@ export interface Dict {
|
||||
|
||||
export enum DictType {
|
||||
collect = 'collect',
|
||||
skip = 'skip',
|
||||
simple = 'simple',
|
||||
wrong = 'wrong',
|
||||
word = 'word',
|
||||
customWord = 'customWord',
|
||||
|
||||
Reference in New Issue
Block a user