添加文章
This commit is contained in:
13
src/App.vue
13
src/App.vue
@@ -8,6 +8,8 @@ import Backgorund from "@/components/Backgorund.vue";
|
||||
import {onMounted, watch} from "vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {SaveKey} from "@/types.ts"
|
||||
import Footer from "@/components/Footer.vue"
|
||||
import TypeArticle from "@/components/TypeArticle.vue"
|
||||
|
||||
const store = useBaseStore()
|
||||
// 查询当前系统主题颜色
|
||||
@@ -34,13 +36,15 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Backgorund/>-->
|
||||
<!-- <Backgorund/>-->
|
||||
<div class="main-page">
|
||||
<div class="center">
|
||||
<Toolbar/>
|
||||
<div class="content">
|
||||
<Type/>
|
||||
<!-- <Type/>-->
|
||||
<TypeArticle/>
|
||||
</div>
|
||||
<Footer/>
|
||||
<Side/>
|
||||
</div>
|
||||
<Statistics></Statistics>
|
||||
@@ -62,7 +66,7 @@ onMounted(() => {
|
||||
background-color: var(--color-main-bg);
|
||||
|
||||
.center {
|
||||
width: 70vw;
|
||||
width: var(--toolbar-width);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -71,12 +75,13 @@ onMounted(() => {
|
||||
position: relative;
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ onMounted(() => {
|
||||
_move() {
|
||||
let dx = this.boomArea.x - this.x,
|
||||
dy = this.boomArea.y - this.y;
|
||||
this.x = this.x + dx * 0.01;
|
||||
this.y = this.y + dy * 0.01;
|
||||
this.x = this.x + dx * 0.02;
|
||||
this.y = this.y + dy * 0.02;
|
||||
// console.log(this.x, this.y, dx, this.ba)
|
||||
|
||||
if (Math.abs(dx) <= this.ba && Math.abs(dy) <= this.ba) {
|
||||
|
||||
156
src/components/Footer.vue
Normal file
156
src/components/Footer.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {$computed, $ref} from "vue/macros"
|
||||
import {onMounted, onUnmounted} from "vue"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import Tooltip from "@/components/Tooltip.vue"
|
||||
import {Down} from "@icon-park/vue-next"
|
||||
|
||||
const store = useBaseStore()
|
||||
|
||||
function format(val: number, suffix: string = '') {
|
||||
return val === -1 ? '-' : (val + suffix)
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!store.chapter.length) return 0
|
||||
return ((store.current.index / store.current.words.length) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - store.current.statistics.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="footer" :class="!store.setting.showToolbar && 'hide'">
|
||||
<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>
|
||||
<div class="bottom">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ store.current.words.length }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ store.current.index }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(store.current.wrongWords.length) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(store.current.statistics.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
.footer {
|
||||
width: 100%;
|
||||
margin-bottom: 30rem;
|
||||
transition: all .3s;
|
||||
position: relative;
|
||||
margin-top: 30rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -90rem;
|
||||
margin-top: 65rem;
|
||||
|
||||
.arrow {
|
||||
transform: translate3d(-50%, -220%, 0) rotate(180deg);
|
||||
}
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 30rem);
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
transform: translate3d(-50%, -100%, 0) rotate(0);
|
||||
padding: 5rem;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rem;
|
||||
background: var(--color-header-bg);
|
||||
padding: 2rem 10rem 10rem 10rem;
|
||||
z-index: 2;
|
||||
|
||||
.stat {
|
||||
margin-top: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
width: 80rem;
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: gainsboro;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
transition: all .3s;
|
||||
padding: 0 10rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -113,9 +113,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.dict)"
|
||||
>
|
||||
<div class="my-button">
|
||||
切换
|
||||
</div>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -136,9 +134,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.newWordDict)"
|
||||
>
|
||||
<div class="my-button">
|
||||
切换
|
||||
</div>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -161,9 +157,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.wrongWordDict)"
|
||||
>
|
||||
<BaseButton>
|
||||
切换
|
||||
</BaseButton>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -184,9 +178,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.skipWordDict)"
|
||||
>
|
||||
<div class="my-button hvr-grow">
|
||||
切换
|
||||
</div>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -209,9 +201,10 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
.side {
|
||||
$width: 20vw;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: calc(100% + $space);
|
||||
width: $width;
|
||||
background: var(--color-second-bg);
|
||||
//background: white;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -285,7 +278,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
justify-content: flex-end;
|
||||
gap: 10rem;
|
||||
font-size: 18rem;
|
||||
color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.word-list {
|
||||
|
||||
@@ -32,21 +32,29 @@ import IconEyeSlash from '~icons/heroicons/eye-slash-solid'
|
||||
import IconRepeat from '~icons/tabler/repeat'
|
||||
import IconRepeatOff from '~icons/tabler/repeat-off'
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts"
|
||||
import {watch} from "vue"
|
||||
|
||||
const {toggle} = useTheme()
|
||||
const store = useBaseStore()
|
||||
const showFeedbackModal = $ref(false)
|
||||
const showSettingModal = $ref(false)
|
||||
const showDictModal = $ref(false)
|
||||
const headerRef = $ref<HTMLDivElement>(null)
|
||||
|
||||
function t() {
|
||||
console.log('t')
|
||||
}
|
||||
|
||||
watch(() => store.setting.showToolbar, n => {
|
||||
if (headerRef) {
|
||||
if (n) {
|
||||
headerRef.style.marginTop = '10rem'
|
||||
} else {
|
||||
let rect = headerRef.getBoundingClientRect()
|
||||
headerRef.style.marginTop = `-${rect.height}px`
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header :class="!store.setting.showToolbar && 'hide'">
|
||||
<header ref="headerRef">
|
||||
<div class="info" @click="showDictModal = true">
|
||||
{{ store.dictTitle }}
|
||||
</div>
|
||||
@@ -122,18 +130,21 @@ function t() {
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
header {
|
||||
top: 10rem;
|
||||
height: 60rem;
|
||||
width: var(--toolbar-width);
|
||||
margin-top: 10rem;
|
||||
min-height: 60rem;
|
||||
width: 100%;
|
||||
background: var(--color-header-bg);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 8rem;
|
||||
position: absolute;
|
||||
margin-bottom: 30rem;
|
||||
//position: absolute;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 10rem $space;
|
||||
box-sizing: border-box;
|
||||
transition: all .3s;
|
||||
gap: 10rem;
|
||||
|
||||
.info {
|
||||
font-size: 16rem;
|
||||
@@ -150,10 +161,6 @@ header {
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
transform: translateY(calc(-100% - 10rem));
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
@@ -30,7 +30,6 @@ export default {
|
||||
this.show = true
|
||||
nextTick(() => {
|
||||
let tip = this.$refs?.tip?.getBoundingClientRect()
|
||||
console.log('re', rect)
|
||||
if (!tip) return
|
||||
if (rect.top < 50) {
|
||||
this.$refs.tip.style.top = rect.top + rect.height + 10 + 'px'
|
||||
|
||||
@@ -179,35 +179,16 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!store.chapter.length) return 0
|
||||
return ((store.current.index / store.current.words.length) * 100)
|
||||
})
|
||||
|
||||
const {toggle} = useTheme()
|
||||
|
||||
function format(val: number, suffix: string = '') {
|
||||
return val === -1 ? '-' : (val + suffix)
|
||||
}
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - store.current.statistics.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="type-word">
|
||||
<div class="translate">{{ store.word.trans.join(';') }}</div>
|
||||
<div class="word-wrapper">
|
||||
<div class="word" :class="wrong&&'is-wrong'">
|
||||
<div class="word" :class="wrong && 'is-wrong'">
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="store.isDictation">
|
||||
@@ -231,50 +212,6 @@ onUnmounted(() => {
|
||||
下一个
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="bottom" :class="!store.setting.showToolbar && 'hide'">
|
||||
<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>
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ store.current.words.length }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ store.current.index }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(store.current.wrongWords.length) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(store.current.statistics.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -301,6 +238,7 @@ onUnmounted(() => {
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
margin-left: -30rem;
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
@@ -327,65 +265,6 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 30rem;
|
||||
width: var(--toolbar-width);
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rem;
|
||||
background: var(--color-header-bg);
|
||||
padding: 2rem 10rem 10rem 10rem;
|
||||
margin-top: 20rem;
|
||||
transition: all .3s;
|
||||
z-index: 2;
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
transform: translate3d(-50%, -100%, 0) rotate(0);
|
||||
padding: 5rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
margin-top: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
width: 80rem;
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: gainsboro;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.hide {
|
||||
transform: translateY(calc(100% + 30rem));
|
||||
|
||||
.arrow {
|
||||
transform: translate3d(-50%, -220%, 0) rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: var(--toolbar-width);
|
||||
padding: 0 10rem;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
bottom: 30rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
|
||||
53
src/components/TypeArticle.vue
Normal file
53
src/components/TypeArticle.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="type-wrapper">
|
||||
<article v-if="true">
|
||||
How does the older investor differ in his approach to investment from the younger investor?
|
||||
There is no shortage of tipsters around offering 'get-rich-quick' opportunities. But if you are a serious private
|
||||
investor, leave the Las Vegas mentality to those with money to fritter. The serious investor needs a proper
|
||||
'portfolio' -- a well-planned selection of investments, with a definite structure and a clear aim. But exactly how
|
||||
does a newcomer to the stock market go about achieving that?
|
||||
Well, if you go to five reputable stock brokers and ask them what you should do with your money, you're likely to
|
||||
get five different answers, -- even if you give all the relevant information about your age age, family, finances
|
||||
and what you want from your investments. Moral? There is no one 'right' way to structure a portfolio. However,
|
||||
there are undoubtedly some wrong ways, and you can be sure that none of our five advisers would have suggested
|
||||
sinking all (or perhaps any) of your money into Periwigs*.
|
||||
So what should you do? We'll assume that you have sorted out the basics -- like mortgages, pensions, insurance and
|
||||
access to sufficient cash reserves. You should then establish your own individual aims. These are partly a matter
|
||||
of personal circumstances, partly a matter of psychology.
|
||||
For instance, if you are older you have less time to recover from any major losses, and you may well wish to boost
|
||||
your pension income. So preserving your capital and generating extra income are your main priorities. In this
|
||||
case, you'd probably construct a portfolio with some shares (but not high risk ones), along with gilts, cash
|
||||
deposits, and perhaps convertibles or the income shares of split capital investment trusts.
|
||||
If you are younger, and in a solid financial position, you may decide to take an aggressive approach -- but only
|
||||
if you're blessed with a sanguine disposition and won't suffer sleepless nights over share prices. If portfolio,
|
||||
alongside your more pedestrian in vestments. Once you have decided on your investment aims, you can then decide
|
||||
where to put your money. The golden rule here is spread your risk -- if you put all of your money into Periwigs
|
||||
International, you're setting yourself up as a hostage to fortune.
|
||||
*'Periwigs' is the name of a fictitious company.
|
||||
INVESTOR'S CHRONICLE, March 23 1990
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style.scss";
|
||||
|
||||
.type-wrapper {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
article {
|
||||
font-size: 24rem;
|
||||
line-height: 1.5;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
letter-spacing: 0rem;
|
||||
color: gray;
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user