save
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
:root {
|
||||
--color-background: #E6E8EB;
|
||||
//--color-main-bg: #E6E8EB;
|
||||
--color-main-bg: rgb(238,240,244);
|
||||
--color-main-bg: rgb(238, 240, 244);
|
||||
--color-second-bg: rgb(240, 242, 244);
|
||||
--color-third-bg: rgb(213, 215, 217);
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
--toolbar-width: 45rem;
|
||||
--toolbar-height: 3.2rem;
|
||||
--panel-width: 24rem;
|
||||
--space: 1.6rem;
|
||||
--space: 1.2rem;
|
||||
--radius: .5rem;
|
||||
--shadow: rgba(0, 0, 0, 0.08) 0px 4px 12px;
|
||||
--panel-margin-left: calc(50% - var(--practice-wrapper-translateX) / 2 + var(--toolbar-width) / 2 + 2rem);
|
||||
@@ -47,6 +47,8 @@
|
||||
|
||||
--color-textarea-bg: white;
|
||||
|
||||
--aside-width: 12rem;
|
||||
|
||||
--font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
--word-font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
}
|
||||
@@ -483,3 +485,7 @@ footer {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.container2 {
|
||||
@apply w-5/10 pt-5;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ defineEmits(['click'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tooltip :disabled="!keyboard" :title="`快捷键: ${keyboard}`">
|
||||
<Tooltip :disabled="!keyboard" :title="`${keyboard}`">
|
||||
<div class="base-button"
|
||||
v-bind="$attrs"
|
||||
@click="e => (!disabled && !loading) && $emit('click',e)"
|
||||
|
||||
@@ -210,7 +210,7 @@ watch(() => props.word, () => {
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
</Tooltip>
|
||||
|
||||
@@ -198,9 +198,6 @@ $header-height: 4rem;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {onMounted} from "vue"
|
||||
import {DefaultDict, Dict, DictResource, DictType, Sort, Word} from "@/types.ts"
|
||||
import {chunk, cloneDeep, reverse, shuffle} from "lodash-es";
|
||||
import {DefaultDict, Dict, DictResource, DictType} from "@/types.ts"
|
||||
import {chunk, cloneDeep} from "lodash-es";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {Icon} from '@iconify/vue';
|
||||
@@ -12,16 +12,11 @@ import {isArticle} from "@/hooks/article.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import Slide from "@/pages/pc/components/Slide.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import EditBatchArticleModal from "@/pages/pc/components/article/EditBatchArticleModal.vue";
|
||||
import {nanoid} from "nanoid";
|
||||
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import BaseList from "@/pages/pc/components/list/BaseList.vue";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {getDictFile} from "@/utils";
|
||||
|
||||
@@ -30,25 +25,14 @@ const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
let router = useRouter()
|
||||
|
||||
let step = $ref(1)
|
||||
let loading = $ref(false)
|
||||
let show = $ref(false)
|
||||
let chapterList2 = $ref([])
|
||||
let chapterWordNumber = $ref(0)
|
||||
let toggleLoading = $ref(false)
|
||||
|
||||
const activeId = $computed(() => {
|
||||
if (dictIsArticle) {
|
||||
return runtimeStore.editDict.articles?.[runtimeStore.editDict.chapterIndex].id ?? ''
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
async function selectDict(val: { dict: DictResource | Dict, index: number }) {
|
||||
let item = val.dict
|
||||
// console.log('item', item)
|
||||
step = 1
|
||||
loading = true
|
||||
let find: Dict = store.myDictList.find((v: Dict) => v.id === item.id)
|
||||
if (find) {
|
||||
runtimeStore.editDict = cloneDeep(find)
|
||||
@@ -74,7 +58,6 @@ async function selectDict(val: { dict: DictResource | Dict, index: number }) {
|
||||
s.id = nanoid(6)
|
||||
})
|
||||
runtimeStore.editDict.originWords = cloneDeep(v)
|
||||
changeSort(runtimeStore.editDict.sort, false)
|
||||
} else {
|
||||
runtimeStore.editDict.length = runtimeStore.editDict.words.length
|
||||
}
|
||||
@@ -93,8 +76,6 @@ async function selectDict(val: { dict: DictResource | Dict, index: number }) {
|
||||
}
|
||||
}
|
||||
chapterWordNumber = runtimeStore.editDict.chapterWordNumber
|
||||
chapterList2 = runtimeStore.editDict.chapterWords.map((v, i) => ({id: i}))
|
||||
loading = false
|
||||
}
|
||||
|
||||
|
||||
@@ -128,45 +109,14 @@ function resetChapterList(v: number) {
|
||||
const temp = () => {
|
||||
runtimeStore.editDict.chapterWordNumber = v
|
||||
runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, runtimeStore.editDict.chapterWordNumber)
|
||||
chapterList2 = runtimeStore.editDict.chapterWords.map((v, i) => ({id: i}))
|
||||
}
|
||||
if (runtimeStore.editDict.isCustom) {
|
||||
MessageBox.confirm(
|
||||
'检测到您已对这本词典自定义修改,修改“每章单词数”将会导致所有章节被重新分配,原有章节内容将被清除且不可恢复,是否继续?',
|
||||
'提示',
|
||||
() => temp(),
|
||||
() => {
|
||||
chapterWordNumber = runtimeStore.editDict.chapterWordNumber
|
||||
}
|
||||
)
|
||||
|
||||
} else {
|
||||
temp()
|
||||
}
|
||||
}
|
||||
|
||||
function changeSort(v: Sort, notice: boolean = true) {
|
||||
const temp = () => {
|
||||
runtimeStore.editDict.sort = v
|
||||
if (v === Sort.normal) {
|
||||
runtimeStore.editDict.words = cloneDeep(runtimeStore.editDict.originWords)
|
||||
} else if (v === Sort.random) {
|
||||
runtimeStore.editDict.words = shuffle(cloneDeep(runtimeStore.editDict.originWords))
|
||||
} else {
|
||||
runtimeStore.editDict.words = reverse(cloneDeep(runtimeStore.editDict.originWords))
|
||||
}
|
||||
resetChapterList(runtimeStore.editDict.chapterWordNumber)
|
||||
notice && ElMessage.success('已重新排序')
|
||||
}
|
||||
if (runtimeStore.editDict.isCustom) {
|
||||
MessageBox.confirm(
|
||||
'检测到您已对这本词典自定义修改,修改“单词排序”将会导致所有章节被重新分配,原有章节内容将被清除且不可恢复,是否继续?',
|
||||
'提示',
|
||||
() => temp()
|
||||
)
|
||||
} else {
|
||||
temp()
|
||||
}
|
||||
}
|
||||
|
||||
function option(type: string) {
|
||||
show = false
|
||||
@@ -180,32 +130,10 @@ onMounted(() => {
|
||||
if (type === "detail") {
|
||||
selectDict({dict: store.currentDict, index: 0})
|
||||
}
|
||||
if (type === "list") {
|
||||
// currentLanguage = 'en'
|
||||
step = 0
|
||||
}
|
||||
if (type === "my") {
|
||||
// currentLanguage = 'my'
|
||||
step = 0
|
||||
}
|
||||
show = true
|
||||
})
|
||||
})
|
||||
|
||||
function showWordListModal(val: { item: Word, index: number }) {
|
||||
emitter.emit(EventKey.openWordListModal, {
|
||||
title: `第${val.index + 1}章`,
|
||||
translateLanguage: runtimeStore.editDict.translateLanguage,
|
||||
list: runtimeStore.editDict.chapterWords[val.index]
|
||||
})
|
||||
}
|
||||
|
||||
function handleChangeArticleChapterIndex(val: any) {
|
||||
let rIndex = runtimeStore.editDict.articles.findIndex(v => v.id === val.item.id)
|
||||
if (rIndex > -1) {
|
||||
runtimeStore.editDict.chapterIndex = rIndex
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -215,205 +143,59 @@ function handleChangeArticleChapterIndex(val: any) {
|
||||
v-model="show"
|
||||
:show-close="false">
|
||||
<div id="DictDialog">
|
||||
<Slide :slide-count="2" :step="step">
|
||||
<DictListPanel
|
||||
@add="option('addDict')"
|
||||
@select-dict="selectDict"
|
||||
/>
|
||||
<div class="dict-detail-page">
|
||||
<header>
|
||||
<div class="left" @click.stop="step = 0">
|
||||
<Icon icon="octicon:arrow-left-24" class="go" width="20"/>
|
||||
<div class="title">
|
||||
词典详情
|
||||
</div>
|
||||
</div>
|
||||
<Icon @click="close"
|
||||
class="hvr-grow pointer"
|
||||
width="20" color="#929596"
|
||||
icon="ion:close-outline"/>
|
||||
</header>
|
||||
<div class="detail">
|
||||
<div class="page-content">
|
||||
<div class="left-column">
|
||||
<BaseIcon
|
||||
v-if="![DictType.collect,DictType.wrong,DictType.simple].includes(runtimeStore.editDict.type)"
|
||||
class="edit-icon"
|
||||
title="编辑词典"
|
||||
icon="tabler:edit"
|
||||
@click='option("editDict")'
|
||||
/>
|
||||
<div class="name">{{ runtimeStore.editDict.name }}</div>
|
||||
<div class="desc">{{ runtimeStore.editDict.description }}</div>
|
||||
<div class="text flex align-center">
|
||||
<div v-if="dictIsArticle">总文章:{{ runtimeStore.editDict.articles.length }}篇
|
||||
</div>
|
||||
<div v-else>总词汇:
|
||||
<span class="count" @click="showAllWordModal">{{
|
||||
runtimeStore.editDict.originWords.length
|
||||
}}词</span>
|
||||
</div>
|
||||
<BaseIcon icon="mi:add"
|
||||
@click='option("addWordOrArticle")'
|
||||
:title="`添加${dictIsArticle?'文章':'单词'}`"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="false">
|
||||
<div class="text">开始日期:-</div>
|
||||
<div class="text">花费时间:-</div>
|
||||
<div class="text">累积错误:-</div>
|
||||
<div class="text">进度:
|
||||
<el-progress :percentage="0"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="center-column">
|
||||
<div class="common-title">学习设置</div>
|
||||
<div class="setting">
|
||||
<template v-if="!dictIsArticle">
|
||||
<div class="row">
|
||||
<div class="label">每章单词数</div>
|
||||
<el-slider
|
||||
class="my-slider"
|
||||
:min="10"
|
||||
:step="10"
|
||||
:max="runtimeStore.editDict.words.length < 10 ? 10 : runtimeStore.editDict.words.length"
|
||||
show-input
|
||||
:show-input-controls="false"
|
||||
size="small"
|
||||
v-model="chapterWordNumber"
|
||||
@change="resetChapterList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="notice">
|
||||
<span class="text">最小:10</span>
|
||||
<span class="text">最大:{{ runtimeStore.editDict.words.length }}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">单词顺序</div>
|
||||
<div class="option">
|
||||
<el-radio-group :model-value="runtimeStore.editDict.sort"
|
||||
@change="changeSort"
|
||||
>
|
||||
<el-radio :label="Sort.normal" size="large">默认</el-radio>
|
||||
<el-radio :label="Sort.random" size="large">随机</el-radio>
|
||||
<el-radio :label="Sort.reverse" size="large">反转</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="label">学习模式</div>
|
||||
<div class="option">
|
||||
<el-radio-group v-model="settingStore.dictation">
|
||||
<el-radio :label="false" size="large">再认</el-radio>
|
||||
<el-radio :label="true" size="large">拼写</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">{{ dictIsArticle ? '句子' : '单词' }}发音</div>
|
||||
<div class="option">
|
||||
<el-radio-group v-model="settingStore.wordSoundType">
|
||||
<el-radio label="us" size="large">美音</el-radio>
|
||||
<el-radio label="uk" size="large">英音</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">{{ dictIsArticle ? '句子' : '单词' }}自动发音</div>
|
||||
<div class="option">
|
||||
<el-switch v-model="settingStore.wordSound"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">是否显示翻译</div>
|
||||
<div class="option">
|
||||
<el-switch v-model="settingStore.translate"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">忽略大小写</div>
|
||||
<div class="option">
|
||||
<el-switch v-model="settingStore.ignoreCase"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<div class="common-title">
|
||||
<span>{{ dictIsArticle ? '文章' : '章节' }}列表</span>
|
||||
<BaseIcon
|
||||
icon="fluent:notepad-edit-20-regular"
|
||||
@click='option("detail")'
|
||||
style="position: absolute;right: 20rem;"
|
||||
:title="`管理${dictIsArticle?'文章':'章节'}`"
|
||||
/>
|
||||
</div>
|
||||
<div class="list-content">
|
||||
<template v-if="dictIsArticle">
|
||||
<ArticleList
|
||||
v-if="runtimeStore.editDict.articles.length"
|
||||
:isActive="false"
|
||||
v-loading="loading"
|
||||
:show-border="true"
|
||||
@title="(val:any) => emitter.emit(EventKey.openArticleContentModal,val.item)"
|
||||
@click="handleChangeArticleChapterIndex"
|
||||
:active-id="activeId"
|
||||
:list="runtimeStore.editDict.articles">
|
||||
<template v-slot:prefix="{item,index}">
|
||||
<input type="radio" :checked="activeId === item.id">
|
||||
</template>
|
||||
</ArticleList>
|
||||
<Empty v-else/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<BaseList
|
||||
ref="chapterListRef"
|
||||
v-if="chapterList2.length"
|
||||
:list="chapterList2"
|
||||
:show-border="true"
|
||||
@click="(val:any) => runtimeStore.editDict.chapterIndex = val.index"
|
||||
:active-index="runtimeStore.editDict.chapterIndex"
|
||||
>
|
||||
<template v-slot:prefix="{ item, index }">
|
||||
<input type="radio" :checked="runtimeStore.editDict.chapterIndex === item.id">
|
||||
</template>
|
||||
<template v-slot="{ item, index }">
|
||||
<div class="item-title" @click.stop="showWordListModal({item,index})">
|
||||
<span>第{{ item.id + 1 }}章</span>
|
||||
<span>{{ runtimeStore.editDict.chapterWords[item.id]?.length }}词</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseList>
|
||||
<Empty v-else/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<!-- <BaseButton @click="step = 0">导出</BaseButton>-->
|
||||
<BaseButton @click="close">关闭</BaseButton>
|
||||
<BaseButton :loading="toggleLoading" @click="changeDict">切换</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header>
|
||||
<div class="text-2xl">
|
||||
{{ runtimeStore.editDict.name }}
|
||||
</div>
|
||||
</Slide>
|
||||
<Icon @click="close"
|
||||
class="hvr-grow pointer"
|
||||
width="20" color="#929596"
|
||||
icon="ion:close-outline"/>
|
||||
</header>
|
||||
<div class="detail">
|
||||
<div class="desc">{{ runtimeStore.editDict.description }}</div>
|
||||
<div class="text flex align-center">
|
||||
<div v-if="dictIsArticle">总文章:{{ runtimeStore.editDict.articles.length }}篇
|
||||
</div>
|
||||
<div v-else>总词汇:
|
||||
<span class="count" @click="showAllWordModal">{{
|
||||
runtimeStore.editDict.originWords.length
|
||||
}}词</span>
|
||||
</div>
|
||||
<BaseIcon icon="mi:add"
|
||||
@click='option("addWordOrArticle")'
|
||||
:title="`添加${dictIsArticle?'文章':'单词'}`"
|
||||
/>
|
||||
</div>
|
||||
<div class="text">开始日期:-</div>
|
||||
<div class="text">花费时间:-</div>
|
||||
<div class="text">累积错误:-</div>
|
||||
<div class="text">进度:
|
||||
<el-progress :percentage="0"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">每日目标</div>
|
||||
<el-slider
|
||||
class="my-slider"
|
||||
:min="10"
|
||||
:step="10"
|
||||
:max="runtimeStore.editDict.words.length < 10 ? 10 : 500"
|
||||
size="small"
|
||||
v-model="chapterWordNumber"
|
||||
@change="resetChapterList"
|
||||
/>
|
||||
</div>
|
||||
<div class="notice">
|
||||
<span class="text">最小:10</span>
|
||||
<span class="text">最大:{{ runtimeStore.editDict.words.length < 10 ? 10 : 500 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<BaseButton @click="close">关闭</BaseButton>
|
||||
<BaseButton :loading="toggleLoading" @click="changeDict">切换</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<WordListDialog/>
|
||||
@@ -432,13 +214,7 @@ $header-height: 4rem;
|
||||
//transform: translate(-50%, -50%);
|
||||
background: var(--color-second-bg);
|
||||
z-index: 99999;
|
||||
width: 60rem;
|
||||
height: 75vh;
|
||||
}
|
||||
|
||||
.dict-detail-page {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
width: 30rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -465,109 +241,48 @@ $header-height: 4rem;
|
||||
padding-left: var(--space);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
gap: .2rem;
|
||||
position: relative;
|
||||
font-size: .9rem;
|
||||
padding-right: var(--space);
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
.column {
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
flex: 5;
|
||||
gap: .6rem;
|
||||
position: relative;
|
||||
font-size: .9rem;
|
||||
padding-right: var(--space);
|
||||
@extend .column;
|
||||
|
||||
.name {
|
||||
font-size: 1.6rem;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.count {
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid var(--color-item-active);
|
||||
}
|
||||
|
||||
:deep(.edit-icon) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.center-column {
|
||||
overflow: auto;
|
||||
flex: 7;
|
||||
@extend .column;
|
||||
|
||||
.setting {
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 2rem;
|
||||
word-break: keep-all;
|
||||
gap: .6rem;
|
||||
|
||||
.el-radio {
|
||||
margin-right: .6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.my-slider {
|
||||
:deep(.el-slider__input) {
|
||||
width: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transform: translate3d(0, -.3rem, 0);
|
||||
padding-left: 6rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-column {
|
||||
flex: 7;
|
||||
@extend .column;
|
||||
|
||||
.list-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
font-size: 1.6rem;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
box-sizing: content-box;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space);
|
||||
padding-right: var(--space);
|
||||
margin: var(--space) 0;
|
||||
.desc {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.count {
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid var(--color-item-active);
|
||||
}
|
||||
|
||||
:deep(.edit-icon) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
box-sizing: content-box;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space);
|
||||
padding-right: var(--space);
|
||||
margin: var(--space) 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -28,13 +28,16 @@ watch(() => props.groupByTag, () => {
|
||||
|
||||
<template>
|
||||
<div class="dict-group">
|
||||
<div class="category">{{ category }}</div>
|
||||
<div class="tags">
|
||||
<div class="tag" :class="i === currentTag &&'active'"
|
||||
@click="currentTag = i"
|
||||
v-for="i in Object.keys(groupByTag)">{{ i }}
|
||||
<div class="flex items-center border">
|
||||
<div class="category">{{ category }}:</div>
|
||||
<div class="tags">
|
||||
<div class="tag" :class="i === currentTag &&'active'"
|
||||
@click="currentTag = i"
|
||||
v-for="i in Object.keys(groupByTag)">{{ i }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DictList
|
||||
@selectDict="e => emit('selectDict',e)"
|
||||
:list="list"
|
||||
@@ -50,8 +53,10 @@ watch(() => props.groupByTag, () => {
|
||||
|
||||
.category {
|
||||
font-size: 1.2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px dashed gray;
|
||||
//padding-bottom: 1rem;
|
||||
}
|
||||
.border{
|
||||
border-top: 1px dashed gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {$computed} from "vue/macros";
|
||||
import DeleteIcon from "@/components/icon/DeleteIcon.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -63,11 +62,11 @@ let length = $computed(() => {
|
||||
.dict-item {
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: 9rem;
|
||||
height: 12rem;
|
||||
//width: 9rem;
|
||||
//height: 12rem;
|
||||
position: relative;
|
||||
background: var(--color-third-bg);
|
||||
//background: white;
|
||||
//background: var(--color-third-bg);
|
||||
background: white;
|
||||
border: 1px solid var(--color-item-border);
|
||||
color: var(--color-font-1);
|
||||
font-size: 1rem;
|
||||
@@ -95,6 +94,7 @@ let length = $computed(() => {
|
||||
}
|
||||
|
||||
.num {
|
||||
margin-top: 2rem;
|
||||
text-align: right;
|
||||
color: var(--color-font-2);
|
||||
//font-weight: bold;
|
||||
|
||||
@@ -18,7 +18,7 @@ const emit = defineEmits<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dict-list">
|
||||
<div class="dict-list1 grid grid-cols-4 gap-4">
|
||||
<DictItem v-for="(dict,index) in list"
|
||||
:active="selectId === dict.id"
|
||||
@click="emit('selectDict',{dict,index})"
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import MiniDialog from "@/pages/pc/components/dialog/MiniDialog.vue";
|
||||
import {useWindowClick} from "@/hooks/event.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {nextTick, watch} from "vue";
|
||||
|
||||
const store = useBaseStore()
|
||||
let timer = 0
|
||||
let show = $ref(false)
|
||||
useWindowClick(() => show = false)
|
||||
|
||||
function toggle(val) {
|
||||
clearTimeout(timer)
|
||||
if (val) {
|
||||
emitter.emit(EventKey.closeOther)
|
||||
show = val
|
||||
} else {
|
||||
timer = setTimeout(() => {
|
||||
show = val
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
function clickJumpSpecifiedChapter(index: number) {
|
||||
emitter.emit(EventKey.jumpSpecifiedChapter, index)
|
||||
}
|
||||
|
||||
const listRef: HTMLElement = $ref(null as any)
|
||||
|
||||
watch(() => show, n => {
|
||||
if (n) {
|
||||
nextTick(() => {
|
||||
listRef?.children[store.currentDict.chapterIndex]?.scrollIntoView({block: 'center'})
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ChapterName" @click.stop="null">
|
||||
<div class="info hvr-grow"
|
||||
@mouseenter="toggle(true)"
|
||||
@mouseleave="toggle(false)"
|
||||
>
|
||||
{{ store.chapterName }}
|
||||
</div>
|
||||
<MiniDialog
|
||||
v-model="show"
|
||||
@mouseenter="toggle(true)"
|
||||
@mouseleave="toggle(false)"
|
||||
style="width: 16rem;"
|
||||
>
|
||||
<div class="chapter-list" ref="listRef">
|
||||
<div class="chapter-list-item"
|
||||
:class="store.currentDict.chapterIndex === index && 'active'"
|
||||
v-for="(item,index) in store.currentDict.chapterWords"
|
||||
@click="clickJumpSpecifiedChapter(index)">
|
||||
<input type="radio" :checked="store.currentDict.chapterIndex === index">
|
||||
<div class="title">第{{ index + 1 }}章 {{ item.length }}词</div>
|
||||
</div>
|
||||
</div>
|
||||
</MiniDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ChapterName {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chapter-list {
|
||||
max-height: 16rem;
|
||||
overflow: auto;
|
||||
padding-right: .3rem;
|
||||
|
||||
.chapter-list-item {
|
||||
margin-bottom: .4rem;
|
||||
height: 2.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
font-size: 1.1rem;
|
||||
border-radius: .5rem;
|
||||
transition: all .3s;
|
||||
padding: .6rem;
|
||||
border: 1px solid var(--color-item-border);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-item-hover);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
</style>
|
||||
@@ -46,7 +46,7 @@ onMounted(() => {
|
||||
v-model="show"
|
||||
@mouseenter="toggle(true)"
|
||||
@mouseleave="toggle(false)"
|
||||
style="width: 230rem;"
|
||||
style="width: 15rem;"
|
||||
>
|
||||
<div class="mini-row-title">
|
||||
单词循环设置
|
||||
|
||||
@@ -42,7 +42,7 @@ function save() {
|
||||
<template>
|
||||
<div class="setting" @click.stop="null">
|
||||
<Tooltip
|
||||
:title="`开关释义显示(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
:title="`开关释义显示(${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon v-if="settingStore.translate" icon="mdi:translate"
|
||||
|
||||
@@ -58,7 +58,7 @@ function toggle2() {
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<MiniDialog
|
||||
width="250rem"
|
||||
width="12rem"
|
||||
v-model="show">
|
||||
<div class="mini-row-title">
|
||||
音效设置
|
||||
@@ -75,7 +75,7 @@ function toggle2() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="mini-row">
|
||||
<label class="item-title">单词/句子自动发音</label>
|
||||
<label class="item-title">自动发音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.wordSound"
|
||||
inline-prompt
|
||||
@@ -85,7 +85,7 @@ function toggle2() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="mini-row">
|
||||
<label class="item-title">单词/句子发音口音</label>
|
||||
<label class="item-title">口音</label>
|
||||
<div class="wrapper">
|
||||
<el-select v-model="settingStore.wordSoundType"
|
||||
placeholder="请选择"
|
||||
|
||||
@@ -14,10 +14,10 @@ import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
import {DictType, ShortcutKey} from "@/types.ts";
|
||||
import ChapterName from "@/pages/pc/components/toolbar/ChapterName.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {useNav} from "@/utils";
|
||||
|
||||
const {toggleTheme} = useTheme()
|
||||
const store = useBaseStore()
|
||||
@@ -56,6 +56,7 @@ watch(() => store.load, n => {
|
||||
}
|
||||
})
|
||||
|
||||
const {nav} = useNav()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,13 +64,14 @@ watch(() => store.load, n => {
|
||||
<div class="content">
|
||||
<div class="dict-name">
|
||||
<Tooltip
|
||||
:title="`词典详情(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.OpenDictDetail]})`">
|
||||
<div class="info hvr-grow" @click="emitter.emit(EventKey.openDictModal,'detail')">
|
||||
:title="`词典详情(${settingStore.shortcutKeyMap[ShortcutKey.OpenDictDetail]})`">
|
||||
<div class="info" @click="emitter.emit(EventKey.openDictModal,'detail')">
|
||||
{{ store.currentDict.name }} {{ practiceStore.repeatNumber ? ' 复习错词' : '' }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Icon icon="gg:arrows-exchange" />
|
||||
<ChapterName v-if="store.currentDict.type === DictType.word"/>
|
||||
<BaseIcon title="切换词典"
|
||||
@click="nav('/dict')"
|
||||
icon="gg:arrows-exchange"/>
|
||||
<div class="info-text" v-if="practiceStore.repeatNumber">
|
||||
复习错词
|
||||
</div>
|
||||
@@ -85,7 +87,7 @@ watch(() => store.load, n => {
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
:title="`开关默写模式(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="majesticons:eye-off-line"
|
||||
@@ -103,16 +105,8 @@ watch(() => store.load, n => {
|
||||
|
||||
<RepeatSetting/>
|
||||
|
||||
<!-- <Add/>-->
|
||||
|
||||
<BaseIcon
|
||||
@click="emitter.emit(EventKey.openDictModal,'my')"
|
||||
title="添加"
|
||||
icon="ic:outline-cloud-upload"/>
|
||||
|
||||
|
||||
<Tooltip
|
||||
:title="`切换主题(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleTheme]})`"
|
||||
:title="`切换主题(${settingStore.shortcutKeyMap[ShortcutKey.ToggleTheme]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="ep:moon" v-if="settingStore.theme === 'dark'"
|
||||
@@ -120,16 +114,9 @@ watch(() => store.load, n => {
|
||||
<Icon icon="tabler:sun" v-else @click="toggleTheme"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="with-bg anim">
|
||||
<BaseIcon
|
||||
@click="runtimeStore.showSettingModal = true"
|
||||
:title="`设置(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.OpenSetting]})`"
|
||||
icon="uil:setting"/>
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`单词本(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
:title="`单词本(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -211,6 +198,7 @@ header {
|
||||
max-width: 45%;
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.hide {
|
||||
@@ -232,13 +220,6 @@ header {
|
||||
align-items: center;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.with-bg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background: var(--color-second-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
185
src/pages/pc/dict2/DictListPanel2.vue
Normal file
185
src/pages/pc/dict2/DictListPanel2.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {DictResource,} from "@/types.ts";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import {dictionaryResources} from "@/assets/dictionary.ts";
|
||||
import {groupBy} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictList from "@/pages/pc/components/list/DictList.vue";
|
||||
import DictGroup from "@/pages/pc/components/list/DictGroup.vue";
|
||||
import bookFlag from "@/assets/img/flags/book.png";
|
||||
import enFlag from "@/assets/img/flags/en.png";
|
||||
import jaFlag from "@/assets/img/flags/ja.png";
|
||||
import deFlag from "@/assets/img/flags/de.png";
|
||||
import codeFlag from "@/assets/img/flags/code.png";
|
||||
import myFlag from "@/assets/img/flags/my.png";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [],
|
||||
selectDict: [val: { dict: any, index: number }]
|
||||
}>()
|
||||
const store = useBaseStore()
|
||||
|
||||
let currentLanguage = $ref('en')
|
||||
let currentTranslateLanguage = $ref('common')
|
||||
let groupByLanguage = groupBy(dictionaryResources, 'language')
|
||||
let translateLanguageList = $ref([])
|
||||
|
||||
function groupByDictTags(dictList: DictResource[]) {
|
||||
return dictList.reduce<Record<string, DictResource[]>>((result, dict) => {
|
||||
dict.tags.forEach((tag) => {
|
||||
if (Object.prototype.hasOwnProperty.call(result, tag)) {
|
||||
result[tag].push(dict)
|
||||
} else {
|
||||
result[tag] = [dict]
|
||||
}
|
||||
})
|
||||
return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
const groupByTranslateLanguage = $computed(() => {
|
||||
let data = groupBy(groupByLanguage[currentLanguage], 'translateLanguage')
|
||||
// console.log('groupByTranslateLanguage', data)
|
||||
translateLanguageList = Object.keys(data)
|
||||
currentTranslateLanguage = translateLanguageList[0]
|
||||
return data
|
||||
})
|
||||
|
||||
const groupedByCategoryAndTag = $computed(() => {
|
||||
const currentTranslateLanguageDictList = groupByTranslateLanguage[currentTranslateLanguage]
|
||||
const groupByCategory = groupBy(currentTranslateLanguageDictList, 'category')
|
||||
|
||||
let data = []
|
||||
for (const [key, value] of Object.entries(groupByCategory)) {
|
||||
data.push([key, groupByDictTags(value)])
|
||||
}
|
||||
// console.log('groupedByCategoryAndTag', data)
|
||||
return data
|
||||
})
|
||||
|
||||
function del(e) {
|
||||
store.myDictList.splice(e.index, 1)
|
||||
}
|
||||
|
||||
const languageCategoryOptions = [
|
||||
{id: 'en', name: '英语', flag: enFlag},
|
||||
{id: 'ja', name: '日语', flag: jaFlag},
|
||||
{id: 'de', name: '德语', flag: deFlag},
|
||||
{id: 'code', name: 'Code', flag: codeFlag},
|
||||
]
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dict-list-panel">
|
||||
<header class="flex justify-center pb-3">
|
||||
<div class="container2 flex justify-between items-center">
|
||||
<div class="flex items-center gap-5">
|
||||
<BaseIcon icon="ion:chevron-back" @click="router.back"/>
|
||||
<div class="tabs">
|
||||
<div class="tab"
|
||||
:class="currentLanguage === item.id && 'active'"
|
||||
@click="currentLanguage = item.id"
|
||||
v-for="item in languageCategoryOptions">
|
||||
<img :src='item.flag' alt=""/>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BaseIcon icon="lucide:search"/>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page-content">
|
||||
<div class="dict-list-wrapper">
|
||||
<div class="translate ">
|
||||
<span>释义:</span>
|
||||
<el-radio-group v-model="currentTranslateLanguage">
|
||||
<el-radio-button border v-for="i in translateLanguageList" :label="i">{{ $t(i) }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<DictGroup
|
||||
v-for="item in groupedByCategoryAndTag"
|
||||
:select-id="store.currentDict.id"
|
||||
@selectDict="e => emit('selectDict',e)"
|
||||
:groupByTag="item[1]"
|
||||
:category="item[0]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.dict-list-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
$header-height: 4rem;
|
||||
//padding: var(--space);
|
||||
padding-top: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: var(--aside-width);
|
||||
width: calc(100vw - var(--aside-width));
|
||||
z-index: 9;
|
||||
background: var(--color-main-bg);
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
|
||||
.tab {
|
||||
color: var(--color-font-1);
|
||||
cursor: pointer;
|
||||
padding: .3rem;
|
||||
transition: all .5s;
|
||||
border-bottom: 2px solid transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
|
||||
&.active {
|
||||
$main: rgb(64, 158, 255);
|
||||
border-bottom: 2px solid $main;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-content {
|
||||
padding-top: 4rem;
|
||||
display: flex;
|
||||
|
||||
.dict-list-wrapper {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
.translate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-font-1);
|
||||
margin-bottom: 1rem;
|
||||
|
||||
& > span {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
37
src/pages/pc/dict2/index.vue
Normal file
37
src/pages/pc/dict2/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictListPanel2 from "./DictListPanel2.vue";
|
||||
import {Icon} from '@iconify/vue'
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const base = useBaseStore()
|
||||
const router = useRouter()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="word flex justify-center ">
|
||||
<div class="container2">
|
||||
<DictListPanel2
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card {
|
||||
@apply rounded-xl bg-white p-4 mt-5;
|
||||
}
|
||||
|
||||
.center {
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
</style>
|
||||
@@ -41,7 +41,7 @@ const router = useRouter()
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="row"
|
||||
:title="`设置(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.OpenSetting]})`"
|
||||
:title="`设置(${settingStore.shortcutKeyMap[ShortcutKey.OpenSetting]})`"
|
||||
@click="runtimeStore.showSettingModal = true">
|
||||
<Icon icon="uil:setting"/>
|
||||
<span>试卷</span>
|
||||
@@ -66,11 +66,12 @@ const router = useRouter()
|
||||
|
||||
.aside {
|
||||
background: white;
|
||||
//position: fixed;
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 12rem;
|
||||
width: var(--aside-width);
|
||||
padding: 1rem 1rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
|
||||
@@ -31,30 +31,30 @@ const settingStore = useSettingStore()
|
||||
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
|
||||
v-if="!isCollect"
|
||||
class="collect"
|
||||
@click="$emit('toggleCollect')"
|
||||
:title="`收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
:title="`收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="$emit('toggleCollect')"
|
||||
:title="`取消收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star-fill"/>
|
||||
|
||||
<Tooltip
|
||||
:title="`跳过(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
:title="`跳过(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="icon-park-outline:go-ahead" class="menu"
|
||||
|
||||
@@ -90,13 +90,13 @@ const showCollectToggleButton = $computed(() => {
|
||||
<div class="panel anim" v-show="settingStore.showPanel">
|
||||
<header>
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">当前</div>
|
||||
<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.simple.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.wrong.name }}</div>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`关闭(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
:title="`关闭(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
>
|
||||
<Close @click="settingStore.showPanel = false"/>
|
||||
</Tooltip>
|
||||
|
||||
@@ -343,7 +343,7 @@ defineExpose({showSentence, play, del,hideSentence,nextSentence})
|
||||
<div class="options-wrapper">
|
||||
<div class="flex gap10">
|
||||
<BaseIcon
|
||||
:title="`编辑(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.EditArticle]})`"
|
||||
:title="`编辑(${settingStore.shortcutKeyMap[ShortcutKey.EditArticle]})`"
|
||||
icon="tabler:edit"
|
||||
@click="emit('edit',props.article)"
|
||||
/>
|
||||
@@ -351,16 +351,16 @@ defineExpose({showSentence, play, del,hideSentence,nextSentence})
|
||||
v-if="!isArticleCollect(props.article)"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect(props.article)"
|
||||
:title="`收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
:title="`收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star"/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect(props.article)"
|
||||
:title="`取消收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star-fill"/>
|
||||
<BaseIcon
|
||||
:title="`跳过(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
:title="`跳过(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
icon="icon-park-outline:go-ahead"
|
||||
@click="emit('over')"/>
|
||||
</div>
|
||||
|
||||
@@ -359,7 +359,7 @@ defineExpose({getCurrentPractice})
|
||||
{{ store.currentDict.name }}
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`下一章(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentDict.chapterIndex < articleData.articles .length - 1">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
|
||||
|
||||
@@ -166,7 +166,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
<!-- <div class="volumeIcon">-->
|
||||
<!-- <Tooltip-->
|
||||
<!-- v-if="i === word.trans.length - 1"-->
|
||||
<!-- :title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayTranslatePronunciation]})`"-->
|
||||
<!-- :title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayTranslatePronunciation]})`"-->
|
||||
<!-- >-->
|
||||
<!-- <VolumeIcon-->
|
||||
<!-- ref="volumeTranslateIconRef"-->
|
||||
@@ -193,7 +193,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
</Tooltip>
|
||||
|
||||
@@ -247,7 +247,7 @@ onUnmounted(() => {
|
||||
v-if="prevWord">
|
||||
<Icon class="arrow" icon="bi:arrow-left" width="22"/>
|
||||
<Tooltip
|
||||
:title="`上一个(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
:title="`上一个(${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
>
|
||||
<div class="word">{{ prevWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -256,7 +256,7 @@ onUnmounted(() => {
|
||||
@click="next(false)"
|
||||
v-if="nextWord">
|
||||
<Tooltip
|
||||
:title="`下一个(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
:title="`下一个(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
>
|
||||
<div class="word" :class="settingStore.dictation && 'text-shadow'">{{ nextWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -288,40 +288,26 @@ onUnmounted(() => {
|
||||
v-loading="!store.load"
|
||||
>
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<div class="title">
|
||||
{{ store.chapterName }}
|
||||
</div>
|
||||
<BaseIcon title="切换词典"
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div style="position:relative;"
|
||||
@click.stop="null">
|
||||
<BaseIcon
|
||||
title="改变顺序"
|
||||
icon="icon-park-outline:sort-two"
|
||||
@click="showSortOption = !showSortOption"
|
||||
/>
|
||||
<MiniDialog
|
||||
v-model="showSortOption"
|
||||
style="width: 130rem;"
|
||||
>
|
||||
<div class="mini-row-title">
|
||||
列表循环设置
|
||||
</div>
|
||||
<div class="mini-row">
|
||||
<BaseButton size="small" @click="sort(Sort.reverse)">翻转</BaseButton>
|
||||
<BaseButton size="small" @click="sort(Sort.random)">随机</BaseButton>
|
||||
</div>
|
||||
</MiniDialog>
|
||||
</div>
|
||||
<BaseIcon icon="bi:arrow-right"
|
||||
@click="emitter.emit(EventKey.next)"
|
||||
:title="`下一章(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentDict.chapterIndex < store.currentDict.chapterWords.length - 1"/>
|
||||
</div>
|
||||
<div class="right">
|
||||
{{ data.words.length }}个单词
|
||||
<div>{{ data.words.length }}个单词</div>
|
||||
<div style="position:relative;"
|
||||
@click.stop="null">
|
||||
<BaseIcon
|
||||
title="改变顺序"
|
||||
icon="icon-park-outline:sort-two"
|
||||
@click="showSortOption = !showSortOption"
|
||||
/>
|
||||
<MiniDialog
|
||||
v-model="showSortOption"
|
||||
style="width: 9rem;"
|
||||
>
|
||||
<div class="mini-row-title">
|
||||
列表循环设置
|
||||
</div>
|
||||
<div class="mini-row">
|
||||
<BaseButton size="small" @click="sort(Sort.reverse)">翻转</BaseButton>
|
||||
<BaseButton size="small" @click="sort(Sort.random)">随机</BaseButton>
|
||||
</div>
|
||||
</MiniDialog>
|
||||
</div>
|
||||
</div>
|
||||
<WordList
|
||||
@@ -396,13 +382,12 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
|
||||
.arrow {
|
||||
min-width: 1.6rem;
|
||||
min-height: 1.6rem;
|
||||
font-size: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.word {
|
||||
font-size: 1.8rem;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: .2rem;
|
||||
font-family: var(--word-font-family);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {Icon} from '@iconify/vue'
|
||||
import {ActivityCalendar} from "vue-activity-calendar";
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useRouter} from "vue-router";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const base = useBaseStore()
|
||||
const router = useRouter()
|
||||
@@ -65,24 +66,12 @@ function clickEvent(e) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card ">
|
||||
<div class="flex justify-between ">
|
||||
<div class="title">
|
||||
所有词典
|
||||
</div>
|
||||
<Icon icon="lucide:search" class="text-2xl" />
|
||||
</div>
|
||||
|
||||
<div class="bg-white ">
|
||||
<DictListPanel
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
其他学习词典
|
||||
<div class="flex justify-between">
|
||||
<div class="title">
|
||||
其他学习词典
|
||||
</div>
|
||||
<BaseIcon icon="ic:round-add" @click="router.push('/dict')"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-6 mt-5 ">
|
||||
<div class=" p-4 rounded-md justify-between items-center bg-slate-200 " v-for="i in 3">
|
||||
|
||||
@@ -19,6 +19,7 @@ import MusicSetting from "@/pages/mobile/my/setting/MusicSetting.vue";
|
||||
import OtherSetting from "@/pages/mobile/my/setting/OtherSetting.vue";
|
||||
import WordHome from "@/pages/pc/word/WordHome.vue";
|
||||
import PC from "@/pages/pc/index.vue";
|
||||
import Dict2 from '@/pages/pc/dict2/index.vue'
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
@@ -26,6 +27,7 @@ export const routes: RouteRecordRaw[] = [
|
||||
redirect: '/word',
|
||||
children: [
|
||||
{path: 'word', component: WordHome},
|
||||
{path: 'dict', component: Dict2},
|
||||
{path: 'practice', component: Practice},
|
||||
]
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ export const EventKey = {
|
||||
keyup: 'keyup',
|
||||
onTyping: 'onTyping',
|
||||
repeat: 'repeat',
|
||||
//TODO 废弃
|
||||
next: 'next',
|
||||
write: 'write',
|
||||
editDict: 'editDict',
|
||||
|
||||
@@ -4,6 +4,7 @@ import {DefaultSettingState, SettingState} from "@/stores/setting.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {ArchiveReader, libarchiveWasm} from "libarchive-wasm";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
export function getRandom(a: number, b: number): number {
|
||||
return Math.random() * (b - a) + a;
|
||||
@@ -186,3 +187,13 @@ export function getDictFile(url: string) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useNav() {
|
||||
const router = useRouter()
|
||||
|
||||
function nav(val) {
|
||||
router.push(val)
|
||||
}
|
||||
|
||||
return {nav, back: router.back}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user