Develop mobile pages
This commit is contained in:
@@ -175,6 +175,7 @@ html, body {
|
||||
& > .page-content {
|
||||
padding: 10rem;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,131 +1,132 @@
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {onMounted, onUnmounted, watch,onDeactivated} from "vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
export function useWindowClick(cb: (e: PointerEvent) => void) {
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.closeOther, cb)
|
||||
window.addEventListener('click', cb)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('click', cb)
|
||||
})
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.closeOther, cb)
|
||||
window.addEventListener('click', cb)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('click', cb)
|
||||
})
|
||||
}
|
||||
|
||||
export function useEventListener(type: string, listener: EventListenerOrEventListenerObject) {
|
||||
onMounted(() => window.addEventListener(type, listener))
|
||||
onUnmounted(() => window.removeEventListener(type, listener))
|
||||
onMounted(() => window.addEventListener(type, listener))
|
||||
onUnmounted(() => window.removeEventListener(type, listener))
|
||||
onDeactivated(() => window.removeEventListener(type, listener))
|
||||
}
|
||||
|
||||
export function getShortcutKey(e: KeyboardEvent) {
|
||||
let shortcutKey = ''
|
||||
if (e.ctrlKey) shortcutKey += 'Ctrl+'
|
||||
if (e.altKey) shortcutKey += 'Alt+'
|
||||
if (e.shiftKey) shortcutKey += 'Shift+'
|
||||
if (e.key !== 'Control' && e.key !== 'Alt' && e.key !== 'Shift') {
|
||||
if (e.keyCode >= 65 && e.keyCode <= 90) {
|
||||
shortcutKey += e.key.toUpperCase()
|
||||
} else {
|
||||
if (e.key === 'ArrowRight') {
|
||||
shortcutKey += '➡'
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
shortcutKey += '⬅'
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
shortcutKey += '⬆'
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
shortcutKey += '⬇'
|
||||
} else {
|
||||
shortcutKey += e.key
|
||||
}
|
||||
}
|
||||
let shortcutKey = ''
|
||||
if (e.ctrlKey) shortcutKey += 'Ctrl+'
|
||||
if (e.altKey) shortcutKey += 'Alt+'
|
||||
if (e.shiftKey) shortcutKey += 'Shift+'
|
||||
if (e.key !== 'Control' && e.key !== 'Alt' && e.key !== 'Shift') {
|
||||
if (e.keyCode >= 65 && e.keyCode <= 90) {
|
||||
shortcutKey += e.key.toUpperCase()
|
||||
} else {
|
||||
if (e.key === 'ArrowRight') {
|
||||
shortcutKey += '➡'
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
shortcutKey += '⬅'
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
shortcutKey += '⬆'
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
shortcutKey += '⬇'
|
||||
} else {
|
||||
shortcutKey += e.key
|
||||
}
|
||||
}
|
||||
shortcutKey = shortcutKey.trim()
|
||||
}
|
||||
shortcutKey = shortcutKey.trim()
|
||||
|
||||
// console.log('key', shortcutKey)
|
||||
return shortcutKey
|
||||
// console.log('key', shortcutKey)
|
||||
return shortcutKey
|
||||
}
|
||||
|
||||
export function useStartKeyboardEventListener() {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
// console.log('e',e.keyCode,e.code)
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
e.preventDefault()
|
||||
let shortcutKey = getShortcutKey(e)
|
||||
// console.log('shortcutKey', shortcutKey)
|
||||
|
||||
let list = Object.entries(settingStore.shortcutKeyMap)
|
||||
let shortcutEvent = ''
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let [k, v] = list[i]
|
||||
if (v === shortcutKey) {
|
||||
console.log('快捷键', k)
|
||||
shortcutEvent = k
|
||||
break
|
||||
}
|
||||
}
|
||||
if (shortcutEvent) {
|
||||
emitter.emit(shortcutEvent, e)
|
||||
} else {
|
||||
//非英文模式下,输入区域的 keyCode 均为 229时,
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90)
|
||||
|| (e.keyCode >= 48 && e.keyCode <= 57)
|
||||
|| e.code === 'Space'
|
||||
|| e.code === 'Slash'
|
||||
|| e.code === 'Quote'
|
||||
|| e.code === 'Comma'
|
||||
|| e.code === 'BracketLeft'
|
||||
|| e.code === 'BracketRight'
|
||||
|| e.code === 'Period'
|
||||
|| e.code === 'Minus'
|
||||
|| e.code === 'Equal'
|
||||
|| e.code === 'Semicolon'
|
||||
// || e.code === 'Backquote'
|
||||
|| e.keyCode === 229
|
||||
) {
|
||||
emitter.emit(EventKey.onTyping, e)
|
||||
} else {
|
||||
emitter.emit(EventKey.keydown, e)
|
||||
}
|
||||
}
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
// console.log('e',e.keyCode,e.code)
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
e.preventDefault()
|
||||
let shortcutKey = getShortcutKey(e)
|
||||
// console.log('shortcutKey', shortcutKey)
|
||||
|
||||
let list = Object.entries(settingStore.shortcutKeyMap)
|
||||
let shortcutEvent = ''
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let [k, v] = list[i]
|
||||
if (v === shortcutKey) {
|
||||
console.log('快捷键', k)
|
||||
shortcutEvent = k
|
||||
break
|
||||
}
|
||||
})
|
||||
useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keyup, e)
|
||||
}
|
||||
if (shortcutEvent) {
|
||||
emitter.emit(shortcutEvent, e)
|
||||
} else {
|
||||
//非英文模式下,输入区域的 keyCode 均为 229时,
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90)
|
||||
|| (e.keyCode >= 48 && e.keyCode <= 57)
|
||||
|| e.code === 'Space'
|
||||
|| e.code === 'Slash'
|
||||
|| e.code === 'Quote'
|
||||
|| e.code === 'Comma'
|
||||
|| e.code === 'BracketLeft'
|
||||
|| e.code === 'BracketRight'
|
||||
|| e.code === 'Period'
|
||||
|| e.code === 'Minus'
|
||||
|| e.code === 'Equal'
|
||||
|| e.code === 'Semicolon'
|
||||
// || e.code === 'Backquote'
|
||||
|| e.keyCode === 229
|
||||
) {
|
||||
emitter.emit(EventKey.onTyping, e)
|
||||
} else {
|
||||
emitter.emit(EventKey.keydown, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keyup, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useOnKeyboardEventListener(onKeyDown: (e: KeyboardEvent) => void, onKeyUp: (e: KeyboardEvent) => void) {
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.keydown, onKeyDown)
|
||||
emitter.on(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.keydown, onKeyDown)
|
||||
emitter.off(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.keydown, onKeyDown)
|
||||
emitter.on(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.keydown, onKeyDown)
|
||||
emitter.off(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
}
|
||||
|
||||
export function useDisableEventListener(watchVal: any) {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
watch(watchVal, (n: any) => {
|
||||
if (n === true) runtimeStore.disableEventListener = true
|
||||
if (n === false) runtimeStore.disableEventListener = false
|
||||
})
|
||||
onMounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = true
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = false
|
||||
}
|
||||
})
|
||||
const runtimeStore = useRuntimeStore()
|
||||
watch(watchVal, (n: any) => {
|
||||
if (n === true) runtimeStore.disableEventListener = true
|
||||
if (n === false) runtimeStore.disableEventListener = false
|
||||
})
|
||||
onMounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = true
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
85
src/pages/mobile/components/CollectList.vue
Normal file
85
src/pages/mobile/components/CollectList.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import WordList from "@/components/list/WordList.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import PopConfirm from "@/components/PopConfirm.vue";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
|
||||
const router = useRouter()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
let practiceType = $ref(DictType.word)
|
||||
|
||||
const showCollectToggleButton = $computed(() => {
|
||||
if (store.currentDict.type === DictType.collect) {
|
||||
if (store.current.practiceType !== practiceType) {
|
||||
return (practiceType === DictType.word && store.collect.words.length) ||
|
||||
(practiceType === DictType.article && store.collect.articles.length)
|
||||
}
|
||||
} else {
|
||||
return (practiceType === DictType.word && store.collect.words.length) ||
|
||||
(practiceType === DictType.article && store.collect.articles.length)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function changeIndex(dict: Dict) {
|
||||
store.changeDict(dict, practiceType)
|
||||
}
|
||||
|
||||
function addCollect() {
|
||||
runtimeStore.editDict = cloneDeep(store.collect)
|
||||
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
|
||||
}
|
||||
|
||||
const {
|
||||
delWrongWord,
|
||||
delSimpleWord,
|
||||
toggleWordCollect,
|
||||
} = useWordOptions()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<div class="dict-name">总词数:{{ store.collect.words.length }}</div>
|
||||
<BaseIcon icon="fluent:add-12-regular" title="添加" @click="addCollect"/>
|
||||
</div>
|
||||
<template v-if="showCollectToggleButton">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.collect)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="store.collect.words.length"
|
||||
class="word-list"
|
||||
:list="store.collect.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="toggleWordCollect(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -23,10 +23,11 @@ import ArticleList from "@/components/list/ArticleList.vue";
|
||||
import Slide from "@/components/Slide.vue";
|
||||
import SlideHorizontal from "@/components/slide/SlideHorizontal.vue";
|
||||
import SlideItem from "@/components/slide/SlideItem.vue";
|
||||
import CollectList from "@/pages/mobile/components/CollectList.vue";
|
||||
import WrongList from "@/pages/mobile/components/WrongList.vue";
|
||||
import SimpleList from "@/pages/mobile/components/SimpleList.vue";
|
||||
|
||||
const router = useRouter()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
let tabIndex = $ref(0)
|
||||
provide('tabIndex', computed(() => tabIndex))
|
||||
@@ -37,12 +38,6 @@ watch(() => settingStore.showPanel, n => {
|
||||
}
|
||||
})
|
||||
|
||||
let practiceType = $ref(DictType.word)
|
||||
|
||||
function changeIndex(dict: Dict) {
|
||||
store.changeDict(dict, practiceType)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.changeDict, () => {
|
||||
tabIndex = 0
|
||||
@@ -53,38 +48,6 @@ onUnmounted(() => {
|
||||
emitter.off(EventKey.changeDict)
|
||||
})
|
||||
|
||||
const {
|
||||
delWrongWord,
|
||||
delSimpleWord,
|
||||
toggleWordCollect,
|
||||
} = useWordOptions()
|
||||
|
||||
const {
|
||||
toggleArticleCollect
|
||||
} = useArticleOptions()
|
||||
|
||||
function addCollect() {
|
||||
runtimeStore.editDict = cloneDeep(store.collect)
|
||||
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
|
||||
}
|
||||
|
||||
function addSimple() {
|
||||
runtimeStore.editDict = cloneDeep(store.simple)
|
||||
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
|
||||
}
|
||||
|
||||
const showCollectToggleButton = $computed(() => {
|
||||
if (store.currentDict.type === DictType.collect) {
|
||||
if (store.current.practiceType !== practiceType) {
|
||||
return (practiceType === DictType.word && store.collect.words.length) ||
|
||||
(practiceType === DictType.article && store.collect.articles.length)
|
||||
}
|
||||
} else {
|
||||
return (practiceType === DictType.word && store.collect.words.length) ||
|
||||
(practiceType === DictType.article && store.collect.articles.length)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
@@ -93,8 +56,8 @@ const showCollectToggleButton = $computed(() => {
|
||||
<div class="tabs">
|
||||
<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 class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{ store.wrong.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.simple.name }}</div>
|
||||
</div>
|
||||
</header>
|
||||
<SlideHorizontal v-model:index="tabIndex">
|
||||
@@ -102,93 +65,13 @@ const showCollectToggleButton = $computed(() => {
|
||||
<slot :active="tabIndex === 0 && settingStore.showPanel"></slot>
|
||||
</SlideItem>
|
||||
<SlideItem>
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<div class="dict-name">总词数:{{ store.collect.words.length }}</div>
|
||||
<BaseIcon icon="fluent:add-12-regular" title="添加" @click="addCollect"/>
|
||||
</div>
|
||||
<template v-if="showCollectToggleButton">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.collect)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="store.collect.words.length"
|
||||
class="word-list"
|
||||
:list="store.collect.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="toggleWordCollect(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
</div>
|
||||
<CollectList/>
|
||||
</SlideItem>
|
||||
<SlideItem>
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<div class="dict-name">总词数:{{ store.simple.words.length }}</div>
|
||||
<BaseIcon icon="fluent:add-12-regular" title="添加" @click="addSimple"/>
|
||||
</div>
|
||||
<template v-if="store.currentDict.type !== DictType.simple && store.simple.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.simple)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="store.simple.words.length"
|
||||
class="word-list"
|
||||
:list="store.simple.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="delSimpleWord(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
<WrongList/>
|
||||
</SlideItem>
|
||||
<SlideItem>
|
||||
<div class="panel-page-item" v-if="store.wrong.words.length">
|
||||
<div class="list-header">
|
||||
<div class="dict-name">总词数:{{ store.wrong.words.length }}</div>
|
||||
<template
|
||||
v-if="store.currentDict.type !== DictType.wrong && store.wrong.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.wrong)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
class="word-list"
|
||||
:list="store.wrong.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="delWrongWord(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
<SimpleList/>
|
||||
</SlideItem>
|
||||
</SlideHorizontal>
|
||||
</div>
|
||||
|
||||
71
src/pages/mobile/components/SimpleList.vue
Normal file
71
src/pages/mobile/components/SimpleList.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import WordList from "@/components/list/WordList.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import PopConfirm from "@/components/PopConfirm.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
const router = useRouter()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
let practiceType = $ref(DictType.word)
|
||||
|
||||
function changeIndex(dict: Dict) {
|
||||
store.changeDict(dict, practiceType)
|
||||
}
|
||||
|
||||
const {
|
||||
delSimpleWord,
|
||||
} = useWordOptions()
|
||||
|
||||
function addSimple() {
|
||||
runtimeStore.editDict = cloneDeep(store.simple)
|
||||
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<div class="dict-name">总词数:{{ store.simple.words.length }}</div>
|
||||
<BaseIcon icon="fluent:add-12-regular" title="添加" @click="addSimple"/>
|
||||
</div>
|
||||
<template v-if="store.currentDict.type !== DictType.simple && store.simple.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.simple)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="store.simple.words.length"
|
||||
class="word-list"
|
||||
:list="store.simple.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="delSimpleWord(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
59
src/pages/mobile/components/WrongList.vue
Normal file
59
src/pages/mobile/components/WrongList.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import WordList from "@/components/list/WordList.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import PopConfirm from "@/components/PopConfirm.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {Dict, DictType} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
|
||||
const store = useBaseStore()
|
||||
let practiceType = $ref(DictType.word)
|
||||
|
||||
function changeIndex(dict: Dict) {
|
||||
store.changeDict(dict, practiceType)
|
||||
}
|
||||
|
||||
const {
|
||||
delWrongWord,
|
||||
} = useWordOptions()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel-page-item">
|
||||
<template v-if="store.wrong.words.length">
|
||||
<div class="list-header">
|
||||
<div class="dict-name">总词数:{{ store.wrong.words.length }}</div>
|
||||
<template
|
||||
v-if="store.currentDict.type !== DictType.wrong && store.wrong.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex( store.wrong)"
|
||||
>
|
||||
<BaseButton size="small">切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</template>
|
||||
</div>
|
||||
<WordList
|
||||
class="word-list"
|
||||
:list="store.wrong.words">
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
class="del"
|
||||
@click="delWrongWord(item)"
|
||||
title="移除"
|
||||
icon="solar:trash-bin-minimalistic-linear"/>
|
||||
</template>
|
||||
</WordList>
|
||||
</template>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -2,14 +2,14 @@
|
||||
import {Icon} from "@iconify/vue";
|
||||
import Home from "@/pages/mobile/Home.vue";
|
||||
import DictListManage from "@/pages/mobile/DictListManage.vue";
|
||||
import My from "@/pages/mobile/My.vue";
|
||||
import My from "@/pages/mobile/my/My.vue";
|
||||
import {onMounted} from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: 'Practice'
|
||||
})
|
||||
|
||||
let index = $ref(0)
|
||||
let index = $ref(2)
|
||||
|
||||
onMounted(() => {
|
||||
console.log('onMounted')
|
||||
|
||||
81
src/pages/mobile/my/About.vue
Normal file
81
src/pages/mobile/my/About.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import {APP_NAME} from "../../../utils/const.ts";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="关于我们"/>
|
||||
<div class="page-content">
|
||||
<div class="name">{{ APP_NAME }}</div>
|
||||
<div class="desc">可在网页上使用的背单词软件</div>
|
||||
<div class="git">
|
||||
Github地址: <a target="_blank" href="https://github.com/zyronon/typing-word">https://github.com/zyronon/typing-word</a>
|
||||
</div>
|
||||
<div class="features">功能列表</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="title">背单词</div>
|
||||
<div class="txt">可以选择记忆或默写单词,提供了音标显示、发音功能(均可选美音、英音)、错误统计</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title">背文章</div>
|
||||
<div class="txt">
|
||||
内置经典教材书籍,可以练习和背诵文章,逐句输入,自动发音。也可以自行添加、导入文章,提供一键翻译、译文对照功能
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title">生词本、错词本、简单词</div>
|
||||
<div class="txt">
|
||||
默写单词时输入错误会自动添加到错词本,以便后续复习。也可以添加到简单词,之后再遇到这个词便会自动跳过,同时也可以将其添加到生词本中,以便巩固复习
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title">默写模式</div>
|
||||
<div class="txt">
|
||||
在用户完成一个章节的练习后,如果有错误词,那么会重复练习错误词,直到没有错误词为止。完成之后弹出选项可选择默写本章、重复本章、下一章
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title">词库</div>
|
||||
<div class="txt">内置了常用的 CET-4 、CET-6 、GMAT 、GRE 、IELTS 、SAT 、TOEFL
|
||||
、考研英语、专业四级英语、专业八级英语,也有程序员常见英语单词以及多种编程语言
|
||||
API 等词库。 尽可能满足大部分用户对背单词的需求,也非常欢迎社区贡献更多的词库。
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobile-page {
|
||||
.name {
|
||||
padding: 30rem 0;
|
||||
font-size: 46rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #c9c9c9;
|
||||
margin-bottom: 20rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 22rem;
|
||||
}
|
||||
|
||||
.features {
|
||||
margin-top: 20rem;
|
||||
font-weight: bold;
|
||||
font-size: 22rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 10rem 0;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
16
src/pages/mobile/my/CollectPage.vue
Normal file
16
src/pages/mobile/my/CollectPage.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import CollectList from "@/pages/mobile/components/CollectList.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="收藏"/>
|
||||
<CollectList/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
62
src/pages/mobile/my/Feedback.vue
Normal file
62
src/pages/mobile/my/Feedback.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import {GITHUB} from "@/config/ENV.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="反馈问题"/>
|
||||
<div class="page-content">
|
||||
<div>
|
||||
给我发Email:<a href="mailto:zyronon@163.com">zyronon@163.com</a>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="github">
|
||||
<span>在<a :href="GITHUB" target="_blank">Github</a>上给我提一个
|
||||
<a :href="`${GITHUB}/issues`" target="_blank">Issue</a>
|
||||
</span>
|
||||
<div class="options">
|
||||
<BaseButton>
|
||||
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=%E5%8D%95%E8%AF%8D%E9%94%99%E8%AF%AF---word-error.md&title=%E5%8D%95%E8%AF%8D%E9%94%99%E8%AF%AF+%7C+Word+error`"
|
||||
target="_blank">词典错误?</a>
|
||||
</BaseButton>
|
||||
<BaseButton>
|
||||
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=问题报告---bug-report-.md&title=问题报告+%7C+Bug+report+`"
|
||||
target="_blank">反馈BUG?</a>
|
||||
</BaseButton>
|
||||
<BaseButton>
|
||||
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=功能请求---feature-request.md&title=功能请求+%7C+Feature+request`"
|
||||
target="_blank">功能请求?</a>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 30rem;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space);
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -47,15 +47,15 @@ function $no() {
|
||||
<header ref="header"></header>
|
||||
<div class="detail">
|
||||
<div class="heat">
|
||||
<div class="text" @click="isShowStarCount = true">
|
||||
<div class="text" @click="router.push('/mobile/collect')">
|
||||
<span>收藏</span>
|
||||
<span class="num">123</span>
|
||||
</div>
|
||||
<div class="text" @click="$nav('/people/follow-and-fans',{type:0})">
|
||||
<div class="text" @click="router.push('/mobile/wrong')">
|
||||
<span>错误</span>
|
||||
<span class="num">123</span>
|
||||
</div>
|
||||
<div class="text" @click="$nav('/people/follow-and-fans',{type:1})">
|
||||
<div class="text" @click="router.push('/mobile/simple')">
|
||||
<span>已掌握</span>
|
||||
<span class="num">123</span>
|
||||
</div>
|
||||
@@ -64,16 +64,16 @@ function $no() {
|
||||
<span>您已坚持了164天,加油!</span>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="item" @click="router.push('/mobile/data-manage')">
|
||||
<img src="@/assets/img/collect.png" alt="">
|
||||
<div class="item" @click="router.push('/mobile/collect')">
|
||||
<img src="../../../assets/img/collect.png" alt="">
|
||||
<span>收藏</span>
|
||||
</div>
|
||||
<div class="item" @click="router.push('/mobile/data-manage')">
|
||||
<img src="@/assets/img/book2.png" alt="">
|
||||
<div class="item" @click="router.push('/mobile/wrong')">
|
||||
<img src="../../../assets/img/book2.png" alt="">
|
||||
<span>错词本</span>
|
||||
</div>
|
||||
<div class="item" @click="router.push('/mobile/data-manage')">
|
||||
<img src="@/assets/img/complete.png" alt="">
|
||||
<div class="item" @click="router.push('/mobile/simple')">
|
||||
<img src="../../../assets/img/complete.png" alt="">
|
||||
<span>简单词</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,14 +92,14 @@ function $no() {
|
||||
<Icon class="arrow" icon="mingcute:right-line" width="20"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item" @click="router.push('/mobile/feedback')">
|
||||
<Icon icon="pepicons-pencil:letter-open" width="22"/>
|
||||
<div class="right">
|
||||
<span>反馈问题</span>
|
||||
<Icon class="arrow" icon="mingcute:right-line" width="20"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item" @click="router.push('/mobile/about')">
|
||||
<Icon icon="mdi:about-circle-outline" width="22"/>
|
||||
<div class="right" style="border-bottom: none">
|
||||
<span>关于我们</span>
|
||||
@@ -113,7 +113,7 @@ function $no() {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "common.scss";
|
||||
@import "../common";
|
||||
|
||||
|
||||
.my {
|
||||
@@ -147,7 +147,7 @@ function $no() {
|
||||
header {
|
||||
color: white;
|
||||
height: 200rem;
|
||||
background-image: url('../../assets/img/a.png');
|
||||
background-image: url('../../../assets/img/a.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
17
src/pages/mobile/my/SimplePage.vue
Normal file
17
src/pages/mobile/my/SimplePage.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import WrongList from "@/pages/mobile/components/WrongList.vue";
|
||||
import SimpleList from "@/pages/mobile/components/SimpleList.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="简单词"/>
|
||||
<SimpleList/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
16
src/pages/mobile/my/WrongPage.vue
Normal file
16
src/pages/mobile/my/WrongPage.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import WrongList from "@/pages/mobile/components/WrongList.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="错词本"/>
|
||||
<WrongList/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
242
src/pages/mobile/my/setting/MusicSetting.vue
Normal file
242
src/pages/mobile/my/setting/MusicSetting.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import {getAudioFileUrl, useChangeAllSound, usePlayAudio} from "@/hooks/sound.ts";
|
||||
import {SoundFileOptions} from "@/utils/const.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const store = useBaseStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="音效设置"/>
|
||||
<div class="page-content">
|
||||
<div class="row">
|
||||
<label class="main-title">所有音效</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.allSound"
|
||||
@change="useChangeAllSound"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">单词/句子自动发音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.wordSound"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">单词/句子发音口音</label>
|
||||
<div class="wrapper">
|
||||
<el-select v-model="settingStore.wordSoundType"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option label="美音" value="us"/>
|
||||
<el-option label="英音" value="uk"/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="settingStore.wordSoundVolume"/>
|
||||
<span>{{ settingStore.wordSoundVolume }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">倍速</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="settingStore.wordSoundSpeed" :step="0.1" :min="0.5" :max="3"/>
|
||||
<span>{{ settingStore.wordSoundSpeed }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">按键音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.keyboardSound"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="item-title">按键音效</label>
|
||||
<div class="wrapper">
|
||||
<el-select v-model="settingStore.keyboardSoundFile"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in SoundFileOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
<div class="el-option-row">
|
||||
<span>{{ item.label }}</span>
|
||||
<VolumeIcon
|
||||
:time="100"
|
||||
@click="usePlayAudio(getAudioFileUrl(item.value)[0])"/>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="settingStore.keyboardSoundVolume"/>
|
||||
<span>{{ settingStore.keyboardSoundVolume }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<!-- <div class="row">-->
|
||||
<!-- <label class="item-title">释义发音</label>-->
|
||||
<!-- <div class="wrapper">-->
|
||||
<!-- <el-switch v-model="settingStore.translateSound"-->
|
||||
<!-- inline-prompt-->
|
||||
<!-- active-text="开"-->
|
||||
<!-- inactive-text="关"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <label class="sub-title">音量</label>-->
|
||||
<!-- <div class="wrapper">-->
|
||||
<!-- <el-slider v-model="settingStore.translateSoundVolume"/>-->
|
||||
<!-- <span>{{ settingStore.translateSoundVolume }}%</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">效果音(章节结算页烟花音效)</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.effectSound"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="settingStore.effectSoundVolume"/>
|
||||
<span>{{ settingStore.effectSoundVolume }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-content {
|
||||
background: var(--color-header-bg);
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 10rem var(--space);
|
||||
|
||||
.row {
|
||||
min-height: 40rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: calc(var(--space) * 5);
|
||||
|
||||
.wrapper {
|
||||
height: 30rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space);
|
||||
|
||||
span {
|
||||
text-align: right;
|
||||
//width: 30rem;
|
||||
font-size: 12rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.set-key {
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 150rem;
|
||||
box-sizing: border-box;
|
||||
margin-right: 10rem;
|
||||
height: 28rem;
|
||||
outline: none;
|
||||
font-size: 16rem;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3rem;
|
||||
padding: 0 5rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 22rem;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16rem;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
flex: 1;
|
||||
padding-right: 10rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-bottom: 20rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 10rem;
|
||||
font-size: 12rem;
|
||||
}
|
||||
|
||||
.line {
|
||||
border-bottom: 1px solid #c4c3c3;
|
||||
}
|
||||
}
|
||||
.el-option-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon-wrapper {
|
||||
transform: translateX(10rem);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
202
src/pages/mobile/my/setting/OtherSetting.vue
Normal file
202
src/pages/mobile/my/setting/OtherSetting.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import {getAudioFileUrl, useChangeAllSound, usePlayAudio} from "@/hooks/sound.ts";
|
||||
import {SoundFileOptions} from "@/utils/const.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const store = useBaseStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<NavBar title="其他设置"/>
|
||||
<div class="page-content">
|
||||
<div class="row">
|
||||
<label class="item-title">显示上一个/下一个单词</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.showNearWord"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
开启后,练习中会在上方显示上一个/下一个单词
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">忽略大小写</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.ignoreCase"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
开启后,输入时不区分大小写,如输入“hello”和“Hello”都会被认为是正确的
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">允许默写模式下显示提示</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="settingStore.allowWordTip"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
开启后,可以通过鼠标 hover 单词或者按 {{ settingStore.shortcutKeyMap[ShortcutKey.ShowWord] }} 显示正确答案
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">字体设置(仅可调整单词练习)</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">外语字体</label>
|
||||
<div class="wrapper">
|
||||
<el-slider
|
||||
:min="10"
|
||||
:max="100"
|
||||
v-model="settingStore.fontSize.wordForeignFontSize"/>
|
||||
<span>{{ settingStore.fontSize.wordForeignFontSize }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">中文字体</label>
|
||||
<div class="wrapper">
|
||||
<el-slider
|
||||
:min="10"
|
||||
:max="100"
|
||||
v-model="settingStore.fontSize.wordTranslateFontSize"/>
|
||||
<span>{{ settingStore.fontSize.wordTranslateFontSize }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">其他设置</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">切换下一个单词时间</label>
|
||||
<div class="wrapper">
|
||||
<el-input-number v-model="settingStore.waitTimeForChangeWord"
|
||||
:min="6"
|
||||
:max="100"
|
||||
type="number"
|
||||
/>
|
||||
<span>毫秒</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-content {
|
||||
background: var(--color-header-bg);
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 10rem var(--space);
|
||||
|
||||
.row {
|
||||
min-height: 40rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: calc(var(--space) * 5);
|
||||
|
||||
.wrapper {
|
||||
height: 30rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space);
|
||||
|
||||
span {
|
||||
text-align: right;
|
||||
//width: 30rem;
|
||||
font-size: 12rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.set-key {
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 150rem;
|
||||
box-sizing: border-box;
|
||||
margin-right: 10rem;
|
||||
height: 28rem;
|
||||
outline: none;
|
||||
font-size: 16rem;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3rem;
|
||||
padding: 0 5rem;
|
||||
background: var(--color-second-bg);
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 22rem;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16rem;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
flex: 1;
|
||||
padding-right: 10rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-bottom: 20rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 10rem;
|
||||
font-size: 12rem;
|
||||
}
|
||||
|
||||
.line {
|
||||
border-bottom: 1px solid #c4c3c3;
|
||||
}
|
||||
}
|
||||
|
||||
.el-option-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon-wrapper {
|
||||
transform: translateX(10rem);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -5,6 +5,7 @@ import useTheme from "@/hooks/theme.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import NavBar from "@/pages/mobile/components/NavBar.vue";
|
||||
import {ref} from "vue";
|
||||
import router from "@/router.ts";
|
||||
|
||||
const {toggleTheme} = useTheme()
|
||||
const settingStore = useSettingStore()
|
||||
@@ -18,14 +19,14 @@ const gitLastCommitHash = ref(LATEST_COMMIT_HASH);
|
||||
<NavBar title="设置"/>
|
||||
<div class="content">
|
||||
<div class="setting-list">
|
||||
<div class="item">
|
||||
<div class="item" @click="router.push('music-setting')">
|
||||
<Icon icon="bx:headphone" width="22"/>
|
||||
<div class="right">
|
||||
<span>音效设置</span>
|
||||
<Icon class="arrow" icon="mingcute:right-line" width="20"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item" @click="router.push('other-setting')">
|
||||
<Icon icon="icon-park-outline:setting-config" width="22"/>
|
||||
<div class="right" style="border-bottom: none">
|
||||
<span>其他设置</span>
|
||||
@@ -41,7 +42,7 @@ const gitLastCommitHash = ref(LATEST_COMMIT_HASH);
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "common";
|
||||
@import "../../common";
|
||||
|
||||
.setting {
|
||||
display: flex;
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {onDeactivated, onMounted, onUnmounted, watch} from "vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
|
||||
@@ -8,8 +8,15 @@ import Test from "@/pages/test/test.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import DictDetail from "@/pages/mobile/DictDetail.vue";
|
||||
import SetDictPlan from "@/pages/mobile/SetDictPlan.vue";
|
||||
import Setting from "@/pages/mobile/Setting.vue";
|
||||
import DataManage from "@/pages/mobile/DataManage.vue";
|
||||
import Setting from "@/pages/mobile/my/setting/Setting.vue";
|
||||
import DataManage from "@/pages/mobile/my/DataManage.vue";
|
||||
import CollectPage from "@/pages/mobile/my/CollectPage.vue";
|
||||
import WrongPage from "@/pages/mobile/my/WrongPage.vue";
|
||||
import SimplePage from "@/pages/mobile/my/SimplePage.vue";
|
||||
import About from "@/pages/mobile/my/About.vue";
|
||||
import Feedback from "@/pages/mobile/my/Feedback.vue";
|
||||
import MusicSetting from "@/pages/mobile/my/setting/MusicSetting.vue";
|
||||
import OtherSetting from "@/pages/mobile/my/setting/OtherSetting.vue";
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{path: '/pc/practice', component: Practice},
|
||||
@@ -20,7 +27,14 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: '/mobile/dict-detail', component: DictDetail},
|
||||
{path: '/mobile/set-dict-plan', name: 'set-dict-plan', component: SetDictPlan},
|
||||
{path: '/mobile/setting', component: Setting},
|
||||
{path: '/mobile/music-setting', component: MusicSetting},
|
||||
{path: '/mobile/other-setting', component: OtherSetting},
|
||||
{path: '/mobile/data-manage', component: DataManage},
|
||||
{path: '/mobile/collect', component: CollectPage},
|
||||
{path: '/mobile/wrong', component: WrongPage},
|
||||
{path: '/mobile/simple', component: SimplePage},
|
||||
{path: '/mobile/about', component: About},
|
||||
{path: '/mobile/feedback', component: Feedback},
|
||||
{path: '/test', component: Test},
|
||||
{path: '/', redirect: '/pc/practice'},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user