mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core&ui: add contest balloon list (#578)
Co-authored-by: undefined <i@undefined.moe>
This commit is contained in:
1 parent
8e8c263477
commit
867cd069c5
15 files changed
+341
-8
No files matched your search
@@ -201,7 +201,7 @@ export default function (env: { watch?: boolean, production?: boolean, measure?:
|
||||
name(module) {
|
||||
const packageName = module.context.replace(/\\/g, '/').split('node_modules/').pop().split('/')[0];
|
||||
if (packageName === 'monaco-editor-nls') {
|
||||
return `i.monaco.${module.userRequest.split('/').pop().split('.')[0]}`;
|
||||
return `i.monaco.${module.userRequest.replace(/\\/g, '/').split('/').pop().split('.')[0]}`;
|
||||
}
|
||||
return `n.${packageName.replace('@', '')}`;
|
||||
},
|
||||
|
||||
@@ -109,6 +109,8 @@ Auto configure: 自动配置
|
||||
Auto detect: 自动检测
|
||||
Auto hide problems during the contest: 在比赛过程中自动隐藏题目
|
||||
Auto Read Tasks: 自动识别任务
|
||||
Awards: 奖项
|
||||
Balloon Status: 气球状态
|
||||
Basic Info: 基础信息
|
||||
Basic: 基础
|
||||
Be Copied: 被复制
|
||||
@@ -159,6 +161,7 @@ Code language: 代码语言
|
||||
Code: 代码
|
||||
codeFontFamily: 代码字体
|
||||
collapse: 收缩
|
||||
Color: 颜色
|
||||
Comment: 评论
|
||||
Comments: 评论
|
||||
CommonMark Syntax: CommonMark 语法
|
||||
@@ -374,6 +377,8 @@ File: 文件
|
||||
Filename: 文件名
|
||||
Files: 文件
|
||||
Filter: 过滤
|
||||
First of Contest: 全场一血
|
||||
First of Problem: 题目一血
|
||||
fontFamily: 字体
|
||||
Footer: 页脚
|
||||
Footnote: 脚注
|
||||
@@ -636,6 +641,7 @@ Please follow the instructions on your device to complete the verification.: 请
|
||||
Please select at least one file to perform this operation.: 请选择至少一个文件来执行此操作。
|
||||
Please select at least one role to perform this operation.: 请选择至少一个角色来进行操作。
|
||||
Please select at least one user to perform this operation.: 请选择至少一个用户来进行操作。
|
||||
Please set the balloon color for each problem first.: 请先为每道题设置气球颜色。
|
||||
Please wait until contest host unfreeze the scoreboard.: 请等待比赛主办方解除封榜。
|
||||
Preference Settings: 偏好设置
|
||||
preferredPrefix_hint: 此选项用于重排题号。例如,若题目包中所给的题号分别是 P1001, P1002, P1003,而此选项填写了 T,则导入后三道题的题号分别为 T1001, T1002 和 T1003。
|
||||
@@ -776,6 +782,7 @@ Selected files have been deleted.: 所选文件已被删除。
|
||||
Selected problems have been deleted.: 所选题目已被删除。
|
||||
Selected roles have been deleted.: 所选角色已删除。
|
||||
Selected users have been removed from the domain.: 所选用户已从此域中移除。
|
||||
Send By: 递送者
|
||||
Send Code after acceptance: 通过题目后发送源代码
|
||||
Send Message: 发送站内信息
|
||||
Send Password Reset Email: 发送密码重置邮件
|
||||
@@ -783,6 +790,7 @@ Send Verification Email: 发送验证邮件
|
||||
Send: 发送
|
||||
Seperated with ',': 用 ',' 分隔
|
||||
Service Status: 服务状态
|
||||
Set Color: 设置颜色
|
||||
Set Privilege: 设置权限
|
||||
Set Role: 设置角色
|
||||
Set Roles for Selected User: 设置所选用户的角色
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"><path d="M763.672 600.137c63.965-90.528 90.996-198.496 76.172-304.063C813.402 108.27 639.719-22.539 451.914 3.906c-187.5 26.348-318.633 200.352-292.285 387.852 14.785 105.254 70.547 201.484 156.973 270.976 48.007 38.438 102.168 65.43 153.554 77.5 8.067 1.938 13.215 9.848 11.719 18.008l-9.512 51.524a31.817 31.817 0 0 0 2.89 20.644 31.247 31.247 0 0 0 27.735 16.817 29.546 29.546 0 0 0 4.356-.313l34.785-4.883a3.905 3.905 0 0 1 4.082 2.227C578.867 915.176 635 968.965 706.66 997.305a31.251 31.251 0 0 0 41.016-18.848c5.526-15.977-3.204-33.34-18.907-39.61A231.571 231.571 0 0 1 612.95 837.52a3.907 3.907 0 0 1 2.831-5.86l14.453-1.953a32.32 32.32 0 0 0 19.688-10.215 31.25 31.25 0 0 0 4.668-34.765l-23.555-47.325c-3.719-7.468-.914-16.539 6.367-20.605 46.075-25.723 90.723-66.406 126.27-116.66Zm-302.54 32.773c-3.933 0-7.827-.742-11.484-2.187-80.586-31.875-149.023-102.93-178.613-185.43a31.242 31.242 0 0 1 5.367-31.004 31.25 31.25 0 0 1 53.461 9.91c28.32 78.985 92.325 128.457 142.774 148.438 13.957 5.515 22.023 20.176 19.21 34.918-2.812 14.742-15.706 25.402-30.714 25.394Zm0 0"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -84,6 +84,7 @@
|
||||
"qrcode": "^1.5.3",
|
||||
"queue-microtask": "^1.2.3",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -195,3 +195,16 @@ $highlight-button-color = #F6DF45
|
||||
.page--contest_user
|
||||
.col--uid
|
||||
width: rem(100px)
|
||||
|
||||
.page--contest_balloon
|
||||
.col--bid
|
||||
width: rem(100px)
|
||||
|
||||
.col--status
|
||||
width: rem(100px)
|
||||
|
||||
.col--problem
|
||||
width: rem(150px)
|
||||
|
||||
.col--awards
|
||||
width: rem(130px)
|
||||
@@ -0,0 +1,123 @@
|
||||
import $ from 'jquery';
|
||||
import yaml from 'js-yaml';
|
||||
import React from 'react';
|
||||
import { HexColorInput, HexColorPicker } from 'react-colorful';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { ActionDialog } from 'vj/components/dialog';
|
||||
import Notification from 'vj/components/notification';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import {
|
||||
i18n, pjax, request, tpl,
|
||||
} from 'vj/utils';
|
||||
|
||||
function Balloon({ tdoc, val }) {
|
||||
const [color, setColor] = React.useState('');
|
||||
const [now, setNow] = React.useState('');
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="medium-12 columns">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{i18n('Problem')}</th>
|
||||
<th>{i18n('Color')}</th>
|
||||
<th>{i18n('Name')}</th>
|
||||
<th><span className="icon icon-wrench"></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tdoc.pids.map((pid) => {
|
||||
const { color: c, name } = val[+pid];
|
||||
return (
|
||||
<tr key={pid}>
|
||||
<td>
|
||||
{now === pid
|
||||
? (<b>{String.fromCharCode(65 + tdoc.pids.indexOf(+pid))}</b>)
|
||||
: (<span>{String.fromCharCode(65 + tdoc.pids.indexOf(+pid))}</span>)}
|
||||
</td>
|
||||
<td>
|
||||
<HexColorInput
|
||||
className='textbox'
|
||||
color={c}
|
||||
onFocus={() => { setNow(pid); setColor(c); }}
|
||||
onChange={(e) => { val[+pid].color = e; setColor(e); }}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
className="textbox"
|
||||
defaultValue={name}
|
||||
onFocus={() => { setNow(pid); setColor(c); }}
|
||||
onChange={(e) => { val[+pid].name = e.target.value; }}
|
||||
/>
|
||||
</td>
|
||||
{tdoc.pids.indexOf(+pid) === 0 && <td rowSpan={0}>
|
||||
{now && <HexColorPicker color={color} onChange={(e) => { val[+now].color = e; setColor(e); }} style={{ padding: '1rem' }} />}
|
||||
</td>}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSetColor(tdoc) {
|
||||
let val = tdoc.balloon;
|
||||
if (!val) {
|
||||
val = {};
|
||||
for (const pid of tdoc.pids) val[+pid] = { color: '#ffffff', name: '' };
|
||||
}
|
||||
Notification.info(i18n('Loading...'));
|
||||
const promise = new ActionDialog({
|
||||
$body: tpl`
|
||||
<div className="row"><div className="columns">
|
||||
<h1>${i18n('Set Color')}</h1>
|
||||
</div></div>
|
||||
<div id="balloon"></div>`,
|
||||
}).open();
|
||||
createRoot($('#balloon').get(0)).render(
|
||||
<Balloon tdoc={tdoc} val={val} />,
|
||||
);
|
||||
const action = await promise;
|
||||
if (action !== 'ok') return;
|
||||
Notification.info(i18n('Updating...'));
|
||||
try {
|
||||
await request.post('', { operation: 'set_color', color: yaml.dump(val) });
|
||||
} catch (e) {
|
||||
Notification.error(`${e.message} ${e.params?.[0]}`);
|
||||
}
|
||||
Notification.info(i18n('Successfully updated.'));
|
||||
pjax.request({ url: '', push: false });
|
||||
}
|
||||
|
||||
const page = new NamedPage('contest_balloon', () => {
|
||||
const { tdoc } = UiContext;
|
||||
|
||||
const beginAt = new Date(tdoc.beginAt).getTime();
|
||||
const endAt = new Date(tdoc.endAt).getTime();
|
||||
function update() {
|
||||
const now = Date.now();
|
||||
if (beginAt <= now && now <= endAt) pjax.request({ url: '', push: false });
|
||||
}
|
||||
|
||||
$('[name="set_color"]').on('click', () => handleSetColor(tdoc));
|
||||
setInterval(update, 60000);
|
||||
|
||||
$(document).on('click', '[value="done"]', async (ev) => {
|
||||
ev.preventDefault();
|
||||
const balloon = $(ev.currentTarget).data('balloon');
|
||||
try {
|
||||
await request.post('', { balloon, operation: 'done' });
|
||||
} catch (e) {
|
||||
Notification.error(`${e.message} ${e.params?.[0]}`);
|
||||
}
|
||||
Notification.info(i18n('Successfully updated.'));
|
||||
pjax.request({ url: '', push: false });
|
||||
});
|
||||
});
|
||||
|
||||
export default page;
|
||||
@@ -0,0 +1,48 @@
|
||||
{% import "components/nothing.html" as nothing with context %}
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
{{ set(UiContext, 'tdoc', tdoc) }}
|
||||
<div class="row">
|
||||
<div class="medium-9 columns">
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Balloon Status') }}</h1>
|
||||
<div class="section__tools">
|
||||
<button class="primary rounded button" name="set_color">{{ _('Set Color') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{ noscript_note.render() }}
|
||||
<div class="section__body no-padding">
|
||||
{% if not tdoc.balloon|length %}
|
||||
{{ nothing.render('Please set the balloon color for each problem first.') }}
|
||||
{% else %}
|
||||
<table class="data-table record_main__table">
|
||||
<colgroup>
|
||||
<col class="col--status">
|
||||
<col class="col--bid">
|
||||
<col class="col--problem">
|
||||
<col class="col--submit-by">
|
||||
<col class="col--send-by">
|
||||
<col class="col--awards">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
<th class="col--bid">{{ _('#') }}</th>
|
||||
<th class="col--problem">{{ _('Problem') }}</th>
|
||||
<th class="col--submit-by">{{ _('Submit By') }}</th>
|
||||
<th class="col--send-by">{{ _('Send By') }}</th>
|
||||
<th class="col--awards">{{ _('Awards') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% include "partials/contest_balloon.html" %}
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="medium-3 columns">
|
||||
{% include 'partials/contest_sidebar_management.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,37 @@
|
||||
{% import "components/user.html" as user with context %}
|
||||
{% import "components/contest.html" as contest with context %}
|
||||
<tbody data-fragment-id="constest_balloon-tbody">
|
||||
{%- for bdoc in bdocs -%}
|
||||
<tr data-bid="{{ bdoc.bid }}">
|
||||
{% if bdoc.sent %}
|
||||
<td class="col--status record-status--border pass">
|
||||
<span class="icon record-status--icon pass"></span>
|
||||
<span class="record-status--text pass">{{ _('Sent') }}</span>
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="col--status record-status--border pending">
|
||||
<span class="icon record-status--icon pending"></span>
|
||||
<span class="record-status--text pending">{{ _('Waiting') }}</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="col--bid">{{ bdoc._id.toHexString()|truncate(8,true,'') }}</td>
|
||||
<td class="col--problem col--problem-name" style="color:{{ tdoc.balloon[bdoc.pid].color }}">
|
||||
{% if not bdoc.sent %}
|
||||
<form class="form--inline" method="post">
|
||||
<input type="hidden" name="balloon" value="{{ bdoc._id.toHexString() }}">
|
||||
<button type="submit" name="operation" value="done" class="link text-maroon lighter" data-balloon="{{ bdoc._id.toHexString() }}">
|
||||
<span class="icon icon-send"></span>
|
||||
{{ _('Send') }}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<b>{{ String.fromCharCode(65+tdoc.pids.indexOf(bdoc.pid)) }}</b> ({{ tdoc.balloon[bdoc.pid].name }})
|
||||
</td>
|
||||
<td class="col--submit-by" data-tooltip="{{ bdoc._id.getTimestamp().format() }}">{{ user.render_inline(udict[bdoc.uid], badge=false) }}</td>
|
||||
<td class="col--send-by"{% if bdoc.sent %} data-tooltip="{{ bdoc.sentAt.format() }}"{% endif %}>
|
||||
{% if bdoc.sent %}{{ user.render_inline(udict[bdoc.sent], avatar=false, badge=false) }}{% else %}-{% endif %}
|
||||
</td>
|
||||
<td class="col--awards">{% if bdoc.first %}{{ _('First of Problem') }}{% endif %}</td>
|
||||
</tr>
|
||||
{%- endfor -%}
|
||||
</tbody>
|
||||
@@ -26,6 +26,9 @@
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('record_main', query={tid:tdoc.docId}) }}">
|
||||
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
||||
</a></li>
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('contest_balloon', tid=tdoc.docId) }}">
|
||||
<span class="icon icon-balloon"></span> {{ _('Balloon Status') }}
|
||||
</a></li>
|
||||
<li class="menu__seperator"></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user