开发反馈弹窗
This commit is contained in:
@@ -22,7 +22,7 @@ import Backgorund from "@/components/Backgorund.vue"
|
||||
import Statistics from "@/components/Modal/Statistics.vue";
|
||||
import useThemeColor from "@/hooks/useThemeColor";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import Toolbar from "@/components/Toolbar.vue"
|
||||
import Toolbar from "@/components/Toolbar/Toolbar.vue"
|
||||
import {KeyboardOne} from "@icon-park/vue-next";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
@@ -168,7 +168,7 @@ const {appearance, toggle} = useThemeColor()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Backgorund/>
|
||||
<!-- <Backgorund/>-->
|
||||
<div class="main-page">
|
||||
<div class="center">
|
||||
<Toolbar/>
|
||||
@@ -215,13 +215,13 @@ const {appearance, toggle} = useThemeColor()
|
||||
.main-page {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
//background: $dark-main-bg;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 14rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: var(--color-main-bg);
|
||||
|
||||
.center {
|
||||
width: 70vw;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
:root {
|
||||
--color-main-bg: rgb(226, 226, 226);
|
||||
--color-second-bg: rgb(238,240,244);
|
||||
--color-second-bg: rgb(238, 240, 244);
|
||||
--color-header-bg: white;
|
||||
--color-font: black;
|
||||
}
|
||||
@@ -27,6 +27,10 @@ html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: $font-color;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
#app {
|
||||
@@ -34,6 +38,12 @@ html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
//color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8rem;
|
||||
height: 10rem;
|
||||
|
||||
@@ -3,17 +3,17 @@ import {KeyboardOne} from "@icon-park/vue-next";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
keyboard: string,
|
||||
keyboard?: string,
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tooltip :title="`快捷键: ${keyboard}`">
|
||||
<Tooltip :disabled="!keyboard" :title="`快捷键: ${keyboard}`">
|
||||
<div class="my-button" :class="active && 'active'">
|
||||
<span><slot></slot></span>
|
||||
<div class="key-notice">
|
||||
<div class="key-notice" v-if="keyboard">
|
||||
<keyboard-one theme="outline" size="14" fill="#ffffff" :strokeWidth="2"/>
|
||||
<span class="key">{{ keyboard }}</span>
|
||||
</div>
|
||||
@@ -36,12 +36,16 @@ const props = defineProps<{
|
||||
//background: #999;
|
||||
//background: rgb(60, 63, 65);
|
||||
//background: var(--color-second-bg);
|
||||
color: white;
|
||||
height: 40rem;
|
||||
line-height: 1;
|
||||
|
||||
& > span {
|
||||
font-size: 16rem;
|
||||
color: white;
|
||||
|
||||
::v-deep a {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
import {Close} from "@icon-park/vue-next"
|
||||
import {onMounted} from "vue";
|
||||
|
||||
const props = defineProps(['modelValue', 'title', 'subTitle'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
interface IProps {
|
||||
modelValue: boolean,
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
modelValue: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'close'
|
||||
])
|
||||
|
||||
function close() {
|
||||
emit('update:modelValue', false)
|
||||
emit('close',)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -123,7 +136,7 @@ $header-height: 60rem;
|
||||
.modal {
|
||||
position: relative;
|
||||
background: var(--color-main-bg);
|
||||
box-shadow: var(--color-main-bg) 0 0 10rem 1rem;
|
||||
//box-shadow: var(--color-main-bg) 0 0 10rem 1rem;
|
||||
//width: 75vw;
|
||||
//height: 70vh;
|
||||
border-radius: $radius;
|
||||
|
||||
@@ -136,13 +136,15 @@ function changeDict(dict: Dict, i: number) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: 0 0;
|
||||
//transform: rotate(-90deg);
|
||||
//transform-origin: 0 0;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
|
||||
&.open {
|
||||
//margin-right: 0;
|
||||
transform: rotate(0deg);
|
||||
//transform: rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
$header-height: 40rem;
|
||||
|
||||
87
src/components/Toolbar/FeedbackModal.vue
Normal file
87
src/components/Toolbar/FeedbackModal.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import Modal from "@/components/Modal/Modal.vue"
|
||||
import {HeadphoneSound, SettingConfig} from "@icon-park/vue-next"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {GITHUB} from "@/config/ENV.ts";
|
||||
|
||||
interface IProps {
|
||||
modelValue: boolean,
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
modelValue: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
])
|
||||
const store = useBaseStore()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
:modelValue="props.modelValue"
|
||||
@close="emit('update:modelValue',false)"
|
||||
title="反馈">
|
||||
<div class="feedback-modal">
|
||||
<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>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
.feedback-modal {
|
||||
width: 500rem;
|
||||
//height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
align-items: center;
|
||||
padding: $space;
|
||||
//justify-content: center;
|
||||
color: var(--color-font);
|
||||
|
||||
p {
|
||||
font-size: 30rem;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space;
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,78 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import Tooltip from "@/components/Tooltip.vue"
|
||||
import {
|
||||
MenuFold,
|
||||
Moon,
|
||||
PreviewCloseOne,
|
||||
PreviewOpen,
|
||||
SettingTwo,
|
||||
SunOne,
|
||||
VolumeNotice,
|
||||
HeadphoneSound,
|
||||
SettingConfig,
|
||||
Down,
|
||||
DatabaseFail
|
||||
} from "@icon-park/vue-next"
|
||||
import IconRepeat from '~icons/tabler/repeat'
|
||||
import useThemeColor from "@/hooks/useThemeColor.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {reactive} from "vue"
|
||||
import Modal from "@/components/Modal/Modal.vue"
|
||||
import {HeadphoneSound, SettingConfig} from "@icon-park/vue-next"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
|
||||
const {appearance, toggle} = useThemeColor()
|
||||
const store = useBaseStore()
|
||||
const tabIndex = $ref(0)
|
||||
const setting = reactive({
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
value1: false,
|
||||
value2: 50,
|
||||
value3: 1,
|
||||
value4: false,
|
||||
const store = useBaseStore()
|
||||
|
||||
interface IProps {
|
||||
modelValue: boolean,
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
modelValue: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header :class="!setting.showToolbar && 'hide'">
|
||||
<div class="info" @click="store.dictModalIsOpen = true">
|
||||
CTE-4 第5章
|
||||
</div>
|
||||
<div class="options">
|
||||
<Tooltip title="切换主题">
|
||||
<moon v-if="appearance === 'dark'" theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"
|
||||
@click="toggle"/>
|
||||
<sun-one v-else theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2" @click="toggle"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="设置">
|
||||
<setting-two @click="setting.show = true" theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="反馈字典错误">
|
||||
<database-fail theme="outline" size="24" fill="#333"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="音效设置">
|
||||
<volume-notice theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<IconRepeat></IconRepeat>
|
||||
<Tooltip title="单词本">
|
||||
<preview-open theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
<preview-close-one theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<div class="my-button" @click="store.dictModalIsOpen2 = true">ok</div>
|
||||
<Tooltip title="单词本">
|
||||
<menu-fold class="menu" @click="store.sideIsOpen = !store.sideIsOpen"
|
||||
theme="outline" size="20" fill="#929596"
|
||||
:strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip :title="setting.showToolbar?'收起':'展开'">
|
||||
<down
|
||||
@click="setting.showToolbar = !setting.showToolbar"
|
||||
class="arrow"
|
||||
:class="!setting.showToolbar && 'down'"
|
||||
theme="outline" size="24" fill="#999"/>
|
||||
</Tooltip>
|
||||
</header>
|
||||
<Modal v-model="setting.show" title="设置" subTitle="修改立即生效,实时保存">
|
||||
<Modal
|
||||
:modelValue="props.modelValue"
|
||||
@close="emit('update:modelValue',false)"
|
||||
title="设置" subTitle="修改立即生效,实时保存">
|
||||
<div class="setting-modal">
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
|
||||
@@ -89,7 +40,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="main-title">所有音效</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -100,7 +51,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="item-title">单词发音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -110,22 +61,22 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}%</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value3" :step="0.1" :max="4"/>
|
||||
<span>{{ setting.value3 }}</span>
|
||||
<el-slider v-model="store.setting.value3" :step="0.1" :max="4"/>
|
||||
<span>{{ store.setting.value3 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">按键音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -135,15 +86,15 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}%</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">释义发音</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -153,15 +104,15 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}%</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">效果音(章节结算页烟花音效)</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -171,8 +122,8 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="sub-title">音量</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}%</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -180,7 +131,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="item-title">章节乱序</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -194,7 +145,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="item-title">练习时展示上一个/下一个单词</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -208,7 +159,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="item-title">是否忽略大小写</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -222,7 +173,7 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="item-title">是否允许默写模式下显示提示</label>
|
||||
<div class="wrapper">
|
||||
<el-switch v-model="setting.value1"
|
||||
<el-switch v-model="store.setting.value1"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@@ -239,15 +190,15 @@ const setting = reactive({
|
||||
<div class="row">
|
||||
<label class="sut-title">外语字体</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}px</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sut-title">中文字体</label>
|
||||
<div class="wrapper">
|
||||
<el-slider v-model="setting.value2"/>
|
||||
<span>{{ setting.value2 }}px</span>
|
||||
<el-slider v-model="store.setting.value2"/>
|
||||
<span>{{ store.setting.value2 }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,53 +210,6 @@ const setting = reactive({
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
header {
|
||||
margin-top: 10rem;
|
||||
height: 60rem;
|
||||
width: 50%;
|
||||
background: var(--color-header-bg);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 8rem;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 10rem $space;
|
||||
box-sizing: border-box;
|
||||
transition: all .3s;
|
||||
|
||||
.info {
|
||||
font-size: 22rem;
|
||||
color: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
transform: translateY(calc(-100% - 10rem));
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .5s;
|
||||
transform: translate3d(-50%, 120%, 0) rotate(180deg);
|
||||
|
||||
&.down {
|
||||
transform: translate3d(-50%, 120%, 0) rotate(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-modal {
|
||||
width: 40vw;
|
||||
height: 80vh;
|
||||
@@ -388,5 +292,4 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
120
src/components/Toolbar/Toolbar.vue
Normal file
120
src/components/Toolbar/Toolbar.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import Tooltip from "@/components/Tooltip.vue"
|
||||
import {
|
||||
DatabaseFail,
|
||||
Down,
|
||||
MenuFold,
|
||||
Moon,
|
||||
PreviewCloseOne,
|
||||
PreviewOpen,
|
||||
SettingTwo,
|
||||
SunOne,
|
||||
VolumeNotice,
|
||||
Bug
|
||||
} from "@icon-park/vue-next"
|
||||
import IconRepeat from '~icons/tabler/repeat'
|
||||
import useThemeColor from "@/hooks/useThemeColor.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import SettingModal from "@/components/Toolbar/SettingModal.vue"
|
||||
import FeedbackModal from "@/components/Toolbar/FeedbackModal.vue"
|
||||
|
||||
const {appearance, toggle} = useThemeColor()
|
||||
const store = useBaseStore()
|
||||
const showFeedbackModal = $ref(false)
|
||||
const showSettingModal = $ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header :class="!store.setting.showToolbar && 'hide'">
|
||||
<div class="info" @click="store.dictModalIsOpen = true">
|
||||
CTE-4 第5章
|
||||
</div>
|
||||
<div class="options">
|
||||
<Tooltip title="切换主题">
|
||||
<moon v-if="appearance === 'dark'" theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"
|
||||
@click="toggle"/>
|
||||
<sun-one v-else theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2" @click="toggle"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="设置">
|
||||
<setting-two @click="showSettingModal = true" theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="反馈">
|
||||
<bug @click="showFeedbackModal = true" theme="outline" size="20" fill="#999" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<Tooltip title="音效设置">
|
||||
<volume-notice theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<IconRepeat></IconRepeat>
|
||||
<Tooltip title="单词本">
|
||||
<preview-open theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
<preview-close-one theme="filled" size="20" fill="#0C8CE9" :strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
<div class="my-button" @click="store.dictModalIsOpen2 = true">ok</div>
|
||||
<Tooltip title="单词本">
|
||||
<menu-fold class="menu" @click="store.sideIsOpen = !store.sideIsOpen"
|
||||
theme="outline" size="20" fill="#929596"
|
||||
:strokeWidth="2"/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip :title="store.setting.showToolbar?'收起':'展开'">
|
||||
<down
|
||||
@click="store.setting.showToolbar = !store.setting.showToolbar"
|
||||
class="arrow"
|
||||
:class="!store.setting.showToolbar && 'down'"
|
||||
theme="outline" size="24" fill="#999"/>
|
||||
</Tooltip>
|
||||
</header>
|
||||
<SettingModal v-model="showSettingModal"/>
|
||||
<FeedbackModal v-model="showFeedbackModal"/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
header {
|
||||
margin-top: 10rem;
|
||||
height: 60rem;
|
||||
width: 50%;
|
||||
background: var(--color-header-bg);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 8rem;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 10rem $space;
|
||||
box-sizing: border-box;
|
||||
transition: all .3s;
|
||||
|
||||
.info {
|
||||
font-size: 22rem;
|
||||
color: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
transform: translateY(calc(-100% - 10rem));
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .5s;
|
||||
transform: translate3d(-50%, 120%, 0) rotate(180deg);
|
||||
|
||||
&.down {
|
||||
transform: translate3d(-50%, 120%, 0) rotate(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -10,6 +10,12 @@ export default {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -18,6 +24,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
showPop(e) {
|
||||
if (this.disabled) return
|
||||
e.stopPropagation()
|
||||
let rect = e.target.getBoundingClientRect()
|
||||
this.show = true
|
||||
|
||||
1
src/config/ENV.ts
Normal file
1
src/config/ENV.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const GITHUB = 'https://github.com/zyronon/bbword'
|
||||
12
src/hooks/useEsc.ts
Normal file
12
src/hooks/useEsc.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {onMounted} from "vue"
|
||||
|
||||
export function useEsc(can: boolean) {
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && can) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
})
|
||||
return [useEsc()]
|
||||
}
|
||||
@@ -3,130 +3,138 @@ import {Config, Dict, DictType, SaveKey, State, Word} from "../types.ts"
|
||||
import {chunk, cloneDeep} from "lodash";
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
type: DictType.newWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
skipWordDict: {
|
||||
type: DictType.skipWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
dict: {
|
||||
type: DictType.inner,
|
||||
name: '新概念英语-2',
|
||||
description: '新概念英语第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: '/dicts/NCE_2.json',
|
||||
length: 858,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
},
|
||||
currentDictType: {
|
||||
name: DictType.inner,
|
||||
dictUrl: '/dicts/NCE_2.json'
|
||||
},
|
||||
sideIsOpen: false,
|
||||
dictModalIsOpen: false,
|
||||
dictModalIsOpen2: false,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.wordList.map(v => v.name)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.currentDictType.name) {
|
||||
case DictType.newWordDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipWordDict:
|
||||
return state.skipWordDict
|
||||
case DictType.inner:
|
||||
case DictType.custom:
|
||||
return state.dict
|
||||
}
|
||||
},
|
||||
chapter(): Word[] {
|
||||
return this.currentDict.chapterList[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
word(): Word {
|
||||
return this.chapter[this.currentDict.wordIndex] ?? {
|
||||
trans: [],
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveKey)
|
||||
if (configStr) {
|
||||
let obj: Config = JSON.parse(configStr)
|
||||
this.setState(obj)
|
||||
}
|
||||
if (this.currentDictType.name === DictType.inner) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
if (this.currentDictType.name === DictType.custom) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
this.dictModalIsOpen = false
|
||||
this.dictModalIsOpen2 = false
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = -1, wordIndex: number = -1) {
|
||||
console.log('changeDict')
|
||||
if ([DictType.newWordDict, DictType.skipWordDict].includes(dict.type)) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = ''
|
||||
this[dict.type].chapterList = [this[dict.type].wordList]
|
||||
this[dict.type].chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this[dict.type].wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
} else {
|
||||
if (dict.name === this.dict.name) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = dict.url
|
||||
if (wordIndex !== -1) this.dict.wordIndex = wordIndex
|
||||
if (chapterIndex !== -1) this.dict.chapterIndex = chapterIndex
|
||||
} else {
|
||||
// let r = await fetch(`/public/${dict.url}`)
|
||||
// r.json().then(v => {
|
||||
// this.currentDictType.name === dict.type
|
||||
// this.currentDictType.dictUrl = dict.url
|
||||
//
|
||||
// })
|
||||
this.dict = cloneDeep(dict)
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
this.dict.chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this.dict.wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
type: DictType.newWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
skipWordDict: {
|
||||
type: DictType.skipWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
dict: {
|
||||
type: DictType.inner,
|
||||
name: '新概念英语-2',
|
||||
description: '新概念英语第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: '/dicts/NCE_2.json',
|
||||
length: 858,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
},
|
||||
currentDictType: {
|
||||
name: DictType.inner,
|
||||
dictUrl: '/dicts/NCE_2.json'
|
||||
},
|
||||
sideIsOpen: false,
|
||||
dictModalIsOpen: false,
|
||||
dictModalIsOpen2: false,
|
||||
setting: {
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
value1: false,
|
||||
value2: 50,
|
||||
value3: 1,
|
||||
value4: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.wordList.map(v => v.name)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.currentDictType.name) {
|
||||
case DictType.newWordDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipWordDict:
|
||||
return state.skipWordDict
|
||||
case DictType.inner:
|
||||
case DictType.custom:
|
||||
return state.dict
|
||||
}
|
||||
},
|
||||
chapter(): Word[] {
|
||||
return this.currentDict.chapterList[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
word(): Word {
|
||||
return this.chapter[this.currentDict.wordIndex] ?? {
|
||||
trans: [],
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveKey)
|
||||
if (configStr) {
|
||||
let obj: Config = JSON.parse(configStr)
|
||||
this.setState(obj)
|
||||
}
|
||||
if (this.currentDictType.name === DictType.inner) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
if (this.currentDictType.name === DictType.custom) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
this.dictModalIsOpen = false
|
||||
this.dictModalIsOpen2 = false
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = -1, wordIndex: number = -1) {
|
||||
console.log('changeDict')
|
||||
if ([DictType.newWordDict, DictType.skipWordDict].includes(dict.type)) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = ''
|
||||
this[dict.type].chapterList = [this[dict.type].wordList]
|
||||
this[dict.type].chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this[dict.type].wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
} else {
|
||||
if (dict.name === this.dict.name) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = dict.url
|
||||
if (wordIndex !== -1) this.dict.wordIndex = wordIndex
|
||||
if (chapterIndex !== -1) this.dict.chapterIndex = chapterIndex
|
||||
} else {
|
||||
// let r = await fetch(`/public/${dict.url}`)
|
||||
// r.json().then(v => {
|
||||
// this.currentDictType.name === dict.type
|
||||
// this.currentDictType.dictUrl = dict.url
|
||||
//
|
||||
// })
|
||||
this.dict = cloneDeep(dict)
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
this.dict.chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this.dict.wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -124,4 +124,12 @@ export interface State {
|
||||
sideIsOpen: boolean,
|
||||
dictModalIsOpen: boolean,
|
||||
dictModalIsOpen2: boolean,
|
||||
setting: {
|
||||
showToolbar: boolean,
|
||||
show: boolean,
|
||||
value1: boolean,
|
||||
value2: number,
|
||||
value3: number,
|
||||
value4: boolean,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user