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