mirror of
https://github.com/bs-community/blessing-skin-server.git
synced 2025-03-01 15:05:39 +08:00
Beautify user dashboard
This commit is contained in:
parent
c168970723
commit
f7c03a3fac
@ -19,6 +19,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.4",
|
||||
"@fortawesome/fontawesome-free": "^5.7.2",
|
||||
"@tweenjs/tween.js": "^17.3.0",
|
||||
"admin-lte": "^2.4.10",
|
||||
"echarts": "^4.1.0",
|
||||
"element-theme-chalk": "^2.6.3",
|
||||
|
@ -1,2 +1,4 @@
|
||||
@import '~element-theme-chalk/src/button.scss';
|
||||
@import '~element-theme-chalk/src/message.scss';
|
||||
@import '~element-theme-chalk/src/message-box.scss';
|
||||
@import '~element-theme-chalk/src/progress.scss';
|
||||
|
@ -1,15 +1,16 @@
|
||||
import Vue from 'vue'
|
||||
import Button from 'element-ui/lib/button'
|
||||
import Message from 'element-ui/lib/message'
|
||||
import MessageBox from 'element-ui/lib/message-box'
|
||||
|
||||
Vue.use(Button)
|
||||
|
||||
Vue.prototype.$message = Message
|
||||
Vue.prototype.$msgbox = MessageBox
|
||||
Vue.prototype.$alert = MessageBox.alert
|
||||
Vue.prototype.$confirm = MessageBox.confirm
|
||||
Vue.prototype.$prompt = MessageBox.prompt
|
||||
|
||||
window.MessageBox = MessageBox
|
||||
|
||||
export {
|
||||
Message,
|
||||
MessageBox,
|
||||
|
@ -7,66 +7,85 @@
|
||||
</div><!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="progress-group">
|
||||
<span v-t="'user.used.players'" class="progress-text" />
|
||||
<span class="progress-number"><b>{{ playersUsed }}</b> / {{ playersTotal }}</span>
|
||||
<div class="progress sm">
|
||||
<div class="progress-bar progress-bar-aqua" :style="{ width: playersPercentage + '%' }" />
|
||||
</div>
|
||||
</div><!-- /.progress-group -->
|
||||
<div class="progress-group">
|
||||
<span v-t="'user.used.storage'" class="progress-text" />
|
||||
<span id="user-storage" class="progress-number">
|
||||
<template v-if="storageUsed > 1024">
|
||||
<b>{{ round(storageUsed / 1024) }}</b> / {{ round(storageTotal / 1024) }} MB
|
||||
</template>
|
||||
<template v-else>
|
||||
<b>{{ storageUsed }}</b> / {{ storageTotal }} KB
|
||||
</template>
|
||||
</span>
|
||||
|
||||
<div class="progress sm">
|
||||
<div
|
||||
id="user-storage-bar"
|
||||
class="progress-bar progress-bar-yellow"
|
||||
:style="{ width: storagePercentage + '%' }"
|
||||
/>
|
||||
</div>
|
||||
</div><!-- /.progress-group -->
|
||||
</div><!-- /.col -->
|
||||
<div class="col-md-1" />
|
||||
<div class="col-md-3">
|
||||
<div class="text-center usage-label">
|
||||
<b v-t="'user.used.players'" />
|
||||
</div>
|
||||
<el-progress
|
||||
type="circle"
|
||||
:percentage="playersPercentage"
|
||||
status="text"
|
||||
color="#00c0ef"
|
||||
class="usage-circle"
|
||||
>
|
||||
<b>{{ playersUsed }}</b> / {{ playersTotal }}
|
||||
</el-progress>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center usage-label">
|
||||
<b v-t="'user.used.storage'" />
|
||||
</div>
|
||||
<el-progress
|
||||
type="circle"
|
||||
:percentage="storagePercentage"
|
||||
status="text"
|
||||
color="#f39c12"
|
||||
class="usage-circle"
|
||||
>
|
||||
<template v-if="storageUsed > 1024">
|
||||
<b>{{ round(storageUsed / 1024) }}</b> / {{ round(storageTotal / 1024) }} MB
|
||||
</template>
|
||||
<template v-else>
|
||||
<b>{{ storageUsed }}</b> / {{ storageTotal }} KB
|
||||
</template>
|
||||
</el-progress>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-center">
|
||||
<strong v-t="'user.cur-score'" />
|
||||
</p>
|
||||
<p id="score" data-toggle="modal" data-target="#modal-score-instruction">
|
||||
{{ score }}
|
||||
{{ animatedScore }}
|
||||
</p>
|
||||
<p v-t="'user.score-notice'" class="text-center" style="font-size: smaller; margin-top: 20px;" />
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
</div><!-- ./box-body -->
|
||||
<div class="box-footer">
|
||||
<button v-if="canSign" class="btn btn-primary pull-left" @click="sign">
|
||||
<el-button
|
||||
v-if="canSign"
|
||||
class="btn btn-primary pull-left"
|
||||
type="primary"
|
||||
round
|
||||
@click="sign"
|
||||
>
|
||||
<i class="far fa-calendar-check" aria-hidden="true" /> {{ $t('user.sign') }}
|
||||
</button>
|
||||
<button
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
class="btn btn-primary pull-left"
|
||||
type="primary"
|
||||
round
|
||||
:title="$t('user.last-sign', { time: lastSignAt.toLocaleString() })"
|
||||
disabled
|
||||
>
|
||||
<i class="far fa-calendar-check" aria-hidden="true" />
|
||||
{{ remainingTimeText }}
|
||||
</button>
|
||||
</el-button>
|
||||
</div><!-- /.box-footer -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Progress from 'element-ui/lib/progress'
|
||||
import Tween from '@tweenjs/tween.js'
|
||||
import EmailVerification from '../../components/EmailVerification.vue'
|
||||
|
||||
Vue.use(Progress)
|
||||
|
||||
const ONE_DAY = 24 * 3600 * 1000
|
||||
|
||||
export default {
|
||||
@ -76,13 +95,15 @@ export default {
|
||||
},
|
||||
data: () => ({
|
||||
score: 0,
|
||||
tweenedScore: 0,
|
||||
lastSignAt: new Date(),
|
||||
signAfterZero: false,
|
||||
signGap: 0,
|
||||
playersUsed: 0,
|
||||
playersTotal: 0,
|
||||
playersTotal: 1,
|
||||
storageUsed: 0,
|
||||
storageTotal: 0,
|
||||
storageTotal: 1,
|
||||
tween: null,
|
||||
}),
|
||||
computed: {
|
||||
playersPercentage() {
|
||||
@ -115,14 +136,31 @@ export default {
|
||||
canSign() {
|
||||
return this.signRemainingTime <= 0
|
||||
},
|
||||
animatedScore() {
|
||||
return this.tweenedScore.toFixed(0)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
score(newValue) {
|
||||
this.tween.to({ tweenedScore: newValue }, 1000).start()
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.tween = new Tween.Tween(this.$data)
|
||||
},
|
||||
beforeMount() {
|
||||
this.fetchScoreInfo()
|
||||
},
|
||||
mounted() {
|
||||
function animate() {
|
||||
requestAnimationFrame(animate)
|
||||
Tween.update()
|
||||
}
|
||||
animate()
|
||||
},
|
||||
methods: {
|
||||
async fetchScoreInfo() {
|
||||
const data = await this.$http.get('/user/score-info')
|
||||
this.score = data.user.score
|
||||
this.lastSignAt = new Date(data.user.lastSignAt)
|
||||
this.signAfterZero = data.signAfterZero
|
||||
this.signGap = data.signGapTime * 3600 * 1000
|
||||
@ -130,6 +168,7 @@ export default {
|
||||
this.playersTotal = data.stats.players.total
|
||||
this.storageUsed = data.stats.storage.used
|
||||
this.storageTotal = data.stats.storage.total
|
||||
this.score = data.user.score
|
||||
},
|
||||
round: Math.round,
|
||||
async sign() {
|
||||
@ -157,6 +196,9 @@ export default {
|
||||
margin-top 20px
|
||||
cursor help
|
||||
|
||||
.progress
|
||||
margin-top 4px
|
||||
.usage-circle
|
||||
padding-top 10px
|
||||
|
||||
.usage-label
|
||||
padding-right 20%
|
||||
</style>
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import 'jest-extended'
|
||||
import Vue from 'vue'
|
||||
import { Button } from 'element-ui'
|
||||
|
||||
window.blessing = {
|
||||
base_url: '',
|
||||
@ -35,6 +36,7 @@ Vue.prototype.$http = {
|
||||
post: jest.fn(),
|
||||
}
|
||||
|
||||
Vue.use(Button)
|
||||
Vue.prototype.$message = {
|
||||
info: jest.fn(),
|
||||
success: jest.fn(),
|
||||
|
@ -1,8 +1,27 @@
|
||||
/* eslint-disable no-mixed-operators */
|
||||
import Vue from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Button } from 'element-ui'
|
||||
import Dashboard from '@/views/user/Dashboard.vue'
|
||||
|
||||
jest.mock('@tweenjs/tween.js', () => ({
|
||||
Tween: class <T> {
|
||||
data: T
|
||||
|
||||
constructor(data: T) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
to(data: Partial<T>, _: number) {
|
||||
Object.assign(this.data, data)
|
||||
return this
|
||||
}
|
||||
|
||||
start() {}
|
||||
},
|
||||
update() {},
|
||||
}))
|
||||
|
||||
window.blessing.extra = { unverified: false }
|
||||
|
||||
function scoreInfo(data = {}) {
|
||||
@ -33,7 +52,6 @@ test('players usage', async () => {
|
||||
const wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.text()).toContain('3 / 15')
|
||||
expect(wrapper.find('.progress-bar-aqua').attributes('style')).toBe('width: 20%;')
|
||||
})
|
||||
|
||||
test('storage usage', async () => {
|
||||
@ -52,12 +70,10 @@ test('storage usage', async () => {
|
||||
let wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.text()).toContain('5 / 20 KB')
|
||||
expect(wrapper.find('.progress-bar-yellow').attributes('style')).toBe('width: 25%;')
|
||||
|
||||
wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.text()).toContain('2 / 4 MB')
|
||||
expect(wrapper.find('.progress-bar-yellow').attributes('style')).toBe('width: 50%;')
|
||||
})
|
||||
|
||||
test('display score', async () => {
|
||||
@ -79,19 +95,19 @@ test('button `sign` state', async () => {
|
||||
|
||||
let wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').attributes('disabled')).toBeNil()
|
||||
expect(wrapper.find(Button).attributes('disabled')).toBeNil()
|
||||
|
||||
wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').attributes('disabled')).toBe('disabled')
|
||||
expect(wrapper.find(Button).attributes('disabled')).toBe('disabled')
|
||||
|
||||
wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').attributes('disabled')).toBeNil()
|
||||
expect(wrapper.find(Button).attributes('disabled')).toBeNil()
|
||||
|
||||
wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').attributes('disabled')).toBe('disabled')
|
||||
expect(wrapper.find(Button).attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
test('remaining time', async () => {
|
||||
@ -108,13 +124,13 @@ test('remaining time', async () => {
|
||||
|
||||
let wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').text()).toMatch(/(29)|(30)/)
|
||||
expect(wrapper.find('button').text()).toContain('min')
|
||||
expect(wrapper.find(Button).text()).toMatch(/(29)|(30)/)
|
||||
expect(wrapper.find(Button).text()).toContain('min')
|
||||
|
||||
wrapper = mount(Dashboard)
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button').text()).toContain('23')
|
||||
expect(wrapper.find('button').text()).toContain('hour')
|
||||
expect(wrapper.find(Button).text()).toContain('23')
|
||||
expect(wrapper.find(Button).text()).toContain('hour')
|
||||
|
||||
Vue.prototype.$t = origin
|
||||
})
|
||||
@ -131,7 +147,7 @@ test('sign', async () => {
|
||||
storage: { used: 3, total: 4 },
|
||||
})
|
||||
const wrapper = mount(Dashboard)
|
||||
const button = wrapper.find('button')
|
||||
const button = wrapper.find(Button)
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
button.trigger('click')
|
||||
|
@ -902,6 +902,11 @@
|
||||
"@types/istanbul-lib-coverage" "^1.1.0"
|
||||
"@types/yargs" "^12.0.9"
|
||||
|
||||
"@tweenjs/tween.js@^17.3.0":
|
||||
version "17.3.0"
|
||||
resolved "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-17.3.0.tgz#1cbea14b4b5baa292274c96766fd1b2a56b7ca9f"
|
||||
integrity sha512-SPkhNj9/wGfbdX2C3B3KhttLQ4iesd+Ny8Dv1RnqF1MFUIqsZz/OJVLzJEHSEl7zheNx70dvqrwfbCFDQ0sWBw==
|
||||
|
||||
"@types/anymatch@*":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
|
||||
|
Loading…
Reference in New Issue
Block a user