diff --git a/static/src/css/tagEditor/jquery.tag-editor.css b/static/src/css/tagEditor/jquery.tag-editor.css new file mode 100644 index 0000000..a8516b5 --- /dev/null +++ b/static/src/css/tagEditor/jquery.tag-editor.css @@ -0,0 +1,45 @@ +/* surrounding tag container */ +.tag-editor { + list-style-type: none; padding: 0 5px 0 0; margin: 0; overflow: hidden; border: 1px solid #eee; cursor: text; + font: normal 14px sans-serif; color: #555; background: #fff; +} + +/* core styles usually need no change */ +.tag-editor li { display: block; float: left; overflow: hidden; margin: 3px 0; line-height: 1.5; } +.tag-editor div { float: left; padding: 0 4px; } +.tag-editor .placeholder { padding: 0 8px; color: #bbb; } +.tag-editor .tag-editor-spacer { padding: 0; width: 8px; overflow: hidden; color: transparent; background: none; } +.tag-editor input { + vertical-align: inherit; border: 0; outline: none; padding: 0; margin: 0; cursor: text; + font-family: inherit; font-weight: inherit; font-size: inherit; font-style: inherit; + box-shadow: none; background: none; +} + +/* tag style */ +.tag-editor .tag-editor-tag { + padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap; + overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px; +} + +/* delete icon */ +.tag-editor .tag-editor-delete { background: #e0eaf1; cursor: pointer; padding-right: 5px; border-radius: 0 2px 2px 0; } +.tag-editor .tag-editor-delete i { + display: inline-block; width: 7px; height: 7px; vertical-align: middle; background: url(/static/img/delete.png) 0 0 no-repeat; + position: relative; top: -1px; +} +.tag-editor .tag-editor-delete:hover i { background-position: 0 -14px; } +.tag-editor .tag-editor-tag.active+.tag-editor-delete, +.tag-editor .tag-editor-tag.active+.tag-editor-delete i { background: none; cursor: text; } + +.tag-editor .tag-editor-tag.active { background: none !important; } + +/* jQuery UI autocomplete - code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css */ +.ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; font-size: 14px; } +.ui-front { z-index: 9999; } +.ui-menu { list-style: none; padding: 1px; margin: 0; display: block; outline: none; } +.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.4; min-height: 0; /* support: IE7 */ } +.ui-widget-content { border: 1px solid #bbb; background: #fff; color: #555; } +.ui-widget-content a { color: #46799b; } +.ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { background: #e0eaf1; } +.ui-helper-hidden-accessible { display: none; } diff --git a/static/src/img/delete.png b/static/src/img/delete.png new file mode 100644 index 0000000..492c9f4 Binary files /dev/null and b/static/src/img/delete.png differ diff --git a/static/src/img/delete.svg b/static/src/img/delete.svg new file mode 100644 index 0000000..f0865cc --- /dev/null +++ b/static/src/img/delete.svg @@ -0,0 +1,4 @@ + + + diff --git a/static/src/js/app/admin/contest/contest.js b/static/src/js/app/admin/contest/contest.js index e9b44ba..3edfb6d 100644 --- a/static/src/js/app/admin/contest/contest.js +++ b/static/src/js/app/admin/contest/contest.js @@ -1,6 +1,5 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", - "validation" - ], + "validation","tagEditor"], function ($, avalon, editor, uploader) { avalon.vmodels.add_contest = null; $("#add-contest-form") @@ -19,7 +18,7 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", } } }, - description:{ + description: { validators: { notEmpty: { message: "请输入描述" @@ -100,13 +99,20 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", }) .on("success.form.fv", function (e) { e.preventDefault(); - var data = {title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime, - password: vm.password, model: vm.model, open_rank: vm.openRank, problems:[]}; + var data = { + title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime, + password: vm.password, model: vm.model, open_rank: vm.openRank, problems: [] + }; for (var i = 0; i < vm.problems.length; i++) { - var problem = {title: vm.problems[i].title, description:vm.problems[i].description, - cpu:vm.problems[i].cpu, memory:vm.problems[i].memory,samples:[]}; + var problem = { + title: vm.problems[i].title, description: vm.problems[i].description, + cpu: vm.problems[i].cpu, memory: vm.problems[i].memory, samples: [] + }; for (var j = 0; j < vm.problems[i].samples.length; j++) { - problem.samples.push({input:vm.problems[i].samples[j].input, output:vm.problems[i].samples[j].output}) + problem.samples.push({ + input: vm.problems[i].samples[j].input, + output: vm.problems[i].samples[j].output + }) } data.problems.push(problem); } @@ -119,11 +125,13 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } + + var editor1 = editor("#editor"); var vm = avalon.define({ $id: "add_contest", - title : "", + title: "", description: "", startTime: "", endTime: "", @@ -133,10 +141,43 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", problems: [], add_problem: function () { var problem_id = make_id(); - var problem={id: problem_id, title: "", cpu: "", memory: "", description: "",samples: [], webuploader: {}, visible: true}; + var problem = { + id: problem_id, + title: "", + cpu: "", + memory: "", + description: "", + samples: [], + visible: true, + test_case_id: "", + testCaseList: [], + hint: "", + isVisible: false, + difficulty: 0, + tags: [], + tag: "" + }; vm.problems.push(problem); - uploader("#problem-" + problem_id + "-uploader",""); - editor("#problem-" + problem_id + "-description") + var id = vm.problems.length - 1; + editor("#problem-" + problem_id + "-description"); + var hinteditor = editor("#problem-" + problem_id +"-hint"); + $("#problem-" + problem_id +"-tags").tagEditor(); + uploader("#problem-" + problem_id + "-uploader", "/api/admin/test_case_upload/", function (file, respond) { + console.log(respond); + if (respond.code) + bs_alert(respond.data); + else { + vm.problems[id].test_case_id = respond.data.test_case_id; + vm.problems[id].uploadSuccess = true; + vm.problems[id].testCaseList = []; + for (var i = 0; i < respond.data.file_list.input.length; i++) { + vm.problems[id].push({ + input: respond.data.file_list.input[i], + output: respond.data.file_list.output[i] + }); + } + } + }); $("#add-contest-form").formValidation('addField', $('[name="problem_name[]"]')); $("#add-contest-form").formValidation('addField', $('[name="cpu[]"]')); $("#add-contest-form").formValidation('addField', $('[name="memory[]"]')); diff --git a/static/src/js/config.js b/static/src/js/config.js index 2f730b4..f852cc8 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -17,6 +17,7 @@ var require = { csrf: "utils/csrf", admin: "app/admin/admin", chart: "lib/chart/Chart", + tagEditor: "lib/tagEditor/jquery.tag-editor.min", //formValidation 不要在代码中单独使用,而是使用和修改utils/validation base: "lib/formValidation/base", diff --git a/static/src/js/lib/tagEditor/jquery.tag-editor.min.js b/static/src/js/lib/tagEditor/jquery.tag-editor.min.js new file mode 100644 index 0000000..911e304 --- /dev/null +++ b/static/src/js/lib/tagEditor/jquery.tag-editor.min.js @@ -0,0 +1,5 @@ +// jQuery tagEditor v1.0.3 +// https://github.com/Pixabay/jQuery-tagEditor +define("tagEditor", ["jquery"], function($){ +!function(t){t.fn.autoGrowInput=function(e){return e=t.extend({maxWidth:250,minWidth:20,comfortZone:0},e),this.filter("input:text").each(function(){var i=(e.minWidth||t(this).width()," "),a=t(this),n=e.comfortZone?e.comfortZone:parseInt(.9*parseInt(t(this).css("fontSize"))),r=t("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:a.css("fontSize"),fontFamily:a.css("fontFamily"),fontWeight:a.css("fontWeight"),letterSpacing:a.css("letterSpacing"),whiteSpace:"nowrap"}),o=function(){if(i!==(i=a.val())){r.html(i.replace(/&/g,"&").replace(/\s/g," ").replace(//g,">"));var t=r.width()+n;t>e.maxWidth&&(t=e.maxWidth),t
 '+r.delimiter[0]+'
').appendTo(o).find(".tag-editor-tag").html('').addClass("active").find("input").val(a).blur(),"blur"!=n?o.click():t(".placeholder",o).remove()):"removeTag"==e?(t(".tag-editor-tag",o).filter(function(){return t(this).html()==a}).closest("li").find(".tag-editor-delete").click(),"blur"!=n&&o.click()):"destroy"==e&&i.css("display",r.elDisplay).removeData("options").next(".tag-editor").remove()}),s}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",r),c.each(function(){function e(){!l.placeholder||c.length||t(".deleted, .placeholder, input",s).length||s.append('
  • '+l.placeholder+"
  • ")}function a(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(l.delimiter[0])),i||a!=c.toString()&&l.onChange(r,s,c),e()}function n(e){var n=e.closest("li"),o=e.val().replace(/ +/," ").split(l.dregex),d=e.data("old_tag"),g=c.slice(0);for(i in o)u=t.trim(o[i]).slice(0,l.maxLength),u&&(l.forceLowercase&&(u=u.toLowerCase()),l.beforeTagSave(r,s,g,d,u),~t.inArray(u,g)&&t(".tag-editor-tag",s).each(function(){t(this).html()==u&&t(this).closest("li").remove()}),g.push(u),n.before('
  •  '+l.delimiter[0]+'
    '+u+'
  • '));e.attr("maxlength",l.maxLength).removeData("old_tag").val("").focus(),a()}var r=t(this),c=[];l.elDisplay=r.css("display"),r.css("display","none");var s=t("
      ').insertAfter(r);r.data("options",l),s.append('
    •  
    • ');var d='
    •  '+l.delimiter[0]+'
    • ';s.click(function(e){if(!window.getSelection||""==getSelection()){if(o=!0,t("input:focus",s).blur(),!o)return!1;o=!0,t(".placeholder",s).remove();var i,a,n,r=99999;return t(".tag-editor-tag",s).each(function(){var o=t(this),l=o.offset(),c=l.left,s=l.top;e.pageY>=s&&e.pageY<=s+o.height()&&(e.pageXi&&(r=i,a=o))}),"before"==n?t(d).insertBefore(a.closest("li")).find(".tag-editor-tag").click():"after"==n?t(d).insertAfter(a.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1}}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var i=t(this).closest("li"),n=i.find(".tag-editor-tag");return l.beforeTagDelete(r,s,c,n.html())===!1?!1:(n.addClass("deleted").animate({width:0},175,function(){i.remove(),e()}),a(),!1)}),l.clickDelete&&s.on("mousedown",".tag-editor-tag",function(i){if(i.ctrlKey||i.which>1){var n=t(this).closest("li"),o=n.find(".tag-editor-tag");return l.beforeTagDelete(r,s,c,o.html())===!1?!1:(o.addClass("deleted").animate({width:0},175,function(){n.remove(),e()}),a(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(l.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).html(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),n=parseInt(i.length*a),r=t(this).html('').addClass("active").find("input");if(r.data("old_tag",i).focus().autoGrowInput().trigger("autogrow").caret(n),l.autocomplete){var o=l.autocomplete,c="select"in o?l.autocomplete.select:"";o.select=function(){c&&c(),setTimeout(function(){t(".active",s).find("input").trigger("autogrow")},20)},r.autocomplete(o)}}return!1}),s.on("blur","input",function(){var i=t(this),d=i.data("old_tag"),g=t.trim(i.val().replace(/ +/," ").replace(l.dregex,l.delimiter[0]));if(g){if(g.indexOf(l.delimiter[0])>=0)return void n(i);g!=d&&(l.forceLowercase&&(g=g.toLowerCase()),l.beforeTagSave(r,s,c,d,g),t(".tag-editor-tag:not(.active)",s).each(function(){t(this).html()==g&&t(this).closest("li").remove()}))}else{if(d&&l.beforeTagDelete(r,s,c,d)===!1)return i.val(d).trigger("autogrow").focus(),o=!1,void a();try{i.closest("li").remove()}catch(f){}d&&a()}i.parent().html(g).removeClass("active"),g!=d&&a(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){n(g)},30)});var f;s.on("keypress","input",function(e){l.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){n(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!l.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):i.val()&&t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!l.autocomplete&&40==e.which)&&i.caret()==i.val().length){var n=i.closest("li").next("li").find(".tag-editor-tag");return n.length?n.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");a.length?a.click().find("input").caret(0):i.val()&&t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}else{var n=i.closest("li").next("li").find(".tag-editor-tag");n.length?n.click().find("input").caret(0):i.val()&&s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var n=i.closest("li").next("li").find(".tag-editor-tag");return n.length?n.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which){var n=i.closest("li").next("li").find(".tag-editor-tag");return n.length?n.click().find("input").caret(0):i.val()&&s.click(),!1}if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});var h=l.initialTags.length?l.initialTags:r.val().split(l.dregex);for(i in h){var u=t.trim(h[i].replace(/ +/," "));u&&(l.forceLowercase&&(u=u.toLowerCase()),c.push(u),s.append('
    •  '+l.delimiter[0]+'
      '+u+'
    • '))}a(!0),l.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){a()}})})},t.fn.tagEditor.defaults={initialTags:[],maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,clickDelete:!1,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery); +}); diff --git a/template/admin/contest/add_contest.html b/template/admin/contest/add_contest.html index 6349814..4a2e37e 100644 --- a/template/admin/contest/add_contest.html +++ b/template/admin/contest/add_contest.html @@ -101,26 +101,40 @@ ms-duplex="problem.title"> -
      - -
      -
      - -
      -
      -
      - -
      -
      -
      -
      - -
      -
      请填写题目描述 +
      + + +
      + +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      + +
      @@ -156,7 +170,8 @@
      - +
      @@ -164,7 +179,22 @@
      - +
      + 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
      + 1.in 1.out 2.in 2.out +
      + + + + + + + + + + + +
      编号输入文件名输出文件名
      {{$index}}{{el.input}}{{el.output}}
      @@ -184,4 +214,5 @@
      - \ No newline at end of file + + \ No newline at end of file diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html index 0c6dad0..6ebb49a 100644 --- a/template/admin/problem/add_problem.html +++ b/template/admin/problem/add_problem.html @@ -12,7 +12,7 @@
      - +