Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # package.json # pnpm-lock.yaml # src/assets/css/style.scss # src/pages/pc/components/Logo.vue # src/pages/pc/components/dialog/DictDiglog.vue # src/pages/pc/components/list/DictItem.vue # src/pages/pc/components/toolbar/index.vue # src/pages/pc/index.vue # src/pages/pc/practice/practice-article/TypingArticle.vue # src/pages/pc/word/WordHome.vue # src/router.ts # vite.config.ts
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Typing Word</title>
|
||||
<script>
|
||||
+(function () {
|
||||
function s() {
|
||||
function rem() {
|
||||
let html = document.documentElement;
|
||||
let max = parseInt('2048rem');
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
rem();
|
||||
window.addEventListener('resize', rem);
|
||||
})();
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
|
||||
|
||||
24
package.json
24
package.json
@@ -19,7 +19,7 @@
|
||||
"dependencies": {
|
||||
"@opentranslate/baidu": "^1.4.2",
|
||||
"@opentranslate/translator": "^1.4.2",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.5.0",
|
||||
"compromise": "^14.10.0",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"element-plus": "^2.3.9",
|
||||
@@ -32,14 +32,14 @@
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^5.0.3",
|
||||
"pinia": "^2.1.6",
|
||||
"sentence-splitter": "^4.2.1",
|
||||
"tesseract.js": "^4.1.1",
|
||||
"vant": "^4.8.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-activity-calendar": "^1.2.2",
|
||||
"vue-i18n": "9",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "3.4.21",
|
||||
"vue-router": "4.3.0",
|
||||
"vue-router": "4",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -47,26 +47,26 @@
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/lodash-es": "^4.17.9",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"@unocss/postcss": "^0.60.2",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"commitizen": "^4.3.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"esm": "^3.2.25",
|
||||
"gulp": "^4.0.2",
|
||||
"husky": "^8.0.3",
|
||||
"postcss": "^8.4.38",
|
||||
"push-dir": "^0.4.1",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.64.2",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "5.3.3",
|
||||
"typescript": "^5.2.0",
|
||||
"unocss": "^0.60.2",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"unplugin-vue-macros": "^2.9.1",
|
||||
"unplugin-vue-define-options": "^1.4.1",
|
||||
"vite": "^5.2.0",
|
||||
"vue-tsc": "^2.0.6",
|
||||
"vue-tsc": "^1.8.5",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"config": {
|
||||
|
||||
9847
pnpm-lock.yaml
generated
9847
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
8
postcss.config.mjs
Normal file
8
postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
// postcss.config.mjs
|
||||
import UnoCSS from '@unocss/postcss'
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
UnoCSS(),
|
||||
],
|
||||
}
|
||||
@@ -3,14 +3,11 @@
|
||||
@import "variable.scss";
|
||||
@import "anim";
|
||||
@import 'element-plus/theme-chalk/dark/css-vars';
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
: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,14 +32,14 @@
|
||||
|
||||
--practice-wrapper-translateX: 1px;
|
||||
--article-width: 50vw;
|
||||
--toolbar-width: 700rem;
|
||||
--toolbar-height: 54rem;
|
||||
--panel-width: 400rem;
|
||||
--space: 20rem;
|
||||
--radius: 8rem;
|
||||
--toolbar-width: 45rem;
|
||||
--toolbar-height: 3.2rem;
|
||||
--panel-width: 24rem;
|
||||
--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 + 24rem);
|
||||
--article-panel-margin-left: calc(50% - var(--practice-wrapper-translateX) / 2 + var(--article-width) / 2 + 24rem);
|
||||
--panel-margin-left: calc(50% - var(--practice-wrapper-translateX) / 2 + var(--toolbar-width) / 2 + 2rem);
|
||||
--article-panel-margin-left: calc(50% - var(--practice-wrapper-translateX) / 2 + var(--article-width) / 2 + 2rem);
|
||||
--anim-time: 0.5s;
|
||||
|
||||
--color-input-bg: white;
|
||||
@@ -50,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;
|
||||
}
|
||||
@@ -141,7 +140,7 @@ html.dark {
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-size: 1px;
|
||||
//font-size: 1px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100vw;
|
||||
@@ -197,13 +196,13 @@ a {
|
||||
.base-textarea {
|
||||
flex: 1;
|
||||
font-family: var(--font-family);
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6rem;
|
||||
padding: 8rem 10rem;
|
||||
border-radius: .4rem;
|
||||
padding: .5rem .6rem;
|
||||
transition: all .3s;
|
||||
min-height: 20rem;
|
||||
min-height: 1.2rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-textarea-bg);
|
||||
@@ -219,24 +218,24 @@ a {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8rem;
|
||||
height: 10rem;
|
||||
width: .5rem;
|
||||
height: .6rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-radius: 2rem;
|
||||
border-radius: .1rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--color-scrollbar);
|
||||
border-radius: 10rem;
|
||||
border-radius: .6rem;
|
||||
}
|
||||
|
||||
|
||||
/* 火狐美化滚动条 */
|
||||
* {
|
||||
scrollbar-color: var(--color-scrollbar) #f3f4f9;
|
||||
scrollbar-color: var(--color-scrollbar) #f3f4f9;
|
||||
/* 滑块颜色 滚动条背景颜色 */
|
||||
scrollbar-width: thin;
|
||||
/* 滚动条宽度有三种:thin、auto、none */
|
||||
@@ -290,20 +289,20 @@ footer {
|
||||
box-sizing: border-box;
|
||||
|
||||
.list-header {
|
||||
min-height: 50rem;
|
||||
padding: 10rem var(--space);
|
||||
min-height: 3rem;
|
||||
padding: .6rem var(--space);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
color: var(--color-font-3);
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -345,7 +344,7 @@ footer {
|
||||
}
|
||||
|
||||
.list-item-wrapper {
|
||||
padding-bottom: 15rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.common-list-item {
|
||||
@@ -354,23 +353,23 @@ footer {
|
||||
box-sizing: border-box;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
font-size: 18rem;
|
||||
border-radius: 8rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
padding: 10rem;
|
||||
gap: 10rem;
|
||||
padding: .6rem;
|
||||
gap: .6rem;
|
||||
border: 1px solid var(--color-item-border);
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
.title-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
gap: .2rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
@@ -378,7 +377,7 @@ footer {
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5rem;
|
||||
gap: .3rem;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
@@ -430,22 +429,22 @@ footer {
|
||||
.item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
gap: .5rem;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.word {
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
font-size: .9rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.item-sub-title {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@@ -458,15 +457,14 @@ footer {
|
||||
}
|
||||
|
||||
.common-title {
|
||||
min-height: 40rem;
|
||||
font-size: 18rem;
|
||||
min-height: 2.8rem;
|
||||
font-size: 1.1rem;
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.slide {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
@@ -487,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)"
|
||||
@@ -52,8 +52,8 @@ defineEmits(['click'])
|
||||
|
||||
.base-button {
|
||||
cursor: pointer;
|
||||
border-radius: 6rem;
|
||||
padding: 0 15rem;
|
||||
border-radius: .4rem;
|
||||
padding: 0 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -61,7 +61,7 @@ defineEmits(['click'])
|
||||
//background: #999;
|
||||
//background: rgb(60, 63, 65);
|
||||
//background: var(--color-second-bg);
|
||||
height: 36rem;
|
||||
height: 2.5rem;
|
||||
line-height: 1;
|
||||
position: relative;
|
||||
|
||||
@@ -76,25 +76,25 @@ defineEmits(['click'])
|
||||
}
|
||||
|
||||
&.small {
|
||||
height: 30rem;
|
||||
height: 2.4rem;
|
||||
|
||||
& > span {
|
||||
font-size: 13rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
height: 50rem;
|
||||
font-size: 18rem;
|
||||
padding: 0 22rem;
|
||||
height: 3rem;
|
||||
font-size: 1.1rem;
|
||||
padding: 0 1.4rem;
|
||||
& > span {
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
& > span {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
color: white;
|
||||
|
||||
:deep(a) {
|
||||
@@ -126,11 +126,11 @@ defineEmits(['click'])
|
||||
}
|
||||
|
||||
.key-notice {
|
||||
margin-left: 10rem;
|
||||
margin-left: .6rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12rem;
|
||||
font-size: .8rem;
|
||||
color: white;
|
||||
//gap: 2rem;
|
||||
|
||||
|
||||
@@ -27,16 +27,16 @@ defineEmits<{
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 12rem;
|
||||
gap: 20rem;
|
||||
font-size: .7rem;
|
||||
gap: 1.3rem;
|
||||
|
||||
span {
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: -50rem;
|
||||
width: 120rem;
|
||||
margin-top: -3rem;
|
||||
width: 9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,6 @@ defineProps<{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -75,7 +75,7 @@ defineExpose({play})
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
$w: 22rem;
|
||||
$w: 1.6rem;
|
||||
|
||||
:deep(svg) {
|
||||
width: $w;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {createI18n} from 'vue-i18n'
|
||||
import router from "@/router.ts";
|
||||
import VueVirtualScroller from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import 'virtual:uno.css';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'zh-CN',
|
||||
|
||||
@@ -207,7 +207,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>
|
||||
|
||||
92
src/pages/pc/article/ArticleIndex.vue
Normal file
92
src/pages/pc/article/ArticleIndex.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
|
||||
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()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="word flex justify-center ">
|
||||
<div class="w-5/10 pt-5">
|
||||
<div class="flex gap-6">
|
||||
<div class="card w-1/2 flex flex-col">
|
||||
<div class="title">
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid flex-1 flex gap-5 mt-4">
|
||||
<div class="p-4 flex-1 rounded-md bg-slate-200 relative" v-for="i in 3">
|
||||
<span>收藏</span>
|
||||
<div class="absolute bottom-4 right-4">333个词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<div class="card ">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="bg-slate-200 p-3 rounded-md cursor-pointer flex items-center">
|
||||
<span class="text-lg font-bold">{{ base.currentDict.name }}</span>
|
||||
<Icon icon="gg:arrows-exchange" class="text-2xl ml-2"/>
|
||||
<Icon icon="uil:setting" class="text-2xl ml-2"/>
|
||||
</div>
|
||||
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
|
||||
@click="router.push('/practice')">
|
||||
开始学习
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<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">
|
||||
<div class="flex justify-between w-full">
|
||||
<span>{{ base.currentDict.name }}</span>
|
||||
<div class="text-2xl ml-2 flex gap-4">
|
||||
<Icon icon="hugeicons:delete-02"/>
|
||||
<Icon icon="nonicons:go-16"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" color="white" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 text-2xl">
|
||||
<Icon icon="mingcute:down-line"/>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
164
src/pages/pc/article/ArticleIndexTEST.vue
Normal file
164
src/pages/pc/article/ArticleIndexTEST.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Toolbar from "@/pages/pc/components/toolbar/index.vue"
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import Footer from "@/pages/pc/practice/Footer.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
import Statistics from "@/pages/pc/practice/Statistics.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(practiceStore, () => {
|
||||
if (practiceStore.inputWordNumber < 1) {
|
||||
return practiceStore.correctRate = -1
|
||||
}
|
||||
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
|
||||
return practiceStore.correctRate = 0
|
||||
}
|
||||
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
repeat()
|
||||
}
|
||||
|
||||
//TODO 需要判断是否已忽略
|
||||
function repeat() {
|
||||
// console.log('repeat')
|
||||
emitter.emit(EventKey.resetWord)
|
||||
practiceRef.getCurrentPractice()
|
||||
}
|
||||
|
||||
function prev() {
|
||||
// console.log('next')
|
||||
if (store.currentDict.chapterIndex === 0) {
|
||||
ElMessage.warning('已经在第一章了~')
|
||||
} else {
|
||||
store.currentDict.chapterIndex--
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTranslate() {
|
||||
settingStore.translate = !settingStore.translate
|
||||
}
|
||||
|
||||
function toggleDictation() {
|
||||
settingStore.dictation = !settingStore.dictation
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
runtimeStore.showSettingModal = true
|
||||
}
|
||||
|
||||
function openDictDetail() {
|
||||
emitter.emit(EventKey.openDictModal, 'detail')
|
||||
}
|
||||
|
||||
function toggleConciseMode() {
|
||||
settingStore.showToolbar = !settingStore.showToolbar
|
||||
settingStore.showPanel = settingStore.showToolbar
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function jumpSpecifiedChapter(val: number) {
|
||||
store.currentDict.chapterIndex = val
|
||||
repeat()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.write, write)
|
||||
emitter.on(EventKey.repeat, repeat)
|
||||
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.on(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.on(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.on(ShortcutKey.DictationChapter, write)
|
||||
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.on(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.on(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.write, write)
|
||||
emitter.off(EventKey.repeat, repeat)
|
||||
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.off(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.off(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.off(ShortcutKey.DictationChapter, write)
|
||||
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.off(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<Toolbar/>
|
||||
<PracticeArticle ref="practiceRef"/>
|
||||
<Footer/>
|
||||
</div>
|
||||
<DictModal/>
|
||||
<Statistics/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -100,15 +100,15 @@ watch(() => settingStore.load, (n) => {
|
||||
right: var(--space);
|
||||
top: var(--space);
|
||||
z-index: 2;
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--color-second-bg);
|
||||
padding: 30rem;
|
||||
border-radius: 12rem;
|
||||
width: 500rem;
|
||||
gap: 40rem;
|
||||
padding: 1.8rem;
|
||||
border-radius: 0.7rem;
|
||||
width: 30rem;
|
||||
gap: 2.4rem;
|
||||
color: var(--color-font-1);
|
||||
line-height: 1.5;
|
||||
border: 1px solid var(--color-item-border);
|
||||
@@ -117,11 +117,11 @@ watch(() => settingStore.load, (n) => {
|
||||
|
||||
&.mobile{
|
||||
width: 95%;
|
||||
padding: 10rem;
|
||||
padding: 0.6rem;
|
||||
}
|
||||
|
||||
.notice {
|
||||
margin-top: 30rem;
|
||||
margin-top: 2.4rem;
|
||||
}
|
||||
|
||||
.active {
|
||||
@@ -136,23 +136,23 @@ watch(() => settingStore.load, (n) => {
|
||||
|
||||
.href-wrapper {
|
||||
display: flex;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: 0.6rem;
|
||||
|
||||
.round {
|
||||
color: var(--color-font-1);
|
||||
border-radius: 50rem;
|
||||
padding: 10rem 10rem;
|
||||
padding-left: 20rem;
|
||||
gap: 30rem;
|
||||
border-radius: 3rem;
|
||||
padding: 0.6rem 0.6rem;
|
||||
padding-left: 1.2rem;
|
||||
gap: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: var(--color-main-bg);
|
||||
|
||||
.href {
|
||||
font-size: 14rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,11 +167,11 @@ watch(() => settingStore.load, (n) => {
|
||||
}
|
||||
|
||||
.collect-keyboard {
|
||||
margin-top: 20rem;
|
||||
font-size: 16rem;
|
||||
margin-top: 1.2rem;
|
||||
font-size: 1rem;
|
||||
|
||||
span {
|
||||
margin-left: 10rem;
|
||||
margin-left: 0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,12 +181,12 @@ watch(() => settingStore.load, (n) => {
|
||||
right: var(--space);
|
||||
top: var(--space);
|
||||
position: absolute;
|
||||
font-size: 14rem;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
color: var(--color-font-1);
|
||||
gap: 10rem;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ function del(e) {
|
||||
:list="groupByTranslateLanguage['common']"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="translate">
|
||||
<span>翻译:</span>
|
||||
<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>
|
||||
@@ -119,10 +119,10 @@ function del(e) {
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.dict-list-panel {
|
||||
width: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
$header-height: 60rem;
|
||||
padding: var(--space);
|
||||
$header-height: 5rem;
|
||||
//padding: var(--space);
|
||||
padding-top: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -134,18 +134,18 @@ function del(e) {
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 20rem;
|
||||
gap: 2rem;
|
||||
|
||||
.tab {
|
||||
color: var(--color-font-1);
|
||||
cursor: pointer;
|
||||
padding: 10rem;
|
||||
padding-bottom: 5rem;
|
||||
padding: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
transition: all .5s;
|
||||
border-bottom: 2px solid transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rem;
|
||||
gap: 0.6rem;
|
||||
|
||||
&.active {
|
||||
$main: rgb(64, 158, 255);
|
||||
@@ -153,7 +153,7 @@ function del(e) {
|
||||
}
|
||||
|
||||
img {
|
||||
height: 30rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,16 +167,16 @@ function del(e) {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
padding-right: 10rem;
|
||||
padding-right: 1rem;
|
||||
|
||||
.translate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-font-1);
|
||||
margin-bottom: 30rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
& > span {
|
||||
font-size: 22rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function toggle() {
|
||||
autosize
|
||||
autofocus
|
||||
type="textarea"
|
||||
:input-style="`color: var(--color-font-1);font-size: 16rem;`"
|
||||
:input-style="`color: var(--color-font-1);font-size: 1rem;`"
|
||||
/>
|
||||
<div class="options">
|
||||
<BaseButton @click="toggle">取消</BaseButton>
|
||||
@@ -53,7 +53,7 @@ function toggle() {
|
||||
<div
|
||||
v-else
|
||||
class="text"
|
||||
:style="`font-size: 16rem;`"
|
||||
:style="`font-size: 1rem;`"
|
||||
@click="toggle">
|
||||
{{ value }}
|
||||
</div>
|
||||
@@ -61,12 +61,12 @@ function toggle() {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.edit-text {
|
||||
margin-top: 10rem;
|
||||
margin-top: .6rem;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.options {
|
||||
margin-top: 10rem;
|
||||
gap: 10rem;
|
||||
margin-top: .6rem;
|
||||
gap: .6rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -74,6 +74,6 @@ function toggle() {
|
||||
|
||||
.text {
|
||||
color: var(--color-font-1);
|
||||
min-height: 18rem;
|
||||
min-height: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -6,16 +6,16 @@
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
$w: 22rem;
|
||||
$w: 1.4rem;
|
||||
.icon-wrapper {
|
||||
cursor: pointer;
|
||||
//padding: 2rem;
|
||||
width: 26rem;
|
||||
height: 26rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 3rem;
|
||||
border-radius: .3rem;
|
||||
background: transparent;
|
||||
transition: all .3s;
|
||||
color: var(--color-main-active);
|
||||
|
||||
@@ -44,9 +44,9 @@ useDisableEventListener(() => focus)
|
||||
|
||||
.base-input {
|
||||
border: 1px solid var(--color-second-bg);
|
||||
border-radius: 6rem;
|
||||
border-radius: .4rem;
|
||||
overflow: hidden;
|
||||
padding: 3rem 5rem;
|
||||
padding: .2rem .3rem;
|
||||
transition: all .3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -68,9 +68,9 @@ useDisableEventListener(() => focus)
|
||||
|
||||
input {
|
||||
font-family: var(--font-family);
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
outline: none;
|
||||
min-height: 20rem;
|
||||
min-height: 1.2rem;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
|
||||
@@ -18,13 +18,13 @@ function goHome(){
|
||||
<style scoped lang="scss">
|
||||
.logo {
|
||||
//position: fixed;
|
||||
//left: var(--space);
|
||||
//top: var(--space);
|
||||
left: var(--space);
|
||||
top: var(--space);
|
||||
z-index: 1;
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
height: 35rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -480,20 +480,20 @@ function importData(e) {
|
||||
align-items: center;
|
||||
|
||||
.tabs {
|
||||
padding: 10rem 20rem;
|
||||
padding: .6rem 1.6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//align-items: center;
|
||||
//justify-content: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
padding: 10rem 15rem;
|
||||
border-radius: 8rem;
|
||||
padding: .6rem .9rem;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-bg);
|
||||
@@ -502,9 +502,9 @@ function importData(e) {
|
||||
}
|
||||
|
||||
.git-log {
|
||||
font-size: 10rem;
|
||||
font-size: .6rem;
|
||||
color: gray;
|
||||
margin-bottom: 5rem;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,17 +513,17 @@ function importData(e) {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 10rem var(--space);
|
||||
padding: 0 var(--space);
|
||||
|
||||
.row {
|
||||
min-height: 40rem;
|
||||
height: 2.6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: calc(var(--space) * 5);
|
||||
|
||||
.wrapper {
|
||||
height: 30rem;
|
||||
height: 2rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -532,7 +532,7 @@ function importData(e) {
|
||||
span {
|
||||
text-align: right;
|
||||
//width: 30rem;
|
||||
font-size: 12rem;
|
||||
font-size: .7rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@@ -540,31 +540,34 @@ function importData(e) {
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 150rem;
|
||||
width: 9rem;
|
||||
box-sizing: border-box;
|
||||
margin-right: 10rem;
|
||||
height: 28rem;
|
||||
margin-right: .6rem;
|
||||
height: 1.8rem;
|
||||
outline: none;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3rem;
|
||||
padding: 0 5rem;
|
||||
border-radius: .2rem;
|
||||
padding: 0 .3rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 22rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 14rem;
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,17 +580,17 @@ function importData(e) {
|
||||
|
||||
.scroll {
|
||||
flex: 1;
|
||||
padding-right: 10rem;
|
||||
padding-right: .6rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-bottom: 20rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 10rem;
|
||||
font-size: 12rem;
|
||||
margin-bottom: .6rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.line {
|
||||
@@ -629,7 +632,7 @@ function importData(e) {
|
||||
color: var(--color-font-1);
|
||||
|
||||
p {
|
||||
font-size: 30rem;
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
.github {
|
||||
@@ -640,7 +643,7 @@ function importData(e) {
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@ export default {
|
||||
|
||||
.tip {
|
||||
position: fixed;
|
||||
font-size: 14rem;
|
||||
font-size: 0.8rem;
|
||||
z-index: 9999;
|
||||
border-radius: 4rem;
|
||||
padding: 10rem;
|
||||
border-radius: .2rem;
|
||||
padding: .8rem;
|
||||
color: var(--color-font-1);
|
||||
background: var(--color-tooltip-bg);
|
||||
//box-shadow: 1px 1px 6px #bbbbbb;
|
||||
|
||||
@@ -389,7 +389,7 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
display: flex;
|
||||
gap: var(--space);
|
||||
padding: var(--space);
|
||||
padding-top: 10rem;
|
||||
padding-top: .6rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
@@ -411,7 +411,7 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22rem;
|
||||
font-size: 1.4rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -420,11 +420,11 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
//margin-bottom: 10rem;
|
||||
|
||||
.label {
|
||||
height: 45rem;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,31 +441,31 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
}
|
||||
|
||||
.article-translate {
|
||||
margin-top: 10rem;
|
||||
margin-bottom: 20rem;
|
||||
margin-top: .6rem;
|
||||
margin-bottom: 1.2rem;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
border-radius: 8rem;
|
||||
border-radius: .5rem;
|
||||
|
||||
.section {
|
||||
background: var(--color-textarea-bg);
|
||||
margin-bottom: 20rem;
|
||||
margin-bottom: 1.2rem;
|
||||
padding: var(--space);
|
||||
border-radius: 8rem;
|
||||
border-radius: .5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sentence {
|
||||
margin-bottom: 20rem;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,14 +484,14 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
.warning {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ useWindowClick(() => showExport = false)
|
||||
:header="false"
|
||||
>
|
||||
<div class="add-article">
|
||||
<div class="slide">
|
||||
<div class="aslide">
|
||||
<header>
|
||||
<div class="dict-name">{{ runtimeStore.editDict.name }}</div>
|
||||
</header>
|
||||
@@ -224,18 +224,18 @@ useWindowClick(() => showExport = false)
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 20rem;
|
||||
top: 20rem;
|
||||
right: 1.2rem;
|
||||
top: 1.2rem;
|
||||
}
|
||||
|
||||
.slide {
|
||||
.aslide {
|
||||
width: 14vw;
|
||||
height: 100%;
|
||||
padding: 0 10rem;
|
||||
padding: 0 .6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
$height: 60rem;
|
||||
$height: 4rem;
|
||||
|
||||
header {
|
||||
height: $height;
|
||||
@@ -245,25 +245,25 @@ useWindowClick(() => showExport = false)
|
||||
//opacity: 0;
|
||||
|
||||
.dict-name {
|
||||
font-size: 30rem;
|
||||
font-size: 2rem;
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.translate-name {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.add {
|
||||
width: 260rem;
|
||||
width: 16rem;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
border-radius: .5rem;
|
||||
margin-bottom: .6rem;
|
||||
padding: .6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
@@ -274,7 +274,7 @@ useWindowClick(() => showExport = false)
|
||||
.footer {
|
||||
height: $height;
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
|
||||
@@ -70,25 +70,25 @@ onUnmounted(() => {
|
||||
.article-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space);
|
||||
font-size: 24rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-indent: 1.5em;
|
||||
line-height: 35rem;
|
||||
line-height: 2rem;
|
||||
overflow: auto;
|
||||
padding-right: 10rem;
|
||||
padding-bottom: 50rem;
|
||||
padding-right: .6rem;
|
||||
padding-bottom: 3rem;
|
||||
|
||||
.sentence {
|
||||
margin-bottom: 30rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,18 +189,15 @@ async function cancel() {
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
$modal-mask-bg: rgba(#000, .45);
|
||||
$radius: 8rem;
|
||||
$radius: .5rem;
|
||||
$time: 0.3s;
|
||||
$header-height: 60rem;
|
||||
$header-height: 4rem;
|
||||
|
||||
@keyframes bounce-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
@@ -288,8 +285,8 @@ $header-height: 60rem;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 20rem;
|
||||
top: 20rem;
|
||||
right: 1.2rem;
|
||||
top: 1.2rem;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
@@ -297,14 +294,14 @@ $header-height: 60rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rem 24rem 16rem;
|
||||
padding: 1.3rem 1.3rem 1rem;
|
||||
border-radius: $radius $radius 0 0;
|
||||
|
||||
.title {
|
||||
color: var(--color-font-1);
|
||||
font-weight: bold;
|
||||
font-size: 24rem;
|
||||
line-height: 33rem;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,21 +309,21 @@ $header-height: 60rem;
|
||||
box-sizing: border-box;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-weight: 400;
|
||||
font-size: 18rem;
|
||||
line-height: 27rem;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.7rem;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
|
||||
&.padding {
|
||||
padding: 4rem 24rem 24rem;
|
||||
padding: .2rem 1.6rem 1.6rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 350rem;
|
||||
width: 25rem;
|
||||
color: var(--color-font-1);
|
||||
padding: 4rem 24rem 24rem;
|
||||
padding: .2rem 1.6rem 1.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,11 +331,11 @@ $header-height: 60rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rem 24rem;
|
||||
padding: 1rem 1.6rem;
|
||||
color: #fff;
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
background: rgba(0, 0, 0, .2);
|
||||
border-radius: 0 0 24rem 24rem;
|
||||
border-radius: 0 0 1.6rem 1.6rem;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
@@ -347,7 +344,7 @@ $header-height: 60rem;
|
||||
|
||||
.text {
|
||||
color: white;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {chunk} from "lodash-es";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {Icon} from '@iconify/vue';
|
||||
import "vue-activity-calendar/style.css";
|
||||
@@ -11,106 +11,24 @@ 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";
|
||||
|
||||
const store = useBaseStore()
|
||||
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)
|
||||
} else {
|
||||
runtimeStore.editDict = cloneDeep({
|
||||
...cloneDeep(DefaultDict),
|
||||
...item,
|
||||
})
|
||||
runtimeStore.editDict.id = nanoid(6)
|
||||
//设置默认章节单词数
|
||||
runtimeStore.editDict.chapterWordNumber = settingStore.chapterWordNumber
|
||||
}
|
||||
|
||||
if ([DictType.collect, DictType.simple, DictType.wrong].includes(runtimeStore.editDict.type)) {
|
||||
} else {
|
||||
//如果不是自定义词典,并且有url地址才去下载
|
||||
if (!runtimeStore.editDict.isCustom && runtimeStore.editDict.url) {
|
||||
let url = `./dicts/${runtimeStore.editDict.language}/${runtimeStore.editDict.type}/${runtimeStore.editDict.translateLanguage}/${runtimeStore.editDict.url}`;
|
||||
if (runtimeStore.editDict.type === DictType.word) {
|
||||
if (!runtimeStore.editDict.originWords.length) {
|
||||
let v = await getDictFile(url)
|
||||
v.map(s => {
|
||||
s.id = nanoid(6)
|
||||
})
|
||||
runtimeStore.editDict.originWords = cloneDeep(v)
|
||||
changeSort(runtimeStore.editDict.sort, false)
|
||||
} else {
|
||||
runtimeStore.editDict.length = runtimeStore.editDict.words.length
|
||||
}
|
||||
}
|
||||
if (runtimeStore.editDict.type === DictType.article) {
|
||||
if (!runtimeStore.editDict.articles.length) {
|
||||
let v = await getDictFile(url)
|
||||
v.map(s => {
|
||||
s.id = nanoid(6)
|
||||
})
|
||||
runtimeStore.editDict.articles = cloneDeep(v)
|
||||
} else {
|
||||
runtimeStore.editDict.length = runtimeStore.editDict.articles.length
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
chapterWordNumber = runtimeStore.editDict.chapterWordNumber
|
||||
chapterList2 = runtimeStore.editDict.chapterWords.map((v, i) => ({id: i}))
|
||||
loading = false
|
||||
}
|
||||
|
||||
|
||||
function close() {
|
||||
show = false
|
||||
}
|
||||
|
||||
//TODO 切大词典太卡了
|
||||
function changeDict() {
|
||||
close()
|
||||
store.changeDict(runtimeStore.editDict)
|
||||
setTimeout(() => {
|
||||
runtimeStore.editDict = cloneDeep(DefaultDict)
|
||||
})
|
||||
ElMessage.success('切换成功')
|
||||
}
|
||||
|
||||
const dictIsArticle = $computed(() => {
|
||||
return isArticle(runtimeStore.editDict.type)
|
||||
})
|
||||
@@ -127,41 +45,9 @@ 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()
|
||||
}
|
||||
@@ -175,36 +61,11 @@ function option(type: string) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openDictModal, (type: 'detail' | 'list' | 'my') => {
|
||||
if (type === "detail") {
|
||||
selectDict({dict: store.currentDict, index: 0})
|
||||
}
|
||||
if (type === "list") {
|
||||
// currentLanguage = 'en'
|
||||
step = 0
|
||||
}
|
||||
if (type === "my") {
|
||||
// currentLanguage = 'my'
|
||||
step = 0
|
||||
}
|
||||
emitter.on(EventKey.openDictModal, (typ) => {
|
||||
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>
|
||||
|
||||
@@ -214,215 +75,67 @@ 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 gap10">
|
||||
<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">
|
||||
{{ store.currentDict.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">{{ store.currentDict.description }}</div>
|
||||
<div class="text flex align-center">
|
||||
<div v-if="dictIsArticle">总文章:{{ store.currentDict.articles.length }}篇
|
||||
</div>
|
||||
<div v-else>总词汇:
|
||||
<span class="count" @click="showAllWordModal">{{
|
||||
store.currentDict.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="store.currentDict.words.length < 10 ? 10 : 500"
|
||||
size="small"
|
||||
v-model="chapterWordNumber"
|
||||
@change="resetChapterList"
|
||||
/>
|
||||
</div>
|
||||
<div class="notice">
|
||||
<span class="text">最小:10</span>
|
||||
<span class="text">最大:{{ store.currentDict.words.length < 10 ? 10 : 500 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<BaseButton @click="close">关闭</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<WordListDialog/>
|
||||
<EditBatchArticleModal/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style";
|
||||
|
||||
$header-height: 60rem;
|
||||
$header-height: 4rem;
|
||||
|
||||
#DictDialog {
|
||||
//position: fixed;
|
||||
@@ -431,13 +144,7 @@ $header-height: 60rem;
|
||||
//transform: translate(-50%, -50%);
|
||||
background: var(--color-second-bg);
|
||||
z-index: 99999;
|
||||
width: 1030rem;
|
||||
height: 75vh;
|
||||
}
|
||||
|
||||
.dict-detail-page {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
width: 30rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -455,7 +162,7 @@ $header-height: 60rem;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -464,110 +171,48 @@ $header-height: 60rem;
|
||||
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: 10rem;
|
||||
position: relative;
|
||||
font-size: 14rem;
|
||||
padding-right: var(--space);
|
||||
@extend .column;
|
||||
|
||||
|
||||
.name {
|
||||
font-size: 24rem;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 16rem;
|
||||
margin-bottom: 20rem;
|
||||
}
|
||||
|
||||
.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: 34rem;
|
||||
word-break: keep-all;
|
||||
gap: 10rem;
|
||||
|
||||
.el-radio {
|
||||
margin-right: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
.my-slider {
|
||||
:deep(.el-slider__input) {
|
||||
width: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transform: translate3d(0, -5rem, 0);
|
||||
padding-left: 100rem;
|
||||
font-size: 13rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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>
|
||||
|
||||
|
||||
@@ -19,19 +19,19 @@ withDefaults(defineProps<IProps>(), {
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.mini-row-title {
|
||||
min-height: 35rem;
|
||||
min-height: 2rem;
|
||||
text-align: center;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
|
||||
.mini-row {
|
||||
min-height: 35rem;
|
||||
min-height: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -43,12 +43,12 @@ withDefaults(defineProps<IProps>(), {
|
||||
.mini-modal {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
width: 180rem;
|
||||
width: 12rem;
|
||||
background: var(--color-second-bg);
|
||||
border-radius: 8rem;
|
||||
border-radius: .5rem;
|
||||
box-shadow: 0 0 8px 2px var(--color-item-border);
|
||||
padding: 10rem var(--space);
|
||||
top: 40rem;
|
||||
padding: .6rem var(--space);
|
||||
top: 2.4rem;
|
||||
left: 50%;
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
//margin-top: 10rem;
|
||||
|
||||
@@ -32,20 +32,20 @@ let disabledDialogEscKey = $ref(true)
|
||||
align-items: center;
|
||||
|
||||
.tabs {
|
||||
padding: 10rem 20rem;
|
||||
padding: .6rem 1.6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//align-items: center;
|
||||
//justify-content: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
padding: 10rem 15rem;
|
||||
border-radius: 8rem;
|
||||
padding: .6rem .9rem;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-bg);
|
||||
@@ -54,9 +54,9 @@ let disabledDialogEscKey = $ref(true)
|
||||
}
|
||||
|
||||
.git-log {
|
||||
font-size: 10rem;
|
||||
font-size: .6rem;
|
||||
color: gray;
|
||||
margin-bottom: 5rem;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,14 +68,14 @@ let disabledDialogEscKey = $ref(true)
|
||||
padding: 0 var(--space);
|
||||
|
||||
.row {
|
||||
height: 40rem;
|
||||
height: 2.6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: calc(var(--space) * 5);
|
||||
|
||||
.wrapper {
|
||||
height: 30rem;
|
||||
height: 2rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -84,7 +84,7 @@ let disabledDialogEscKey = $ref(true)
|
||||
span {
|
||||
text-align: right;
|
||||
//width: 30rem;
|
||||
font-size: 12rem;
|
||||
font-size: .7rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@@ -92,15 +92,15 @@ let disabledDialogEscKey = $ref(true)
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 150rem;
|
||||
width: 9rem;
|
||||
box-sizing: border-box;
|
||||
margin-right: 10rem;
|
||||
height: 28rem;
|
||||
margin-right: .6rem;
|
||||
height: 1.8rem;
|
||||
outline: none;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3rem;
|
||||
padding: 0 5rem;
|
||||
border-radius: .2rem;
|
||||
padding: 0 .3rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
@@ -110,16 +110,16 @@ let disabledDialogEscKey = $ref(true)
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 18rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 14rem;
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,17 +132,17 @@ let disabledDialogEscKey = $ref(true)
|
||||
|
||||
.scroll {
|
||||
flex: 1;
|
||||
padding-right: 10rem;
|
||||
padding-right: .6rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-bottom: 20rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 10rem;
|
||||
font-size: 12rem;
|
||||
margin-bottom: .6rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.line {
|
||||
|
||||
@@ -77,7 +77,7 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15rem;
|
||||
gap: 1rem;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -88,7 +88,7 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -26,13 +26,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"
|
||||
@@ -43,26 +46,28 @@ watch(() => props.groupByTag, () => {
|
||||
<style scoped lang="scss">
|
||||
.dict-group {
|
||||
color: var(--color-font-1);
|
||||
margin-bottom: 40rem;
|
||||
margin-bottom: 1.5rem;
|
||||
//border-bottom: 1px dashed gray;
|
||||
|
||||
.category {
|
||||
font-size: 24rem;
|
||||
padding-bottom: 10rem;
|
||||
border-bottom: 1px dashed gray;
|
||||
font-size: 1.2rem;
|
||||
//padding-bottom: 1rem;
|
||||
}
|
||||
.border{
|
||||
border-top: 1px dashed gray;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 10rem 0;
|
||||
margin: 1rem 0;
|
||||
|
||||
.tag {
|
||||
color: var(--color-font-1);
|
||||
cursor: pointer;
|
||||
padding: 5rem 10rem;
|
||||
border-radius: 20rem;
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 2rem;
|
||||
|
||||
&.active {
|
||||
color: var(--color-font-active-1);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {$computed} from "vue/macros";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -34,7 +35,7 @@ let length = $computed(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="dict-item anim"
|
||||
class="dict-item anim rounded-md p-4"
|
||||
:class="active && 'active'"
|
||||
>
|
||||
<template v-if="dict.id">
|
||||
@@ -61,22 +62,21 @@ let length = $computed(() => {
|
||||
.dict-item {
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
padding: 10rem;
|
||||
width: 125rem;
|
||||
height: 165rem;
|
||||
border-radius: 10rem;
|
||||
//width: 9rem;
|
||||
//height: 12rem;
|
||||
position: relative;
|
||||
background: var(--color-third-bg);
|
||||
//background: var(--color-third-bg);
|
||||
background: white;
|
||||
border: 1px solid var(--color-item-border);
|
||||
color: var(--color-font-1);
|
||||
font-size: 14rem;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
|
||||
.name {
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box; //作为弹性伸缩盒子模型显示。
|
||||
@@ -94,6 +94,7 @@ let length = $computed(() => {
|
||||
}
|
||||
|
||||
.num {
|
||||
margin-top: 2rem;
|
||||
text-align: right;
|
||||
color: var(--color-font-2);
|
||||
//font-weight: bold;
|
||||
@@ -101,14 +102,14 @@ let length = $computed(() => {
|
||||
|
||||
.go {
|
||||
position: absolute;
|
||||
right: 10rem;
|
||||
bottom: 15rem;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
}
|
||||
|
||||
.del {
|
||||
position: absolute;
|
||||
top: 10rem;
|
||||
right: 10rem;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
opacity: 0;
|
||||
transition: opacity .3s;
|
||||
}
|
||||
@@ -137,17 +138,17 @@ let length = $computed(() => {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 55rem;
|
||||
width: 55rem;
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
color: white;
|
||||
//background-color: skyblue;
|
||||
background-color: var(--color-main-active);
|
||||
clip-path: polygon(0 10%, 0% 100%, 100% 100%);
|
||||
font-size: 12rem;
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
padding: 4rem;
|
||||
padding: 0.2rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})"
|
||||
@@ -32,7 +32,7 @@ const emit = defineEmits<{
|
||||
.dict-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -161,10 +161,10 @@ defineExpose({scrollBottom})
|
||||
transition: all .3s;
|
||||
flex: 1;
|
||||
overflow: overlay;
|
||||
padding-right: 5rem;
|
||||
padding-right: .3rem;
|
||||
|
||||
.search {
|
||||
margin: 10rem 0;
|
||||
margin: .6rem 0;
|
||||
}
|
||||
|
||||
.list {
|
||||
@@ -172,9 +172,9 @@ defineExpose({scrollBottom})
|
||||
box-sizing: border-box;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
border-radius: .5rem;
|
||||
margin-bottom: .6rem;
|
||||
padding: .6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
|
||||
@@ -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: 230rem;"
|
||||
>
|
||||
<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: 250rem;
|
||||
overflow: auto;
|
||||
padding-right: 5rem;
|
||||
|
||||
.chapter-list-item {
|
||||
margin-bottom: 7rem;
|
||||
height: 36rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
font-size: 18rem;
|
||||
border-radius: 8rem;
|
||||
transition: all .3s;
|
||||
padding: 10rem;
|
||||
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"
|
||||
@@ -114,9 +114,9 @@ function save() {
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 10rem;
|
||||
margin-top: .6rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
</style>
|
||||
@@ -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="请选择"
|
||||
|
||||
@@ -13,11 +13,11 @@ import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
import {DictType, ShortcutKey} from "@/types.ts";
|
||||
import ChapterName from "@/pages/pc/components/toolbar/ChapterName.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
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()
|
||||
@@ -31,7 +31,7 @@ const moreOptionsRef = $ref<HTMLDivElement>(null)
|
||||
watch([() => settingStore.showToolbar, () => headerRef], n => {
|
||||
if (n[1]) {
|
||||
if (n[0]) {
|
||||
n[1].style.marginTop = '10rem'
|
||||
n[1].style.marginTop = '.8rem'
|
||||
} else {
|
||||
let rect = n[1].getBoundingClientRect()
|
||||
n[1].style.marginTop = `-${rect.height}px`
|
||||
@@ -56,6 +56,7 @@ watch(() => store.load, n => {
|
||||
}
|
||||
})
|
||||
|
||||
const {nav} = useNav()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,12 +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>
|
||||
<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>
|
||||
@@ -84,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"
|
||||
@@ -102,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'"
|
||||
@@ -119,12 +114,9 @@ watch(() => store.load, n => {
|
||||
<Icon icon="tabler:sun" v-else @click="toggleTheme"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="with-bg anim">
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`单词本(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
:title="`单词本(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -143,14 +135,14 @@ watch(() => store.load, n => {
|
||||
|
||||
<style lang="scss">
|
||||
.info {
|
||||
border-radius: 6rem;
|
||||
border-radius: .5rem;
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
padding: 6rem 8rem;
|
||||
padding: .5rem .6rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-main-active);
|
||||
@@ -182,15 +174,15 @@ watch(() => store.load, n => {
|
||||
|
||||
header {
|
||||
width: var(--toolbar-width);
|
||||
margin-top: 10rem;
|
||||
margin-top: 1rem;
|
||||
background: var(--color-second-bg);
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 30rem;
|
||||
border-radius: .8rem;
|
||||
margin-bottom: 3rem;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 4rem var(--space);
|
||||
padding: .4rem var(--space);
|
||||
box-sizing: border-box;
|
||||
gap: 10rem;
|
||||
gap: 1rem;
|
||||
border: 1px solid var(--color-item-border);
|
||||
transition: all var(--anim-time);
|
||||
box-shadow: var(--shadow);
|
||||
@@ -204,12 +196,13 @@ header {
|
||||
.dict-name {
|
||||
display: flex;
|
||||
max-width: 45%;
|
||||
font-size: 17rem;
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.hide {
|
||||
transform: translateX(calc(100% - 36rem));
|
||||
transform: translateX(calc(100% - 2rem));
|
||||
}
|
||||
|
||||
.options {
|
||||
@@ -217,12 +210,9 @@ header {
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
.icon-wrapper {
|
||||
margin-left: 10rem;
|
||||
}
|
||||
|
||||
:deep(.icon-wrapper) {
|
||||
margin-left: 10rem;
|
||||
margin-left: .2rem;
|
||||
}
|
||||
|
||||
.more {
|
||||
@@ -230,13 +220,6 @@ header {
|
||||
align-items: center;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.with-bg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background: var(--color-second-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +230,7 @@ header {
|
||||
cursor: pointer;
|
||||
transition: all .5s;
|
||||
transform: translate3d(-50%, 100%, 0) rotate(180deg);
|
||||
padding: 5rem;
|
||||
padding: .5rem;
|
||||
|
||||
&.down {
|
||||
transform: translate3d(-50%, 100%, 0) rotate(0);
|
||||
|
||||
@@ -145,7 +145,7 @@ onMounted(() => {
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
#DictDialog {
|
||||
font-size: 14rem;
|
||||
font-size: .9rem;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
|
||||
@@ -407,8 +407,8 @@ defineExpose({getDictDetail, add, editDict})
|
||||
align-items: center;
|
||||
color: var(--color-font-1);
|
||||
padding: 0 var(--space);
|
||||
gap: 20rem;
|
||||
margin-bottom: 20rem;
|
||||
gap: 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
.back {
|
||||
height: 100%;
|
||||
@@ -419,15 +419,15 @@ defineExpose({getDictDetail, add, editDict})
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
flex-direction: column;
|
||||
color: var(--color-font-2);
|
||||
|
||||
.top {
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
font-size: 20rem;
|
||||
gap: .6rem;
|
||||
font-size: 1.2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ defineExpose({getDictDetail, add, editDict})
|
||||
|
||||
.box {
|
||||
background: white;
|
||||
border-radius: 10rem;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
padding-bottom: var(--space);
|
||||
@@ -463,7 +463,7 @@ defineExpose({getDictDetail, add, editDict})
|
||||
}
|
||||
|
||||
.chapter-list {
|
||||
width: 400rem;
|
||||
width: 25rem;
|
||||
height: 100%;
|
||||
@extend .box;
|
||||
|
||||
@@ -478,19 +478,19 @@ defineExpose({getDictDetail, add, editDict})
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.select {
|
||||
height: 45rem;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 5rem;
|
||||
gap: .3rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,7 @@ defineExpose({getDictDetail, add, editDict})
|
||||
flex: 1;
|
||||
padding: var(--space);
|
||||
overflow: hidden;
|
||||
font-size: 20rem;
|
||||
font-size: 1.2rem;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
@@ -517,18 +517,18 @@ defineExpose({getDictDetail, add, editDict})
|
||||
align-items: center;
|
||||
position: relative;
|
||||
margin-bottom: var(--space);
|
||||
font-size: 24rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-indent: 1.5em;
|
||||
line-height: 35rem;
|
||||
line-height: 2.6rem;
|
||||
overflow: auto;
|
||||
padding-right: 10rem;
|
||||
padding-bottom: 50rem;
|
||||
padding-right: .6rem;
|
||||
padding-bottom: 3rem;
|
||||
|
||||
.sentence {
|
||||
margin-bottom: 30rem;
|
||||
margin-bottom: 2.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,19 +183,19 @@ useWindowClick(() => show = false)
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.select {
|
||||
height: 45rem;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 5rem;
|
||||
gap: .3rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ useWindowClick(() => show = false)
|
||||
.column {
|
||||
flex: 1;
|
||||
background: white;
|
||||
border-radius: 10rem;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
padding-bottom: var(--space);
|
||||
|
||||
@@ -180,7 +180,7 @@ onMounted(() => {
|
||||
justify-content: center;
|
||||
|
||||
.wrapper {
|
||||
width: 500rem;
|
||||
width: 80rem;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
|
||||
@@ -779,8 +779,8 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
align-items: center;
|
||||
color: var(--color-font-1);
|
||||
padding: 0 var(--space);
|
||||
gap: 20rem;
|
||||
margin-bottom: 20rem;
|
||||
gap: 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
.back {
|
||||
height: 100%;
|
||||
@@ -791,15 +791,15 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
flex-direction: column;
|
||||
color: var(--color-font-2);
|
||||
|
||||
.top {
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
font-size: 20rem;
|
||||
gap: .6rem;
|
||||
font-size: 1.2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -842,14 +842,14 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
}
|
||||
|
||||
.select {
|
||||
height: 45rem;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 5rem;
|
||||
gap: .3rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -864,7 +864,7 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
.column {
|
||||
flex: 1;
|
||||
background: white;
|
||||
border-radius: 10rem;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
padding-bottom: var(--space);
|
||||
@@ -873,7 +873,7 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
}
|
||||
|
||||
.left-column {
|
||||
max-width: 250rem;
|
||||
max-width: 16rem;
|
||||
width: 16vw;
|
||||
@extend .column;
|
||||
}
|
||||
@@ -882,7 +882,7 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
@@ -898,39 +898,39 @@ defineExpose({getDictDetail, add: addWord, editDict})
|
||||
}
|
||||
|
||||
.allocation-chapter {
|
||||
width: 500rem;
|
||||
width: 30rem;
|
||||
padding: var(--space);
|
||||
padding-top: 0;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.desc {
|
||||
margin-top: 10rem;
|
||||
margin-bottom: 35rem;
|
||||
margin-top: .6rem;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rem;
|
||||
margin-bottom: 15rem;
|
||||
gap: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
word-break: keep-all;
|
||||
|
||||
.label {
|
||||
width: 90rem;
|
||||
width: 5.6rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 12rem;
|
||||
font-size: .7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transform: translate3d(0, -15rem, 0);
|
||||
padding-left: 110rem;
|
||||
font-size: 13rem;
|
||||
transform: translate3d(0, -1rem, 0);
|
||||
padding-left: 7rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ onMounted(() => {
|
||||
|
||||
header {
|
||||
background: var(--color-second-bg);
|
||||
height: 60rem;
|
||||
height: 4rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -47,12 +47,12 @@ onMounted(() => {
|
||||
|
||||
.nav-list {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
gap: .6rem;
|
||||
|
||||
nav {
|
||||
padding: 7rem 20rem;
|
||||
padding: .4rem 1.2rem;
|
||||
cursor: pointer;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
transition: all .3s;
|
||||
|
||||
&:hover {
|
||||
|
||||
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>
|
||||
129
src/pages/pc/home/HomeIndex.vue
Normal file
129
src/pages/pc/home/HomeIndex.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
|
||||
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()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="word flex justify-center ">
|
||||
<div class="w-5/10 pt-5">
|
||||
<div class="flex gap-6">
|
||||
<div class="card w-1/2 flex flex-col">
|
||||
<div class="title">
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid flex-1 flex gap-5 mt-4">
|
||||
<div class="p-4 flex-1 rounded-md bg-slate-200 relative" v-for="i in 3">
|
||||
<span>收藏</span>
|
||||
<div class="absolute bottom-4 right-4">333个词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<div class="card ">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="bg-slate-200 p-3 rounded-md cursor-pointer flex items-center">
|
||||
<span class="text-lg font-bold">{{ base.currentDict.name }}</span>
|
||||
<Icon icon="gg:arrows-exchange" class="text-2xl ml-2"/>
|
||||
<Icon icon="uil:setting" class="text-2xl ml-2"/>
|
||||
</div>
|
||||
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
|
||||
@click="router.push('/practice')">
|
||||
开始学习
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" :show-text="false"></el-progress>
|
||||
</div>
|
||||
<div class="card flex gap-3">
|
||||
<div class="bg-slate-200 w-10 h-10 flex center text-2xl rounded">
|
||||
0
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between">
|
||||
<div class="title">
|
||||
每日目标
|
||||
</div>
|
||||
<div style="color:#ac6ed1;" class="cursor-pointer">
|
||||
更改目标
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs">学习 50 个单词</div>
|
||||
<el-progress class="flex-1 mt-1" percentage="80" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<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">
|
||||
<div class="flex justify-between w-full">
|
||||
<span>{{ base.currentDict.name }}</span>
|
||||
<div class="text-2xl ml-2 flex gap-4">
|
||||
<Icon icon="hugeicons:delete-02"/>
|
||||
<Icon icon="nonicons:go-16"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" color="white" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 text-2xl">
|
||||
<Icon icon="mingcute:down-line"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
学习记录
|
||||
</div>
|
||||
<div class="center">
|
||||
<ActivityCalendar
|
||||
:data="[{ date: '2023-05-22', count: 5 }]"
|
||||
:width="40"
|
||||
:height="7"
|
||||
:cellLength="16"
|
||||
:cellInterval="8"
|
||||
:fontSize="12"
|
||||
:showLevelFlag="false"
|
||||
:showWeekDayFlag="false"
|
||||
:clickEvent="clickEvent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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>
|
||||
@@ -7,11 +7,13 @@ import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -19,12 +21,17 @@ const runtimeStore = useRuntimeStore()
|
||||
<div class="aside">
|
||||
<div class="top">
|
||||
<Logo/>
|
||||
<div class="row">
|
||||
<div class="row" @click="router.push('/home')">
|
||||
<Icon icon="material-symbols-light:dictionary-outline-sharp"/>
|
||||
<!-- <Icon icon="streamline:dictionary-language-book"/>-->
|
||||
<span>主页</span>
|
||||
</div>
|
||||
<div class="row" @click="router.push('/word')">
|
||||
<Icon icon="material-symbols-light:dictionary-outline-sharp"/>
|
||||
<!-- <Icon icon="streamline:dictionary-language-book"/>-->
|
||||
<span>单词</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" @click="router.push('/article')">
|
||||
<Icon icon="ph:article-ny-times"/>
|
||||
<span>文章</span>
|
||||
</div>
|
||||
@@ -39,14 +46,14 @@ const runtimeStore = useRuntimeStore()
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content overflow-auto">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,32 +67,33 @@ const runtimeStore = useRuntimeStore()
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
font-size: 14rem;
|
||||
}
|
||||
|
||||
.aside {
|
||||
background: white;
|
||||
//position: fixed;
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 200rem;
|
||||
padding: 20rem 10rem;
|
||||
width: var(--aside-width);
|
||||
padding: 1rem 1rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.row {
|
||||
padding: 10rem;
|
||||
@apply cursor-pointer;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
font-size: 16rem;
|
||||
gap: 0.5rem;
|
||||
font-size: 1rem;
|
||||
//font-weight: bold;
|
||||
|
||||
svg {
|
||||
font-size: 36rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,17 +79,17 @@ onUnmounted(() => {
|
||||
|
||||
.footer {
|
||||
width: var(--toolbar-width);
|
||||
margin-bottom: 10rem;
|
||||
margin-bottom: .8rem;
|
||||
transition: all var(--anim-time);
|
||||
position: relative;
|
||||
margin-top: 15rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -90rem;
|
||||
margin-top: 50rem;
|
||||
margin-bottom: -7rem;
|
||||
margin-top: 3rem;
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 25rem);
|
||||
bottom: calc(100% + 1.8rem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,15 +97,15 @@ onUnmounted(() => {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rem;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
padding: 3rem var(--space) 6rem var(--space);
|
||||
padding: .2rem var(--space) .4rem var(--space);
|
||||
z-index: 2;
|
||||
border: 1px solid var(--color-item-border);
|
||||
box-shadow: var(--shadow);
|
||||
|
||||
.stat {
|
||||
margin-top: 8rem;
|
||||
margin-top: .5rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
@@ -113,8 +113,8 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5rem;
|
||||
width: 80rem;
|
||||
gap: .3rem;
|
||||
width: 5rem;
|
||||
color: gray;
|
||||
|
||||
.line {
|
||||
@@ -129,7 +129,7 @@ onUnmounted(() => {
|
||||
.progress {
|
||||
width: 100%;
|
||||
transition: all .3s;
|
||||
padding: 0 10rem;
|
||||
padding: 0 .6rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
@@ -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"
|
||||
@@ -66,9 +66,9 @@ const settingStore = useSettingStore()
|
||||
|
||||
<style scoped lang="scss">
|
||||
.options {
|
||||
margin-top: 10rem;
|
||||
margin-top: 1.2rem;
|
||||
display: flex;
|
||||
gap: 15rem;
|
||||
font-size: 18rem;
|
||||
gap: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -89,13 +89,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>
|
||||
@@ -226,14 +226,14 @@ const showCollectToggleButton = $computed(() => {
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
$header-height: 50rem;
|
||||
$header-height: 3rem;
|
||||
.slide-item {
|
||||
width: var(--panel-width);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 8rem;
|
||||
border-radius: .5rem;
|
||||
width: var(--panel-width);
|
||||
background: var(--color-second-bg);
|
||||
height: 100%;
|
||||
@@ -246,15 +246,15 @@ $header-height: 50rem;
|
||||
|
||||
|
||||
& > header {
|
||||
min-height: 50rem;
|
||||
min-height: 3rem;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10rem 15rem;
|
||||
padding: .6rem .9rem;
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
gap: 15rem;
|
||||
gap: 1rem;
|
||||
|
||||
.close {
|
||||
cursor: pointer;
|
||||
@@ -263,13 +263,13 @@ $header-height: 50rem;
|
||||
.tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15rem;
|
||||
font-size: 14rem;
|
||||
gap: .9rem;
|
||||
font-size: .8rem;
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
word-break: keep-all;
|
||||
font-size: 16rem;
|
||||
font-size: 1rem;
|
||||
transition: all .3s;
|
||||
color: gray;
|
||||
|
||||
|
||||
@@ -143,9 +143,7 @@ useStartKeyboardEventListener()
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<Toolbar/>
|
||||
<!-- <BaseButton @click="test">test</BaseButton>-->
|
||||
<PracticeArticle ref="practiceRef" v-if="store.isArticle"/>
|
||||
<PracticeWord ref="practiceRef" v-else/>
|
||||
<PracticeWord ref="practiceRef"/>
|
||||
<Footer/>
|
||||
</div>
|
||||
<DictModal/>
|
||||
@@ -154,7 +152,7 @@ useStartKeyboardEventListener()
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 13rem;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, onMounted, onUnmounted, watch} from "vue"
|
||||
import {$ref} from "vue/macros";
|
||||
import {Article, ArticleWord, DefaultArticle, ShortcutKey, Word} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
@@ -342,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)"
|
||||
/>
|
||||
@@ -350,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>
|
||||
|
||||
@@ -358,7 +358,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"/>
|
||||
|
||||
@@ -154,7 +154,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
<div class="typing-word">
|
||||
<div class="translate"
|
||||
:style="{
|
||||
fontSize: settingStore.fontSize.wordTranslateFontSize +'rem',
|
||||
fontSize: settingStore.fontSize.wordTranslateFontSize +'px',
|
||||
opacity: settingStore.translate ? 1 : 0
|
||||
}"
|
||||
>
|
||||
@@ -163,7 +163,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"-->
|
||||
@@ -176,7 +176,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
<div class="word-wrapper">
|
||||
<div class="word"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'rem'}"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'px'}"
|
||||
>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
@@ -190,7 +190,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>
|
||||
@@ -215,19 +215,19 @@ defineExpose({del, showWord, hideWord, play})
|
||||
color: var(--color-font-2);
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
font-size: 1.6rem;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
margin-top: 5rem;
|
||||
margin-top: .3rem;
|
||||
font-family: var(--word-font-family);
|
||||
}
|
||||
|
||||
.translate {
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
margin-bottom: 90rem;
|
||||
margin-bottom: 7rem;
|
||||
|
||||
&:hover {
|
||||
.volumeIcon {
|
||||
@@ -238,7 +238,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
.translate-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: .8rem;
|
||||
}
|
||||
|
||||
.volumeIcon {
|
||||
@@ -248,17 +248,17 @@ defineExpose({del, showWord, hideWord, play})
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
margin-left: 30rem;
|
||||
margin-left: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
gap: .8rem;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.word {
|
||||
font-size: 48rem;
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
font-family: var(--word-font-family);
|
||||
letter-spacing: 5rem;
|
||||
letter-spacing: .3rem;
|
||||
|
||||
.input {
|
||||
color: rgb(22, 163, 74);
|
||||
|
||||
@@ -246,7 +246,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>
|
||||
@@ -255,7 +255,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>
|
||||
@@ -287,40 +287,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
|
||||
@@ -378,9 +364,9 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
font-size: 14rem;
|
||||
font-size: 1rem;
|
||||
color: gray;
|
||||
gap: 6rem;
|
||||
gap: .4rem;
|
||||
position: relative;
|
||||
width: var(--toolbar-width);
|
||||
|
||||
@@ -395,14 +381,13 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
|
||||
.arrow {
|
||||
min-width: 22rem;
|
||||
min-height: 22rem;
|
||||
font-size: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.word {
|
||||
font-size: 24rem;
|
||||
margin-bottom: 4rem;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: .2rem;
|
||||
font-family: var(--word-font-family);
|
||||
}
|
||||
|
||||
@@ -410,31 +395,31 @@ onUnmounted(() => {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
float: left;
|
||||
gap: 10rem;
|
||||
gap: .8rem;
|
||||
}
|
||||
|
||||
.next {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10rem;
|
||||
gap: .8rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.options-wrapper {
|
||||
position: absolute;
|
||||
margin-top: 120rem;
|
||||
margin-top: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.word-panel-wrapper {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 10rem;
|
||||
top: .8rem;
|
||||
z-index: 1;
|
||||
margin-left: var(--panel-margin-left);
|
||||
height: calc(100% - 20rem);
|
||||
height: calc(100% - 1.5rem);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,13 +1,129 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
|
||||
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()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="word flex s font-bold underline">
|
||||
asdf
|
||||
</div>
|
||||
<div class="word flex justify-center ">
|
||||
<div class="w-5/10 pt-5">
|
||||
<div class="flex gap-6">
|
||||
<div class="card w-1/2 flex flex-col">
|
||||
<div class="title">
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid flex-1 flex gap-5 mt-4">
|
||||
<div class="p-4 flex-1 rounded-md bg-slate-200 relative" v-for="i in 3">
|
||||
<span>收藏</span>
|
||||
<div class="absolute bottom-4 right-4">333个词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<div class="card ">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="bg-slate-200 p-3 rounded-md cursor-pointer flex items-center">
|
||||
<span class="text-lg font-bold">{{ base.currentDict.name }}</span>
|
||||
<Icon icon="gg:arrows-exchange" class="text-2xl ml-2"/>
|
||||
<Icon icon="uil:setting" class="text-2xl ml-2"/>
|
||||
</div>
|
||||
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
|
||||
@click="router.push('/practice')">
|
||||
开始学习
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" :show-text="false"></el-progress>
|
||||
</div>
|
||||
<div class="card flex gap-3">
|
||||
<div class="bg-slate-200 w-10 h-10 flex center text-2xl rounded">
|
||||
0
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between">
|
||||
<div class="title">
|
||||
每日目标
|
||||
</div>
|
||||
<div style="color:#ac6ed1;" class="cursor-pointer">
|
||||
更改目标
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs">学习 50 个单词</div>
|
||||
<el-progress class="flex-1 mt-1" percentage="80" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<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">
|
||||
<div class="flex justify-between w-full">
|
||||
<span>{{ base.currentDict.name }}</span>
|
||||
<div class="text-2xl ml-2 flex gap-4">
|
||||
<Icon icon="hugeicons:delete-02"/>
|
||||
<Icon icon="nonicons:go-16"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" percentage="80" color="white" :show-text="false"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mt-2 text-2xl">
|
||||
<Icon icon="mingcute:down-line"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
学习记录
|
||||
</div>
|
||||
<div class="center">
|
||||
<ActivityCalendar
|
||||
:data="[{ date: '2023-05-22', count: 5 }]"
|
||||
:width="40"
|
||||
:height="7"
|
||||
:cellLength="16"
|
||||
:cellInterval="8"
|
||||
:fontSize="12"
|
||||
:showLevelFlag="false"
|
||||
:showWeekDayFlag="false"
|
||||
:clickEvent="clickEvent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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>
|
||||
@@ -19,15 +19,22 @@ 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'
|
||||
import ArticleIndex from "@/pages/pc/article/ArticleIndex.vue";
|
||||
import HomeIndex from "@/pages/pc/home/HomeIndex.vue";
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/', component: PC,
|
||||
redirect: '/word',
|
||||
children: [
|
||||
{path: '/word', component: WordHome},
|
||||
{path: 'home', component: HomeIndex},
|
||||
{path: 'word', component: WordHome},
|
||||
{path: 'dict', component: Dict2},
|
||||
{path: 'practice', component: Practice},
|
||||
{path: 'article', component: ArticleIndex},
|
||||
]
|
||||
},
|
||||
{path: '/pc/practice', component: Practice},
|
||||
{path: '/pc/dict', component: Dict},
|
||||
|
||||
{path: '/mobile', component: Mobile,},
|
||||
@@ -44,7 +51,7 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: '/mobile/about', component: About},
|
||||
{path: '/mobile/feedback', component: Feedback},
|
||||
{path: '/test', component: Test},
|
||||
{path: '/', redirect: '/pc/practice'},
|
||||
// {path: '/', redirect: '/pc/practice'},
|
||||
]
|
||||
|
||||
const router = VueRouter.createRouter({
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
13
uno.config.ts
Normal file
13
uno.config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// uno.config.ts
|
||||
import {defineConfig, presetUno} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
content: {
|
||||
filesystem: [
|
||||
'**/*.{html,js,ts,jsx,tsx,vue,svelte,astro}',
|
||||
],
|
||||
},
|
||||
presets: [
|
||||
presetUno(),
|
||||
],
|
||||
})
|
||||
@@ -1,13 +1,14 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import Vue from '@vitejs/plugin-vue'
|
||||
import VueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import VueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import {resolve} from 'path'
|
||||
import {visualizer} from "rollup-plugin-visualizer";
|
||||
import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import {getLastCommit} from "git-last-commit";
|
||||
import VueMacros from 'unplugin-vue-macros/vite'
|
||||
import DefineOptions from 'unplugin-vue-define-options/vite' // 引入插件
|
||||
import UnoCSS from 'unocss/vite'
|
||||
|
||||
function pathResolve(dir: string) {
|
||||
return resolve(__dirname, ".", dir)
|
||||
@@ -23,16 +24,11 @@ export default defineConfig(async () => {
|
||||
})
|
||||
return {
|
||||
plugins: [
|
||||
VueMacros({
|
||||
plugins: {
|
||||
vue: Vue(),
|
||||
vueJsx: VueJsx() // if needed
|
||||
}
|
||||
// betterDefine: true,
|
||||
// reactivityTransform: {
|
||||
// exclude: [/node_modules/, /jQuery\.js/]
|
||||
// }
|
||||
Vue({
|
||||
reactivityTransform: true
|
||||
}),
|
||||
VueJsx(),
|
||||
UnoCSS(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
@@ -40,7 +36,7 @@ export default defineConfig(async () => {
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
//用于给setup组件定义名字的,keep-alive需要name才能正常工作
|
||||
// DefineOptions(),
|
||||
DefineOptions(),
|
||||
lifecycle === 'report' ?
|
||||
visualizer({
|
||||
gzipSize: true,
|
||||
|
||||
Reference in New Issue
Block a user