优化加载本地保存的代码的逻辑; 部分细节优化

This commit is contained in:
zema1 2017-11-13 16:51:08 +08:00
parent 1910fc3bb0
commit 67c3b97be8
14 changed files with 173 additions and 152 deletions

View File

@ -1,11 +1,11 @@
language: node_js
node_js:
- "6.2.2"
- "6.11.2"
env:
- CXX=g++-4.8
script:
- cd admin
- npm install
- npm run build:dll
- npm run build
addons:
apt:

View File

@ -32,10 +32,9 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.cyan(' Congratulations, the project build complete without error\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
' You can now check the onlinejudge in http://YouIP/'
))
})
})

View File

@ -19,15 +19,11 @@
</Tag>
</div>
<div v-html="contest.description"></div>
<div v-if="passwordFormVisible">
<Form inline class="contest-password">
<FormItem>
<Input v-model="contestPassword" type="password" placeholder="contest password"/>
</FormItem>
<FormItem>
<Button type="info" @click="checkPassword">Enter</Button>
</FormItem>
</Form>
<div v-if="passwordFormVisible" class="contest-password">
<Input v-model="contestPassword" type="password"
placeholder="contest password" class="contest-password-input"
@on-enter="checkPassword" />
<Button type="info" @click="checkPassword">Enter</Button>
</div>
</Panel>
<Table :columns="columns" :data="contest_table" disabled-hover style="margin-bottom: 40px;"></Table>
@ -61,7 +57,7 @@
:disabled="contestMenuDisabled"
:route="{name: 'contest-rank', params: {contestID: contestID}}">
<Icon type="stats-bars"></Icon>
Ranklist
Rankings
</VerticalMenu-item>
<VerticalMenu-item :route="{name: 'contest-details', params: {contestID: contestID}}">
@ -205,6 +201,10 @@
.contest-password {
margin-top: 20px;
margin-bottom: -10px;
&-input {
width: 200px;
margin-right: 10px;
}
}
}
</style>

View File

@ -13,7 +13,7 @@
</p>
<p style="margin-top: 10px">
<span>Auto Refresh(10s)</span>
<i-switch @on-change="handleAutoRefresh"></i-switch>
<i-switch :disabled="refreshDisabled" @on-change="handleAutoRefresh"></i-switch>
</p>
</div>
</Poptip>
@ -32,18 +32,20 @@
</template>
<script>
import moment from 'moment'
import Pagination from '@oj/components/Pagination'
import { mapActions } from 'vuex'
import { mapActions, mapState } from 'vuex'
import { types } from '@oj/store'
import Pagination from '@oj/components/Pagination'
import ContestRankMixin from './contestRankMixin'
import api from '@oj/api'
import time from '@/utils/time'
import utils from '@/utils/utils'
export default {
name: 'acm-contest-rank',
components: {
Pagination
},
mixins: [ContestRankMixin],
data () {
return {
total: 0,
@ -60,7 +62,6 @@
{
title: 'User',
align: 'center',
width: 250,
render: (h, params) => {
return h('Button', {
props: {
@ -69,6 +70,9 @@
'class': {
'link-button': true
},
style: {
'max-width': '150px'
},
on: {
click: () => {
this.$router.push(
@ -132,7 +136,7 @@
right: 0,
data: [],
formatter: (value) => {
return value.replace(/(.{16})/g, '$1\n')
return utils.breakLongWords(value, 16)
},
textStyle: {
fontSize: 12
@ -140,7 +144,7 @@
},
grid: {
x: 80,
x2: 250
x2: 200
},
xAxis: [{
type: 'time',
@ -266,6 +270,9 @@
type: 'text',
size: 'large'
},
style: {
padding: 0
},
on: {
click: () => {
this.$router.push({
@ -294,55 +301,7 @@
}
})
})
},
handleAutoRefresh (status) {
if (status === true) {
this.refreshFunc = setInterval(() => {
this.getContestRankData(1, true)
}, 10000)
} else {
clearInterval(this.refreshFunc)
}
}
},
computed: {
...mapState({
'contest': state => state.contest.contest,
'contestProblems': state => state.contest.contestProblems
}),
showChart: {
get () {
return this.$store.state.contest.showChart
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_CHART_VISIBLE, {visible: value})
}
},
showMenu: {
get () {
return this.$store.state.contest.showMenu
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_MENU_VISIBLE, {visible: value})
this.$nextTick(() => {
this.$refs.tableRank.handleResize()
if (this.showChart) {
this.$refs.chart.resize()
}
})
}
},
limit: {
get () {
return this.$store.state.contest.rankLimit
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_RANK_LIMIT, {rankLimit: value})
}
}
},
beforeDestroy () {
clearInterval(this.refreshFunc)
}
}
</script>

View File

@ -13,7 +13,7 @@
</p>
<p style="margin-top: 10px">
<span>Auto Refresh(10s)</span>
<i-switch @on-change="handleAutoRefresh"></i-switch>
<i-switch :disabled="refreshDisabled" @on-change="handleAutoRefresh"></i-switch>
</p>
</div>
</Poptip>
@ -31,10 +31,11 @@
</Panel>
</template>
<script>
import Pagination from '@oj/components/Pagination'
import { mapActions } from 'vuex'
import { mapActions, mapState } from 'vuex'
import { types } from '@oj/store'
import Pagination from '@oj/components/Pagination'
import ContestRankMixin from './contestRankMixin'
import utils from '@/utils/utils'
import api from '@oj/api'
export default {
@ -42,6 +43,7 @@
components: {
Pagination
},
mixins: [ContestRankMixin],
data () {
return {
total: 0,
@ -58,7 +60,6 @@
{
title: 'User',
align: 'center',
width: 250,
render: (h, params) => {
return h('Button', {
props: {
@ -67,6 +68,9 @@
'class': {
'link-button': true
},
style: {
'max-width': '150px'
},
on: {
click: () => {
this.$router.push(
@ -116,7 +120,7 @@
showMaxLabel: true,
align: 'center',
formatter: (value, index) => {
return value.replace(/(.{8})/g, '$1\n')
return utils.breakLongWords(value, 14)
}
},
axisTick: {
@ -207,6 +211,9 @@
type: 'text',
size: 'large'
},
style: {
padding: 0
},
on: {
click: () => {
this.$router.push({
@ -225,55 +232,7 @@
}
})
})
},
handleAutoRefresh (status) {
if (status === true) {
this.refreshFunc = setInterval(() => {
this.getContestRankData(1, true)
}, 10000)
} else {
clearInterval(this.refreshFunc)
}
}
},
computed: {
...mapState({
'contest': state => state.contest.contest,
'contestProblems': state => state.contest.contestProblems
}),
showChart: {
get () {
return this.$store.state.contest.showChart
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_CHART_VISIBLE, {visible: value})
}
},
showMenu: {
get () {
return this.$store.state.contest.showMenu
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_MENU_VISIBLE, {visible: value})
this.$nextTick(() => {
this.$refs.tableRank.handleResize()
if (this.showChart) {
this.$refs.chart.resize()
}
})
}
},
limit: {
get () {
return this.$store.state.contest.rankLimit
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_RANK_LIMIT, {rankLimit: value})
}
}
},
beforeDestroy () {
clearInterval(this.refreshFunc)
}
}
</script>

View File

@ -0,0 +1,60 @@
import { mapState } from 'vuex'
import { types } from '@oj/store/index'
import { CONTEST_STATUS } from '@/utils/constants'
export default {
methods: {
handleAutoRefresh (status) {
if (status === true) {
this.refreshFunc = setInterval(() => {
this.page = 1
this.getContestRankData(1, true)
}, 10000)
} else {
clearInterval(this.refreshFunc)
}
}
},
computed: {
...mapState({
'contest': state => state.contest.contest,
'contestProblems': state => state.contest.contestProblems
}),
showChart: {
get () {
return this.$store.state.contest.showChart
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_CHART_VISIBLE, {visible: value})
}
},
showMenu: {
get () {
return this.$store.state.contest.showMenu
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_MENU_VISIBLE, {visible: value})
this.$nextTick(() => {
this.$refs.tableRank.handleResize()
if (this.showChart) {
this.$refs.chart.resize()
}
})
}
},
refreshDisabled () {
return this.contest.status === CONTEST_STATUS.ENDED
},
limit: {
get () {
return this.$store.state.contest.rankLimit
},
set (value) {
this.$store.commit(types.CHANGE_CONTEST_RANK_LIMIT, {rankLimit: value})
}
}
},
beforeDestroy () {
clearInterval(this.refreshFunc)
}
}

View File

@ -26,6 +26,7 @@
</CarouselItem>
</Carousel>
</panel>
<Announcements class="announcement"></Announcements>
</Col>
</Row>

View File

@ -105,7 +105,7 @@
<VerticalMenu-item v-if="!this.contestID || OIContestRealTimePermission"
:route="{name: 'contest-rank', params: {contestID: contestID}}">
<Icon type="stats-bars"></Icon>
Ranklist
Rankings
</VerticalMenu-item>
<VerticalMenu-item :route="{name: 'contest-details', params: {contestID: contestID}}">
<Icon type="home"></Icon>
@ -184,7 +184,7 @@
import storage from '@/utils/storage'
import { FormMixin } from '@oj/components/mixins'
import { types } from '@oj/store'
import { JUDGE_STATUS, CONTEST_STATUS, STORAGE_KEY } from '@/utils/constants'
import { JUDGE_STATUS, CONTEST_STATUS, buildProblemCodeKey } from '@/utils/constants'
import api from '@oj/api'
import { pie, largePie } from './chartData'
@ -231,6 +231,16 @@
}
}
},
beforeRouteEnter (to, from, next) {
let problemCode = storage.get(buildProblemCodeKey(to.params.problemID, to.params.contestID))
if (problemCode) {
next(vm => {
vm.language = problemCode.language
vm.code = problemCode.code
})
}
next()
},
mounted () {
this.$store.commit(types.CHANGE_CONTEST_MENU_VISIBLE, {visible: false})
this.init()
@ -245,16 +255,13 @@
this.$Loading.finish()
this.problem = res.data.data
this.changePie(res.data.data)
let problemCode = storage.get(STORAGE_KEY.PROBLEM_CODE + this.problem.id)
// beforeRouteEnter, code template
if (this.language !== 'C++' || this.code !== '') {
return
}
let template = this.problem.template
if (problemCode) {
this.language = problemCode.language
if (problemCode.code === '' && template[this.language]) {
this.code = template[this.language]
} else {
this.code = problemCode.code
}
} else if (template[this.language]) {
if (template && template[this.language]) {
this.code = template[this.language]
}
this.$nextTick(() => {
@ -391,8 +398,9 @@
beforeDestroy () {
//
clearInterval(this.refreshStatus)
this.$store.commit(types.CHANGE_CONTEST_MENU_VISIBLE, {visible: true})
storage.set(STORAGE_KEY.PROBLEM_CODE + this.problem.id, {
storage.set(buildProblemCodeKey(this.problem._id, this.$route.params.contestID), {
code: this.code,
language: this.language
})

View File

@ -36,6 +36,7 @@
<Table style="width: 100%; font-size: 16px;"
:columns="problemTableColumns"
:data="problemList"
:loading="loadings.table"
disabled-hover></Table>
</Panel>
<Pagination :total="total" :page-size="limit" @on-change="pushRouter" :current.sync="query.page"></Pagination>
@ -59,7 +60,7 @@
Pick one
</Button>
</Panel>
<Spin v-if="spinShow" fix size="large"></Spin>
<Spin v-if="loadings.tag" fix size="large"></Spin>
</Col>
</Row>
</template>
@ -145,16 +146,17 @@
problemList: [],
limit: 15,
total: 0,
problemLoading: false,
tagLoading: false,
loadings: {
table: true,
tag: true
},
routeName: '',
query: {
keyword: '',
difficulty: '',
tag: '',
page: 1
},
spinShow: true
}
}
},
mounted () {
@ -181,21 +183,24 @@
},
getProblemList () {
let offset = (this.query.page - 1) * this.limit
this.loadings.table = true
api.getProblemList(offset, this.limit, this.query).then(res => {
this.loadings.table = false
this.total = res.data.data.total
this.problemList = res.data.data.results
if (this.isAuthenticated) {
this.addStatusColumn(this.problemTableColumns, res.data.data.results)
}
}, res => {
this.loadings.table = false
})
},
getTagList () {
api.getProblemTagList().then(res => {
this.tagList = res.data.data
this.spinShow = false
this.loadings.tag = false
}, res => {
this.spinShow = false
this.loadings.tag = false
})
},
filterByTag (tagName) {

View File

@ -44,15 +44,17 @@
{
title: 'user',
align: 'center',
width: 250,
render: (h, params) => {
return h('Button', {
props: {
type: 'text'
},
class: {
'class': {
'link-button': true
},
style: {
'max-width': '200px'
},
on: {
click: () => {
this.$router.push(
@ -82,6 +84,7 @@
},
{
title: 'Rating',
align: 'center',
render: (h, params) => {
return h('span', utils.getACRate(params.row.accepted_number, params.row.submission_number))
}
@ -118,7 +121,7 @@
showMaxLabel: true,
align: 'center',
formatter: (value, index) => {
return value.replace(/(.{8})/g, '$1\n')
return utils.breakLongWords(value, 10)
}
}
}
@ -165,7 +168,7 @@
api.getUserRank(offset, this.limit, RULE_TYPE.ACM).then(res => {
this.loadingTable = false
if (page === 1) {
this.changeCharts(res.data.data.results)
this.changeCharts(res.data.data.results.slice(0, 10))
}
this.total = res.data.data.total
this.dataRank = res.data.data.results

View File

@ -43,7 +43,6 @@
{
title: 'user',
align: 'center',
width: 250,
render: (h, params) => {
return h('Button', {
props: {
@ -52,6 +51,9 @@
'class': {
'link-button': true
},
style: {
'max-width': '200px'
},
on: {
click: () => {
this.$router.push(
@ -86,6 +88,7 @@
},
{
title: 'Rating',
align: 'center',
render: (h, params) => {
return h('span', utils.getACRate(params.row.accepted_number, params.row.submission_number))
}
@ -123,7 +126,7 @@
showMaxLabel: true,
align: 'center',
formatter: (value, index) => {
return value.replace(/(.{8})/g, '$1\n')
return utils.breakLongWords(value, 14)
}
},
axisTick: {
@ -162,7 +165,7 @@
bar.showLoading({maskColor: 'rgba(250, 250, 250, 0.8)'})
api.getUserRank(offset, this.limit, RULE_TYPE.OI).then(res => {
if (page === 1) {
this.changeCharts(res.data.data.results)
this.changeCharts(res.data.data.results.slice(0, 10))
}
this.total = res.data.data.total
this.dataRank = res.data.data.results

View File

@ -147,7 +147,6 @@
{
title: 'Author',
align: 'center',
width: 250,
render: (h, params) => {
return h('Button', {
props: {
@ -156,6 +155,9 @@
'class': {
'link-button': true
},
style: {
'max-width': '200px'
},
on: {
click: () => {
this.$router.push(

View File

@ -112,3 +112,10 @@ export const STORAGE_KEY = {
PROBLEM_CODE: 'problem_code_',
languages: 'languages'
}
export function buildProblemCodeKey (problemID, contestID = null) {
if (contestID) {
return `${STORAGE_KEY.PROBLEM_CODE}_${contestID}_${problemID}`
}
return `${STORAGE_KEY.PROBLEM_CODE}_NaN_${problemID}`
}

View File

@ -25,9 +25,24 @@ function filterEmptyValue (object) {
})
return query
}
// 按指定字符数截断添加换行,非英文字符按指定字符的半数添加
function breakLongWords (value, length = 16) {
let re
if (escape(value).indexOf('%u') === -1) {
// 没有中文
re = new RegExp('(.{' + length + '})', 'g')
} else {
// 中文字符
re = new RegExp('(.{' + (length / 2 + 1) + '})', 'g')
}
return value.replace(re, '$1\n')
}
export default {
submissionMemoryFormat: submissionMemoryFormat,
submissionTimeFormat: submissionTimeFormat,
getACRate: getACRate,
filterEmptyValue: filterEmptyValue
filterEmptyValue: filterEmptyValue,
breakLongWords: breakLongWords
}