Browse Source

feat: 后台静态页面完善,初步完成新增分类接口并接入页面中。

pull/6/head
zjz1993 5 years ago
parent
commit
31ef8e1208
  1. 60
      models-built/classify.js
  2. 1
      models-built/classify.js.map
  3. 9
      models-built/common.js
  4. 2
      models-built/common.js.map
  5. 7
      models-built/problem.js
  6. 2
      models-built/problem.js.map
  7. 26
      models/classify.ts
  8. 31
      modules/practice.js
  9. 85
      views/admin_classify.ejs
  10. 2
      views/admin_config.ejs
  11. 2
      views/admin_header.ejs
  12. 42
      views/header.ejs
  13. 214
      views/practice.ejs

60
models-built/classify.js

@ -0,0 +1,60 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
exports.__esModule = true;
var TypeORM = require("typeorm");
var common_1 = require("./common");
var Classify = /** @class */ (function (_super) {
__extends(Classify, _super);
function Classify() {
return _super !== null && _super.apply(this, arguments) || this;
}
Classify.cache = true;
__decorate([
TypeORM.PrimaryGeneratedColumn(),
__metadata("design:type", Number)
], Classify.prototype, "id");
__decorate([
TypeORM.Index({ unique: true }),
TypeORM.Column({ unique: true, nullable: true, type: "varchar", length: 20 }),
__metadata("design:type", String)
], Classify.prototype, "name");
__decorate([
TypeORM.Column({ nullable: true, type: "varchar", length: 100 }),
__metadata("design:type", String)
], Classify.prototype, "intro");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], Classify.prototype, "update_time");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], Classify.prototype, "create_time");
Classify = __decorate([
TypeORM.Entity()
], Classify);
return Classify;
}(common_1["default"]));
exports["default"] = Classify;
//# sourceMappingURL=classify.js.map

1
models-built/classify.js.map

@ -0,0 +1 @@
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../models/classify.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,iCAAmC;AACnC,mCAA6B;AAM7B;IAAsC,4BAAK;IAA3C;;IAkBA,CAAC;IAjBU,cAAK,GAAG,IAAI,CAAC;IAGpB;QADC,OAAO,CAAC,sBAAsB,EAAE;;gCACtB;IAIX;QAFC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;kCACjE;IAGb;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;mCACnD;IAGd;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;yCAChC;IAGpB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;yCAChC;IAjBH,QAAQ;QAD5B,OAAO,CAAC,MAAM,EAAE;OACI,QAAQ,CAkB5B;IAAD,eAAC;CAAA,AAlBD,CAAsC,mBAAK,GAkB1C;qBAlBoB,QAAQ"}

9
models-built/common.js

@ -178,18 +178,17 @@ var Model = /** @class */ (function (_super) {
};
Model.countQuery = function (query) {
return __awaiter(this, void 0, void 0, function () {
var parameters, _a;
var _b;
var _a, parameters, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
parameters = null;
if (typeof query !== 'string') {
_b = query.getQueryAndParameters(), query = _b[0], parameters = _b[1];
_a = query.getQueryAndParameters(), query = _a[0], parameters = _a[1];
}
_a = parseInt;
_b = parseInt;
return [4 /*yield*/, TypeORM.getManager().query("SELECT COUNT(*) FROM (" + query + ") AS `__tmp_table`", parameters)];
case 1: return [2 /*return*/, _a.apply(void 0, [(_c.sent())[0]['COUNT(*)']])];
case 1: return [2 /*return*/, _b.apply(void 0, [(_c.sent())[0]['COUNT(*)']])];
}
});
});

2
models-built/common.js.map

File diff suppressed because one or more lines are too long

7
models-built/problem.js

@ -566,18 +566,17 @@ var Problem = /** @class */ (function (_super) {
if (this.type === ProblemType.SubmitAnswer && statisticsCodeOnly.includes(type))
return [2 /*return*/];
return [4 /*yield*/, syzoj.utils.lock(['Problem::UpdateStatistics', this.id, type], function () { return __awaiter(_this, void 0, void 0, function () {
var _a, column, order, result, resultRow, toDelete, baseColumns, record;
var _b;
var _a, _b, column, order, result, resultRow, toDelete, baseColumns, record;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = statisticsTypes[type], column = _a[0], order = _a[1];
_b = statisticsTypes[type], column = _b[0], order = _b[1];
return [4 /*yield*/, judge_state_1["default"].createQueryBuilder()
.select([column, "id"])
.where("user_id = :user_id", { user_id: user_id })
.andWhere("status = :status", { status: "Accepted" })
.andWhere("problem_id = :problem_id", { problem_id: this.id })
.orderBy((_b = {}, _b[column] = order, _b))
.orderBy((_a = {}, _a[column] = order, _a))
.take(1)
.getRawMany()];
case 1:

2
models-built/problem.js.map

File diff suppressed because one or more lines are too long

26
models/classify.ts

@ -0,0 +1,26 @@
import * as TypeORM from "typeorm";
import Model from "./common";
import JudgeState from "./judge_state";
import Article from "./article";
import UserPrivilege from "./user_privilege";
@TypeORM.Entity()
export default class Classify extends Model {
static cache = true;
@TypeORM.PrimaryGeneratedColumn()
id: number;
@TypeORM.Index({ unique: true })
@TypeORM.Column({ unique: true, nullable: true, type: "varchar", length: 20 })
name: string;
@TypeORM.Column({ nullable: true, type: "varchar", length: 100 })
intro: string;
@TypeORM.Column({ nullable: true, type: "integer" })
update_time: number;
@TypeORM.Column({ nullable: true, type: "integer" })
create_time: number;
}

31
modules/practice.js

@ -0,0 +1,31 @@
let Classify = syzoj.model('classify');
app.get('/practice', async (req, res) => {
try {
res.render('practice', {
})
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
});
}
});
app.post('/api/practice/create', async (req, res) => {
try {
const {name, intro} = req.body;
let classify = await Classify.create({
name,
intro,
updateTime: parseInt((new Date()).getTime() / 1000),
createTime: parseInt((new Date()).getTime() / 1000)
});
await classify.save();
res.send();
} catch(e) {
res.send({ error_code: e.errno, error_msg: '创建失败' });
}
});

85
views/admin_classify.ejs

@ -9,12 +9,43 @@
}
</style>
<div id="classify" v-cloak>
<div class="ui modal classify">
<i class="close icon"></i>
<div class="header">
新增练习阶段
</div>
<div class="content">
<div class="ui form">
<div class="field required" :class="{ error: classifyName.length > 20 }">
<label>练习阶段名称(20字以内)</label>
<input type="text" v-model="classifyName" placeholder="请输入分类名称">
</div>
<div class="field required" :class="{ error: classifyName.length > 100 }">
<label>练习阶段简介(100字以内)</label>
<input type="text" v-model="classifyIntro" placeholder="请输入分类简介">
</div>
<div class="field">
<label>Gender</label>
<div class="ui selection dropdown">
<input type="hidden" name="gender">
<i class="dropdown icon"></i>
<div class="default text">Gender</div>
<div class="menu">
<div class="item" data-value="1">Male</div>
<div class="item" data-value="0">Female</div>
</div>
</div>
</div>
<button class="ui button" @click="submitInfo">提交</button>
</div>
</div>
</div>
<div v-if="mockClassify.length === 0">
暂无分类
暂无练习阶段
</div>
<div v-else>
<div class="current-classify">
<h4>目前分类</h4>
<h4>目前练习阶段</h4>
<div class="ui cards">
<div class="card" v-for="item in mockClassify">
<div class="content">
@ -25,15 +56,15 @@
</div>
<div class="ui bottom attached button">
<i class="add icon"></i>
编辑分类信息
编辑练习阶段信息
</div>
</div>
</div>
</div>
</div>
<div class="ui labeled button" tabindex="0">
<div class="ui labeled button" tabindex="0" @click="show">
<div class="ui button">
<i class="plus icon"></i> 新增分类
<i class="plus icon"></i> 新增练习阶段
</div>
</div>
</div>
@ -41,15 +72,57 @@
new window.Vue({
el: '#classify',
data: {
classifyName: '',
classifyIntro: '',
message: 'Hello Vue!',
mockClassify: [{
title: '测试分类',
title: '测试分类a',
description: '测试介绍'
}, {
title: '测试分类1',
description: '测试介绍1'
}]
},
methods: {
show: function(){
$('.ui.modal.classify')
.modal('show')
},
checkStringLength: function(string, maxLen) {
return string.length > 0 && string.length <= maxLen;
},
submitInfo: function() {
let name = this.classifyName;
let intro = this.classifyIntro;
if(this.checkStringLength(name, 20) && this.checkStringLength(intro, 100)) {
const obj = {
name: this.classifyName,
intro: this.classifyIntro
}
$.ajax({
url: '/api/practice/create',
type: 'POST',
async: true,
data: obj,
success: function (data) {
console.log(data);
if (data.error_code) {
alert('创建失败');
} else {
alert('添加成功')
$('.ui.modal.classify')
.modal('hide')
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(XMLHttpRequest);
alert('创建失败');
}
});
}
}
}
})
</script>
<% include admin_footer %>

2
views/admin_config.ejs

@ -34,7 +34,7 @@ function showBooleanItem(name, text, val) {
</div>
<%
}
console.log(items);
for (let item in items) {
if (items[item] === null) {
showTitle(item);

2
views/admin_header.ejs

@ -2,7 +2,7 @@
let items = {
info: '统计信息',
config: '系统配置',
classify: '分类管理',
classify: '练习阶段管理',
privilege: '权限管理',
rejudge: '一键重测',
links: '友链管理',

42
views/header.ejs

@ -61,7 +61,7 @@
<% } %>
<a class="item<% if (active === '') { %> active<% } %>" href="/"><i class="home icon"></i> 首页</a>
<a class="item<% if (active.startsWith('problem')) { %> active<% } %>" href="/problems"><i class="list icon"></i> 题库</a>
<a class="item<% if (active.startsWith('contest')) { %> active<% } %>" href="/contests"><i class="pencil icon"></i> 练习</a>
<a class="item<% if (active.startsWith('contest')) { %> active<% } %>" href="/practice"><i class="pencil icon"></i> 练习</a>
<a class="item<% if (active.startsWith('contest')) { %> active<% } %>" href="/contests"><i class="calendar icon"></i> 比赛</a>
<a class="item<% if (active.startsWith('submission')) { %> active<% } %>" href="/submissions"><i class="tasks icon"></i> 评测</a>
<a class="item<% if (active.startsWith('ranklist')) { %> active<% } %>" href="/ranklist"><i class="signal icon"></i> 排名</a>
@ -75,29 +75,29 @@
<a href="<%= syzoj.utils.makeUrl(['user', user.id]) %>" style="color: inherit; ">
<div class="ui simple dropdown item">
<%= user.username %><% if (user.nameplate) { %><%- user.nameplate %><% } %> <i class="dropdown icon"></i>
<div class="menu">
<div class="menu"><div class="ui basic modal" id="modal-restart">
<div class="ui icon header">
<i class="redo icon"></i>
<p style="margin-top: 15px; ">重启服务</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认重新启动 SYZOJ Web 服务吗?</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" href-post="<%= syzoj.utils.makeUrl(['admin', 'restart'], { url: req.query['url'] || req.originalUrl }) %>">
<i class="checkmark icon"></i>
</a>
</div>
</div>
<a class="item" href="<%= syzoj.utils.makeUrl(['user', user.id, 'edit']) %>"><i class="edit icon"></i>修改资料</a>
<% if (user.is_admin) { %>
<a class="item" href="<%= syzoj.utils.makeUrl(['admin', 'info']) %>"><i class="settings icon"></i>后台管理</a>
<div class="ui basic modal" id="modal-restart">
<div class="ui icon header">
<i class="redo icon"></i>
<p style="margin-top: 15px; ">重启服务</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认重新启动 SYZOJ Web 服务吗?</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" href-post="<%= syzoj.utils.makeUrl(['admin', 'restart'], { url: req.query['url'] || req.originalUrl }) %>">
<i class="checkmark icon"></i>
</a>
</div>
</div>
<a class="item" href="#" onclick="$('#modal-restart').modal('show')"><i class="redo icon"></i>重启服务</a>
<% } %>
<a class="item" href-post="<%= syzoj.utils.makeUrl(['logout'], { url: req.originalUrl }) %>"><i class="power icon"></i>注销</a>

214
views/practice.ejs

@ -0,0 +1,214 @@
<% this.title = '练习' %>
<style>
.practice{
display: flex;
width: 100%;
}
.practice .practice-main{
width: 80%;
margin-right: 30px;
}
.practice .practice-main .practice-main-navigation{
margin-bottom: 10px;
background-color: #efefef;
padding: 10px;
}
.practice .practice-main .practice-main-navigation .classify-card{
width: 90px;
height: 90px;
display: inline-block;
margin: 5px;
vertical-align: top;
cursor: pointer;
background-color: red;
}
.practice .practice-main .practice-main-navigation .classify-card small{
font-size: 12px;
color: #fff;
display: block;
}
.practice .practice-main .practice-main-navigation .classify-card .classify-name{
text-align: center;
line-height: 50px;
color: white;
}
.practice .practice-main .practice-main-body{
background-color: #efefef;
padding: 10px;
}
.practice .practice-main .practice-main-body .classify-card{
margin-bottom: 20px;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-title{
margin-bottom: 10px;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-description{
border-bottom:1px solid #e4e4e4;
margin-bottom: 10px;
padding-bottom: 5px;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-problem{
border-top:1px solid #e4e4e4;
padding-top: 10px;
display: flex;
flex-wrap: wrap;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-problem .problem{
border-radius: 5px;
width: 30%;
background-color: white;
margin: 10px;
height: 100px;
overflow: hidden;
cursor: pointer;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-problem .problem .problem-title{
text-align: center;
padding: 5px 0;
color: #14a6ef;
background-color: rgba(59,180,242,.15);
border-color: #caebfb;
}
.practice .practice-main .practice-main-body .classify-card .classify-card-problem .problem .problem-info{
font-size: 13px;
padding: 5px;
}
.practice .practice-right{
width: 300px;
padding: 30px 10px;
height:200px;
background-color: #efefef;
}
.practice .practice-right .username{
color:#3590D2;
}
</style>
<% include header %>
<div class="practice">
<div class="practice-main">
<div class="practice-main-navigation">
<div class="classify-card">
<small>#1</small>
<div class="classify-name">分类名字</div>
</div>
<div class="classify-card">
<small>#1</small>
<div class="classify-name">分类名字</div>
</div>
</div>
<div class="practice-main-body">
<div class="classify-card">
<div class="classify-card-title">
<h2>测试分类</h2>
</div>
<div class="classify-card-description">
测试分类的介绍
</div>
<div class="ui indicating progress" data-value="124" data-total="200">
<div class="bar">
<div class="progress"></div>
</div>
<div class="label">当前分类完成进度</div>
</div>
<div class="classify-card-problem">
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介发送到发送到范德萨发士大夫撒打发斯蒂芬
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
</div>
</div>
<div class="classify-card">
<div class="classify-card-title">
<h2>测试分类</h2>
</div>
<div class="classify-card-description">
测试分类的介绍
</div>
<div class="ui indicating progress" data-value="1" data-total="200">
<div class="bar">
<div class="progress"></div>
</div>
<div class="label">当前分类完成进度</div>
</div>
<div class="classify-card-problem">
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介发送到发送到范德萨发士大夫撒打发斯蒂芬
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
<div class="problem">
<div class="problem-title">
第一个问题
</div>
<div class="problem-info">
题目简介简介题目简介简介题目简介简介题目简介简介题目简介简介
</div>
</div>
</div>
</div>
</div>
</div>
<div class="practice-right">
<h1 class="username">用户名</h1>
<p>已通过数/分类总题数:5/20</p>
</div>
</div>
<script>
function getColor() {
let colorArray =["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];
let color="#";
for(let i=0;i<6;i++) {
color+=colorArray[Math.floor(Math.random()*16)];
}
return color;
}
</script>
<% include footer %>
Loading…
Cancel
Save