mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: file upload support
This commit is contained in:
1 parent
2539dfa412
commit
595f28b1df
5 files changed
+64
-22
No files matched your search
@@ -389,10 +389,11 @@ export class ProblemFilesHandler extends ProblemDetailHandler {
|
|||||||
this.response.body.links = links;
|
this.response.body.links = links;
|
||||||
}
|
}
|
||||||
|
|
||||||
@post('filename', Types.Name)
|
@post('filename', Types.Name, true)
|
||||||
@post('type', Types.Content, true)
|
@post('type', Types.Range(['testdata', 'additional_file']), true)
|
||||||
async postUploadFile(domainId: string, filename: string, type = 'testdata') {
|
async postUploadFile(domainId: string, filename: string, type = 'testdata') {
|
||||||
if (!this.request.files.file) throw new ValidationError('file');
|
if (!this.request.files.file) throw new ValidationError('file');
|
||||||
|
if (!filename) filename = this.request.files.file.name || String.random(16);
|
||||||
if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
||||||
if (filename.endsWith('.zip')) {
|
if (filename.endsWith('.zip')) {
|
||||||
const zip = new AdmZip(this.request.files.file.path);
|
const zip = new AdmZip(this.request.files.file.path);
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export async function parseConfig(config: string | ProblemConfig = {}) {
|
|||||||
let cfg: ProblemConfig = {};
|
let cfg: ProblemConfig = {};
|
||||||
if (typeof config === 'string' && config.length) {
|
if (typeof config === 'string' && config.length) {
|
||||||
// TODO should validate here?
|
// TODO should validate here?
|
||||||
cfg = await readYamlCases(load(config) as Record<string, any>) as ProblemConfig;
|
cfg = await readYamlCases(load(config) as Record<string, any>);
|
||||||
} else if (typeof config === 'object') cfg = config;
|
} else if (typeof config === 'object') cfg = await readYamlCases(config);
|
||||||
if (cfg.subtasks.length) {
|
if (cfg.subtasks.length) {
|
||||||
for (const subtask of cfg.subtasks) {
|
for (const subtask of cfg.subtasks) {
|
||||||
result.memoryMax = Math.max(result.memoryMax, subtask.memory);
|
result.memoryMax = Math.max(result.memoryMax, subtask.memory);
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ export const config = {
|
|||||||
toolbar: [
|
toolbar: [
|
||||||
'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|',
|
'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|',
|
||||||
'list', 'ordered-list', 'check', 'outdent', 'indent', '|',
|
'list', 'ordered-list', 'check', 'outdent', 'indent', '|',
|
||||||
'quote', 'line', 'code', 'inline-code', '|',
|
'quote', 'line', 'code', 'inline-code', 'table', '|',
|
||||||
'upload', 'record', 'table', '|',
|
'upload', 'record', '|',
|
||||||
'edit-mode', 'content-theme', 'code-theme', 'export', 'preview',
|
'edit-mode', 'content-theme', 'code-theme', 'export', 'preview',
|
||||||
],
|
],
|
||||||
mode: 'ir',
|
mode: 'ir',
|
||||||
@@ -38,20 +38,11 @@ export default class CmEditor extends DOMAttachedObject {
|
|||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
this.editor = new Vditor(ele, {
|
this.editor = new Vditor(ele, {
|
||||||
...config,
|
...config,
|
||||||
|
...this.options,
|
||||||
after: resolve,
|
after: resolve,
|
||||||
input(v) { $dom.text(v); },
|
input(v) { $dom.text(v); },
|
||||||
value,
|
value,
|
||||||
cache: { id: Math.random().toString() },
|
cache: { id: Math.random().toString() },
|
||||||
upload: {
|
|
||||||
accept: 'image/*,.mp3, .wav, .zip',
|
|
||||||
url: '/api/upload/editor',
|
|
||||||
linkToImgUrl: '/api/upload/fetch',
|
|
||||||
filename(name) {
|
|
||||||
return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '')
|
|
||||||
.replace(/[?\\/:|<>*[\]()$%{}@~]/g, '')
|
|
||||||
.replace('/\\s/g', '');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(ele).addClass('textbox');
|
$(ele).addClass('textbox');
|
||||||
@@ -71,10 +62,6 @@ export default class CmEditor extends DOMAttachedObject {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
destory() {
|
|
||||||
this.editor.destory();
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this.ensureValid();
|
this.ensureValid();
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import tpl from 'vj/utils/tpl';
|
|||||||
import i18n from 'vj/utils/i18n';
|
import i18n from 'vj/utils/i18n';
|
||||||
import { ConfirmDialog } from 'vj/components/dialog';
|
import { ConfirmDialog } from 'vj/components/dialog';
|
||||||
import Dropdown from 'vj/components/dropdown/Dropdown';
|
import Dropdown from 'vj/components/dropdown/Dropdown';
|
||||||
|
import CmEditor from 'vj/components/cmeditor/index';
|
||||||
|
|
||||||
const categories = {};
|
const categories = {};
|
||||||
const dirtyCategories = [];
|
const dirtyCategories = [];
|
||||||
@@ -141,7 +142,7 @@ function buildCategoryFilter() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new NamedPage(['problem_create', 'problem_edit'], () => {
|
export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
|
||||||
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
|
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
|
||||||
$(ev.currentTarget).hide();
|
$(ev.currentTarget).hide();
|
||||||
$('[name="problem-sidebar__categories"]').show();
|
$('[name="problem-sidebar__categories"]').show();
|
||||||
@@ -169,4 +170,57 @@ export default new NamedPage(['problem_create', 'problem_edit'], () => {
|
|||||||
$(document).on('change', '[name="tag"]', parseCategorySelection);
|
$(document).on('change', '[name="tag"]', parseCategorySelection);
|
||||||
buildCategoryFilter();
|
buildCategoryFilter();
|
||||||
parseCategorySelection();
|
parseCategorySelection();
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
$('img').each(function () {
|
||||||
|
if (this.src.startsWith('file://')) this.setAttribute('src', this.src.replace('file://', './file/'));
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
CmEditor.getOrConstruct($('textarea[data-markdown-upload]'), {
|
||||||
|
upload: {
|
||||||
|
accept: 'image/*,.mp3, .wav, .zip',
|
||||||
|
url: './files',
|
||||||
|
extraData: {
|
||||||
|
type: 'additional_file',
|
||||||
|
operation: 'upload_file',
|
||||||
|
},
|
||||||
|
multiple: false,
|
||||||
|
fieldName: 'file',
|
||||||
|
setHeaders() {
|
||||||
|
return { accept: 'application/json' };
|
||||||
|
},
|
||||||
|
format(files, resp) {
|
||||||
|
const res = JSON.parse(resp);
|
||||||
|
if (res.error) {
|
||||||
|
return JSON.stringify({
|
||||||
|
msg: res.error.message,
|
||||||
|
code: -1,
|
||||||
|
data: {
|
||||||
|
errFiles: [files[0].name],
|
||||||
|
succMap: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return JSON.stringify({
|
||||||
|
msg: '',
|
||||||
|
code: 0,
|
||||||
|
data: {
|
||||||
|
errFiles: [],
|
||||||
|
succMap: {
|
||||||
|
[files[0].name]: `file://${files[0].name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '')
|
||||||
|
.replace(/[?\\/:|<>*[\]()$%{}@~]/g, '')
|
||||||
|
.replace('/\\s/g', '')}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
filename(name) {
|
||||||
|
return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '')
|
||||||
|
.replace(/[?\\/:|<>*[\]()$%{}@~]/g, '')
|
||||||
|
.replace('/\\s/g', '');
|
||||||
|
},
|
||||||
|
validate: () => (pagename === 'problem_create' ? i18n('Cannot upload file before problem is created.') : true),
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<div class="row"><div class="columns">
|
<div class="row"><div class="columns">
|
||||||
<label>
|
<label>
|
||||||
{{ _('Content') }}
|
{{ _('Content') }}
|
||||||
<textarea name="content" class="textbox" data-markdown style="height: 500px">{% if pdoc %}{{ pdoc['content']|toString }}{% else %}{% include 'partials/problem_default.md' %}{% endif %}</textarea>
|
<textarea name="content" class="textbox" data-markdown-upload style="height: 500px">{% if pdoc %}{{ pdoc['content']|toString }}{% else %}{% include 'partials/problem_default.md' %}{% endif %}</textarea>
|
||||||
</label>
|
</label>
|
||||||
</div></div>
|
</div></div>
|
||||||
<div class="row"><div class="columns">
|
<div class="row"><div class="columns">
|
||||||
|
|||||||
Reference in new issue
Block a user