From 2b11b7117ba5392c87ac34baceacacc6d903b0d3 Mon Sep 17 00:00:00 2001 From: zema1 Date: Wed, 25 Oct 2017 18:02:37 +0800 Subject: [PATCH] add login view; fix bugs in add contest problem. --- admin/config/index.js | 2 +- admin/package.json | 2 +- admin/src/App.vue | 73 ++------ admin/src/api.js | 223 +++++++++-------------- admin/src/components/SideMenu.vue | 1 + admin/src/main.js | 93 +--------- admin/src/router.js | 113 ++++++++++++ admin/src/utils.js | 0 admin/src/utils/constants.js | 5 + admin/src/{ => utils}/filters.js | 0 admin/src/views/Home.vue | 111 +++++++++++ admin/src/views/contest/ContestList.vue | 16 +- admin/src/views/general/Announcement.vue | 4 +- admin/src/views/general/JudgeServer.vue | 10 +- admin/src/views/general/User.vue | 6 +- admin/src/views/general/login.vue | 83 +++++++++ admin/src/views/index.js | 4 +- admin/src/views/problem/Problem.vue | 193 ++++++++++---------- admin/src/views/problem/ProblemList.vue | 5 +- deploy/nginx.conf | 2 +- deploy/run.sh | 8 +- oj/src/router/index.js | 2 +- 22 files changed, 555 insertions(+), 401 deletions(-) create mode 100644 admin/src/router.js delete mode 100644 admin/src/utils.js create mode 100644 admin/src/utils/constants.js rename admin/src/{ => utils}/filters.js (100%) create mode 100644 admin/src/views/Home.vue create mode 100644 admin/src/views/general/login.vue diff --git a/admin/config/index.js b/admin/config/index.js index 8c14189..aaafa28 100644 --- a/admin/config/index.js +++ b/admin/config/index.js @@ -7,7 +7,7 @@ module.exports = { index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', - assetsPublicPath: '/admin', + assetsPublicPath: '/admin/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. diff --git a/admin/package.json b/admin/package.json index 2c4bd47..e6bb5b4 100644 --- a/admin/package.json +++ b/admin/package.json @@ -11,6 +11,7 @@ "lint": "eslint --ext .js,.vue src" }, "dependencies": { + "axios": "^0.17.0", "element-ui": "^1.3.7", "font-awesome": "^4.7.0", "moment": "^2.18.1", @@ -19,7 +20,6 @@ "vue": "^2.3.3", "vue-codemirror": "^3.1.0", "vue-i18n": "^7.0.5", - "vue-resource": "^1.3.4", "vue-router": "^2.6.0" }, "devDependencies": { diff --git a/admin/src/App.vue b/admin/src/App.vue index 9119e91..cd93472 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -1,80 +1,31 @@ diff --git a/admin/src/api.js b/admin/src/api.js index 76f397c..4ca3599 100644 --- a/admin/src/api.js +++ b/admin/src/api.js @@ -1,65 +1,50 @@ import Vue from 'vue' -import VueResource from 'vue-resource' +import router from './router' +import axios from 'axios' -Vue.use(VueResource) -Vue.http.options.root = '/api' -Vue.http.options.emulateJSON = false - -function getCookie (name) { - let allCookies = document.cookie.split('; ') - for (let i = 0; i < allCookies.length; i++) { - let cookie = allCookies[i].split('=') - if (cookie[0] === name) { - return cookie[1] - } else { - return '' - } - } -} - -Vue.http.interceptors.push((request, next) => { - request.headers.set('X-CSRFToken', getCookie('csrftoken')) - next() -}) +Vue.prototype.$http = axios +axios.defaults.baseURL = '/api' +axios.defaults.xsrfHeaderName = 'X-CSRFToken' +axios.defaults.xsrfCookieName = 'csrftoken' export default { // 登录 login (username, password) { - return ajax('login', 'get', { - options: { - params: { - username, - password - } + return ajax('login', 'post', { + data: { + username, + password } }) }, + logout () { + return ajax('logout', 'get') + }, + getProfile () { + return ajax('profile', 'get') + }, // 获取公告列表 getAnnouncementList (offset, limit) { return ajax('admin/announcement', 'get', { - options: { - params: { - paging: true, - offset, - limit - } + params: { + paging: true, + offset, + limit } }) }, // 删除公告 deleteAnnouncement (id) { return ajax('admin/announcement', 'delete', { - options: { - params: { - id - } + params: { + id } }) }, // 修改公告 modifyAnnouncement (id, title, content, visible) { return ajax('admin/announcement', 'put', { - body: { + data: { id, title, content, @@ -70,7 +55,7 @@ export default { // 添加公告 createAnnouncement (title, content, visible) { return ajax('admin/announcement', 'post', { - body: { + data: { title, content, visible @@ -92,17 +77,15 @@ export default { // 获取单个用户信息 getUser (id) { return ajax('admin/user', 'get', { - options: { - params: { - id - } + params: { + id } }) }, // 编辑用户 - editUser (body) { + editUser (data) { return ajax('admin/user', 'put', { - body + data }) }, getLanguages () { @@ -111,22 +94,22 @@ export default { getSMTPConfig () { return ajax('admin/smtp', 'get') }, - createSMTPConfig (body) { + createSMTPConfig (data) { return ajax('admin/smtp', 'post', { - body + data }) }, - editSMTPConfig (body) { + editSMTPConfig (data) { return ajax('admin/smtp', 'put', { - body + data }) }, getWebsiteConfig () { return ajax('admin/website', 'get') }, - editWebsiteConfig (config) { + editWebsiteConfig (data) { return ajax('admin/website', 'post', { - body: config + data }) }, getJudgeServer () { @@ -134,30 +117,26 @@ export default { }, deleteJudgeServer (hostname) { return ajax('admin/judge_server', 'delete', { - options: { - params: { - hostname: hostname - } + params: { + hostname: hostname } }) }, - createContest (body) { + createContest (data) { return ajax('admin/contest', 'post', { - body: body + data }) }, getContest (id) { return ajax('admin/contest', 'get', { - options: { - params: { - id - } + params: { + id } }) }, - editContest (body) { + editContest (data) { return ajax('admin/contest', 'put', { - body + data }) }, getContestList (offset, limit, keyword) { @@ -166,53 +145,45 @@ export default { params.keyword = keyword } return ajax('admin/contest', 'get', { - options: { - params: params - } + params: params }) }, getContestAnnouncementList (contestId) { return ajax('admin/contest/announcement', 'get', { - options: { - params: { - contest_id: contestId - } + params: { + contest_id: contestId } }) }, - createContestAnnouncement (body) { + createContestAnnouncement (data) { return ajax('admin/contest/announcement', 'post', { - body + data }) }, deleteContestAnnouncement (id) { return ajax('admin/contest/announcement', 'delete', { - options: { - params: { - id - } + params: { + id } }) }, getProblemTagList () { return ajax('problem/tags', 'get') }, - createProblem (body) { + createProblem (data) { return ajax('admin/problem', 'post', { - body + data }) }, - editProblem (body) { + editProblem (data) { return ajax('admin/problem', 'put', { - body + data }) }, getProblem (id) { return ajax('admin/problem', 'get', { - options: { - params: { - id - } + params: { + id } }) }, @@ -222,9 +193,7 @@ export default { params.keyword = keyword } return ajax('admin/problem', 'get', { - options: { - params: params - } + params }) }, getContestProblemList (offset, limit, keyword, contestId) { @@ -233,76 +202,66 @@ export default { params.keyword = keyword } return ajax('admin/contest/problem', 'get', { - options: { - params: params - } + params }) }, getContestProblem (id) { return ajax('admin/contest/problem', 'get', { - options: { - params: { - id - } + params: { + id } }) }, - createContestProblem (body) { + createContestProblem (data) { return ajax('admin/contest/problem', 'post', { - body + data }) }, - editContestProblem (body) { + editContestProblem (data) { return ajax('admin/contest/problem', 'put', { - body + data }) } } -/** - ajax 请求 - @param url - @param type get|post|put|jsonp .... - @param options options = { - body: request body - options: .., - succCallBack: Function - errCallBack: Function - } - @return Promise - */ -function ajax (url, type, options) { - return new Promise(function (resolve, reject) { - options = options || {} - if (options.body === undefined) { - options.body = options.options - options.options = undefined - } - Vue.http[type](url, options.body, options.options).then(res => { - // 出错了 +/** + * @param url + * @param method get|post|put|delete... + * @param params like queryString. if a url is index?a=1&b=2, params = {a: '1', b: '2'} + * @param data post data, use for method put|post + * @returns {Promise} + */ +function ajax (url, method, options) { + if (options !== undefined) { + var {params = {}, data = {}} = options + } else { + params = data = {} + } + return new Promise((resolve, reject) => { + axios({ + url, + method, + params, + data + }).then(res => { + // API正常返回(status=20x), 是否错误通过有无error判断 if (res.data.error !== null) { Vue.prototype.$error(res.data.data) reject(res) - if (options.errCallBack !== undefined) { - options.errCallBack(res) + // // 若后端返回为登录,则为session失效,应退出当前登录用户 + if (res.data.data.startsWith('Please login')) { + router.push({name: 'login'}) } } else { - // 请求成功 resolve(res) - if (options.succCallBack !== undefined) { - options.succCallBack(res) - } else if (type !== 'get') { - Vue.prototype.$success() + if (method !== 'get') { + Vue.prototype.$success('Succeeded') } } }, res => { - // 请求失败 + // API请求异常,一般为Server error 或 network error reject(res) - if (options.errCallBack !== undefined) { - options.errCallBack(res) - } else { - Vue.prototype.$error('Network Error') - } + Vue.prototype.$error(res.data.data) }) }) } diff --git a/admin/src/components/SideMenu.vue b/admin/src/components/SideMenu.vue index 61448b0..800c1ed 100644 --- a/admin/src/components/SideMenu.vue +++ b/admin/src/components/SideMenu.vue @@ -42,6 +42,7 @@ export default{ width: 200px; height: 100%; position: fixed!important; + z-index: 100; top: 0; bottom: 0; left: 0; diff --git a/admin/src/main.js b/admin/src/main.js index 92458af..83cd63b 100644 --- a/admin/src/main.js +++ b/admin/src/main.js @@ -2,10 +2,10 @@ import Vue from 'vue' import App from './App' import Element from 'element-ui' import 'element-ui/lib/theme-default/index.css' -import VueRouter from 'vue-router' import locale from 'element-ui/lib/locale/lang/en' -import filters from './filters.js' +import filters from './utils/filters.js' +import router from './router' import Panel from './components/Panel.vue' import IconBtn from './components/btn/IconBtn.vue' @@ -17,98 +17,13 @@ Object.keys(filters).forEach(key => { Vue.filter(key, filters[key]) }) -Vue.use(Element, { locale }) -Vue.use(VueRouter) +Vue.use(Element, {locale}) // Vue.use(VueI18n) Vue.component(IconBtn.name, IconBtn) Vue.component(Panel.name, Panel) Vue.component(Save.name, Save) Vue.component(Cancel.name, Cancel) -// 引入 view 组件 -import { Announcement, User, Conf, JudgeServer, Problem, Contest, ContestList, - ContestAnnouncement, ProblemList } from './views' - -const router = new VueRouter({ - mode: 'history', - scrollBehavior: () => ({ y: 0 }), - routes: [ - { - path: '/announcement', - name: 'announcement', - component: Announcement - }, - { - path: '/user', - name: 'user', - component: User - }, - { - path: '/conf', - name: 'conf', - component: Conf - }, - { - path: '/judge-server', - name: 'judge-server', - component: JudgeServer - }, - { - path: '/problems', - name: 'problem-list', - component: ProblemList - }, - { - path: '/problem/create', - name: 'create-problem', - component: Problem - }, - { - path: '/problem/edit/:problemId', - name: 'edit-problem', - component: Problem - }, - { - path: '/contest/create', - name: 'create-contest', - component: Contest - }, - { - path: '/contest', - name: 'contest-list', - component: ContestList - }, - { - path: '/contest/:contestId/edit', - name: 'edit-contest', - component: Contest - }, - { - path: '/contest/:contestId/announcement', - name: 'contest-announcement', - component: ContestAnnouncement - }, - { - path: '/contest/:contestId/problems', - name: 'contest-problem-list', - component: ProblemList - }, - { - path: '/contest/:contestId/problem/create', - name: 'create-contest-problem', - component: Problem - }, - { - path: '/contest/:contestId/problem/:problemId/edit', - name: 'edit-contest-problem', - component: Problem - }, - { - path: '*', redirect: '/announcement' - } - ] -}) - Vue.prototype.$error = (msg) => { Vue.prototype.$message({'message': msg, 'type': 'error'}) } @@ -125,4 +40,4 @@ Vue.prototype.$success = (msg) => { } } -new Vue(Vue.util.extend({ router }, App)).$mount('#app') +new Vue(Vue.util.extend({router}, App)).$mount('#app') diff --git a/admin/src/router.js b/admin/src/router.js new file mode 100644 index 0000000..01c3c02 --- /dev/null +++ b/admin/src/router.js @@ -0,0 +1,113 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' +// 引入 view 组件 +import { + Announcement, + Conf, + Contest, + ContestAnnouncement, + ContestList, + Home, + JudgeServer, + Login, + Problem, + ProblemList, + User +} from './views' + +Vue.use(VueRouter) + +export default new VueRouter({ + mode: 'history', + base: '/admin/', + scrollBehavior: () => ({y: 0}), + routes: [ + { + path: '/login', + name: 'login', + component: Login + }, + { + path: '/', + component: Home, + children: [ + { + path: '', + component: Announcement + }, + { + path: '/announcement', + name: 'announcement', + component: Announcement + }, + { + path: '/user', + name: 'user', + component: User + }, + { + path: '/conf', + name: 'conf', + component: Conf + }, + { + path: '/judge-server', + name: 'judge-server', + component: JudgeServer + }, + { + path: '/problems', + name: 'problem-list', + component: ProblemList + }, + { + path: '/problem/create', + name: 'create-problem', + component: Problem + }, + { + path: '/problem/edit/:problemId', + name: 'edit-problem', + component: Problem + }, + { + path: '/contest/create', + name: 'create-contest', + component: Contest + }, + { + path: '/contest', + name: 'contest-list', + component: ContestList + }, + { + path: '/contest/:contestId/edit', + name: 'edit-contest', + component: Contest + }, + { + path: '/contest/:contestId/announcement', + name: 'contest-announcement', + component: ContestAnnouncement + }, + { + path: '/contest/:contestId/problems', + name: 'contest-problem-list', + component: ProblemList + }, + { + path: '/contest/:contestId/problem/create', + name: 'create-contest-problem', + component: Problem + }, + { + path: '/contest/:contestId/problem/:problemId/edit', + name: 'edit-contest-problem', + component: Problem + }, + { + path: '*', redirect: '/problem-list' + } + ] + }] +}) diff --git a/admin/src/utils.js b/admin/src/utils.js deleted file mode 100644 index e69de29..0000000 diff --git a/admin/src/utils/constants.js b/admin/src/utils/constants.js new file mode 100644 index 0000000..7dabab2 --- /dev/null +++ b/admin/src/utils/constants.js @@ -0,0 +1,5 @@ +export const CONTEST_STATUS_REVERSE = { + '-1': 'Ended', + '0': 'Underway', + '1': 'Not Started' +} diff --git a/admin/src/filters.js b/admin/src/utils/filters.js similarity index 100% rename from admin/src/filters.js rename to admin/src/utils/filters.js diff --git a/admin/src/views/Home.vue b/admin/src/views/Home.vue new file mode 100644 index 0000000..3fea09d --- /dev/null +++ b/admin/src/views/Home.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/admin/src/views/contest/ContestList.vue b/admin/src/views/contest/ContestList.vue index ec4d2fb..befed17 100644 --- a/admin/src/views/contest/ContestList.vue +++ b/admin/src/views/contest/ContestList.vue @@ -15,7 +15,7 @@ :data="contestList" style="width: 100%"> -