From 90443546c8a0b63c8b1821dcbab5079fa6caac5a Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 11 Apr 2021 18:22:47 +0800 Subject: [PATCH] ui: add ProblemSelectAutoComplete --- packages/hydrooj/src/handler/problem.ts | 10 +++++ packages/hydrooj/src/model/problem.ts | 9 +++- packages/hydrooj/src/model/user.ts | 3 +- packages/hydrooj/test/db.ts | 2 +- .../autocomplete/ProblemSelectAutoComplete.js | 41 +++++++++++++++++++ .../autocomplete/autoComplete.page.js | 12 ++++++ .../problemselectautocomplete.page.styl | 6 +++ 7 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js create mode 100644 packages/ui-default/components/autocomplete/autoComplete.page.js create mode 100644 packages/ui-default/components/autocomplete/problemselectautocomplete.page.styl diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index da6d501cc..8f9f3dd2f 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -581,6 +581,15 @@ export class ProblemCreateHandler extends Handler { } } +export class ProblemPrefixListHandler extends Handler { + @param('prefix', Types.Name) + async get(domainId: string, prefix: string) { + this.response.body = { + pdocs: await problem.getPrefixList(domainId, prefix), + }; + } +} + export async function apply() { Route('problem_main', '/p', ProblemMainHandler, PERM.PERM_VIEW_PROBLEM); Route('problem_category', '/p/category/:category', ProblemCategoryHandler, PERM.PERM_VIEW_PROBLEM); @@ -596,6 +605,7 @@ export async function apply() { Route('problem_solution_raw', '/p/:pid/solution/:psid/raw', ProblemSolutionRawHandler, PERM.PERM_VIEW_PROBLEM); Route('problem_solution_reply_raw', '/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionRawHandler, PERM.PERM_VIEW_PROBLEM); Route('problem_create', '/problem/create', ProblemCreateHandler, PERM.PERM_CREATE_PROBLEM); + Route('problem_prefix_list', '/problem/list', ProblemPrefixListHandler, PERM.PERM_VIEW_PROBLEM); Connection('problem_pretest_conn', '/conn/pretest', ProblemPretestConnectionHandler, PERM.PERM_SUBMIT_PROBLEM); } diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index 0645752c2..27e630c07 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -1,6 +1,6 @@ import type { Readable } from 'stream'; import { ObjectID, FilterQuery } from 'mongodb'; -import { Dictionary, pick } from 'lodash'; +import { Dictionary, escapeRegExp, pick } from 'lodash'; import { STATUS } from './builtin'; import * as document from './document'; import domain from './domain'; @@ -208,6 +208,13 @@ export class ProblemModel { return Object.assign(r, l); } + static async getPrefixList(domainId: string, prefix: string) { + prefix = prefix.toLowerCase(); + const $regex = new RegExp(`\\A${escapeRegExp(prefix)}`, 'gmi'); + const filter = { $or: [{ pid: { $regex } }, { title: { $regex } }] }; + return await document.getMulti(domainId, document.TYPE_PROBLEM, filter, ['domainId', 'docId', 'pid', 'title']).toArray(); + } + static async getListStatus(domainId: string, uid: number, pids: number[]) { const psdocs = await ProblemModel.getMultiStatus( domainId, { uid, docId: { $in: Array.from(new Set(pids)) } }, diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index 407a2cc72..75373d6a0 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -1,4 +1,5 @@ import { Collection } from 'mongodb'; +import { escapeRegExp } from 'lodash'; import * as system from './system'; import token from './token'; import * as setting from './setting'; @@ -224,7 +225,7 @@ class UserModel { @ArgMethod static async getPrefixList(domainId: string, prefix: string, limit: number = 50) { prefix = prefix.toLowerCase(); - const $regex = new RegExp(`\\A\\Q${prefix}\\E`, 'gmi'); + const $regex = new RegExp(`\\A${escapeRegExp(prefix)}`, 'gmi'); const uids = await coll.find({ unameLower: { $regex } }) .limit(limit).map((doc) => doc._id).toArray(); const users = []; diff --git a/packages/hydrooj/test/db.ts b/packages/hydrooj/test/db.ts index 9812e8c99..cb58498c7 100644 --- a/packages/hydrooj/test/db.ts +++ b/packages/hydrooj/test/db.ts @@ -11,7 +11,7 @@ export async function connect() { while (dbVer < expected) { const func = scripts[dbVer]; dbVer++; - if (func.toString().includes('_FRESH_INSTALL_IGNORE')) continue; + if (typeof func !== 'function' || func.toString().includes('_FRESH_INSTALL_IGNORE')) continue; // eslint-disable-next-line no-await-in-loop await func(); } diff --git a/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js b/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js new file mode 100644 index 000000000..1eb3c194a --- /dev/null +++ b/packages/ui-default/components/autocomplete/ProblemSelectAutoComplete.js @@ -0,0 +1,41 @@ +import _ from 'lodash'; +import tpl from 'vj/utils/tpl'; +import request from 'vj/utils/request'; +import DOMAttachedObject from 'vj/components/DOMAttachedObject'; +import AutoComplete from '.'; + +function getText(pdoc) { + return pdoc.docId; +} + +function getItems(val) { + return request.get('/problem/list', { q: val }); +} + +function renderItem(pdoc) { + return tpl` +
+
+
${pdoc.pid ? `${pdoc.pid}` : ''}${pdoc.title}
+
ID = ${pdoc.docId}
+
+
+ `; +} + +export default class ProblemSelectAutoComplete extends AutoComplete { + static DOMAttachKey = 'vjProblemSelectAutoCompleteInstance'; + + constructor($dom, options) { + super($dom, { + classes: 'problem-select', + items: getItems, + render: renderItem, + text: getText, + ...options, + }); + } +} + +_.assign(ProblemSelectAutoComplete, DOMAttachedObject); +window.Hydro.components.UserSelectAutoComplete = ProblemSelectAutoComplete; diff --git a/packages/ui-default/components/autocomplete/autoComplete.page.js b/packages/ui-default/components/autocomplete/autoComplete.page.js new file mode 100644 index 000000000..5183d43fc --- /dev/null +++ b/packages/ui-default/components/autocomplete/autoComplete.page.js @@ -0,0 +1,12 @@ +import { AutoloadPage } from 'vj/misc/Page'; +import UserSelectAutoComplete from './UserSelectAutoComplete'; +import DomainSelectAutoComplete from './DomainSelectAutoComplete'; +import ProblemSelectAutoComplete from './ProblemSelectAutoComplete'; + +const page = new AutoloadPage('autocomplete', () => { + UserSelectAutoComplete.getOrConstruct($('[autocomplete="user"]')); + DomainSelectAutoComplete.getOrConstruct($('[autocomplete="domain"]')); + ProblemSelectAutoComplete.getOrConstruct($('[autocomplete="problem"]')); +}); + +export default page; diff --git a/packages/ui-default/components/autocomplete/problemselectautocomplete.page.styl b/packages/ui-default/components/autocomplete/problemselectautocomplete.page.styl new file mode 100644 index 000000000..d7910b617 --- /dev/null +++ b/packages/ui-default/components/autocomplete/problemselectautocomplete.page.styl @@ -0,0 +1,6 @@ +.menu.problem-select + width: rem(200px) + +.problem-select__uid + font-size: rem($font-size-small) + color: $userselect-uid-color