添加后台i18n,路由懒加载
This commit is contained in:
parent
71490fa713
commit
6e96a7011c
|
@ -68,6 +68,7 @@ Password: 开启SMTP服务后生成的随机授权码
|
|||
| 2021-05-28 | 增加导入导出题目,增加用户页面的最近登录,开发正式结束,进入维护摸鱼 | Himit_ZH |
|
||||
| 2021-06-02 | 大更新,完善补充前端页面,修正判题等待超时时间,修补一系列bug | Himit_ZH |
|
||||
| 2021-06-07 | 修正特殊判题,增加前台i18n | Himit_ZH |
|
||||
| 2021-06-08 | 添加后台i18n,路由懒加载 | Himit_ZH |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package top.hcode.hoj.controller.admin;
|
|||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.UnicodeUtil;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
|
@ -63,15 +64,15 @@ public class ConfigController {
|
|||
public CommonResult getWebConfig() {
|
||||
|
||||
return CommonResult.successResponse(
|
||||
MapUtil.builder().put("baseUrl", configVo.getBaseUrl())
|
||||
.put("name", configVo.getName())
|
||||
.put("shortName", configVo.getShortName())
|
||||
.put("description", configVo.getDescription())
|
||||
MapUtil.builder().put("baseUrl", UnicodeUtil.toString(configVo.getBaseUrl()))
|
||||
.put("name", UnicodeUtil.toString(configVo.getName()))
|
||||
.put("shortName", UnicodeUtil.toString(configVo.getShortName()))
|
||||
.put("description", UnicodeUtil.toString(configVo.getDescription()))
|
||||
.put("register", configVo.getRegister())
|
||||
.put("recordName", configVo.getRecordName())
|
||||
.put("recordUrl", configVo.getRecordUrl())
|
||||
.put("projectName", configVo.getProjectName())
|
||||
.put("projectUrl", configVo.getProjectUrl()).map()
|
||||
.put("recordName", UnicodeUtil.toString(configVo.getRecordName()))
|
||||
.put("recordUrl", UnicodeUtil.toString(configVo.getRecordUrl()))
|
||||
.put("projectName", UnicodeUtil.toString(configVo.getProjectName()))
|
||||
.put("projectUrl", UnicodeUtil.toString(configVo.getProjectUrl())).map()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package top.hcode.hoj.controller.oj;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.UnicodeUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
@ -131,16 +132,16 @@ public class HomeController {
|
|||
public CommonResult getWebConfig() {
|
||||
|
||||
return CommonResult.successResponse(
|
||||
MapUtil.builder().put("baseUrl", configVo.getBaseUrl())
|
||||
.put("name", configVo.getName())
|
||||
.put("shortName", configVo.getShortName())
|
||||
MapUtil.builder().put("baseUrl", UnicodeUtil.toString(configVo.getBaseUrl()))
|
||||
.put("name", UnicodeUtil.toString(configVo.getName()))
|
||||
.put("shortName", UnicodeUtil.toString(configVo.getShortName()))
|
||||
.put("register", configVo.getRegister())
|
||||
.put("recordName", configVo.getRecordName())
|
||||
.put("recordUrl", configVo.getRecordUrl())
|
||||
.put("description", configVo.getDescription())
|
||||
.put("email", configVo.getEmailUsername())
|
||||
.put("projectName", configVo.getProjectName())
|
||||
.put("projectUrl", configVo.getProjectUrl()).map()
|
||||
.put("recordName", UnicodeUtil.toString(configVo.getRecordName()))
|
||||
.put("recordUrl", UnicodeUtil.toString(configVo.getRecordUrl()))
|
||||
.put("description", UnicodeUtil.toString(configVo.getDescription()))
|
||||
.put("email", UnicodeUtil.toString(configVo.getEmailUsername()))
|
||||
.put("projectName", UnicodeUtil.toString(configVo.getProjectName()))
|
||||
.put("projectUrl", UnicodeUtil.toString(configVo.getProjectUrl())).map()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package top.hcode.hoj.utils;
|
||||
|
||||
import cn.hutool.core.text.UnicodeUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.pojo.vo.ConfigVo;
|
||||
|
@ -52,18 +53,18 @@ public class ConfigUtils {
|
|||
" port: " + configVo.getRedisPort() + "\n" +
|
||||
" password: " + configVo.getRedisPassword() + "\n" +
|
||||
" web-config:\n" +
|
||||
" base-url: " + configVo.getBaseUrl() + "\n" +
|
||||
" name: " + configVo.getName() + "\n" +
|
||||
" short-name: " + configVo.getShortName() + "\n" +
|
||||
" description: " + configVo.getDescription() + "\n" +
|
||||
" base-url: " + UnicodeUtil.toUnicode(configVo.getBaseUrl()) + "\n" +
|
||||
" name: " + UnicodeUtil.toUnicode(configVo.getName()) + "\n" +
|
||||
" short-name: " + UnicodeUtil.toUnicode(configVo.getShortName()) + "\n" +
|
||||
" description: " + UnicodeUtil.toUnicode(configVo.getDescription(),true) + "\n" +
|
||||
" register: " + configVo.getRegister() + "\n" +
|
||||
" footer:\n" +
|
||||
" record:\n" +
|
||||
" name: " + configVo.getRecordName() + "\n" +
|
||||
" url: " + configVo.getRecordUrl() + "\n" +
|
||||
" name: " + UnicodeUtil.toUnicode(configVo.getRecordName()) + "\n" +
|
||||
" url: " + UnicodeUtil.toUnicode(configVo.getRecordUrl()) + "\n" +
|
||||
" project:\n" +
|
||||
" name: " + configVo.getProjectName() + "\n" +
|
||||
" url: " + configVo.getProjectUrl() + "\n" +
|
||||
" name: " + UnicodeUtil.toUnicode(configVo.getProjectName()) + "\n" +
|
||||
" url: " + UnicodeUtil.toUnicode(configVo.getProjectUrl()) + "\n" +
|
||||
" hdu:\n" +
|
||||
" account:\n" +
|
||||
" username: " + listToStr(configVo.getHduUsernameList()) + "\n" +
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
<el-row>
|
||||
<el-col :md="6" :xs="24">
|
||||
<h1>{{ toUpper(websiteConfig.shortName) }}</h1>
|
||||
<p style="line-height:25px">
|
||||
{{ websiteConfig.description }}
|
||||
</p>
|
||||
<span style="line-height:25px" v-html="websiteConfig.description">
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col class="hr-none">
|
||||
<el-divider></el-divider>
|
||||
|
|
|
@ -83,7 +83,7 @@ function downloadFile (url) {
|
|||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
myMessage.success("Request success, Downloading...")
|
||||
myMessage.success("Downloading...")
|
||||
resolve()
|
||||
}).catch((error) => {
|
||||
reject(error)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div style="text-align:center">
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
|
@ -18,15 +18,11 @@
|
|||
>
|
||||
<vxe-table-column title="ID" min-width="100" field="problemId">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" title="Title" field="title">
|
||||
<vxe-table-column min-width="150" :title="$t('m.Title')" field="title">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="option" align="center" min-width="100">
|
||||
<vxe-table-column :title="$t('m.Option')" align="center" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="添加该题目到比赛中"
|
||||
placement="top"
|
||||
>
|
||||
<el-tooltip effect="dark" :content="$t('m.Add')" placement="top">
|
||||
<el-button
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
|
@ -97,7 +93,10 @@ export default {
|
|||
});
|
||||
},
|
||||
handleAddProblem(problemID) {
|
||||
this.$prompt('请输入该题目在比赛中显示的序号ID', '确认').then(
|
||||
this.$prompt(
|
||||
this.$i18n.t('m.Enter_The_Problem_Display_ID_in_the_Contest'),
|
||||
'Tips'
|
||||
).then(
|
||||
({ value }) => {
|
||||
let data = {
|
||||
pid: problemID,
|
||||
|
@ -107,7 +106,7 @@ export default {
|
|||
api.admin_addProblemFromPublic(data).then(
|
||||
(res) => {
|
||||
this.$emit('on-change');
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Add_Successfully'));
|
||||
this.getPublicProblem(this.page);
|
||||
},
|
||||
() => {}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<template v-slot:left-toolbar-after v-if="isAdminRole">
|
||||
<button
|
||||
type="button"
|
||||
title="文件上传"
|
||||
:title="$t('m.Upload_file')"
|
||||
class="op-icon fa markdown-upload"
|
||||
aria-hidden="true"
|
||||
@click="uploadFile"
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
<template>
|
||||
<div class="panel" :class="{'small': small}">
|
||||
<header>
|
||||
<div class="title">
|
||||
<template v-if="$slots.title">
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{title}}
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="header_right">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="body">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Panel',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
small: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.panel {
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
|
||||
&.small {
|
||||
max-width: 830px;
|
||||
min-width: 700px;
|
||||
margin-left: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
header {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
> .title {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
border-color: #ddd;
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.025em;
|
||||
height: 60px;
|
||||
line-height: 45px;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
> .header_right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20px;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
}
|
||||
.body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.panel-options {
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
height: 50px;
|
||||
button {
|
||||
margin-top: 18px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
> .page {
|
||||
position: absolute;
|
||||
right:20px;
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -104,7 +104,7 @@ export default {
|
|||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$i18n.t('m.Username_Check'),
|
||||
message: this.$i18n.t('m.Username_Check_Required'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -51,7 +51,7 @@ export default {
|
|||
api.checkUsernameOrEmail(undefined, value).then(
|
||||
(res) => {
|
||||
if (res.data.data.email === false) {
|
||||
callback(new Error('The email does not exist'));
|
||||
callback(new Error(this.$i18n.t('m.The_email_does_not_exists')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ export default {
|
|||
captcha: [
|
||||
{
|
||||
required: true,
|
||||
message: 'The captcha is required',
|
||||
message: this.$i18n.t('m.Code_Check_Required'),
|
||||
trigger: 'blur',
|
||||
min: 1,
|
||||
max: 8,
|
||||
|
@ -82,7 +82,7 @@ export default {
|
|||
email: [
|
||||
{
|
||||
required: true,
|
||||
message: 'The email is required',
|
||||
message: this.$i18n.t('m.Email_Check_Required'),
|
||||
type: 'email',
|
||||
trigger: 'blur',
|
||||
},
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
export const m = {
|
||||
// /views/admin/Login.vue
|
||||
Welcome_to_Login_Admin: 'Welcome to Login Background Management System',
|
||||
Login: 'Login',
|
||||
Please_enter_username: 'Please enter username',
|
||||
Please_enter_password: 'Please enter password',
|
||||
Admin_Login_Success:'Dear administrator, welcome back~',
|
||||
Please_check_your_username_or_password:'Please check your username or password',
|
||||
|
||||
// /views/admin/Home.vue
|
||||
Dashboard: 'Dashboard',
|
||||
General: 'General',
|
||||
User_Admin: 'User Admin',
|
||||
Announcement: 'Announcement',
|
||||
System_Config: 'System Config',
|
||||
Problem: 'Problem',
|
||||
Problem_List: 'Problem List',
|
||||
Create_Problem: 'Create Problem',
|
||||
Export_Import_Problem: 'Export | Import Problem',
|
||||
Contest: 'Contest',
|
||||
Contest_List: 'Contest List',
|
||||
Create_Contest: 'Create Contest',
|
||||
Discussion:'Discussion',
|
||||
Discussion_Admin:'Discussion Admin',
|
||||
Home_Page:'Home Page',
|
||||
Logout:'Logout',
|
||||
|
||||
// /views/admin/Dashboard.vue
|
||||
Last_Login: 'Last Login',
|
||||
Super_Admin:'Super Admin',
|
||||
Admin:'Admin',
|
||||
Total_Users:'Total Users',
|
||||
Today_Submissions:'Today Submissions',
|
||||
Recent_14_Days_Contests:'Recent 14 Days Contests',
|
||||
Backend_System:'Backend System',
|
||||
Server_Number:'Server Number',
|
||||
Nacos_Status:'Nacos Status',
|
||||
HTTPS_Status: 'HTTPS Status',
|
||||
Backend_Service: 'Backend Service',
|
||||
Name:'Name',
|
||||
Host:'Host',
|
||||
Port:'Port',
|
||||
CPU_Core:'CPU Core',
|
||||
CPU_Usage:'CPU Usage',
|
||||
Mem_Usage:'Mem Usage',
|
||||
Healthy:'Healthy',
|
||||
Secure:'Secure',
|
||||
Healthy_Status:'Healthy',
|
||||
Unhealthy:'Unhealthy',
|
||||
Judge_Server:'Judge Server',
|
||||
|
||||
// /views/admin/general/User.vue
|
||||
General_User: 'User',
|
||||
Delete:'Delete',
|
||||
OnlyAdmin:'OnlyAdmin',
|
||||
User_Type: 'User Type',
|
||||
Normal:'Normal',
|
||||
Disable:'Disable',
|
||||
Edit_User:'Edit User',
|
||||
Delete_User:'Delete User',
|
||||
Import_User: 'Import User',
|
||||
Import_User_Tips1:'The imported user data only supports user data in CSV format.',
|
||||
Import_User_Tips2:'There are three columns of data: user name, password, and mailbox. Any column cannot be empty, otherwise the data in this row may fail to be imported.',
|
||||
Import_User_Tips3:'The first line does not need to write the three column names ("username", "password", "email").',
|
||||
Import_User_Tips4:'Please import the file saved as UTF-8 code, otherwise Chinese may be garbled.',
|
||||
Choose_File:'Choose File',
|
||||
Password: 'Password',
|
||||
Upload_All:'Upload All',
|
||||
Clear_All:'Clear All',
|
||||
Generate_User: 'Generate User',
|
||||
Prefix:'Prefix',
|
||||
Suffix:'Suffix',
|
||||
Start_Number:'Start Number',
|
||||
End_Number:'End Number',
|
||||
Password_Length:'Password Length',
|
||||
Generate_and_Export:'Generate & Export',
|
||||
The_usernames_will_be:'The usernames will be',
|
||||
Set_New_PWD:'Set New PWD',
|
||||
General_New_Password: 'New PWD',
|
||||
The_end_number_cannot_be_less_than_the_start_number:'The end number cannot be less than the start number',
|
||||
Please_select_6_to_25_characters_for_password_length:'Please select 6 ~ 25 characters for password length',
|
||||
Start_Number_Required:'The Start Number is required.',
|
||||
End_Number_Required:'The End Number is required.',
|
||||
Password_Length_Checked:'Password length must be numeric',
|
||||
Delete_User_Tips:'Are you sure you want to delete this user? May be associated to delete the user created announcements, topics, competitions, etc.',
|
||||
The_number_of_users_selected_cannot_be_empty:'The number of users selected cannot be empty',
|
||||
Error_Please_check_your_choice:'Wrong, please check your choice.',
|
||||
Generate_User_Success:'All users in the specified format have been created successfully, and the user table has been downloaded to your computer successfully!',
|
||||
Generate_Skipped_Reason:'rows user data are filtered because it may be an empty row or a column value is empty.',
|
||||
Upload_Users_Successfully:'Upload Users Successfully',
|
||||
|
||||
// /views/admin/general/Announcement.vue
|
||||
General_Announcement: 'Announcement',
|
||||
Create:'Create',
|
||||
Modified_Time:'Modified Time',
|
||||
Edit_Announcement:'Edit Announcement',
|
||||
Create_Announcement:'Create Announcement',
|
||||
Delete_Announcement:'Delete Announcement',
|
||||
Announcement_Title: 'Title',
|
||||
Announcement_Content: 'Content',
|
||||
Announcement_visible: 'Visible',
|
||||
Delete_Announcement_Tips:'Are you sure you want to delete this announcement?',
|
||||
|
||||
// /views/admin/general/SystemConfig.vue
|
||||
Website_Config:'Website Config',
|
||||
Base_Url: 'Base Url',
|
||||
Web_Name: 'Web Name',
|
||||
Short_Name: 'Short Name',
|
||||
Record_Name:'Record Name',
|
||||
Record_Url:'Record Url',
|
||||
Project_Name:'Project Name',
|
||||
Project_Url:'Project Url',
|
||||
Web_Desc: 'Web Desc',
|
||||
Allow_Register: 'Allow Register',
|
||||
SMTP_Config: 'SMTP Config',
|
||||
Email_BG:'BG IMG',
|
||||
Email_BG_Desc:'SMTP Template Background IMG Address',
|
||||
Send_Test_Email:'Send Test Email',
|
||||
Email: 'Email',
|
||||
DataSource_Config:'DataSource Config',
|
||||
Please_input_your_email:'Please input your email',
|
||||
|
||||
// /views/admin/problem/ProblemList.vue
|
||||
Contest_Problem_List: 'Contest Problem List',
|
||||
Add_Rmote_OJ_Problem:'Add Remote OJ Problem',
|
||||
Add_From_Public_Problem:'Add From Public Problem',
|
||||
Auth:'Auth',
|
||||
Public_Problem:'Public Problem',
|
||||
Private_Problem:'Private Problem',
|
||||
Contest_Problem:'Contest Problem',
|
||||
Download_Testcase:'Download Testcase',
|
||||
Add_Contest_Problem:'Add Contest Problem',
|
||||
Remote_OJ:'Remote OJ',
|
||||
Add:'Add',
|
||||
Delete_Problem_Tips:'Are you sure you want to delete this problem? Note: the relevant submission data for this issue will also be deleted.',
|
||||
Add_Successfully:'Add Successfully',
|
||||
Download_Testcase_Success:'The testcase of this problem has been downloaded successfully!',
|
||||
Enter_The_Problem_Display_ID_in_the_Contest:'Enter The Problem Display ID in the Contest',
|
||||
|
||||
|
||||
// /views/admin/problem/Problem.vue
|
||||
Problem_Display_ID: 'Problem Display ID',
|
||||
Title: 'Title',
|
||||
Contest_Display_Title:'Contest Display Title',
|
||||
Contest_Display_ID: 'Contest Display ID',
|
||||
Description: 'Description',
|
||||
Input_Description: 'Input Description',
|
||||
Output_Description: 'Output Description',
|
||||
Time_Limit: 'Time Limit',
|
||||
Memory_Limit: 'Memory Limit',
|
||||
Stack_Limit:'Stack Limit',
|
||||
Code_Shareable:'Code Shareable',
|
||||
Languages: 'Languages',
|
||||
Problem_Examples:'Problem Examples',
|
||||
Problem_Examples_Desc:'Problem Examples: please do not have more than 2 problem examples. Problem examples are not included in the testcase.',
|
||||
Problem_Example:'Example',
|
||||
Example_Input:'Example Input',
|
||||
Example_Output:'Example Output',
|
||||
Add_Example: 'Add Example',
|
||||
Special_Judge: 'Special Judge',
|
||||
Special_Judge_Code: 'Special Judge Code',
|
||||
Special_Judge_Tips1:'Why use special judge?',
|
||||
Special_Judge_Tips2:'The output required by the problem may not be unique, and different results are allowed.',
|
||||
Special_Judge_Tips3:'The output within a certain precision range is acceptable.',
|
||||
Use_Special_Judge: 'Use Special Judge',
|
||||
SPJ_language: 'SPJ language',
|
||||
Compile: 'Compile',
|
||||
Code_Template: 'Code Template',
|
||||
Type: 'Type',
|
||||
Judge_Samples:'Judge Samples',
|
||||
Problem_Sample:'Sample',
|
||||
Sample_Input:'Sample Input',
|
||||
Sample_Output:'Sample Output',
|
||||
Sample_Input_File:'Input File',
|
||||
Sample_Output_File:'Output File',
|
||||
Sample_Tips:'Sample: the data source of the judger to test the submission.',
|
||||
Add_Sample: 'Add Sample',
|
||||
Use_Upload_File:'Use Upload File',
|
||||
Use_Manual_Input:'Use Manual Input',
|
||||
Hint: 'Hint',
|
||||
Source: 'Source',
|
||||
Auto_Remove_the_Blank_at_the_End_of_Code:'Auto Remove the Blank at the End of Code',
|
||||
Publish_the_Judging_Result_of_Test_Data:'Publish the Judging Result of Test Data',
|
||||
Edit_Problem: 'Edit Problme',
|
||||
Create_Problme: 'Create Problem',
|
||||
Change_Judge_Method:'If you want to change the judging method, you need to upload the testcase again.',
|
||||
Add_Tag_Error:'The tag has been added, please do not add it repeatedly!',
|
||||
|
||||
Upload_Testcase_Successfully:'Upload Testcase Successfully',
|
||||
Upload_Testcase_Failed:'Upload Testcase Failed',
|
||||
is_required:'is required!',
|
||||
Score_must_be_greater_than_or_equal_to_0:'Score must be greater than or equal to 0!',
|
||||
Score_must_be_an_integer:'Score must be an integer!',
|
||||
Spj_Code:'Spj Code',
|
||||
Spj_Code_not_Compile_Success:'Spj Code was not compiled successfully, please compile again!',
|
||||
|
||||
|
||||
// /views/admin/problem/ImportAndExport.vue
|
||||
Export_Problem:'Export Problem',
|
||||
Export:'Export',
|
||||
Import_Problem:'Import Problem',
|
||||
Import_QDOJ_Problem:'Import QDOJ Problem',
|
||||
Export_Problem_NULL_Tips:'The problem selected for export cannot be empty',
|
||||
|
||||
// /views/admin/contest/ContestList.vue
|
||||
Visible:'Visible',
|
||||
Info:'Info',
|
||||
View_Contest_Problem_List:'View Contest Problem List',
|
||||
View_Contest_Announcement_List:'View Contest Announcement List',
|
||||
Download_Contest_AC_Submission:'Download Contest AC Submissions',
|
||||
Exclude_admin_submissions:'Exclude admin submissions',
|
||||
|
||||
Delete_Contest_Tips:'This operation will delete the contest and its submission, discussion, announcement, record and other data. Do you want to continue?',
|
||||
|
||||
// /views/admin/contest/Contest.vue
|
||||
Contest_Title: 'Contest Title',
|
||||
Contest_Description: 'Contest Description',
|
||||
Contest_Start_Time: 'Start Time',
|
||||
Contest_End_Time: 'End Time',
|
||||
Contest_Duration:'Contest Duration',
|
||||
Contest_Rule_Type: 'Contest Rule Type',
|
||||
Seal_Time_Rank:'Seal Time Rank',
|
||||
Real_Time_Rank: 'Real Time Rank',
|
||||
Seal_Rank_Time:'Seal Rank Time',
|
||||
Contest_Auth: 'Contest Auth',
|
||||
Contest_Password:'Contest Password',
|
||||
Contest_Seal_Half_Hour:'Half an hour',
|
||||
Contest_Seal_An_Hour:'An hour',
|
||||
Contest_Seal_All_Hour:'All hours',
|
||||
Edit_Contest:'Edit Contest',
|
||||
Create_Contest:'Create Contest',
|
||||
Contest_Duration_Check:'The duration of the contest cannot be less than or equal to zero!',
|
||||
Contets_Time_Check:'The start time should be earlier than the end time!',
|
||||
|
||||
// /views/admin/discussion/Discussion.vue
|
||||
Discussion_ID:'Discussion ID',
|
||||
Top:'Top',
|
||||
Discussion_Report:'Discussion Report',
|
||||
Reporter:'Reporter',
|
||||
Report_Time:'Report Time',
|
||||
View_Report_content:'View Report Content',
|
||||
View_Discussion:'View Discussion Detail',
|
||||
Content:'Content',
|
||||
Report_Content:'Report Content',
|
||||
The_number_of_discussions_selected_cannot_be_empty:'The number of discussions selected cannot be empty',
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
export const m = {
|
||||
// /views/admin/Login.vue
|
||||
Welcome_to_Login_Admin: '欢迎登录后台管理系统',
|
||||
Login: '登录',
|
||||
Please_enter_username: '用户名',
|
||||
Please_enter_password: '密码',
|
||||
Admin_Login_Success:'尊敬的管理员,欢迎回来~',
|
||||
Please_check_your_username_or_password:'请检查你的用户名或密码',
|
||||
|
||||
// /views/admin/Home.vue
|
||||
Dashboard: '仪表盘',
|
||||
General: '常用设置',
|
||||
User_Admin: '用户管理',
|
||||
Announcement: '公告管理',
|
||||
System_Config: '系统配置',
|
||||
Problem: '题目管理',
|
||||
Problem_List: '题目列表',
|
||||
Create_Problem: '增加题目',
|
||||
Export_Import_Problem: '导入|导出题目',
|
||||
Contest: '比赛管理',
|
||||
Contest_List: '比赛列表',
|
||||
Create_Contest: '创建比赛',
|
||||
Discussion:'讨论管理',
|
||||
Discussion_Admin:'讨论管理',
|
||||
Home_Page:'主页',
|
||||
Logout:'退出登录',
|
||||
|
||||
// /views/admin/Dashboard.vue
|
||||
Last_Login: '最近登录',
|
||||
Super_Admin:'超级管理员',
|
||||
Admin:'管理员',
|
||||
Total_Users:'总用户数',
|
||||
Today_Submissions:'今日总交题数',
|
||||
Recent_14_Days_Contests:'最近两周比赛',
|
||||
Backend_System:'后端系统',
|
||||
Server_Number:'服务器数量',
|
||||
Nacos_Status:'Nacos 状态',
|
||||
HTTPS_Status: 'HTTPS 状态',
|
||||
Backend_Service: '后端服务',
|
||||
Name:'名称',
|
||||
Host:'主机',
|
||||
Port:'端口',
|
||||
CPU_Core:'CPU核心数',
|
||||
CPU_Usage:'CPU使用率',
|
||||
Mem_Usage:'内存使用率',
|
||||
Secure:'不稳定',
|
||||
Healthy_Status:'状态',
|
||||
Healthy:'健康',
|
||||
Unhealthy:'不健康',
|
||||
Judge_Server:'判题服务器',
|
||||
|
||||
// /views/admin/general/User.vue
|
||||
General_User: '用户管理',
|
||||
Delete:'删除',
|
||||
OnlyAdmin:'仅显示管理员',
|
||||
User_Type: '用户角色',
|
||||
Normal:'正常',
|
||||
Disable:'封禁',
|
||||
Edit_User:'编辑用户',
|
||||
Delete_User:'删除用户',
|
||||
Import_User: '导入用户',
|
||||
Import_User_Tips1:'用户数据导入仅支持csv格式的用户数据。',
|
||||
Import_User_Tips2:'共三列数据:用户名,密码,邮箱,任一列不能为空,否则该行数据可能导入失败。',
|
||||
Import_User_Tips3:'第一行不必写(“用户名”,“密码”,“邮箱”)这三个列名',
|
||||
Import_User_Tips4:'请导入保存为UTF-8编码的文件,否则中文可能会乱码。',
|
||||
Choose_File:'选择文件',
|
||||
Password: '密码',
|
||||
Upload_All:'上传全部',
|
||||
Clear_All:'清除全部',
|
||||
Generate_User: '生成用户',
|
||||
Prefix:'前缀',
|
||||
Suffix:'后缀',
|
||||
Start_Number:'开始数字',
|
||||
End_Number:'结束数字',
|
||||
Password_Length:'密码长度',
|
||||
Generate_and_Export:'生成 & 导出',
|
||||
The_usernames_will_be:'生成的用户名将会是',
|
||||
Set_New_PWD:'设置新密码',
|
||||
General_New_Password: '新密码',
|
||||
The_end_number_cannot_be_less_than_the_start_number:'结束数字不能小于开始数字',
|
||||
Please_select_6_to_25_characters_for_password_length:'请输入6~25作为密码的长度',
|
||||
Start_Number_Required:'开始数字不能为空',
|
||||
End_Number_Required:'结束数字不能为空',
|
||||
Password_Length_Checked:'密码长度必须是数字',
|
||||
Delete_User_Tips:'你确定要删除该用户?可能会关联删除该用户创建的公告,题目,比赛等。',
|
||||
The_number_of_users_selected_cannot_be_empty:'选择的用户不能为空',
|
||||
Error_Please_check_your_choice:'错误,请检查你的输入或选择是否准确',
|
||||
Generate_User_Success:'所有用户已经被成功创建, 用户的列表数据文件将下载到你的电脑里',
|
||||
Generate_Skipped_Reason:'行用户数据被过滤,原因是可能为空行或某个列值为空',
|
||||
Upload_Users_Successfully:'上传用户成功',
|
||||
|
||||
// /views/admin/general/Announcement.vue
|
||||
General_Announcement: '公告管理',
|
||||
Create:'创建',
|
||||
Modified_Time:'修改时间',
|
||||
Edit_Announcement:'编辑公告',
|
||||
Create_Announcement:'创建公告',
|
||||
Delete_Announcement:'删除公告',
|
||||
Announcement_Title: '公告标题',
|
||||
Announcement_Content: '公告内容',
|
||||
Announcement_visible: '是否可见',
|
||||
Delete_Announcement_Tips:'你确实是否删除该公告?',
|
||||
|
||||
// /views/admin/general/SystemConfig.vue
|
||||
Website_Config:'网站设置',
|
||||
Base_Url: '基础URL',
|
||||
Web_Name: '网站名称',
|
||||
Short_Name: '网站简称',
|
||||
Record_Name:'备案名',
|
||||
Record_Url:'备案地址',
|
||||
Project_Name:'项目名',
|
||||
Project_Url:'项目地址',
|
||||
Web_Desc: '网站简介',
|
||||
Allow_Register: '是否允许注册',
|
||||
SMTP_Config: 'SMTP 设置',
|
||||
Email_BG:'邮件背景',
|
||||
Email_BG_Desc:'请输入邮件背景图的URL链接',
|
||||
Send_Test_Email:'发送测试邮件',
|
||||
Email: '邮箱',
|
||||
DataSource_Config:'数据源设置',
|
||||
Please_input_your_email:'请输入你的邮箱',
|
||||
|
||||
// /views/admin/problem/ProblemList.vue
|
||||
Contest_Problem_List: '比赛题目列表',
|
||||
Add_Rmote_OJ_Problem:'添加远程OJ题目',
|
||||
Add_From_Public_Problem:'从公共题库添加题目',
|
||||
Auth:'权限',
|
||||
Public_Problem:'公开题目',
|
||||
Private_Problem:'私有题目',
|
||||
Contest_Problem:'比赛题目',
|
||||
Download_Testcase:'下载评测数据',
|
||||
Add_Contest_Problem:'添加比赛题目',
|
||||
Remote_OJ:'远程OJ',
|
||||
Add:'添加',
|
||||
Delete_Problem_Tips:'确定要删除此问题吗?注意:该问题的相关提交数据也将被删除。',
|
||||
Add_Successfully:'添加成功',
|
||||
Download_Testcase_Success:'该题目的评测数据已经被成功下载!',
|
||||
Enter_The_Problem_Display_ID_in_the_Contest:'请输入该题目在比赛中展示ID',
|
||||
|
||||
|
||||
// /views/admin/problem/Problem.vue
|
||||
Problem_Display_ID: '题目展示ID',
|
||||
Title: '题目标题',
|
||||
Contest_Display_Title:'比赛中的展示标题',
|
||||
Contest_Display_ID: '比赛中的展示ID',
|
||||
Description: '描述',
|
||||
Input_Description: '输入描述',
|
||||
Output_Description: '输出描述',
|
||||
Time_Limit: '时间限制',
|
||||
Memory_Limit: '内存限制',
|
||||
Stack_Limit:'栈限制',
|
||||
Code_Shareable:'代码是否可分享',
|
||||
Languages: '语言列表',
|
||||
Problem_Examples:'题面样例',
|
||||
Problem_Examples_Desc:'题目样例:请最好不要超过2个题目样例,题面样例不纳入评测数据。',
|
||||
Problem_Example:'样例',
|
||||
Example_Input:'样例输入',
|
||||
Example_Output:'样例输出',
|
||||
Add_Example: '添加样例',
|
||||
Special_Judge: '特殊判题',
|
||||
Special_Judge_Code:'特殊判题代码',
|
||||
Special_Judge_Tips1:'为什么要使用特殊判题?',
|
||||
Special_Judge_Tips2:'题目要求的输出结果可能不唯一,允许不同结果存在。',
|
||||
Special_Judge_Tips3:'题目最终要求输出一个浮点数,而且会告诉只要答案和标准答案相差不超过某个较小的数就可以。例如题目要求保留几位小数,输出结果后几位小数不相同也是正确的。',
|
||||
Use_Special_Judge: '使用特殊判题',
|
||||
SPJ_language: 'SPJ语言',
|
||||
Compile: '编译',
|
||||
Code_Template: '代码模板',
|
||||
Type: '类型',
|
||||
Judge_Samples:'评测数据',
|
||||
Problem_Sample:'测试用例',
|
||||
Sample_Input:'用例输入',
|
||||
Sample_Output:'用例输出',
|
||||
Sample_Input_File:'输入文件名',
|
||||
Sample_Output_File:'输出文件名',
|
||||
Sample_Tips:'评测数据:判题机对该题目的相关提交进行评测的数据来源。',
|
||||
Add_Sample: '添加用例',
|
||||
Use_Upload_File:'使用上传文件',
|
||||
Use_Manual_Input:'使用手动输入',
|
||||
Hint: '提示',
|
||||
Source: '来源',
|
||||
Auto_Remove_the_Blank_at_the_End_of_Code:'自动去除代码末尾空白符',
|
||||
Publish_the_Judging_Result_of_Test_Data:'公开评测点数据结果',
|
||||
Edit_Problem: '编辑题目',
|
||||
Create_Problme: '创建题目',
|
||||
Change_Judge_Method:'如果你想改变该题目的判题方法,那么你需要重新上传测试数据。',
|
||||
Add_Tag_Error:'不要添加已有的标签!',
|
||||
|
||||
Upload_Testcase_Successfully:'上传评测数据成功',
|
||||
Upload_Testcase_Failed:'上传评测数据失败',
|
||||
is_required:'不能为空!',
|
||||
Score_must_be_greater_than_or_equal_to_0:'分数必须大于0!',
|
||||
Score_must_be_an_integer:'分数必须是整数!',
|
||||
Spj_Code:'Spj代码',
|
||||
Spj_Code_not_Compile_Success:'Spj代码没有编译成功,请重新编译!',
|
||||
|
||||
|
||||
// /views/admin/problem/ImportAndExport.vue
|
||||
Export_Problem:'导出题目',
|
||||
Export:'导出',
|
||||
Import_Problem:'导入题目',
|
||||
Import_QDOJ_Problem:'导入QDOJ题目',
|
||||
Export_Problem_NULL_Tips:'选择导出的题目不能为空',
|
||||
|
||||
// /views/admin/contest/ContestList.vue
|
||||
Visible:'是否可见',
|
||||
Info:'信息',
|
||||
View_Contest_Problem_List:'查看比赛题目列表',
|
||||
View_Contest_Announcement_List:'查看比赛公告列表',
|
||||
Download_Contest_AC_Submission:'下载比赛通过的提交代码',
|
||||
Exclude_admin_submissions:'排除管理员的提交',
|
||||
Delete_Contest_Tips:'此操作将删除该比赛以及比赛的提交、讨论、公告、记录等数据, 是否继续?',
|
||||
|
||||
// /views/admin/contest/Contest.vue
|
||||
Contest_Title: '比赛标题',
|
||||
Contest_Description: '比赛描述',
|
||||
Contest_Start_Time: '开始时间',
|
||||
Contest_End_Time: '结束时间',
|
||||
Contest_Duration:'比赛时长',
|
||||
Contest_Rule_Type: '比赛赛制',
|
||||
Seal_Time_Rank:'开启封榜',
|
||||
Real_Time_Rank: '实时榜单',
|
||||
Seal_Rank_Time:'封榜时间',
|
||||
Contest_Auth: '比赛权限',
|
||||
Contest_Password:'比赛密码',
|
||||
Contest_Seal_Half_Hour:'比赛结束前半小时',
|
||||
Contest_Seal_An_Hour:'比赛结束前一小时',
|
||||
Contest_Seal_All_Hour:'比赛全程',
|
||||
Edit_Contest:'编辑比赛',
|
||||
Create_Contest:'创建比赛',
|
||||
Contest_Duration_Check:'比赛时长不能小于0',
|
||||
Contets_Time_Check:'开始时间应该早于结束时间',
|
||||
|
||||
// /views/admin/discussion/Discussion.vue
|
||||
Discussion_ID:'讨论ID',
|
||||
Top:'置顶',
|
||||
Discussion_Report:'讨论举报',
|
||||
Reporter:'举报者',
|
||||
Report_Time:'举报时间',
|
||||
View_Report_content:'查看举报内容',
|
||||
View_Discussion:'查看讨论详情',
|
||||
Content:'内容',
|
||||
Report_Content:'举报内容',
|
||||
The_number_of_discussions_selected_cannot_be_empty:'勾选的讨论不能为空',
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import Vue from 'vue'
|
||||
import store from "@/store"
|
||||
import VueI18n from 'vue-i18n'
|
||||
import elenUS from 'element-ui/lib/locale/lang/en'
|
||||
import elzhCN from 'element-ui/lib/locale/lang/zh-CN'
|
||||
|
||||
import storage from '@/common/storage'
|
||||
Vue.use(VueI18n)
|
||||
|
||||
const languages = [
|
||||
|
@ -16,14 +15,14 @@ const messages = {}
|
|||
for (let lang of languages) {
|
||||
let locale = lang.value
|
||||
let m = require(`./oj/${locale}`).m
|
||||
// Object.assign(m, require(`./admin/${locale}`).m)
|
||||
Object.assign(m, require(`./admin/${locale}`).m)
|
||||
messages[locale] = Object.assign({m: m}, lang.el);
|
||||
}
|
||||
|
||||
|
||||
// load language packages
|
||||
export default new VueI18n({
|
||||
locale: store.getters.webLanguage,
|
||||
locale: storage.get('Web_Language') || 'zh-CN',
|
||||
messages: messages
|
||||
})
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ export const m = {
|
|||
Set_New_Password:'Set New Password',
|
||||
Set_New_Password_Msg: 'Please Enter New Password',
|
||||
Set_New_Password_Again_Msg: 'Please Enter New Password Again',
|
||||
The_username_does_not_exists:'The username does not exists.',
|
||||
The_username_does_not_exists:'The username does not exist.',
|
||||
The_email_does_not_exists:'The email does not exist.',
|
||||
Your_password_has_been_reset: 'Your password has been reset.',
|
||||
|
||||
// /components/oj/setting/Account.vue 账号信息管理页面
|
||||
|
@ -193,18 +194,6 @@ export const m = {
|
|||
Copied_failed:'Copied failed',
|
||||
|
||||
|
||||
// 状态码表示的结果
|
||||
|
||||
Accepted: 'Accepted',
|
||||
Time_Limit_Exceeded: 'Time Limit Exceeded',
|
||||
Memory_Limit_Exceeded: 'Memory Limit Exceeded',
|
||||
Runtime_Error: 'Runtime Error',
|
||||
System_Error: 'System Error',
|
||||
Pending: 'Pending',
|
||||
Partial_Accepted: 'Partial Accepted',
|
||||
Compile_Error: 'Compile Error',
|
||||
|
||||
|
||||
// /views/oj/status/SubmissionList.vue
|
||||
Mine:'Mine',
|
||||
Time: 'Time',
|
||||
|
@ -398,5 +387,7 @@ export const m = {
|
|||
Load_More:'Load More',
|
||||
Delete_Comment_Tips:'This operation will delete the comment and all its replies. Do you want to continue?',
|
||||
Delete_Reply_Tips:'This operation will delete the reply. Do you want to continue?',
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ export const m = {
|
|||
Login_No_Account: '没有账号?立即注册!',
|
||||
Login_Forget_Password: '忘记密码',
|
||||
Username_Check_Required:'用户名不能为空',
|
||||
Username_Check_Max:'用户名长度超过255位',
|
||||
Username_Check_Max:'用户名长度不能超过255位',
|
||||
Password_Check_Required:'密码不能为空',
|
||||
Password_Check_Between:'请输入长度为6~20位的密码',
|
||||
Welcome_Back: '欢迎回来~',
|
||||
|
@ -66,6 +66,7 @@ export const m = {
|
|||
Set_New_Password_Msg: '请输入新密码',
|
||||
Set_New_Password_Again_Msg: '请再次输入新密码',
|
||||
The_username_does_not_exists:'用户名不存在',
|
||||
The_email_does_not_exists:'邮箱不存在',
|
||||
Your_password_has_been_reset: '您的密码已重置',
|
||||
|
||||
// /components/oj/setting/Account.vue 账号信息管理页面
|
||||
|
@ -145,7 +146,7 @@ export const m = {
|
|||
Score: '分数',
|
||||
|
||||
// /views/oj/problem/problemList.vue
|
||||
Problem_List:'问题列表',
|
||||
Problem_List:'题目列表',
|
||||
All:'全部',
|
||||
My_OJ:'本OJ',
|
||||
Level:'难度',
|
||||
|
@ -161,8 +162,8 @@ export const m = {
|
|||
Statistic: '统计',
|
||||
Solution:'提交记录',
|
||||
Description: '题目描述',
|
||||
Input: '输入格式',
|
||||
Output: '输出格式',
|
||||
Input: '输入描述',
|
||||
Output: '输出描述',
|
||||
Sample_Input: '样例输入',
|
||||
Sample_Output: '样例输出',
|
||||
Hint: '说明',
|
||||
|
@ -189,7 +190,7 @@ export const m = {
|
|||
You_have_submission_in_this_problem_sure_to_cover_it: '您已经提交过该问题的代码,确定重新提交?',
|
||||
Close:'关闭',
|
||||
Cancel:'取消',
|
||||
OK:'OK',
|
||||
OK:'确定',
|
||||
Copied_successfully:'复制成功',
|
||||
Copied_failed:'复制失败',
|
||||
|
||||
|
@ -222,7 +223,7 @@ export const m = {
|
|||
|
||||
// /views/oj/rank/ACMRank.vue
|
||||
ACM_Ranklist: 'ACM 排行榜',
|
||||
User:'用户名',
|
||||
User:'用户',
|
||||
Nickname:'昵称',
|
||||
Mood: '格言',
|
||||
Rating: '通过率',
|
||||
|
@ -388,6 +389,4 @@ export const m = {
|
|||
Load_More:'加载更多',
|
||||
Delete_Comment_Tips:'此操作将删除该评论及其所有回复, 是否继续?',
|
||||
Delete_Reply_Tips:'此操作将删除该回复, 是否继续?',
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
|
||||
// 引入 view 组件
|
||||
import Login from '@/views/admin/Login'
|
||||
import Home from '@/views/admin/Home'
|
||||
import Dashboard from '@/views/admin/Dashboard'
|
||||
import User from '@/views/admin/general/User'
|
||||
import Announcement from '@/views/admin/general/Announcement'
|
||||
import SystemConfig from '@/views/admin/general/SystemConfig'
|
||||
import ProblemList from '@/views/admin/problem/ProblemList'
|
||||
import Problem from '@/views/admin/problem/Problem'
|
||||
import ProblemImportAndExport from '@/views/admin/problem/ImportAndExport'
|
||||
import Contest from '@/views/admin/contest/Contest'
|
||||
import ContestList from '@/views/admin/contest/ContestList'
|
||||
import DiscussionList from '@/views/admin/discussion/Discussion'
|
||||
const Login= ()=>import('@/views/admin/Login')
|
||||
const Home= ()=>import('@/views/admin/Home')
|
||||
const Dashboard= ()=>import('@/views/admin/Dashboard')
|
||||
const User= ()=>import('@/views/admin/general/User')
|
||||
const Announcement= ()=>import('@/views/admin/general/Announcement')
|
||||
const SystemConfig= ()=>import('@/views/admin/general/SystemConfig')
|
||||
const ProblemList= ()=>import('@/views/admin/problem/ProblemList')
|
||||
const Problem= ()=>import('@/views/admin/problem/Problem')
|
||||
const ProblemImportAndExport= ()=>import('@/views/admin/problem/ImportAndExport')
|
||||
const Contest= ()=>import('@/views/admin/contest/Contest')
|
||||
const ContestList= ()=>import('@/views/admin/contest/ContestList')
|
||||
const DiscussionList= ()=>import('@/views/admin/discussion/Discussion')
|
||||
const adminRoutes= [
|
||||
{
|
||||
path: '/admin/login',
|
||||
|
@ -40,7 +40,7 @@ const adminRoutes= [
|
|||
path: 'user',
|
||||
name: 'admin-user',
|
||||
component: User,
|
||||
meta: { requireSuperAdmin: true,title:'User'},
|
||||
meta: { requireSuperAdmin: true,title:'User Admin'},
|
||||
},
|
||||
{
|
||||
path: 'announcement',
|
||||
|
@ -76,7 +76,7 @@ const adminRoutes= [
|
|||
path: 'problem/batch-operation',
|
||||
name: 'admin-problem_batch_operation',
|
||||
component: ProblemImportAndExport,
|
||||
meta: { title:'Problem Operation'},
|
||||
meta: { title:'Export Import_Problem'},
|
||||
},
|
||||
{
|
||||
path: 'contest/create',
|
||||
|
@ -124,7 +124,7 @@ const adminRoutes= [
|
|||
path: 'discussion',
|
||||
name: 'admin-discussion-list',
|
||||
component: DiscussionList,
|
||||
meta: { title:'Discussion List'}
|
||||
meta: { title:'Discussion Admin'}
|
||||
},
|
||||
]
|
||||
},
|
||||
|
|
|
@ -61,7 +61,7 @@ router.beforeEach((to, from, next) => {
|
|||
})
|
||||
store.commit('changeModalStatus',{mode: 'Login', visible: true})
|
||||
}
|
||||
mMessage.error('对不起!您并非超级管理员,您无权操作,请重新登陆!')
|
||||
mMessage.error('Error!Please Login Again!')
|
||||
store.commit("clearUserInfoAndToken");
|
||||
}
|
||||
}else if(to.matched.some(record => record.meta.requireAdmin)){ //判断是否需要管理员权限
|
||||
|
@ -78,7 +78,7 @@ router.beforeEach((to, from, next) => {
|
|||
})
|
||||
store.commit('changeModalStatus',{mode: 'Login', visible: true})
|
||||
}
|
||||
mMessage.error('对不起!您并非管理员,您无权操作,请重新登录!')
|
||||
mMessage.error('Error!Please Login Again!')
|
||||
store.commit("clearUserInfoAndToken");
|
||||
}
|
||||
}else{
|
||||
|
@ -98,7 +98,7 @@ router.beforeEach((to, from, next) => {
|
|||
store.commit('changeModalStatus',{mode: 'Login', visible: true})
|
||||
}
|
||||
store.commit("clearUserInfoAndToken");
|
||||
mMessage.error('请您先登录!')
|
||||
mMessage.error('Please Login First!')
|
||||
}
|
||||
} else { // 不需要认证的页面
|
||||
next()
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
import Home from '@/views/oj/Home.vue'
|
||||
import SetNewPassword from "@/views/oj/user/SetNewPassword.vue"
|
||||
import UserHome from "@/views/oj/user/UserHome.vue"
|
||||
import Setting from "@/views/oj/user/Setting.vue"
|
||||
import ProblemLIst from "@/views/oj/problem/ProblemList.vue"
|
||||
import Logout from "@/views/oj/user/Logout.vue"
|
||||
import SubmissionList from "@/views/oj/status/SubmissionList.vue"
|
||||
import SubmissionDetails from "@/views/oj/status/SubmissionDetails.vue"
|
||||
import ContestList from "@/views/oj/contest/ContestList.vue"
|
||||
import Problem from "@/views/oj/problem/Problem.vue"
|
||||
import ACMRank from "@/views/oj/rank/ACMRank.vue"
|
||||
import OIRank from "@/views/oj/rank/OIRank.vue"
|
||||
import ContestDetails from "@/views/oj/contest/ContestDetails.vue"
|
||||
import ContestProblemList from "@/views/oj/contest/children/ContestProblemList.vue"
|
||||
import ContestRank from "@/views/oj/contest/children/ContestRank.vue"
|
||||
import ACMInfoAdmin from "@/views/oj/contest/children/ACMInfoAdmin.vue"
|
||||
import Announcements from "@/components/oj/common/Announcements.vue"
|
||||
import ContestComment from "@/views/oj/contest/children/ContestComment.vue"
|
||||
import ContestRejudgeAdmin from "@/views/oj/contest/children/ContestRejudgeAdmin.vue"
|
||||
import DiscussionList from "@/views/oj/discussion/discussionList.vue"
|
||||
import Discussion from "@/views/oj/discussion/discussion.vue"
|
||||
import Introduction from "@/views/oj/about/Introduction.vue"
|
||||
import Developer from "@/views/oj/about/Developer.vue"
|
||||
import NotFound from "@/views/404.vue"
|
||||
const Home= ()=>import('@/views/oj/Home.vue')
|
||||
const SetNewPassword= ()=>import("@/views/oj/user/SetNewPassword.vue")
|
||||
const UserHome= ()=>import("@/views/oj/user/UserHome.vue")
|
||||
const Setting= ()=>import("@/views/oj/user/Setting.vue")
|
||||
const ProblemLIst= ()=>import("@/views/oj/problem/ProblemList.vue")
|
||||
const Logout= ()=>import("@/views/oj/user/Logout.vue")
|
||||
const SubmissionList= ()=>import("@/views/oj/status/SubmissionList.vue")
|
||||
const SubmissionDetails= ()=>import("@/views/oj/status/SubmissionDetails.vue")
|
||||
const ContestList= ()=>import("@/views/oj/contest/ContestList.vue")
|
||||
const Problem= ()=>import("@/views/oj/problem/Problem.vue")
|
||||
const ACMRank= ()=>import("@/views/oj/rank/ACMRank.vue")
|
||||
const OIRank= ()=>import("@/views/oj/rank/OIRank.vue")
|
||||
const ContestDetails= ()=>import("@/views/oj/contest/ContestDetails.vue")
|
||||
const ContestProblemList= ()=>import("@/views/oj/contest/children/ContestProblemList.vue")
|
||||
const ContestRank= ()=>import("@/views/oj/contest/children/ContestRank.vue")
|
||||
const ACMInfoAdmin= ()=>import("@/views/oj/contest/children/ACMInfoAdmin.vue")
|
||||
const Announcements= ()=>import("@/components/oj/common/Announcements.vue")
|
||||
const ContestComment= ()=>import("@/views/oj/contest/children/ContestComment.vue")
|
||||
const ContestRejudgeAdmin= ()=>import("@/views/oj/contest/children/ContestRejudgeAdmin.vue")
|
||||
const DiscussionList= ()=>import("@/views/oj/discussion/discussionList.vue")
|
||||
const Discussion= ()=>import("@/views/oj/discussion/discussion.vue")
|
||||
const Introduction= ()=>import("@/views/oj/about/Introduction.vue")
|
||||
const Developer= ()=>import("@/views/oj/about/Developer.vue")
|
||||
const NotFound= ()=>import("@/views/404.vue")
|
||||
|
||||
|
||||
const ojRoutes = [
|
||||
{
|
||||
path: '/',
|
||||
|
|
|
@ -12,12 +12,16 @@
|
|||
<span class="panel-title admin-info-name">{{
|
||||
userInfo.username
|
||||
}}</span>
|
||||
<p>{{ isSuperAdmin == true ? '超级管理员' : '管理员' }}</p>
|
||||
<p>
|
||||
{{
|
||||
isSuperAdmin == true ? $t('m.Super_Admin') : $t('m.Admin')
|
||||
}}
|
||||
</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="last-info">
|
||||
<p class="last-info-title home-title">Last Login</p>
|
||||
<p class="last-info-title home-title">{{ $t('m.Last_Login') }}</p>
|
||||
<el-form label-width="80px" class="last-info-body">
|
||||
<el-form-item label="Time:">
|
||||
<span>{{ session.gmtCreate | localtime }}</span>
|
||||
|
@ -42,7 +46,7 @@
|
|||
<info-card
|
||||
color="#909399"
|
||||
icon="fa fa-users"
|
||||
message="Total Users"
|
||||
:message="$t('m.Total_Users')"
|
||||
iconSize="30px"
|
||||
class="info-item"
|
||||
:value="infoData.userNum"
|
||||
|
@ -50,14 +54,14 @@
|
|||
<info-card
|
||||
color="#67C23A"
|
||||
icon="fa fa-list"
|
||||
message="Today Submissions"
|
||||
:message="$t('m.Today_Submissions')"
|
||||
class="info-item"
|
||||
:value="infoData.todayJudgeNum"
|
||||
></info-card>
|
||||
<info-card
|
||||
color="#409EFF"
|
||||
icon="fa fa-trophy"
|
||||
message="Recent 14 Days Contests"
|
||||
:message="$t('m.Recent_14_Days_Contests')"
|
||||
class="info-item"
|
||||
:value="infoData.recentContestNum"
|
||||
></info-card>
|
||||
|
@ -65,12 +69,14 @@
|
|||
<!-- <el-card title="System_Overview" v-if="isSuperAdmin"> -->
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">General System</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.Backend_System')
|
||||
}}</span>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="8">
|
||||
<span
|
||||
>HOJ Server Num:
|
||||
>{{ $t('m.Server_Number') }}:
|
||||
<el-tag effect="dark" color="#2d8cf0" size="mini">{{
|
||||
generalInfo.backupService.length
|
||||
}}</el-tag>
|
||||
|
@ -78,7 +84,7 @@
|
|||
</el-col>
|
||||
<el-col :xs="24" :md="8">
|
||||
<span
|
||||
>Nacos Status:
|
||||
>{{ $t('m.Nacos_Status') }}:
|
||||
<el-tag
|
||||
effect="dark"
|
||||
color="#19be6b"
|
||||
|
@ -93,7 +99,7 @@
|
|||
</el-col>
|
||||
<el-col :xs="24" :md="8">
|
||||
<span
|
||||
>Https Status:
|
||||
>{{ $t('m.HTTPS_Status') }}:
|
||||
<el-tag
|
||||
:type="https ? 'success' : 'danger'"
|
||||
size="small"
|
||||
|
@ -104,49 +110,53 @@
|
|||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<h2 class="home-title">Backup Service</h2>
|
||||
<h2 class="home-title">{{ $t('m.Backend_Service') }}</h2>
|
||||
<vxe-table
|
||||
stripe
|
||||
auto-resize
|
||||
:data="generalInfo.backupService"
|
||||
align="center"
|
||||
>
|
||||
<vxe-table-column title="Name" min-width="130">
|
||||
<vxe-table-column :title="$t('m.Name')" min-width="130">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row['serviceId'] }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="host"
|
||||
title="Host"
|
||||
:title="$t('m.Host')"
|
||||
min-width="110"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="port"
|
||||
title="Port"
|
||||
:title="$t('m.Port')"
|
||||
min-width="80"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="80"
|
||||
field="backupCores"
|
||||
title="CPU Core"
|
||||
:title="$t('m.CPU_Core')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="backupPercentCpuLoad"
|
||||
title="CPU Usage"
|
||||
:title="$t('m.CPU_Usage')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="backupPercentMemoryLoad"
|
||||
title="Mem Usage"
|
||||
:title="$t('m.Mem_Usage')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="secure" title="Secure" min-width="80">
|
||||
<vxe-table-column
|
||||
field="secure"
|
||||
:title="$t('m.Secure')"
|
||||
min-width="80"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip content="是否触发保护阈值" placement="top">
|
||||
<el-tag effect="dark" color="#ed3f14" v-if="row.secure"
|
||||
|
@ -156,15 +166,17 @@
|
|||
</el-tooltip>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Healthy" min-width="100">
|
||||
<vxe-table-column :title="$t('m.Healthy_Status')" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tag
|
||||
effect="dark"
|
||||
color="#19be6b"
|
||||
v-if="row.metadata['nacos.healthy'] == 'true'"
|
||||
>Healthy</el-tag
|
||||
>{{ $t('m.Healthy') }}</el-tag
|
||||
>
|
||||
<el-tag effect="dark" color="#f90" v-else>Unhealthy</el-tag>
|
||||
<el-tag effect="dark" color="#f90" v-else>{{
|
||||
$t('m.Unhealthy')
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
|
@ -174,44 +186,48 @@
|
|||
|
||||
<el-card style="margin-top:10px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Judger Service</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Judge_Server') }}</span>
|
||||
</div>
|
||||
<vxe-table stripe auto-resize :data="judgeInfo" align="center">
|
||||
<vxe-table-column type="seq" width="50"></vxe-table-column>
|
||||
<vxe-table-column title="Name" min-width="150">
|
||||
<vxe-table-column :title="$t('m.Name')" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.service['serviceId'] }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Host" min-width="80">
|
||||
<vxe-table-column :title="$t('m.Host')" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.service.host }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Port" min-width="80">
|
||||
<vxe-table-column :title="$t('m.Port')" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<span>{{ row.service.port }}</span>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column min-width="80" field="cpuCores" title="CPU Core">
|
||||
<vxe-table-column
|
||||
min-width="80"
|
||||
field="cpuCores"
|
||||
:title="$t('m.CPU_Core')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="percentCpuLoad"
|
||||
title="CPU Usage"
|
||||
:title="$t('m.CPU_Usage')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column
|
||||
min-width="110"
|
||||
field="percentMemoryLoad"
|
||||
title="Memory Usage"
|
||||
:title="$t('m.Mem_Usage')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column title="Secure" min-width="80">
|
||||
<vxe-table-column :title="$t('m.Secure')" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip content="是否触发保护阈值" placement="top">
|
||||
<el-tag effect="dark" color="#ed3f14" v-if="row.service.secure"
|
||||
|
@ -221,15 +237,17 @@
|
|||
</el-tooltip>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Healthy" min-width="100">
|
||||
<vxe-table-column :title="$t('m.Healthy_Status')" min-width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tag
|
||||
effect="dark"
|
||||
color="#19be6b"
|
||||
v-if="row.service.metadata['nacos.healthy'] == 'true'"
|
||||
>Healthy</el-tag
|
||||
>{{ $t('m.Healthy') }}</el-tag
|
||||
>
|
||||
<el-tag effect="dark" color="#f90" v-else>Unhealthy</el-tag>
|
||||
<el-tag effect="dark" color="#f90" v-else>{{
|
||||
$t('m.Unhealthy')
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</vxe-table>
|
||||
|
@ -240,7 +258,7 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import browserDetector from 'browser-detect';
|
||||
import InfoCard from '@/components/admin/infoCard.vue';
|
||||
const InfoCard = () => import('@/components/admin/infoCard.vue');
|
||||
import api from '@/common/api';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -10,46 +10,61 @@
|
|||
<img :src="imgUrl" alt="oj admin" />
|
||||
</div>
|
||||
<el-menu-item index="/admin/">
|
||||
<i class="fa fa-tachometer" aria-hidden="true"></i>Dashboard
|
||||
<i class="fa fa-tachometer fa-size" aria-hidden="true"></i
|
||||
>{{ $t('m.Dashboard') }}
|
||||
</el-menu-item>
|
||||
<!-- <el-submenu v-if="isSuperAdmin" index="general"> -->
|
||||
<el-submenu index="general" v-if="isSuperAdmin">
|
||||
<template slot="title"><i class="el-icon-menu"></i>General</template>
|
||||
<el-menu-item index="/admin/user">User</el-menu-item>
|
||||
<el-menu-item index="/admin/announcement">Announcement</el-menu-item>
|
||||
<el-menu-item index="/admin/conf">System Config</el-menu-item>
|
||||
<template slot="title"
|
||||
><i class="el-icon-menu"></i>{{ $t('m.General') }}</template
|
||||
>
|
||||
<el-menu-item index="/admin/user">{{
|
||||
$t('m.User_Admin')
|
||||
}}</el-menu-item>
|
||||
<el-menu-item index="/admin/announcement">{{
|
||||
$t('m.Announcement')
|
||||
}}</el-menu-item>
|
||||
<el-menu-item index="/admin/conf">{{
|
||||
$t('m.System_Config')
|
||||
}}</el-menu-item>
|
||||
</el-submenu>
|
||||
<!-- <el-submenu index="problem" v-if="hasProblemPermission"> -->
|
||||
<el-submenu index="problem">
|
||||
<template slot="title"
|
||||
><i class="fa fa-bars" aria-hidden="true"></i>Problem</template
|
||||
>
|
||||
<el-menu-item index="/admin/problems">Problem List</el-menu-item>
|
||||
<el-menu-item index="/admin/problem/create"
|
||||
>Create Problem</el-menu-item
|
||||
>
|
||||
<el-menu-item index="/admin/problem/batch-operation"
|
||||
>Export&Import Problem</el-menu-item
|
||||
><i class="fa fa-bars fa-size" aria-hidden="true"></i
|
||||
>{{ $t('m.Problem') }}</template
|
||||
>
|
||||
<el-menu-item index="/admin/problems">{{
|
||||
$t('m.Problem_List')
|
||||
}}</el-menu-item>
|
||||
<el-menu-item index="/admin/problem/create">{{
|
||||
$t('m.Create_Problem')
|
||||
}}</el-menu-item>
|
||||
<el-menu-item index="/admin/problem/batch-operation">{{
|
||||
$t('m.Export_Import_Problem')
|
||||
}}</el-menu-item>
|
||||
</el-submenu>
|
||||
<el-submenu index="contest">
|
||||
<template slot="title"
|
||||
><i class="fa fa-trophy" aria-hidden="true"></i>Contest</template
|
||||
>
|
||||
<el-menu-item index="/admin/contest">Contest List</el-menu-item>
|
||||
<el-menu-item index="/admin/contest/create"
|
||||
>Create Contest</el-menu-item
|
||||
><i class="fa fa-trophy fa-size" aria-hidden="true"></i
|
||||
>{{ $t('m.Contest') }}</template
|
||||
>
|
||||
<el-menu-item index="/admin/contest">{{
|
||||
$t('m.Contest_List')
|
||||
}}</el-menu-item>
|
||||
<el-menu-item index="/admin/contest/create">{{
|
||||
$t('m.Create_Contest')
|
||||
}}</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
<el-submenu index="discussion">
|
||||
<template slot="title"
|
||||
><i class="fa fa-comments" aria-hidden="true"></i
|
||||
>Discussion</template
|
||||
>
|
||||
<el-menu-item index="/admin/discussion"
|
||||
>Discussion Admin</el-menu-item
|
||||
><i class="fa fa-comments fa-size" aria-hidden="true"></i
|
||||
>{{ $t('m.Discussion') }}</template
|
||||
>
|
||||
<el-menu-item index="/admin/discussion">{{
|
||||
$t('m.Discussion_Admin')
|
||||
}}</el-menu-item>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
<div id="header">
|
||||
|
@ -57,17 +72,20 @@
|
|||
<el-col :span="20">
|
||||
<div class="breadcrumb-container">
|
||||
<el-breadcrumb separator-class="el-icon-arrow-right">
|
||||
<el-breadcrumb-item :to="{ path: '/admin/' }"
|
||||
>Home page</el-breadcrumb-item
|
||||
>
|
||||
<el-breadcrumb-item :to="{ path: '/admin/' }">{{
|
||||
$t('m.Home_Page')
|
||||
}}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="item in routeList" :key="item.path">
|
||||
{{ item.meta.title }}
|
||||
{{ $t('m.' + item.meta.title.replace(' ', '_')) }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4" v-show="isAuthenticated">
|
||||
<i class="fa fa-font katex-editor" @click="katexVisible = true"></i>
|
||||
<i
|
||||
class="fa fa-font katex-editor fa-size"
|
||||
@click="katexVisible = true"
|
||||
></i>
|
||||
<avatar
|
||||
:username="userInfo.username"
|
||||
:inline="true"
|
||||
|
@ -85,7 +103,9 @@
|
|||
}}<i class="el-icon-caret-bottom el-icon--right"></i
|
||||
></span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="logout">Logout</el-dropdown-item>
|
||||
<el-dropdown-item command="logout">{{
|
||||
$t('m.Logout')
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
|
@ -101,7 +121,7 @@
|
|||
{{
|
||||
websiteConfig.shortName
|
||||
? websiteConfig.shortName.toUpperCase() + ' ADMIN'
|
||||
: 'OJ ADMIN'
|
||||
: 'ADMIN'
|
||||
}}
|
||||
<mu-menu slot="right" v-show="isAuthenticated">
|
||||
<mu-button flat @click="katexVisible = true">
|
||||
|
@ -119,7 +139,7 @@
|
|||
<mu-list slot="content" @change="handleCommand">
|
||||
<mu-list-item button value="logout">
|
||||
<mu-list-item-content>
|
||||
<mu-list-item-title>Logout</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.Logout') }}</mu-list-item-title>
|
||||
</mu-list-item-content>
|
||||
</mu-list-item>
|
||||
</mu-list>
|
||||
|
@ -139,7 +159,7 @@
|
|||
<mu-list-item-action>
|
||||
<mu-icon value="dashboard" size="24"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>Dashboard</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.Dashboard') }}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
|
||||
<mu-list-item
|
||||
|
@ -153,7 +173,7 @@
|
|||
<mu-list-item-action>
|
||||
<mu-icon value="view_list"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>General</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.General') }}</mu-list-item-title>
|
||||
<mu-list-item-action>
|
||||
<mu-icon
|
||||
class="toggle-icon"
|
||||
|
@ -169,7 +189,7 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>User</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.User_Admin') }}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
<mu-list-item
|
||||
button
|
||||
|
@ -179,7 +199,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Announcement</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Announcement')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
<mu-list-item
|
||||
button
|
||||
|
@ -189,7 +211,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>System Config</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.System_Config')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
</mu-list-item>
|
||||
|
||||
|
@ -203,7 +227,7 @@
|
|||
<mu-list-item-action>
|
||||
<mu-icon value="menu"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>Problem</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.Problem') }}</mu-list-item-title>
|
||||
<mu-list-item-action>
|
||||
<mu-icon
|
||||
class="toggle-icon"
|
||||
|
@ -219,7 +243,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Problem List</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Problem_List')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
<mu-list-item
|
||||
button
|
||||
|
@ -229,7 +255,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Create Problem</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Create_Problem')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
<mu-list-item
|
||||
button
|
||||
|
@ -239,7 +267,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Export&Import Problem</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Export_Import_Problem')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
</mu-list-item>
|
||||
|
||||
|
@ -251,9 +281,9 @@
|
|||
@toggle-nested="openSideMenu = arguments[0] ? 'contest' : ''"
|
||||
>
|
||||
<mu-list-item-action>
|
||||
<mu-icon value=":fa fa-trophy"></mu-icon>
|
||||
<mu-icon value=":fa fa-trophy fa-size"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>Contest</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.Contest') }}</mu-list-item-title>
|
||||
<mu-list-item-action>
|
||||
<mu-icon
|
||||
class="toggle-icon"
|
||||
|
@ -269,7 +299,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Contest List</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Contest_List')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
<mu-list-item
|
||||
button
|
||||
|
@ -279,7 +311,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Create Contest</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Create_Contest')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
</mu-list-item>
|
||||
|
||||
|
@ -291,9 +325,9 @@
|
|||
@toggle-nested="openSideMenu = arguments[0] ? 'discussion' : ''"
|
||||
>
|
||||
<mu-list-item-action>
|
||||
<mu-icon value=":fa fa-comments"></mu-icon>
|
||||
<mu-icon value=":fa fa-comments fa-size"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>Discussion</mu-list-item-title>
|
||||
<mu-list-item-title>{{ $t('m.Discussion') }}</mu-list-item-title>
|
||||
<mu-list-item-action>
|
||||
<mu-icon
|
||||
class="toggle-icon"
|
||||
|
@ -309,7 +343,9 @@
|
|||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-title>Discussion Admin</mu-list-item-title>
|
||||
<mu-list-item-title>{{
|
||||
$t('m.Discussion_Admin')
|
||||
}}</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
</mu-list-item>
|
||||
</mu-list>
|
||||
|
@ -319,6 +355,28 @@
|
|||
<transition name="fadeInUp" mode="out-in">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
<div class="footer">
|
||||
Powered by
|
||||
<a
|
||||
:href="websiteConfig.projectUrl"
|
||||
style="color:#1E9FFF"
|
||||
target="_blank"
|
||||
>{{ websiteConfig.projectName }}</a
|
||||
>
|
||||
<span style="margin-left:10px">
|
||||
<el-dropdown @command="changeLanguage" placement="top">
|
||||
<span class="el-dropdown-link" style="font-size:14px">
|
||||
<i class="fa fa-globe" aria-hidden="true">
|
||||
{{ this.webLanguage == 'zh-CN' ? '简体中文' : 'English' }}</i
|
||||
><i class="el-icon-arrow-up el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="zh-CN">简体中文</el-dropdown-item>
|
||||
<el-dropdown-item command="en-US">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="Latex Editor" :visible.sync="katexVisible" width="350px">
|
||||
|
@ -329,7 +387,7 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import KatexEditor from '@/components/admin/KatexEditor.vue';
|
||||
const KatexEditor = () => import('@/components/admin/KatexEditor.vue');
|
||||
import api from '@/common/api';
|
||||
import mMessage from '@/common/message';
|
||||
import Avatar from 'vue-avatar';
|
||||
|
@ -381,6 +439,9 @@ export default {
|
|||
let matched = this.$route.matched.filter((item) => item.meta.title); //获取路由信息,并过滤保留路由标题信息存入数组
|
||||
this.routeList = matched;
|
||||
},
|
||||
changeLanguage(language) {
|
||||
this.$store.commit('changeWebLanguage', { language: language });
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
|
@ -388,6 +449,7 @@ export default {
|
|||
'isSuperAdmin',
|
||||
'isAuthenticated',
|
||||
'websiteConfig',
|
||||
'webLanguage',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
|
@ -419,11 +481,12 @@ export default {
|
|||
width: 110px;
|
||||
height: 110px;
|
||||
}
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
width: 24px;
|
||||
.fa-size {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
width: 24px;
|
||||
}
|
||||
a {
|
||||
background-color: transparent;
|
||||
|
@ -462,6 +525,11 @@ img {
|
|||
height: 50px;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.footer {
|
||||
margin: 15px;
|
||||
text-align: center;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1080px) {
|
||||
.content-app {
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<template>
|
||||
<div>
|
||||
<vue-particles
|
||||
color="#dedede"
|
||||
:particleOpacity="0.7"
|
||||
:particlesNumber="80"
|
||||
shapeType="circle"
|
||||
:particleSize="4"
|
||||
linesColor="#dedede"
|
||||
:linesWidth="1"
|
||||
:lineLinked="true"
|
||||
:lineOpacity="0.4"
|
||||
:linesDistance="150"
|
||||
:moveSpeed="3"
|
||||
:hoverEffect="true"
|
||||
hoverMode="grab"
|
||||
:clickEffect="true"
|
||||
clickMode="push"
|
||||
>
|
||||
</vue-particles>
|
||||
<div>
|
||||
<vue-particles
|
||||
color="#dedede"
|
||||
:particleOpacity="0.7"
|
||||
:particlesNumber="80"
|
||||
shapeType="circle"
|
||||
:particleSize="4"
|
||||
linesColor="#dedede"
|
||||
:linesWidth="1"
|
||||
:lineLinked="true"
|
||||
:lineOpacity="0.4"
|
||||
:linesDistance="150"
|
||||
:moveSpeed="3"
|
||||
:hoverEffect="true"
|
||||
hoverMode="grab"
|
||||
:clickEffect="true"
|
||||
clickMode="push"
|
||||
>
|
||||
</vue-particles>
|
||||
<div class="form">
|
||||
<el-form
|
||||
:model="ruleForm2"
|
||||
|
@ -27,13 +27,13 @@
|
|||
label-width="0px"
|
||||
class="demo-ruleForm login-container"
|
||||
>
|
||||
<h1 class="title">HOJ后台管理</h1>
|
||||
<h1 class="title">{{ $t('m.Welcome_to_Login_Admin') }}</h1>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="ruleForm2.username"
|
||||
auto-complete="off"
|
||||
placeholder="Please enter username"
|
||||
:placeholder="$t('m.Please_enter_username')"
|
||||
@keyup.enter.native="handleLogin"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
@ -42,7 +42,7 @@
|
|||
type="password"
|
||||
v-model="ruleForm2.password"
|
||||
auto-complete="off"
|
||||
placeholder="Please enter password"
|
||||
:placeholder="$t('m.Please_enter_password')"
|
||||
@keyup.enter.native="handleLogin"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
@ -52,28 +52,40 @@
|
|||
style="width: 100%"
|
||||
@click.native.prevent="handleLogin"
|
||||
:loading="logining"
|
||||
>GO
|
||||
>{{ $t('m.Login') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "@/common/api";
|
||||
import mMessage from '@/common/message'
|
||||
import api from '@/common/api';
|
||||
import mMessage from '@/common/message';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
logining: false,
|
||||
ruleForm2: {
|
||||
username: "",
|
||||
password: "",
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
rules2: {
|
||||
username: [{ required: true, trigger: "blur" }],
|
||||
password: [{ required: true, trigger: "blur" }],
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: this.$i18n.t('m.Username_Check_Required'),
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: this.$i18n.t('m.Password_Check_Required'),
|
||||
},
|
||||
],
|
||||
},
|
||||
checked: true,
|
||||
};
|
||||
|
@ -83,21 +95,25 @@ export default {
|
|||
this.$refs.ruleForm2.validate((valid) => {
|
||||
if (valid) {
|
||||
this.logining = true;
|
||||
api.admin_login(this.ruleForm2.username, this.ruleForm2.password).then(
|
||||
(res) => {
|
||||
this.logining = false;
|
||||
const jwt = res.headers["authorization"];
|
||||
this.$store.commit('changeUserToken',jwt);
|
||||
this.$store.dispatch('setUserInfo',res.data.data);
|
||||
mMessage.success("尊敬的管理员,欢迎回来~")
|
||||
this.$router.push({ name: "admin-dashboard" });
|
||||
},
|
||||
() => {
|
||||
this.logining = false;
|
||||
}
|
||||
);
|
||||
api
|
||||
.admin_login(this.ruleForm2.username, this.ruleForm2.password)
|
||||
.then(
|
||||
(res) => {
|
||||
this.logining = false;
|
||||
const jwt = res.headers['authorization'];
|
||||
this.$store.commit('changeUserToken', jwt);
|
||||
this.$store.dispatch('setUserInfo', res.data.data);
|
||||
mMessage.success(this.$i18n.t('m.Admin_Login_Success'));
|
||||
this.$router.push({ name: 'admin-dashboard' });
|
||||
},
|
||||
() => {
|
||||
this.logining = false;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
mMessage.error("请检查您的用户名和密码输入是否正确");
|
||||
mMessage.error(
|
||||
this.$i18n.t('m.Please_check_your_username_or_password')
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -121,15 +137,15 @@ export default {
|
|||
.login-container .title {
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
color: #1E9FFF;
|
||||
color: #1e9fff;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.login-container .remember {
|
||||
margin: 0px 0px 35px 0px;
|
||||
}
|
||||
.form{
|
||||
position:relative;
|
||||
z-index:9999;
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,48 +9,48 @@
|
|||
<el-form label-position="top">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Contest Title" required>
|
||||
<el-form-item :label="$t('m.Contest_Title')" required>
|
||||
<el-input
|
||||
v-model="contest.title"
|
||||
placeholder="Enter the Contest Title"
|
||||
:placeholder="$t('m.Contest_Title')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Contest Description" required>
|
||||
<el-form-item :label="$t('m.Contest_Description')" required>
|
||||
<Editor :value.sync="contest.description"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Start Time" required>
|
||||
<el-form-item :label="$t('m.Contest_Start_Time')" required>
|
||||
<el-date-picker
|
||||
v-model="contest.startTime"
|
||||
@change="changeDuration"
|
||||
type="datetime"
|
||||
placeholder="Enter the Contest Start Time"
|
||||
:placeholder="$t('m.Contest_Start_Time')"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest End Time" required>
|
||||
<el-form-item :label="$t('m.Contest_End_Time')" required>
|
||||
<el-date-picker
|
||||
v-model="contest.endTime"
|
||||
@change="changeDuration"
|
||||
type="datetime"
|
||||
placeholder="Enter the Contest End Time"
|
||||
:placeholder="$t('m.Contest_End_Time')"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Duration" required>
|
||||
<el-form-item :label="$t('m.Contest_Duration')" required>
|
||||
<el-input v-model="durationText" disabled> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Type">
|
||||
<el-form-item :label="$t('m.Contest_Rule_Type')">
|
||||
<el-radio
|
||||
class="radio"
|
||||
v-model="contest.type"
|
||||
|
@ -69,7 +69,7 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :md="8" :xs="24" v-if="contest.sealRank">
|
||||
<el-form-item label="Seal Time Rank">
|
||||
<el-form-item :label="$t('m.Seal_Time_Rank')">
|
||||
<el-switch
|
||||
v-model="contest.sealRank"
|
||||
active-color="#13ce66"
|
||||
|
@ -80,7 +80,7 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :md="16" :xs="24" v-else>
|
||||
<el-form-item label="Real Time Rank">
|
||||
<el-form-item :label="$t('m.Real_Time_Rank')">
|
||||
<el-switch
|
||||
v-model="contest.sealRank"
|
||||
active-color="#13ce66"
|
||||
|
@ -92,44 +92,47 @@
|
|||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item
|
||||
label="Seal Rank Time"
|
||||
:label="$t('m.Seal_Rank_Time')"
|
||||
:required="contest.sealRank"
|
||||
v-show="contest.sealRank"
|
||||
>
|
||||
<el-select v-model="seal_rank_time">
|
||||
<el-option
|
||||
label="比赛结束前半小时"
|
||||
:label="$t('m.Contest_Seal_Half_Hour')"
|
||||
:value="0"
|
||||
:disabled="contest.duration < 1800"
|
||||
></el-option>
|
||||
<el-option
|
||||
label="比赛结束前一小时"
|
||||
:label="$t('m.Contest_Seal_An_Hour')"
|
||||
:value="1"
|
||||
:disabled="contest.duration < 3600"
|
||||
></el-option>
|
||||
<el-option label="比赛全程时间" :value="2"></el-option>
|
||||
<el-option
|
||||
:label="$t('m.Contest_Seal_All_Hour')"
|
||||
:value="2"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Auth" required>
|
||||
<el-form-item :label="$t('m.Contest_Auth')" required>
|
||||
<el-select v-model="contest.auth">
|
||||
<el-option label="公开赛" :value="0"></el-option>
|
||||
<el-option label="私有赛" :value="1"></el-option>
|
||||
<el-option label="保护赛" :value="2"></el-option>
|
||||
<el-option :label="$t('m.Public')" :value="0"></el-option>
|
||||
<el-option :label="$t('m.Private')" :value="1"></el-option>
|
||||
<el-option :label="$t('m.Protected')" :value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item
|
||||
label="Contest Password"
|
||||
:label="$t('m.Contest_Password')"
|
||||
v-show="contest.auth != 0"
|
||||
:required="contest.auth != 0"
|
||||
>
|
||||
<el-input
|
||||
v-model="contest.pwd"
|
||||
placeholder="Contest Password"
|
||||
:placeholder="$t('m.Contest_Password')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -150,18 +153,20 @@
|
|||
</el-col> -->
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-button type="primary" @click.native="saveContest">Save</el-button>
|
||||
<el-button type="primary" @click.native="saveContest">{{
|
||||
$t('m.Save')
|
||||
}}</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/common/api';
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import time from '@/common/time';
|
||||
import moment from 'moment';
|
||||
import { mapGetters } from 'vuex';
|
||||
import myMessage from '@/common/message';
|
||||
const Editor = () => import('@/components/admin/Editor.vue');
|
||||
export default {
|
||||
name: 'CreateContest',
|
||||
components: {
|
||||
|
@ -192,7 +197,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
if (this.$route.name === 'admin-edit-contest') {
|
||||
this.title = 'Edit Contest';
|
||||
this.title = this.$i18n.t('m.Edit_Contest');
|
||||
this.disableRuleType = true;
|
||||
this.getContestByCid();
|
||||
}
|
||||
|
@ -200,11 +205,11 @@ export default {
|
|||
watch: {
|
||||
$route() {
|
||||
if (this.$route.name === 'admin-edit-contest') {
|
||||
this.title = 'Edit Contest';
|
||||
this.title = this.$i18n.t('m.Edit_Contest');
|
||||
this.disableRuleType = true;
|
||||
this.getContestByCid();
|
||||
} else {
|
||||
this.title = 'Create Contest';
|
||||
this.title = this.$i18n.t('m.Create_Contest');
|
||||
this.disableRuleType = false;
|
||||
this.contest = [];
|
||||
}
|
||||
|
@ -255,27 +260,45 @@ export default {
|
|||
|
||||
saveContest() {
|
||||
if (!this.contest.title) {
|
||||
myMessage.error('比赛的标题不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Title') + ' ' + this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.contest.description) {
|
||||
myMessage.error('比赛的描述不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Description') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.contest.startTime) {
|
||||
myMessage.error('比赛的开始时间不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Start_Time') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.contest.endTime) {
|
||||
myMessage.error('比赛的结束时间不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_End_Time') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.contest.duration || this.contest.duration <= 0) {
|
||||
myMessage.error('比赛的时长不能小于或等于0!');
|
||||
myMessage.error(this.$i18n.t('m.Contest_Duration_Check'));
|
||||
return;
|
||||
}
|
||||
if (this.contest.auth != 0 && !this.contest.pwd) {
|
||||
myMessage.error('当前的比赛模式密码不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Password') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -315,7 +338,7 @@ export default {
|
|||
|
||||
api[funcName](data)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success('success');
|
||||
this.$router.push({
|
||||
name: 'admin-contest-list',
|
||||
query: { refresh: 'true' },
|
||||
|
@ -328,7 +351,7 @@ export default {
|
|||
let end = this.contest.endTime;
|
||||
let durationMS = time.durationMs(start, end);
|
||||
if (durationMS < 0) {
|
||||
this.durationText = '比赛起始时间不应该晚于结束时间!';
|
||||
this.durationText = this.$i18n.t('m.Contets_Time_Check');
|
||||
this.contest.duration = 0;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<div>
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Contest List</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Contest_List') }}</span>
|
||||
<div class="filter-row">
|
||||
<span>
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
|
@ -25,17 +25,17 @@
|
|||
align="center"
|
||||
>
|
||||
<vxe-table-column field="id" width="80" title="ID"> </vxe-table-column>
|
||||
<vxe-table-column field="title" min-width="150" title="Title">
|
||||
<vxe-table-column field="title" min-width="150" :title="$t('m.Title')">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Type" width="100">
|
||||
<vxe-table-column :title="$t('m.Type')" width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tag type="gray">{{ row.type | parseContestType }}</el-tag>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Auth" width="100">
|
||||
<vxe-table-column :title="$t('m.Auth')" width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip
|
||||
:content="CONTEST_TYPE_REVERSE[row.auth].tips"
|
||||
:content="$t('m.' + CONTEST_TYPE_REVERSE[row.auth].tips)"
|
||||
placement="top"
|
||||
effect="light"
|
||||
>
|
||||
|
@ -48,7 +48,7 @@
|
|||
</el-tooltip>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Status" width="100">
|
||||
<vxe-table-column :title="$t('m.Status')" width="100">
|
||||
<template v-slot="{ row }">
|
||||
<el-tag
|
||||
effect="dark"
|
||||
|
@ -59,7 +59,7 @@
|
|||
</el-tag>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Status" min-width="80">
|
||||
<vxe-table-column :title="$t('m.Visible')" min-width="80">
|
||||
<template v-slot="{ row }">
|
||||
<el-switch
|
||||
v-model="row.visible"
|
||||
|
@ -68,7 +68,7 @@
|
|||
</el-switch>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="210" title="More">
|
||||
<vxe-table-column min-width="210" :title="$t('m.Info')">
|
||||
<template v-slot="{ row }">
|
||||
<p>Start Time: {{ row.startTime | localtime }}</p>
|
||||
<p>End Time: {{ row.endTime | localtime }}</p>
|
||||
|
@ -76,10 +76,10 @@
|
|||
<p>Creator: {{ row.author }}</p>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" title="Option">
|
||||
<vxe-table-column min-width="150" :title="$t('m.Option')">
|
||||
<template v-slot="{ row }">
|
||||
<div style="margin-bottom:10px">
|
||||
<el-tooltip effect="dark" content="编辑比赛" placement="top">
|
||||
<el-tooltip effect="dark" :content="$t('m.Edit')" placement="top">
|
||||
<el-button
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
|
@ -90,7 +90,7 @@
|
|||
</el-tooltip>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="查看比赛题目列表"
|
||||
:content="$t('m.View_Contest_Problem_List')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -105,7 +105,7 @@
|
|||
<div style="margin-bottom:10px">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="查看比赛公告列表"
|
||||
:content="$t('m.View_Contest_Announcement_List')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -119,7 +119,7 @@
|
|||
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="下载通过的提交代码"
|
||||
:content="$t('m.Download_Contest_AC_Submission')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -133,7 +133,7 @@
|
|||
</div>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="删除比赛"
|
||||
:content="$t('m.Delete')"
|
||||
placement="top"
|
||||
v-if="isSuperAdmin"
|
||||
>
|
||||
|
@ -160,16 +160,18 @@
|
|||
</div>
|
||||
</el-card>
|
||||
<el-dialog
|
||||
title="Download Contest Submissions"
|
||||
:title="$t('m.Download_Contest_AC_Submission')"
|
||||
width="320px"
|
||||
:visible.sync="downloadDialogVisible"
|
||||
>
|
||||
<el-switch
|
||||
v-model="excludeAdmin"
|
||||
active-text="Exclude admin submissions"
|
||||
:active-text="$t('m.Exclude_admin_submissions')"
|
||||
></el-switch>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="downloadSubmissions">确 定</el-button>
|
||||
<el-button type="primary" @click="downloadSubmissions">{{
|
||||
$t('m.OK')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
@ -260,24 +262,20 @@ export default {
|
|||
});
|
||||
},
|
||||
deleteContest(contestId) {
|
||||
this.$confirm(
|
||||
'此操作将删除该比赛以及比赛的提交、讨论、公告、记录等数据, 是否继续?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
this.$confirm(this.$i18n.t('m.Delete_Contest_Tips'), 'Tips', {
|
||||
confirmButtonText: this.$i18n.t('m.OK'),
|
||||
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
api.admin_deleteContest(contestId).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Delete_successfully'));
|
||||
this.currentChange(1);
|
||||
});
|
||||
});
|
||||
},
|
||||
changeContestVisible(contestId, visible) {
|
||||
api.admin_changeContestVisible(contestId, visible).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
});
|
||||
},
|
||||
filterByKeyword() {
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
<div>
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Discussion List</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.Discussion_Admin')
|
||||
}}</span>
|
||||
<div class="filter-row">
|
||||
<span>
|
||||
<el-button
|
||||
|
@ -10,13 +12,13 @@
|
|||
icon="el-icon-delete-solid"
|
||||
@click="deleteDiscussion(null)"
|
||||
size="small"
|
||||
>Delete
|
||||
>{{ $t('m.Delete') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span>
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
|
@ -30,6 +32,7 @@
|
|||
auto-resize
|
||||
:data="discussionList"
|
||||
ref="xTable"
|
||||
align="center"
|
||||
:loading="discussionLoadingTable"
|
||||
:checkbox-config="{ highlight: true, range: true }"
|
||||
@checkbox-change="handleSelectionChange"
|
||||
|
@ -39,42 +42,58 @@
|
|||
<vxe-table-column field="id" title="ID" width="60"></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="title"
|
||||
title="Title"
|
||||
:title="$t('m.Title')"
|
||||
min-width="150"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="author"
|
||||
title="Author"
|
||||
:title="$t('m.Author')"
|
||||
min-width="150"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="likeNum"
|
||||
title="Like Num"
|
||||
:title="$t('m.Likes')"
|
||||
min-width="96"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="viewNum"
|
||||
title="View Num"
|
||||
:title="$t('m.Views')"
|
||||
min-width="96"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column field="gmtCreate" title="Create Time" min-width="150">
|
||||
<vxe-table-column
|
||||
field="gmtCreate"
|
||||
:title="$t('m.Created_Time')"
|
||||
min-width="150"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="status" title="Status" min-width="100">
|
||||
<vxe-table-column
|
||||
field="status"
|
||||
:title="$t('m.Status')"
|
||||
min-width="100"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-select
|
||||
v-model="row.status"
|
||||
@change="changeDiscussionStatus(row)"
|
||||
size="small"
|
||||
>
|
||||
<el-option label="正常" :value="0" :key="0"></el-option
|
||||
><el-option label="封禁" :value="1" :key="1"></el-option>
|
||||
<el-option :label="$t('m.Normal')" :value="0" :key="0"></el-option
|
||||
><el-option
|
||||
:label="$t('m.Disable')"
|
||||
:value="1"
|
||||
:key="1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="100" field="topPriority" title="Top">
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="topPriority"
|
||||
:title="$t('m.Top')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-switch
|
||||
v-model="row.topPriority"
|
||||
|
@ -87,9 +106,9 @@
|
|||
</el-switch>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Option" min-width="130">
|
||||
<vxe-table-column :title="$t('m.Option')" min-width="130">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip effect="dark" content="删除讨论" placement="top">
|
||||
<el-tooltip effect="dark" :content="$t('m.Delete')" placement="top">
|
||||
<el-button
|
||||
icon="el-icon-delete-solid"
|
||||
size="mini"
|
||||
|
@ -98,7 +117,11 @@
|
|||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="查看讨论" placement="top">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('m.View_Discussion')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-search"
|
||||
size="mini"
|
||||
|
@ -123,7 +146,9 @@
|
|||
</el-card>
|
||||
<el-card style="margin-top:20px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Discussion Report</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.Discussion_Report')
|
||||
}}</span>
|
||||
</div>
|
||||
<vxe-table
|
||||
:loading="discussionReportLoadingTable"
|
||||
|
@ -135,20 +160,32 @@
|
|||
>
|
||||
<vxe-table-column min-width="60" field="id" title="ID">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="did" title="Discussion ID">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="did"
|
||||
:title="$t('m.Discussion_ID')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="reporter"
|
||||
title="Reporter Name"
|
||||
:title="$t('m.Reporter')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="gmtCreate" title="Report Time">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtCreate"
|
||||
:title="$t('m.Report_Time')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="100" field="status" title="Checked">
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="status"
|
||||
:title="$t('m.Checked')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
|
@ -161,12 +198,12 @@
|
|||
</el-switch>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Content" min-width="150">
|
||||
<vxe-table-column :title="$t('m.Option')" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="点击查看举报内容"
|
||||
:content="$t('m.View_Report_content')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -176,7 +213,11 @@
|
|||
type="success"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="查看讨论" placement="top">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('m.View_Discussion')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-search"
|
||||
size="mini"
|
||||
|
@ -296,7 +337,7 @@ export default {
|
|||
status: row.status,
|
||||
};
|
||||
api.admin_updateDiscussion(discussion).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
});
|
||||
},
|
||||
handleTopSwitch(row) {
|
||||
|
@ -305,7 +346,7 @@ export default {
|
|||
topPriority: row.topPriority,
|
||||
};
|
||||
api.admin_updateDiscussion(discussion).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -315,7 +356,7 @@ export default {
|
|||
status: row.status,
|
||||
};
|
||||
api.admin_updateDiscussionReport(discussionReport).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -328,13 +369,9 @@ export default {
|
|||
didList = this.selectedDiscussions;
|
||||
}
|
||||
if (didList.length > 0) {
|
||||
this.$confirm(
|
||||
'你确定要删除该讨论?这样会关联删除该讨论的评论及回复',
|
||||
'确认',
|
||||
{
|
||||
type: 'warning',
|
||||
}
|
||||
).then(
|
||||
this.$confirm(this.$i18n.t('m.Delete_Discussion_Tips'), 'Tips', {
|
||||
type: 'warning',
|
||||
}).then(
|
||||
() => {
|
||||
api
|
||||
.admin_deleteDiscussion(didList)
|
||||
|
@ -351,21 +388,26 @@ export default {
|
|||
() => {}
|
||||
);
|
||||
} else {
|
||||
myMessage.warning('勾选的讨论不能为空!');
|
||||
myMessage.warning(
|
||||
this.$i18n.t('m.The_number_of_discussions_selected_cannot_be_empty')
|
||||
);
|
||||
}
|
||||
},
|
||||
openReportDialog(content) {
|
||||
let reg = '#(.*?)# ';
|
||||
let re = RegExp(reg, 'g');
|
||||
let tmp;
|
||||
let showContent = '<strong>标签</strong>:';
|
||||
let showContent = '<strong>' + this.$i18n.t('m.Tags') + '</strong>:';
|
||||
while ((tmp = re.exec(content))) {
|
||||
showContent += tmp[1] + ' ';
|
||||
}
|
||||
showContent +=
|
||||
'<br><br><strong>内容</strong>:' + content.replace(/#(.*?)# /g, '');
|
||||
this.$alert(showContent, '举报内容', {
|
||||
confirmButtonText: '确定',
|
||||
'<br><br><strong>' +
|
||||
this.$i18n.t('m.Content') +
|
||||
'</strong>:' +
|
||||
content.replace(/#(.*?)# /g, '');
|
||||
this.$alert(showContent, this.$i18n.t('m.Report_Content'), {
|
||||
confirmButtonText: this.$i18n.t('m.OK'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
<div>
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Announcement</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.General_Announcement')
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="create">
|
||||
<el-button
|
||||
|
@ -10,7 +12,7 @@
|
|||
size="small"
|
||||
@click="openAnnouncementDialog(null)"
|
||||
icon="el-icon-plus"
|
||||
>Create</el-button
|
||||
>{{ $t('m.Create') }}</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="list">
|
||||
|
@ -23,12 +25,16 @@
|
|||
>
|
||||
<vxe-table-column min-width="50" field="id" title="ID">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="title" title="Title">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="title"
|
||||
:title="$t('m.Announcement_Title')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtCreate"
|
||||
title="Create Time"
|
||||
:title="$t('m.Created_Time')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
|
@ -37,15 +43,23 @@
|
|||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtModified"
|
||||
title="Last Update Time"
|
||||
:title="$t('m.Modified_Time')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtModified | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="username" title="Author">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="username"
|
||||
:title="$t('m.Author')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="100" field="status" title="Visible">
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="status"
|
||||
:title="$t('m.Announcement_visible')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
|
@ -63,7 +77,7 @@
|
|||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="编辑公告"
|
||||
:content="$t('m.Edit_Announcement')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -76,7 +90,7 @@
|
|||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="删除公告"
|
||||
:content="$t('m.Delete_Announcement')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
|
@ -112,19 +126,19 @@
|
|||
@open="onOpenEditDialog"
|
||||
>
|
||||
<el-form label-position="top" :model="announcement">
|
||||
<el-form-item label="公告标题" required>
|
||||
<el-form-item :label="$t('m.Announcement_Title')" required>
|
||||
<el-input
|
||||
v-model="announcement.title"
|
||||
placeholder="请输入公告标题"
|
||||
:placeholder="$t('m.Announcement_Title')"
|
||||
class="title-input"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="公告内容" required>
|
||||
<el-form-item :label="$t('m.Announcement_Content')" required>
|
||||
<Editor :value.sync="announcement.content"></Editor>
|
||||
</el-form-item>
|
||||
<div class="visible-box">
|
||||
<span>是否显示</span>
|
||||
<span>{{ $t('m.Announcement_visible') }}</span>
|
||||
<el-switch
|
||||
v-model="announcement.status"
|
||||
:active-value="0"
|
||||
|
@ -139,21 +153,21 @@
|
|||
<el-button
|
||||
type="danger"
|
||||
@click.native="showEditAnnouncementDialog = false"
|
||||
>Cancel</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="submitAnnouncement"
|
||||
>Save</el-button
|
||||
>{{ $t('m.Cancel') }}</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="submitAnnouncement">{{
|
||||
$t('m.OK')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { mapGetters } from 'vuex';
|
||||
const Editor = () => import('@/components/admin/Editor.vue');
|
||||
export default {
|
||||
name: 'announcement',
|
||||
components: {
|
||||
|
@ -278,7 +292,7 @@ export default {
|
|||
api[funcName](requestData)
|
||||
.then((res) => {
|
||||
this.showEditAnnouncementDialog = false;
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Post_successfully'));
|
||||
this.init();
|
||||
})
|
||||
.catch();
|
||||
|
@ -286,9 +300,9 @@ export default {
|
|||
|
||||
// 删除公告
|
||||
deleteAnnouncement(announcementId) {
|
||||
this.$confirm('你确定要删除该公告?', 'Warning', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
this.$confirm(this.$i18n.t('m.Delete_Announcement_Tips'), 'Warning', {
|
||||
confirmButtonText: this.$i18n.t('m.OK'),
|
||||
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -312,11 +326,11 @@ export default {
|
|||
openAnnouncementDialog(row) {
|
||||
this.showEditAnnouncementDialog = true;
|
||||
if (row !== null) {
|
||||
this.announcementDialogTitle = 'Edit Announcement';
|
||||
this.announcementDialogTitle = this.$i18n.t('m.Edit_Announcement');
|
||||
this.announcement = Object.assign({}, row);
|
||||
this.mode = 'edit';
|
||||
} else {
|
||||
this.announcementDialogTitle = 'Create Announcement';
|
||||
this.announcementDialogTitle = this.$i18n.t('m.Create_Announcement');
|
||||
this.announcement.title = '';
|
||||
this.announcement.status = 0;
|
||||
this.announcement.content = '';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Website Config</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Website_Config') }}</span>
|
||||
</div>
|
||||
<el-form
|
||||
label-position="left"
|
||||
|
@ -12,67 +12,67 @@
|
|||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Base URL" required>
|
||||
<el-form-item :label="$t('m.Base_Url')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.baseUrl"
|
||||
placeholder="Website Base URL"
|
||||
:placeholder="$t('m.Base_Url')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Name" required>
|
||||
<el-form-item :label="$t('m.Web_Name')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.name"
|
||||
placeholder="Website Name"
|
||||
:placeholder="$t('m.Web_Name')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Short Name" required>
|
||||
<el-form-item :label="$t('m.Short_Name')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.shortName"
|
||||
placeholder="Website Name Shortcut"
|
||||
:placeholder="$t('m.Short_Name')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Record Name" required>
|
||||
<el-form-item :label="$t('m.Record_Name')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.recordName"
|
||||
placeholder="Website Record Name"
|
||||
:placeholder="$t('m.Record_Name')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Record URL" required>
|
||||
<el-form-item :label="$t('m.Record_Url')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.recordUrl"
|
||||
placeholder="Website Record URL"
|
||||
:placeholder="$t('m.Record_Url')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Project Name" required>
|
||||
<el-form-item :label="$t('m.Project_Name')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.projectName"
|
||||
placeholder="Website Project Namet"
|
||||
:placeholder="$t('m.Project_Name')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Project URL" required>
|
||||
<el-form-item :label="$t('m.Project_Url')" required>
|
||||
<el-input
|
||||
v-model="websiteConfig.projectUrl"
|
||||
placeholder="Website Project URL"
|
||||
:placeholder="$t('m.Project_Url')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="Website Desc" required>
|
||||
<el-form-item :label="$t('m.Web_Desc')" required>
|
||||
<el-input
|
||||
type="textarea"
|
||||
placeholder="Website Description"
|
||||
:placeholder="$t('m.Web_Desc')"
|
||||
v-model="websiteConfig.description"
|
||||
maxlength="150"
|
||||
show-word-limit
|
||||
|
@ -81,7 +81,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="Allow Register" label-width="120px">
|
||||
<el-form-item :label="$t('m.Allow_Register')" label-width="120px">
|
||||
<el-switch
|
||||
v-model="websiteConfig.register"
|
||||
active-color="#13ce66"
|
||||
|
@ -92,55 +92,58 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-button type="primary" @click.native="saveWebsiteConfig" size="small"
|
||||
>Save</el-button
|
||||
<el-button
|
||||
type="primary"
|
||||
@click.native="saveWebsiteConfig"
|
||||
size="small"
|
||||
>{{ $t('m.Save') }}</el-button
|
||||
>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-top:15px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">SMTP Config</span>
|
||||
<span class="panel-title home-title">{{ $t('m.SMTP_Config') }}</span>
|
||||
</div>
|
||||
<el-form label-position="left" label-width="80px" :model="smtp">
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="Host" required label-width="80px">
|
||||
<el-form-item :label="$t('m.Host')" required label-width="80px">
|
||||
<el-input
|
||||
v-model="smtp.emailHost"
|
||||
placeholder="SMTP Host Address"
|
||||
:placeholder="$t('m.Host')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="Port" required label-width="80px">
|
||||
<el-form-item :label="$t('m.Port')" required label-width="80px">
|
||||
<el-input
|
||||
v-model="smtp.emailPort"
|
||||
placeholder="SMTP Port"
|
||||
:placeholder="$t('m.Port')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Username" required label-width="80px">
|
||||
<el-form-item :label="$t('m.Email')" required label-width="80px">
|
||||
<el-input
|
||||
v-model="smtp.emailUsername"
|
||||
placeholder="Account Username"
|
||||
:placeholder="$t('m.Email')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Password" label-width="80px" required>
|
||||
<el-form-item :label="$t('m.Password')" label-width="80px" required>
|
||||
<el-input
|
||||
v-model="smtp.emailPassword"
|
||||
type="password"
|
||||
placeholder="SMTP Server Password"
|
||||
:placeholder="$t('m.Password')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="BG IMG" label-width="80px" required>
|
||||
<el-form-item :label="$t('m.Email_BG')" label-width="80px" required>
|
||||
<el-input
|
||||
v-model="smtp.emailBGImg"
|
||||
placeholder="SMTP Template Background IMG Address"
|
||||
:placeholder="$t('m.Email_BG_Desc')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -151,90 +154,123 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-button type="primary" @click.native="saveSMTPConfig" size="small"
|
||||
>Save</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="saveSMTPConfig" size="small">{{
|
||||
$t('m.Save')
|
||||
}}</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click.native="testSMTPConfig"
|
||||
v-if="saved"
|
||||
:loading="loadingBtnTest"
|
||||
size="small"
|
||||
>Send Test Email</el-button
|
||||
>{{ $t('m.Send_Test_Email') }}</el-button
|
||||
>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-top:15px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">DataBase Config</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.DataSource_Config')
|
||||
}}</span>
|
||||
</div>
|
||||
<el-form label-position="top" :model="databaseConfig">
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="MySQL Host" required label-width="80px">
|
||||
<el-form-item
|
||||
:label="'MySQL ' + $t('m.Host')"
|
||||
required
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.dbHost"
|
||||
placeholder="MySQL Host Address"
|
||||
:placeholder="'MySQL ' + $t('m.Host')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="MySQL Port" required label-width="80px">
|
||||
<el-form-item
|
||||
:label="'MySQL ' + $t('m.Port')"
|
||||
required
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input
|
||||
type="number"
|
||||
v-model="databaseConfig.dbPost"
|
||||
placeholder="MySQL Port"
|
||||
:placeholder="'MySQL ' + $t('m.Port')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="MySQL Username" required label-width="80px">
|
||||
<el-form-item
|
||||
:label="'MySQL ' + $t('m.Username')"
|
||||
required
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.dbUsername"
|
||||
placeholder="MySQL Username"
|
||||
:placeholder="'MySQL ' + $t('m.Username')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="MySQL Password" label-width="80px" required>
|
||||
<el-form-item
|
||||
:label="'MySQL ' + $t('m.Password')"
|
||||
label-width="80px"
|
||||
required
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.dbPassword"
|
||||
type="password"
|
||||
placeholder="MySQL Password"
|
||||
:placeholder="'MySQL ' + $t('m.Password')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Redis Host" label-width="80px" required>
|
||||
<el-form-item
|
||||
:label="'Redis ' + $t('m.Host')"
|
||||
label-width="80px"
|
||||
required
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.redisHost"
|
||||
type="password"
|
||||
placeholder="Redis Host Address"
|
||||
:placeholder="'Redis ' + $t('m.Host')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Redis Port" label-width="80px" required>
|
||||
<el-form-item
|
||||
:label="'Redis ' + $t('m.Port')"
|
||||
label-width="80px"
|
||||
required
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.redisPort"
|
||||
type="password"
|
||||
placeholder="Redis Port"
|
||||
:placeholder="'Redis ' + $t('m.Port')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Redis Password" label-width="80px" required>
|
||||
<el-form-item
|
||||
:label="'Redis ' + $t('m.Password')"
|
||||
label-width="80px"
|
||||
required
|
||||
>
|
||||
<el-input
|
||||
v-model="databaseConfig.redisPassword"
|
||||
type="password"
|
||||
placeholder="Redis Password"
|
||||
:placeholder="'Redis ' + $t('m.Password')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-button type="primary" @click.native="saveDataBaseConfig" size="small"
|
||||
>Save</el-button
|
||||
<el-button
|
||||
type="primary"
|
||||
@click.native="saveDataBaseConfig"
|
||||
size="small"
|
||||
>{{ $t('m.Save') }}</el-button
|
||||
>
|
||||
</el-card>
|
||||
</div>
|
||||
|
@ -268,7 +304,7 @@ export default {
|
|||
this.smtp = res.data.data;
|
||||
} else {
|
||||
this.init = true;
|
||||
myMessage.warning('请先添加STMP的配置!');
|
||||
myMessage.warning('No STMP Config');
|
||||
}
|
||||
});
|
||||
api
|
||||
|
@ -288,22 +324,22 @@ export default {
|
|||
saveSMTPConfig() {
|
||||
api.admin_editSMTPConfig(this.smtp).then(
|
||||
(res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
this.saved = true;
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
},
|
||||
testSMTPConfig() {
|
||||
this.$prompt('Please input your email', '', {
|
||||
this.$prompt(this.$i18n.t('m.Please_input_your_email'), '', {
|
||||
inputPattern: /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
|
||||
inputErrorMessage: 'Error email format',
|
||||
inputErrorMessage: this.$i18n.t('m.Email_Check_Format'),
|
||||
})
|
||||
.then(({ value }) => {
|
||||
this.loadingBtnTest = true;
|
||||
api.admin_testSMTPConfig(value).then(
|
||||
(res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Post_Successfully'));
|
||||
this.loadingBtnTest = false;
|
||||
},
|
||||
() => {
|
||||
|
@ -314,10 +350,19 @@ export default {
|
|||
.catch(() => {});
|
||||
},
|
||||
saveWebsiteConfig() {
|
||||
for (var key in this.websiteConfig) {
|
||||
if (key == 'register') {
|
||||
continue;
|
||||
} else {
|
||||
if (this.websiteConfig[key].replace(/(^\s*)|(\s*$)/g, '')) {
|
||||
this.websiteConfig[key] = 'None';
|
||||
}
|
||||
}
|
||||
}
|
||||
api
|
||||
.admin_editWebsiteConfig(this.websiteConfig)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
@ -325,7 +370,7 @@ export default {
|
|||
api
|
||||
.admin_editDataBaseConfig(this.databaseConfig)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="view">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">User</span>
|
||||
<span class="panel-title home-title">{{ $t('m.General_User') }}</span>
|
||||
<div class="filter-row">
|
||||
<span>
|
||||
<el-button
|
||||
|
@ -10,13 +10,13 @@
|
|||
icon="el-icon-delete-solid"
|
||||
@click="deleteUsers(null)"
|
||||
size="small"
|
||||
>Delete
|
||||
>{{ $t('m.Delete') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span>
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
|
@ -26,10 +26,10 @@
|
|||
<span>
|
||||
<el-switch
|
||||
v-model="onlyAdmin"
|
||||
active-text="OnlyAdmin"
|
||||
:active-text="$t('m.OnlyAdmin')"
|
||||
:width="40"
|
||||
@change="filterByAdmin"
|
||||
inactive-text="All"
|
||||
:inactive-text="$t('m.All')"
|
||||
>
|
||||
</el-switch>
|
||||
</span>
|
||||
|
@ -53,40 +53,58 @@
|
|||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="username"
|
||||
title="Username"
|
||||
:title="$t('m.User')"
|
||||
min-width="140"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="realname"
|
||||
title="Real Name"
|
||||
:title="$t('m.RealName')"
|
||||
min-width="140"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column
|
||||
field="email"
|
||||
title="Email"
|
||||
:title="$t('m.Email')"
|
||||
min-width="150"
|
||||
></vxe-table-column>
|
||||
<vxe-table-column field="gmtCreate" title="Create Time" min-width="150">
|
||||
<vxe-table-column
|
||||
field="gmtCreate"
|
||||
:title="$t('m.Created_Time')"
|
||||
min-width="150"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="role" title="User Type" min-width="100">
|
||||
<vxe-table-column
|
||||
field="role"
|
||||
:title="$t('m.User_Type')"
|
||||
min-width="100"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ getRole(row.roles) | parseRole }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="status" title="Status" min-width="100">
|
||||
<vxe-table-column
|
||||
field="status"
|
||||
:title="$t('m.Status')"
|
||||
min-width="100"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-tag effect="dark" color="#19be6b" v-if="row.status == 0"
|
||||
>正常</el-tag
|
||||
>
|
||||
<el-tag effect="dark" color="#ed3f14" v-else>禁用</el-tag>
|
||||
<el-tag effect="dark" color="#19be6b" v-if="row.status == 0">{{
|
||||
$t('m.Normal')
|
||||
}}</el-tag>
|
||||
<el-tag effect="dark" color="#ed3f14" v-else>{{
|
||||
$t('m.Disable')
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Option" min-width="150">
|
||||
<vxe-table-column :title="$t('m.Option')" min-width="150">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip effect="dark" content="编辑用户" placement="top">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('m.Edit_User')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-edit-outline"
|
||||
size="mini"
|
||||
|
@ -95,7 +113,11 @@
|
|||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="删除用户" placement="top">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('m.Delete_User')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-delete-solid"
|
||||
size="mini"
|
||||
|
@ -122,15 +144,12 @@
|
|||
<!-- 导入csv用户数据 -->
|
||||
<el-card style="margin-top:20px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Import Users</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Import_User') }}</span>
|
||||
</div>
|
||||
<p>1. 用户数据导入仅支持csv格式的用户数据。</p>
|
||||
<p>
|
||||
2. 共三列数据:
|
||||
用户名,密码,邮箱,任一列不能为空,否则该行数据可能导入失败。
|
||||
</p>
|
||||
<p>3. 第一行不必写(“用户名”,“密码”,“邮箱”)这三个列名。</p>
|
||||
<p>4. 请导入保存为UTF-8编码的文件,否则中文可能会乱码。</p>
|
||||
<p>1. {{ $t('m.Import_User_Tips1') }}</p>
|
||||
<p>2. {{ $t('m.Import_User_Tips2') }}</p>
|
||||
<p>3. {{ $t('m.Import_User_Tips3') }}</p>
|
||||
<p>4. {{ $t('m.Import_User_Tips4') }}</p>
|
||||
<el-upload
|
||||
v-if="!uploadUsers.length"
|
||||
action=""
|
||||
|
@ -138,23 +157,35 @@
|
|||
accept=".csv"
|
||||
:before-upload="handleUsersCSV"
|
||||
>
|
||||
<el-button size="small" icon="el-icon-folder-opened" type="primary"
|
||||
>Choose File</el-button
|
||||
>
|
||||
<el-button size="small" icon="el-icon-folder-opened" type="primary">{{
|
||||
$t('m.Choose_File')
|
||||
}}</el-button>
|
||||
</el-upload>
|
||||
<template v-else>
|
||||
<vxe-table :data="uploadUsersPage" stripe auto-resize>
|
||||
<vxe-table-column title="Username" field="username" min-width="150">
|
||||
<vxe-table-column
|
||||
:title="$t('m.Username')"
|
||||
field="username"
|
||||
min-width="150"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row[0] }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Password" field="password" min-width="150">
|
||||
<vxe-table-column
|
||||
:title="$t('m.Password')"
|
||||
field="password"
|
||||
min-width="150"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row[1] }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column title="Email" field="email" min-width="150">
|
||||
<vxe-table-column
|
||||
:title="$t('m.Email')"
|
||||
field="email"
|
||||
min-width="150"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row[2] }}
|
||||
</template>
|
||||
|
@ -167,14 +198,14 @@
|
|||
size="small"
|
||||
icon="el-icon-upload"
|
||||
@click="handleUsersUpload"
|
||||
>Upload All
|
||||
>{{ $t('m.Upload_All') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="el-icon-delete"
|
||||
@click="handleResetData"
|
||||
>Clear All
|
||||
>{{ $t('m.Clear_All') }}
|
||||
</el-button>
|
||||
<el-pagination
|
||||
class="page"
|
||||
|
@ -191,7 +222,7 @@
|
|||
<!--生成用户数据-->
|
||||
<el-card style="margin-top:20px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Generate Users</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Generate_User') }}</span>
|
||||
</div>
|
||||
<el-form
|
||||
:model="formGenerateUser"
|
||||
|
@ -200,7 +231,7 @@
|
|||
>
|
||||
<el-row :gutter="10">
|
||||
<el-col :md="5" :xs="24">
|
||||
<el-form-item label="Prefix" prop="prefix">
|
||||
<el-form-item :label="$t('m.Prefix')" prop="prefix">
|
||||
<el-input
|
||||
v-model="formGenerateUser.prefix"
|
||||
placeholder="Prefix"
|
||||
|
@ -208,7 +239,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="5" :xs="24">
|
||||
<el-form-item label="Suffix" prop="suffix">
|
||||
<el-form-item :label="$t('m.Suffix')" prop="suffix">
|
||||
<el-input
|
||||
v-model="formGenerateUser.suffix"
|
||||
placeholder="Suffix"
|
||||
|
@ -216,7 +247,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="5" :xs="24">
|
||||
<el-form-item label="Start Number" prop="number_from">
|
||||
<el-form-item :label="$t('m.Start_Number')" prop="number_from">
|
||||
<el-input-number
|
||||
v-model="formGenerateUser.number_from"
|
||||
style="width: 100%"
|
||||
|
@ -224,7 +255,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="5" :xs="24">
|
||||
<el-form-item label="End Number" prop="number_to">
|
||||
<el-form-item :label="$t('m.End_Number')" prop="number_to">
|
||||
<el-input-number
|
||||
v-model="formGenerateUser.number_to"
|
||||
style="width: 100%"
|
||||
|
@ -232,10 +263,13 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="4" :xs="24">
|
||||
<el-form-item label="Password Length" prop="password_length">
|
||||
<el-form-item
|
||||
:label="$t('m.Password_Length')"
|
||||
prop="password_length"
|
||||
>
|
||||
<el-input
|
||||
v-model.number="formGenerateUser.password_length"
|
||||
placeholder="Password Length"
|
||||
:placeholder="$t('m.Password_Length')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -249,13 +283,13 @@
|
|||
:loading="loadingGenerate"
|
||||
size="small"
|
||||
>
|
||||
Generate & Export
|
||||
{{ $t('m.Generate_and_Export') }}
|
||||
</el-button>
|
||||
<span
|
||||
class="userPreview"
|
||||
v-if="formGenerateUser.number_from <= formGenerateUser.number_to"
|
||||
>
|
||||
The usernames will be
|
||||
{{ $t('m.The_usernames_will_be') }}
|
||||
{{
|
||||
formGenerateUser.prefix +
|
||||
formGenerateUser.number_from +
|
||||
|
@ -290,7 +324,11 @@
|
|||
</el-card>
|
||||
|
||||
<!--编辑用户的对话框-->
|
||||
<el-dialog title="User Info" :visible.sync="showUserDialog" width="350px">
|
||||
<el-dialog
|
||||
:title="$t('m.User')"
|
||||
:visible.sync="showUserDialog"
|
||||
width="350px"
|
||||
>
|
||||
<el-form
|
||||
:model="selectUser"
|
||||
label-width="100px"
|
||||
|
@ -300,22 +338,22 @@
|
|||
>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Username" required prop="username">
|
||||
<el-form-item :label="$t('m.Username')" required prop="username">
|
||||
<el-input v-model="selectUser.username"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Real Name" prop="realname">
|
||||
<el-form-item :label="$t('m.RealName')" prop="realname">
|
||||
<el-input v-model="selectUser.realname"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Email" prop="email">
|
||||
<el-form-item :label="$t('m.Email')" prop="email">
|
||||
<el-input v-model="selectUser.email"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Set NewPwd">
|
||||
<el-form-item :label="$t('m.Set_New_PWD')">
|
||||
<el-switch
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
|
@ -325,15 +363,19 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" v-if="selectUser.setNewPwd == 1">
|
||||
<el-form-item label="New Pwd" required prop="password">
|
||||
<el-form-item
|
||||
:label="$t('m.General_New_Password')"
|
||||
required
|
||||
prop="password"
|
||||
>
|
||||
<el-input
|
||||
v-model="selectUser.password"
|
||||
placeholder="Enter the new password"
|
||||
:placeholder="$t('m.General_New_Password')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="User Type">
|
||||
<el-form-item :label="$t('m.User_Type')">
|
||||
<el-select v-model="selectUser.type">
|
||||
<el-option
|
||||
label="超级管理员"
|
||||
|
@ -375,7 +417,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Status">
|
||||
<el-form-item :label="$t('m.Status')">
|
||||
<el-switch
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
|
@ -387,10 +429,12 @@
|
|||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="danger" @click.native="showUserDialog = false"
|
||||
>Cancel</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="saveUser">Save</el-button>
|
||||
<el-button type="danger" @click.native="showUserDialog = false">{{
|
||||
$t('m.Cancel')
|
||||
}}</el-button>
|
||||
<el-button type="primary" @click.native="saveUser">{{
|
||||
$t('m.OK')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
@ -406,13 +450,25 @@ export default {
|
|||
data() {
|
||||
const CheckTogtFrom = (rule, value, callback) => {
|
||||
if (value < this.formGenerateUser.number_from) {
|
||||
callback(new Error('用户结束编号不能小于起始编号'));
|
||||
callback(
|
||||
new Error(
|
||||
this.$i18n.t(
|
||||
'm.The_end_number_cannot_be_less_than_the_start_number'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const CheckPwdLength = (rule, value, callback) => {
|
||||
if (value < 6 || value > 25) {
|
||||
callback(new Error('密码长度请选择6~25字符长度'));
|
||||
callback(
|
||||
new Error(
|
||||
this.$i18n.t(
|
||||
'm.Please_select_6_to_25_characters_for_password_length'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
@ -423,7 +479,7 @@ export default {
|
|||
res.data.data.username === true &&
|
||||
value != this.selectUser.username
|
||||
) {
|
||||
callback(new Error('用户名已存在'));
|
||||
callback(new Error(this.$i18n.t('m.The_username_already_exists')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -435,7 +491,7 @@ export default {
|
|||
api.checkUsernameOrEmail(undefined, value).then(
|
||||
(res) => {
|
||||
if (res.data.data.email === true && value != this.selectUser.email) {
|
||||
callback(new Error('邮箱已存在'));
|
||||
callback(new Error(this.$i18n.t('m.The_email_already_exists')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -477,30 +533,29 @@ export default {
|
|||
{
|
||||
validator: CheckUsernameNotExist,
|
||||
trigger: 'blur',
|
||||
message: 'The username already exists',
|
||||
message: this.$i18n.t('m.The_username_already_exists'),
|
||||
},
|
||||
{
|
||||
max: 255,
|
||||
message: 'The longest length of a username is 255',
|
||||
message: this.$i18n.t('m.Username_Check_Max'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
realname: [
|
||||
{
|
||||
max: 255,
|
||||
message: 'The longest length of a username is 255',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
email: [
|
||||
{
|
||||
type: 'email',
|
||||
message: 'The email format is incorrect',
|
||||
message: this.$i18n.t('m.Email_Check_Format'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
validator: CheckEmailNotExist,
|
||||
message: 'The email already exists',
|
||||
message: this.$i18n.t('m.The_email_already_exists'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
|
@ -519,17 +574,29 @@ export default {
|
|||
},
|
||||
formGenerateRules: {
|
||||
number_from: [
|
||||
{ required: true, message: '编号起始不能为空', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: this.$i18n.t('m.Start_Number_Required'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
number_to: [
|
||||
{ required: true, message: '最大编号不能为空', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: this.$i18n.t('m.End_Number_Required'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ validator: CheckTogtFrom, trigger: 'blur' },
|
||||
],
|
||||
password_length: [
|
||||
{ required: true, message: '密码长度不能为空', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: this.$i18n.t('m.Password_Check_Required'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
message: '密码长度必须为数字类型',
|
||||
message: this.$i18n.t('m.Password_Length_Checked'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ validator: CheckPwdLength, trigger: 'blur' },
|
||||
|
@ -554,7 +621,7 @@ export default {
|
|||
.admin_editUser(this.selectUser)
|
||||
.then((res) => {
|
||||
// 更新列表
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
this.getUserList(this.currentPage);
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -606,13 +673,11 @@ export default {
|
|||
ids = this.selectedUsers;
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
this.$confirm(
|
||||
'你确定要删除该用户?可能会关联删除该用户创建的公告,题目,比赛等。',
|
||||
'确认',
|
||||
{
|
||||
type: 'warning',
|
||||
}
|
||||
).then(
|
||||
this.$confirm(this.$i18n.t('m.Delete_User_Tips'), 'Tips', {
|
||||
confirmButtonText: this.$i18n.t('m.OK'),
|
||||
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||
type: 'warning',
|
||||
}).then(
|
||||
() => {
|
||||
api
|
||||
.admin_deleteUsers(ids)
|
||||
|
@ -629,7 +694,9 @@ export default {
|
|||
() => {}
|
||||
);
|
||||
} else {
|
||||
myMessage.warning('勾选的用户不能为空!');
|
||||
myMessage.warning(
|
||||
this.$i18n.t('m.The_number_of_users_selected_cannot_be_empty')
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -651,7 +718,7 @@ export default {
|
|||
generateUser() {
|
||||
this.$refs['formGenerateUser'].validate((valid) => {
|
||||
if (!valid) {
|
||||
myMessage.error('请在生成用户名规则中选择或输入正确的值');
|
||||
myMessage.error(this.$i18n.t('m.Error_Please_check_your_choice'));
|
||||
return;
|
||||
}
|
||||
this.loadingGenerate = true;
|
||||
|
@ -661,12 +728,9 @@ export default {
|
|||
.then((res) => {
|
||||
this.loadingGenerate = false;
|
||||
myMessage.success(res.data.msg);
|
||||
let url = '/file/generate-user-excel?key=' + res.data.data.key;
|
||||
let url = '/api/file/generate-user-excel?key=' + res.data.data.key;
|
||||
utils.downloadFile(url).then(() => {
|
||||
this.$alert(
|
||||
'所有指定格式用户创建成功,用户表已成功下载到您的电脑里了!',
|
||||
'提醒'
|
||||
);
|
||||
this.$alert(this.$i18n.t('m.Generate_User_Success'), 'Tips');
|
||||
});
|
||||
this.getUserList(1);
|
||||
})
|
||||
|
@ -684,7 +748,7 @@ export default {
|
|||
let delta = results.data.length - data.length;
|
||||
if (delta > 0) {
|
||||
myMessage.warning(
|
||||
delta + '行用户数据被过滤,原因是可能为空行或某个列值为空'
|
||||
delta + this.$i18n.t('m.Generate_Skipped_Reason')
|
||||
);
|
||||
}
|
||||
this.uploadUsersCurrentPage = 1;
|
||||
|
@ -702,7 +766,7 @@ export default {
|
|||
.then((res) => {
|
||||
this.getUserList(1);
|
||||
this.handleResetData();
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Upload_Users_Successfully'));
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Export Problems</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Export_Problem') }}</span>
|
||||
<div class="filter-row">
|
||||
<span>
|
||||
<el-button
|
||||
|
@ -10,13 +10,13 @@
|
|||
size="small"
|
||||
@click="exportProblems"
|
||||
icon="el-icon-arrow-down"
|
||||
>Export
|
||||
>{{ $t('m.Export') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span>
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@keyup.enter.native="filterByKeyword"
|
||||
|
@ -38,12 +38,16 @@
|
|||
<vxe-table-column type="checkbox" width="60"> </vxe-table-column>
|
||||
<vxe-table-column title="ID" min-width="100" field="id">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" title="Title" field="title">
|
||||
<vxe-table-column min-width="150" :title="$t('m.Title')" field="title">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="author" title="Author">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="author"
|
||||
:title="$t('m.Author')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
|
||||
<vxe-table-column field="gmtCreate" title="Create Time">
|
||||
<vxe-table-column field="gmtCreate" :title="$t('m.Created_Time')">
|
||||
<template v-slot="{ row }">
|
||||
{{ row.create_time | localtime }}
|
||||
</template>
|
||||
|
@ -64,7 +68,7 @@
|
|||
|
||||
<el-card style="margin-top:15px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Import Problems</span>
|
||||
<span class="panel-title home-title">{{ $t('m.Import_Problem') }}</span>
|
||||
</div>
|
||||
<el-upload
|
||||
ref="HOJ"
|
||||
|
@ -84,7 +88,7 @@
|
|||
type="primary"
|
||||
slot="trigger"
|
||||
icon="el-icon-folder-opened"
|
||||
>Choose File</el-button
|
||||
>{{ $t('m.Choose_File') }}</el-button
|
||||
>
|
||||
<el-button
|
||||
style="margin-left: 10px;"
|
||||
|
@ -92,14 +96,16 @@
|
|||
type="success"
|
||||
@click="submitUpload('HOJ')"
|
||||
icon="el-icon-upload"
|
||||
>Upload</el-button
|
||||
>{{ $t('m.Upload') }}</el-button
|
||||
>
|
||||
</el-upload>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-top:15px">
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">Import QDOJ Problems</span>
|
||||
<span class="panel-title home-title">{{
|
||||
$t('m.Import_QDOJ_Problem')
|
||||
}}</span>
|
||||
</div>
|
||||
<el-upload
|
||||
ref="QDOJ"
|
||||
|
@ -119,7 +125,7 @@
|
|||
type="primary"
|
||||
slot="trigger"
|
||||
icon="el-icon-folder-opened"
|
||||
>Choose File</el-button
|
||||
>{{ $t('m.Choose_File') }}</el-button
|
||||
>
|
||||
<el-button
|
||||
style="margin-left: 10px;"
|
||||
|
@ -127,7 +133,7 @@
|
|||
type="success"
|
||||
@click="submitUpload('QDOJ')"
|
||||
icon="el-icon-upload"
|
||||
>Upload</el-button
|
||||
>{{ $t('m.Upload') }}</el-button
|
||||
>
|
||||
</el-upload>
|
||||
</el-card>
|
||||
|
@ -183,6 +189,10 @@ export default {
|
|||
},
|
||||
exportProblems() {
|
||||
let params = [];
|
||||
if (this.selected_problems.length <= 0) {
|
||||
myMessage.error(this.$i18n.t('m.Export_Problem_NULL_Tips'));
|
||||
return;
|
||||
}
|
||||
for (let p of this.selected_problems) {
|
||||
params.push('pid=' + p.id);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,13 @@
|
|||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="problemId" label="Problem ID" required>
|
||||
<el-form-item
|
||||
prop="problemId"
|
||||
:label="$t('m.Problem_Display_ID')"
|
||||
required
|
||||
>
|
||||
<el-input
|
||||
placeholder="Enter the display id of problem"
|
||||
:placeholder="$t('m.Problem_Display_ID')"
|
||||
v-model="problem.problemId"
|
||||
>
|
||||
</el-input>
|
||||
|
@ -24,9 +28,9 @@
|
|||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="title" label="Title" required>
|
||||
<el-form-item prop="title" :label="$t('m.Title')" required>
|
||||
<el-input
|
||||
placeholder="Enter the title of problem"
|
||||
:placeholder="$t('m.Title')"
|
||||
v-model="problem.title"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
@ -35,18 +39,18 @@
|
|||
|
||||
<el-row :gutter="20" v-if="contestID">
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Display Title" required>
|
||||
<el-form-item :label="$t('m.Contest_Display_Title')" required>
|
||||
<el-input
|
||||
placeholder="Enter the display title of problem in contest"
|
||||
:placeholder="$t('m.Contest_Display_Title')"
|
||||
v-model="contestProblem.displayTitle"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="12" :xs="24">
|
||||
<el-form-item label="Display ID" required>
|
||||
<el-form-item :label="$t('m.Contest_Display_ID')" required>
|
||||
<el-input
|
||||
placeholder="Enter the display ID of problem in contest"
|
||||
:placeholder="$t('m.Contest_Display_ID')"
|
||||
v-model="contestProblem.displayId"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
@ -55,7 +59,11 @@
|
|||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="description" label="Description" required>
|
||||
<el-form-item
|
||||
prop="description"
|
||||
:label="$t('m.Description')"
|
||||
required
|
||||
>
|
||||
<Editor :value.sync="problem.description"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -63,37 +71,37 @@
|
|||
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="6" :xs="24">
|
||||
<el-form-item label="Time Limit(ms)" required>
|
||||
<el-form-item :label="$t('m.Time_Limit') + '(ms)'" required>
|
||||
<el-input
|
||||
type="Number"
|
||||
placeholder="Enter the time limit of problem"
|
||||
:placeholder="$t('m.Time_Limit')"
|
||||
v-model="problem.timeLimit"
|
||||
:disabled="problem.isRemote"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="6" :xs="24">
|
||||
<el-form-item label="Memory Limit(mb)" required>
|
||||
<el-form-item :label="$t('m.Memory_Limit') + '(mb)'" required>
|
||||
<el-input
|
||||
type="Number"
|
||||
placeholder="Enter the memory limit of problem"
|
||||
:placeholder="$t('m.Memory_Limit')"
|
||||
v-model="problem.memoryLimit"
|
||||
:disabled="problem.isRemote"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="6" :xs="24">
|
||||
<el-form-item label="Stack Limit(mb)" required>
|
||||
<el-form-item :label="$t('m.Stack_Limit') + '(mb)'" required>
|
||||
<el-input
|
||||
type="Number"
|
||||
placeholder="Enter the stack limit of problem"
|
||||
:placeholder="$t('m.Stack_Limit')"
|
||||
v-model="problem.stackLimit"
|
||||
:disabled="problem.isRemote"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="6" :xs="24">
|
||||
<el-form-item label="Level" required>
|
||||
<el-form-item :label="$t('m.Level')" required>
|
||||
<el-select
|
||||
class="difficulty-select"
|
||||
placeholder="Enter the level of problem"
|
||||
|
@ -114,7 +122,7 @@
|
|||
<el-col :span="24">
|
||||
<el-form-item
|
||||
prop="input_description"
|
||||
label="Input Description"
|
||||
:label="$t('m.Input')"
|
||||
required
|
||||
>
|
||||
<Editor :value.sync="problem.input"></Editor>
|
||||
|
@ -123,7 +131,7 @@
|
|||
<el-col :span="24">
|
||||
<el-form-item
|
||||
prop="output_description"
|
||||
label="Output Description"
|
||||
:label="$t('m.Output')"
|
||||
required
|
||||
>
|
||||
<Editor :value.sync="problem.output"></Editor>
|
||||
|
@ -133,16 +141,25 @@
|
|||
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="4" :xs="24">
|
||||
<el-form-item label="Auth">
|
||||
<el-form-item :label="$t('m.Auth')">
|
||||
<el-select v-model="problem.auth" size="small">
|
||||
<el-option label="公开" :value="1"></el-option>
|
||||
<el-option label="私有" :value="2"></el-option>
|
||||
<el-option label="比赛题目" :value="3"></el-option>
|
||||
<el-option
|
||||
:label="$t('m.Public_Problem')"
|
||||
:value="1"
|
||||
></el-option>
|
||||
<el-option
|
||||
:label="$t('m.Private_Problem')"
|
||||
:value="2"
|
||||
></el-option>
|
||||
<el-option
|
||||
:label="$t('m.Contest_Problem')"
|
||||
:value="3"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="4" :xs="24">
|
||||
<el-form-item label="Code Shareable">
|
||||
<el-form-item :label="$t('m.Code_Shareable')">
|
||||
<el-switch
|
||||
v-model="problem.codeShare"
|
||||
active-text=""
|
||||
|
@ -152,7 +169,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="16" :xs="24">
|
||||
<el-form-item label="Tags" required>
|
||||
<el-form-item :label="$t('m.Tags')" required>
|
||||
<el-tag
|
||||
v-for="tag in problemTags"
|
||||
closable
|
||||
|
@ -179,7 +196,7 @@
|
|||
</el-autocomplete>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="添加新标签"
|
||||
:content="$t('m.Add')"
|
||||
placement="top"
|
||||
v-else
|
||||
>
|
||||
|
@ -195,7 +212,11 @@
|
|||
</el-row>
|
||||
<el-row>
|
||||
<el-col :md="24" :xs="24">
|
||||
<el-form-item label="Language" :error="error.languages" required>
|
||||
<el-form-item
|
||||
:label="$t('m.Languages')"
|
||||
:error="error.languages"
|
||||
required
|
||||
>
|
||||
<el-checkbox-group v-model="problemLanguages">
|
||||
<el-tooltip
|
||||
class="spj-radio"
|
||||
|
@ -214,10 +235,10 @@
|
|||
|
||||
<div>
|
||||
<div class="panel-title home-title">
|
||||
Problem Examples
|
||||
{{ $t('m.Problem_Examples') }}
|
||||
<el-popover placement="right" trigger="hover">
|
||||
<p>
|
||||
题目样例:请最好不要超过2个题目样例,题面样例不纳入评测数据。
|
||||
{{ $t('m.Problem_Examples_Desc') }}
|
||||
</p>
|
||||
<i slot="reference" class="el-icon-question"></i>
|
||||
</el-popover>
|
||||
|
@ -227,7 +248,7 @@
|
|||
:key="'example' + index"
|
||||
>
|
||||
<Accordion
|
||||
:title="'Example' + (index + 1)"
|
||||
:title="$t('m.Problem_Example') + (index + 1)"
|
||||
:isOpen="example.isOpen ? true : false"
|
||||
:index="index"
|
||||
@changeVisible="changeExampleVisible"
|
||||
|
@ -239,15 +260,15 @@
|
|||
slot="header"
|
||||
@click="deleteExample(index)"
|
||||
>
|
||||
Delete
|
||||
{{ $t('m.Delete') }}
|
||||
</el-button>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-form-item label="Input Example" required>
|
||||
<el-form-item :label="$t('m.Example_Input')" required>
|
||||
<el-input
|
||||
:rows="5"
|
||||
type="textarea"
|
||||
placeholder="Input Example"
|
||||
:placeholder="$t('m.Example_Input')"
|
||||
v-model="example.input"
|
||||
style="white-space: pre-line"
|
||||
>
|
||||
|
@ -255,11 +276,11 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-form-item label="Output Example" required>
|
||||
<el-form-item :label="$t('m.Example_Output')" required>
|
||||
<el-input
|
||||
:rows="5"
|
||||
type="textarea"
|
||||
placeholder="Output Example"
|
||||
:placeholder="$t('m.Example_Output')"
|
||||
v-model="example.output"
|
||||
>
|
||||
</el-input>
|
||||
|
@ -276,20 +297,16 @@
|
|||
@click="addExample()"
|
||||
icon="el-icon-plus"
|
||||
type="small"
|
||||
>Add Example
|
||||
>{{ $t('m.Add_Example') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<template v-if="!problem.isRemote">
|
||||
<div class="panel-title home-title">
|
||||
Special Judge
|
||||
{{ $t('m.Special_Judge') }}
|
||||
<el-popover placement="right" trigger="hover">
|
||||
<p>使用特殊判题的原因:</p>
|
||||
<p>1. 题目要求的输出结果可能不唯一,允许不同结果存在。</p>
|
||||
<p>
|
||||
2.
|
||||
题目最终要求输出一个浮点数,而且会告诉只要答案和标准答案相差不超过某个较小的数就可以。
|
||||
例如题目要求保留几位小数,输出结果后几位小数不相同也是正确的。
|
||||
</p>
|
||||
<p>{{ $t('m.Special_Judge_Tips1') }}</p>
|
||||
<p>1. {{ $t('m.Special_Judge_Tips2') }}</p>
|
||||
<p>2. {{ $t('m.Special_Judge_Tips3') }}</p>
|
||||
<i slot="reference" class="el-icon-question"></i>
|
||||
</el-popover>
|
||||
</div>
|
||||
|
@ -298,14 +315,16 @@
|
|||
<el-checkbox
|
||||
v-model="problem.spj"
|
||||
@click.native.prevent="switchSpj()"
|
||||
>Use Special Judge</el-checkbox
|
||||
>{{ $t('m.Use_Special_Judge') }}</el-checkbox
|
||||
>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="problem.spj">
|
||||
<Accordion title="Special Judge Code">
|
||||
<Accordion :title="$t('m.Special_Judge_Code')">
|
||||
<template slot="header">
|
||||
<span style="margin-right:5px;">SPJ language:</span>
|
||||
<span style="margin-right:5px;"
|
||||
>{{ $t('m.SPJ_language') }}:</span
|
||||
>
|
||||
<el-radio-group v-model="problem.spjLanguage">
|
||||
<el-tooltip
|
||||
class="spj-radio"
|
||||
|
@ -325,7 +344,7 @@
|
|||
@click="compileSPJ"
|
||||
:loading="loadingCompile"
|
||||
style="margin-left:10px"
|
||||
>Complie
|
||||
>{{ $t('m.Compile') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<code-mirror
|
||||
|
@ -336,11 +355,11 @@
|
|||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item style="margin-top: 20px" label="Hint">
|
||||
<el-form-item style="margin-top: 20px" :label="$t('m.Hint')">
|
||||
<Editor :value.sync="problem.hint"></Editor>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Code Template">
|
||||
<el-form-item :label="$t('m.Code_Template')">
|
||||
<el-row>
|
||||
<el-col
|
||||
:span="24"
|
||||
|
@ -359,7 +378,7 @@
|
|||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Type">
|
||||
<el-form-item :label="$t('m.Type')">
|
||||
<el-radio-group
|
||||
v-model="problem.type"
|
||||
:disabled="disableRuleType || problem.isRemote"
|
||||
|
@ -373,17 +392,17 @@
|
|||
|
||||
<el-row :gutter="20" v-if="!problem.isRemote">
|
||||
<div class="panel-title home-title">
|
||||
Judge Samples
|
||||
{{ $t('m.Judge_Samples') }}
|
||||
<el-popover placement="right" trigger="hover">
|
||||
<p>评测数据:判题机对该题目的相关提交进行评测的数据来源。</p>
|
||||
<p>{{ $t('m.Sample_Tips') }}</p>
|
||||
<i slot="reference" class="el-icon-question"></i>
|
||||
</el-popover>
|
||||
</div>
|
||||
|
||||
<el-switch
|
||||
v-model="problem.isUploadCase"
|
||||
active-text="Use Upload File"
|
||||
inactive-text="Use Manual Input"
|
||||
:active-text="$t('m.Use_Upload_File')"
|
||||
:inactive-text="$t('m.Use_Manual_Input')"
|
||||
style="margin: 10px 0"
|
||||
>
|
||||
</el-switch>
|
||||
|
@ -398,8 +417,11 @@
|
|||
:on-success="uploadSucceeded"
|
||||
:on-error="uploadFailed"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="el-icon-upload"
|
||||
>Choose File</el-button
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
>{{ $t('m.Choose_File') }}</el-button
|
||||
>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
@ -411,15 +433,27 @@
|
|||
:data="problem.testCaseScore"
|
||||
align="center"
|
||||
>
|
||||
<vxe-table-column field="input" title="Input" min-width="100">
|
||||
<vxe-table-column
|
||||
field="input"
|
||||
:title="$t('m.Sample_Input_File')"
|
||||
min-width="100"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="output" title="Output" min-width="100">
|
||||
<vxe-table-column
|
||||
field="output"
|
||||
:title="$t('m.Sample_Output_File')"
|
||||
min-width="100"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="score" title="Score" min-width="100">
|
||||
<vxe-table-column
|
||||
field="score"
|
||||
:title="$t('m.Score')"
|
||||
min-width="100"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
<el-input
|
||||
size="small"
|
||||
placeholder="Score"
|
||||
:placeholder="$t('m.Score')"
|
||||
v-model="row.score"
|
||||
:disabled="problem.type != 1"
|
||||
>
|
||||
|
@ -436,7 +470,7 @@
|
|||
:key="'sample' + index"
|
||||
>
|
||||
<Accordion
|
||||
:title="'Sample' + (index + 1)"
|
||||
:title="$t('m.Problem_Sample') + (index + 1)"
|
||||
:isOpen="sample.isOpen ? true : false"
|
||||
:index="index"
|
||||
@changeVisible="changeSampleVisible"
|
||||
|
@ -448,26 +482,26 @@
|
|||
slot="header"
|
||||
@click="deleteSample(index)"
|
||||
>
|
||||
Delete
|
||||
{{ $t('m.Delete') }}
|
||||
</el-button>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-form-item label="Input Sample" required>
|
||||
<el-form-item :label="$t('m.Sample_Input')" required>
|
||||
<el-input
|
||||
:rows="5"
|
||||
type="textarea"
|
||||
placeholder="Input Sample"
|
||||
:placeholder="$t('m.Sample_Input')"
|
||||
v-model="sample.input"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-form-item label="Output Sample" required>
|
||||
<el-form-item :label="$t('m.Sample_Output')" required>
|
||||
<el-input
|
||||
:rows="5"
|
||||
type="textarea"
|
||||
placeholder="Output Sample"
|
||||
:placeholder="$t('m.Sample_Output')"
|
||||
v-model="sample.output"
|
||||
>
|
||||
</el-input>
|
||||
|
@ -477,11 +511,11 @@
|
|||
:span="24"
|
||||
v-show="problem.type == 1 && sample.score != null"
|
||||
>
|
||||
<el-form-item label="IO Score">
|
||||
<el-form-item :label="$t('m.Score')">
|
||||
<el-input
|
||||
type="number"
|
||||
size="small"
|
||||
placeholder="The score of the testcase"
|
||||
:placeholder="$t('m.Score')"
|
||||
v-model="sample.score"
|
||||
>
|
||||
</el-input>
|
||||
|
@ -497,21 +531,21 @@
|
|||
@click="addSample()"
|
||||
icon="el-icon-plus"
|
||||
type="small"
|
||||
>Add Sample
|
||||
>{{ $t('m.Add_Sample') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="Source">
|
||||
<el-form-item :label="$t('m.Source')">
|
||||
<el-input
|
||||
placeholder="Enter the problem where from"
|
||||
:placeholder="$t('m.Source')"
|
||||
v-model="problem.source"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
label="Auto Remove the Blank at the End of Code"
|
||||
:label="$t('m.Auto_Remove_the_Blank_at_the_End_of_Code')"
|
||||
v-if="!problem.isRemote"
|
||||
>
|
||||
<el-switch
|
||||
|
@ -523,7 +557,7 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
label="Publish the Judging Result of Test Data"
|
||||
:label="$t('m.Publish_the_Judging_Result_of_Test_Data')"
|
||||
v-if="!problem.isRemote"
|
||||
>
|
||||
<el-switch
|
||||
|
@ -534,23 +568,23 @@
|
|||
</el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" @click.native="submit()" size="small"
|
||||
>Save</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="submit()" size="small">{{
|
||||
$t('m.Save')
|
||||
}}</el-button>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Editor from '@/components/admin/Editor';
|
||||
import Accordion from '@/components/admin/Accordion';
|
||||
import CodeMirror from '@/components/admin/CodeMirror';
|
||||
import utils from '@/common/utils';
|
||||
import { mapGetters } from 'vuex';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { PROBLEM_LEVEL_RESERVE } from '@/common/constants';
|
||||
const Editor = () => import('@/components/admin/Editor.vue');
|
||||
const Accordion = () => import('@/components/admin/Accordion.vue');
|
||||
const CodeMirror = () => import('@/components/admin/CodeMirror.vue');
|
||||
export default {
|
||||
name: 'Problem',
|
||||
components: {
|
||||
|
@ -780,7 +814,7 @@ export default {
|
|||
init() {
|
||||
if (this.mode === 'edit') {
|
||||
this.pid = this.$route.params.problemId;
|
||||
this.title = 'Edit Problem';
|
||||
this.title = this.$i18n.t('m.Edit_Problem');
|
||||
let funcName = {
|
||||
'admin-edit-problem': 'admin_getProblem',
|
||||
'admin-edit-contest-problem': 'admin_getContestProblem',
|
||||
|
@ -826,7 +860,7 @@ export default {
|
|||
});
|
||||
} else {
|
||||
this.addExample();
|
||||
this.title = 'Create Problem';
|
||||
this.title = this.$i18n.t('m.Create_Problem');
|
||||
for (let item of this.allLanguage) {
|
||||
this.problemLanguages.push(item.name);
|
||||
}
|
||||
|
@ -848,15 +882,11 @@ export default {
|
|||
|
||||
switchSpj() {
|
||||
if (this.testCaseUploaded) {
|
||||
this.$confirm(
|
||||
'如果你想改变该题目的判题方法,那么你需要重新上传测试数据。',
|
||||
'注意',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
this.$confirm(this.$i18n.t('m.Change_Judge_Method'), 'Tips', {
|
||||
confirmButtonText: this.$i18n.t('m.OK'),
|
||||
cancelButtonText: this.$i18n.t('m.Cancel'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
this.problem.spj = !this.problem.spj;
|
||||
this.resetTestCase();
|
||||
|
@ -890,7 +920,7 @@ export default {
|
|||
selectTag(item) {
|
||||
for (var i = 0; i < this.problemTags.length; i++) {
|
||||
if (this.problemTags[i].name == item.value) {
|
||||
myMessage.warning('该标签已添加,请不要重复添加!');
|
||||
myMessage.warning(this.$i18n.t('m.Add_Tag_Error'));
|
||||
this.tagInput = '';
|
||||
return;
|
||||
}
|
||||
|
@ -904,7 +934,7 @@ export default {
|
|||
if (this.tagInput) {
|
||||
for (var i = 0; i < this.problemTags.length; i++) {
|
||||
if (this.problemTags[i].name == this.tagInput) {
|
||||
myMessage.warning('该标签已添加,请不要重复添加!');
|
||||
myMessage.warning(this.$i18n.t('m.Add_Tag_Error'));
|
||||
this.tagInput = '';
|
||||
return;
|
||||
}
|
||||
|
@ -963,7 +993,7 @@ export default {
|
|||
this.testCaseUploaded = false;
|
||||
return;
|
||||
}
|
||||
myMessage.success('上传测试数据包成功');
|
||||
myMessage.success(this.$i18n.t('m.Upload_Testcase_Successfully'));
|
||||
let fileList = response.data.fileList;
|
||||
let averSorce = (100 / fileList.length).toFixed(0);
|
||||
|
||||
|
@ -981,7 +1011,7 @@ export default {
|
|||
this.problem.uploadTestcaseDir = response.data.fileListDir;
|
||||
},
|
||||
uploadFailed() {
|
||||
myMessage.error('上传测试数据包失败');
|
||||
myMessage.error(this.$i18n.t('m.Upload_Testcase_Failed'));
|
||||
},
|
||||
|
||||
compileSPJ() {
|
||||
|
@ -1016,42 +1046,62 @@ export default {
|
|||
|
||||
submit() {
|
||||
if (!this.problem.problemId) {
|
||||
myMessage.error('题目的展示ID不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Problem_Display_ID') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.contestID) {
|
||||
if (!this.contestProblem.displayId) {
|
||||
myMessage.error('比赛题目的展示ID不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Display_ID') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.contestProblem.displayTitle) {
|
||||
myMessage.error('比赛题目的展示标题不能为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Contest_Display_Title') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.problem.examples.length) {
|
||||
myMessage.error('题面测试数据是不能为空!至少输入一项!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Problem_Examples') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (let example of this.problem.examples) {
|
||||
if (!example.input || !example.output) {
|
||||
myMessage.error('每一项题面测试数据的输入输出都不能为空!');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!this.problem.isRemote) {
|
||||
// 选择手动输入
|
||||
if (!this.problem.isUploadCase) {
|
||||
if (!this.problemSamples.length) {
|
||||
myMessage.error('评测数据不能为空!请手动输入评测数据!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Judge_Samples') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let sample of this.problemSamples) {
|
||||
if (!sample.input && !sample.output) {
|
||||
myMessage.error('每一项评测数据的输入和输出都不能同时为空!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Sample_Input') +
|
||||
' or ' +
|
||||
this.$i18n.t('m.Sample_Output') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required')
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1061,11 +1111,13 @@ export default {
|
|||
for (let item of this.problemSamples) {
|
||||
try {
|
||||
if (parseInt(item.score) < 0) {
|
||||
myMessage.error('测评得分小于0是无效的!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Score_must_be_greater_than_or_equal_to_0')
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
myMessage.error('测评得分的结果必须是整数类型!');
|
||||
myMessage.error(this.$i18n.t('m.Score_must_be_an_integer'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1075,7 +1127,10 @@ export default {
|
|||
|
||||
// 两种情况:create模式是需要校验是否上传成功了,edit模式获取题目数据已经默认为true了,若是edit又重新上传数据,需要校验
|
||||
if (!this.testCaseUploaded) {
|
||||
this.error.testCase = '评测数据不能为空!请先上传评测数据!';
|
||||
this.error.testCase =
|
||||
this.$i18n.t('m.Judge_Samples') +
|
||||
' ' +
|
||||
this.$i18n.t('m.is_required');
|
||||
myMessage.error(this.error.testCase);
|
||||
return;
|
||||
}
|
||||
|
@ -1085,11 +1140,13 @@ export default {
|
|||
for (let item of this.problem.testCaseScore) {
|
||||
try {
|
||||
if (parseInt(item.score) <= 0) {
|
||||
myMessage.error('测评得分小于0是无效的!');
|
||||
myMessage.error(
|
||||
this.$i18n.t('m.Score_must_be_greater_than_or_equal_to_0')
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
myMessage.error('测评得分的结果必须是数字类型!');
|
||||
myMessage.error(this.$i18n.t('m.Score_must_be_an_integer'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1097,17 +1154,19 @@ export default {
|
|||
}
|
||||
}
|
||||
if (!this.problemTags.length) {
|
||||
this.error.tags = '请添加至少一个题目标签!';
|
||||
this.error.tags =
|
||||
this.$i18n.t('m.Tags') + ' ' + this.$i18n.t('m.is_required');
|
||||
myMessage.error(this.error.tags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.problem.spj) {
|
||||
if (!this.problem.spjCode) {
|
||||
this.error.spj = '特殊判题的程序代码不能为空!';
|
||||
this.error.spj =
|
||||
this.$i18n.t('m.Spj_Code') + ' ' + this.$i18n.t('m.is_required');
|
||||
myMessage.error(this.error.spj);
|
||||
} else if (!this.problem.spjCompileOk) {
|
||||
this.error.spj = '特殊判题的程序没有编译成功,请重新编译!';
|
||||
this.error.spj = this.$i18n.t('m.Spj_Code_not_Compile_Success');
|
||||
}
|
||||
if (this.error.spj) {
|
||||
myMessage.error(this.error.spj);
|
||||
|
@ -1116,7 +1175,8 @@ export default {
|
|||
}
|
||||
|
||||
if (!this.problemLanguages.length) {
|
||||
this.error.languages = '请至少给题目选择一项编程语言!';
|
||||
this.error.languages =
|
||||
this.$i18n.t('m.Language') + ' ' + this.$i18n.t('m.is_required');
|
||||
myMessage.error(this.error.languages);
|
||||
return;
|
||||
}
|
||||
|
@ -1211,14 +1271,14 @@ export default {
|
|||
this.contestProblem['cid'] = this.$route.params.contestId;
|
||||
}
|
||||
api.admin_setContestProblemInfo(this.contestProblem).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success('success');
|
||||
this.$router.push({
|
||||
name: 'admin-contest-problem-list',
|
||||
params: { contestId: this.$route.params.contestId },
|
||||
});
|
||||
});
|
||||
} else {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success('success');
|
||||
this.$router.push({ name: 'admin-problem-list' });
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">{{
|
||||
contestId ? 'Contest Problem List' : 'Problem List'
|
||||
contestId ? $t('m.Contest_Problem_List') : $t('m.Problem_List')
|
||||
}}</span>
|
||||
<div class="filter-row">
|
||||
<span>
|
||||
|
@ -12,7 +12,7 @@
|
|||
size="small"
|
||||
@click="goCreateProblem"
|
||||
icon="el-icon-plus"
|
||||
>Create
|
||||
>{{ $t('m.Create') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
|
@ -20,7 +20,7 @@
|
|||
v-if="!contestId"
|
||||
@click="AddRemoteOJProblemDialogVisible = true"
|
||||
icon="el-icon-plus"
|
||||
>Add Remote OJ Problem
|
||||
>{{ $t('m.Add_Rmote_OJ_Problem') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="contestId"
|
||||
|
@ -28,13 +28,13 @@
|
|||
size="small"
|
||||
icon="el-icon-plus"
|
||||
@click="addProblemDialogVisible = true"
|
||||
>Add From Public Problem
|
||||
>{{ $t('m.Add_From_Public_Problem') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span>
|
||||
<vxe-input
|
||||
v-model="keyword"
|
||||
placeholder="Enter keyword"
|
||||
:placeholder="$t('m.Enter_keyword')"
|
||||
type="search"
|
||||
size="medium"
|
||||
@search-click="filterByKeyword"
|
||||
|
@ -54,16 +54,24 @@
|
|||
>
|
||||
<vxe-table-column min-width="100" field="problemId" title="ID">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="title" min-width="150" title="Title">
|
||||
<vxe-table-column field="title" min-width="150" :title="$t('m.Title')">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column field="author" min-width="150" title="Author">
|
||||
<vxe-table-column
|
||||
field="author"
|
||||
min-width="150"
|
||||
:title="$t('m.Author')"
|
||||
>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="150" field="gmtCreate" title="Create Time">
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtCreate"
|
||||
:title="$t('m.Created_Time')"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column min-width="130" field="auth" title="Auth">
|
||||
<vxe-table-column min-width="130" field="auth" :title="$t('m.Auth')">
|
||||
<template v-slot="{ row }">
|
||||
<el-select
|
||||
v-model="row.auth"
|
||||
|
@ -71,10 +79,13 @@
|
|||
size="small"
|
||||
:disabled="row.auth == 3 && !contestId"
|
||||
>
|
||||
<el-option label="公开" :value="1"></el-option>
|
||||
<el-option label="私有" :value="2"></el-option>
|
||||
<el-option :label="$t('m.Public_Problem')" :value="1"></el-option>
|
||||
<el-option
|
||||
label="比赛题目"
|
||||
:label="$t('m.Private_Problem')"
|
||||
:value="2"
|
||||
></el-option>
|
||||
<el-option
|
||||
:label="$t('m.Contest_Problem')"
|
||||
:value="3"
|
||||
:disabled="!contestId"
|
||||
></el-option>
|
||||
|
@ -83,7 +94,7 @@
|
|||
</vxe-table-column>
|
||||
<vxe-table-column title="Option" min-width="200">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip effect="dark" content="编辑题目" placement="top">
|
||||
<el-tooltip effect="dark" :content="$t('m.Edit')" placement="top">
|
||||
<el-button
|
||||
icon="el-icon-edit-outline"
|
||||
size="mini"
|
||||
|
@ -93,7 +104,11 @@
|
|||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip effect="dark" content="下载测试样例" placement="top">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('m.Download_Testcase')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
|
@ -103,7 +118,7 @@
|
|||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip effect="dark" content="删除题目" placement="top">
|
||||
<el-tooltip effect="dark" :content="$t('m.Delete')" placement="top">
|
||||
<el-button
|
||||
icon="el-icon-delete-solid"
|
||||
size="mini"
|
||||
|
@ -129,7 +144,7 @@
|
|||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
title="Add Contest Problem"
|
||||
:title="$t('m.Add_Contest_Problem')"
|
||||
v-if="contestId"
|
||||
width="90%"
|
||||
:visible.sync="addProblemDialogVisible"
|
||||
|
@ -142,13 +157,13 @@
|
|||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="Add Remote OJ Problem"
|
||||
:title="$t('m.Add_Rmote_OJ_Problem')"
|
||||
width="350px"
|
||||
:visible.sync="AddRemoteOJProblemDialogVisible"
|
||||
@close-on-click-modal="false"
|
||||
>
|
||||
<el-form>
|
||||
<el-form-item label="Remote OJ">
|
||||
<el-form-item :label="$t('m.Remote_OJ')">
|
||||
<el-select v-model="otherOJName" size="small">
|
||||
<el-option
|
||||
:label="remoteOj.name"
|
||||
|
@ -158,7 +173,7 @@
|
|||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Problem ID">
|
||||
<el-form-item :label="$t('m.Problem_ID')">
|
||||
<el-input v-model="otherOJProblemId" size="small"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item style="text-align:center">
|
||||
|
@ -167,7 +182,7 @@
|
|||
icon="el-icon-plus"
|
||||
@click="addRemoteOJProblem"
|
||||
:loading="addRemoteOJproblemLoading"
|
||||
>Add
|
||||
>{{ $t('m.Add') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -274,18 +289,14 @@ export default {
|
|||
|
||||
changeProblemAuth(row) {
|
||||
api.admin_changeProblemPublic(row).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
});
|
||||
},
|
||||
|
||||
deleteProblem(id) {
|
||||
this.$confirm(
|
||||
'确定要删除此问题吗?注意:该问题的相关提交数据也将被删除。',
|
||||
'删除题目',
|
||||
{
|
||||
type: 'warning',
|
||||
}
|
||||
).then(
|
||||
this.$confirm(this.$i18n.t('m.Delete_Problem_Tips'), 'Tips', {
|
||||
type: 'warning',
|
||||
}).then(
|
||||
() => {
|
||||
let funcName =
|
||||
this.routeName === 'admin-problem-list'
|
||||
|
@ -293,7 +304,7 @@ export default {
|
|||
: 'admin_deleteContestProblem';
|
||||
api[funcName](id)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Delete_Successfully'));
|
||||
this.getProblemList(this.currentPage);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
@ -312,7 +323,7 @@ export default {
|
|||
}
|
||||
api[funcName](data)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Update_Successfully'));
|
||||
this.getProblemList(this.currentPage);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
@ -320,7 +331,7 @@ export default {
|
|||
downloadTestCase(problemID) {
|
||||
let url = '/api/file/download-testcase?pid=' + problemID;
|
||||
utils.downloadFile(url).then(() => {
|
||||
this.$alert('该题目的测试样例已成功下载!', '提醒');
|
||||
this.$alert(this.$i18n.t('m.Download_Testcase_Success'), 'Tips');
|
||||
});
|
||||
},
|
||||
filterByKeyword() {
|
||||
|
@ -334,7 +345,7 @@ export default {
|
|||
(res) => {
|
||||
this.addRemoteOJproblemLoading = false;
|
||||
this.AddRemoteOJProblemDialogVisible = false;
|
||||
myMessage.success(res.data.msg);
|
||||
myMessage.success(this.$i18n.t('m.Add_Successfully'));
|
||||
this.currentChange(1);
|
||||
},
|
||||
(err) => {
|
||||
|
|
|
@ -207,11 +207,11 @@
|
|||
<script>
|
||||
import time from '@/common/time';
|
||||
import api from '@/common/api';
|
||||
import Announcements from '@/components/oj/common/Announcements.vue';
|
||||
import { CONTEST_STATUS_REVERSE } from '@/common/constants';
|
||||
import { mapState } from 'vuex';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import Avatar from 'vue-avatar';
|
||||
const Announcements = () => import('@/components/oj/common/Announcements.vue');
|
||||
export default {
|
||||
name: 'home',
|
||||
components: {
|
||||
|
|
|
@ -106,9 +106,9 @@
|
|||
|
||||
<script>
|
||||
import utils from '@/common/utils';
|
||||
import Highlight from '@/components/oj/common/Highlight';
|
||||
import { JUDGE_STATUS } from '@/common/constants';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
const Highlight = () => import('@/components/oj/common/Highlight');
|
||||
export default {
|
||||
components: {
|
||||
Highlight,
|
||||
|
|
|
@ -197,15 +197,13 @@
|
|||
import api from '@/common/api';
|
||||
import { mapGetters } from 'vuex';
|
||||
import utils from '@/common/utils';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import time from '@/common/time';
|
||||
import {
|
||||
CONTEST_STATUS_REVERSE,
|
||||
CONTEST_TYPE,
|
||||
CONTEST_TYPE_REVERSE,
|
||||
} from '@/common/constants';
|
||||
import myMessage from '@/common/message';
|
||||
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
const limit = 10;
|
||||
|
||||
export default {
|
||||
|
@ -278,7 +276,7 @@ export default {
|
|||
this.filterByChange();
|
||||
},
|
||||
toContest(contest) {
|
||||
if (contest.type !== CONTEST_TYPE.PUBLIC && !this.isAuthenticated) {
|
||||
if (!this.isAuthenticated) {
|
||||
myMessage.warning(this.$i18n.t('m.Please_login_first'));
|
||||
this.$store.dispatch('changeModalStatus', { visible: true });
|
||||
} else {
|
||||
|
|
|
@ -145,8 +145,7 @@
|
|||
<script>
|
||||
import moment from 'moment';
|
||||
import { mapActions } from 'vuex';
|
||||
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
import time from '@/common/time';
|
||||
import utils from '@/common/utils';
|
||||
import ContestRankMixin from './contestRankMixin';
|
||||
|
|
|
@ -106,10 +106,9 @@
|
|||
</template>
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import Pagination from '@/components/oj/common/Pagination.vue';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
export default {
|
||||
name: 'ACM-Info-Admin',
|
||||
components: {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<comment :cid="$route.params.contestID"></comment>
|
||||
</template>
|
||||
<script>
|
||||
import comment from '@/components/oj/comment/comment';
|
||||
export default {
|
||||
name: 'ContestComment',
|
||||
components: {
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import ACMContestRank from './ACMContestRank.vue';
|
||||
import OIContestRank from './OIContestRank.vue';
|
||||
import { RULE_TYPE } from '@/common/constants';
|
||||
|
||||
const ACMContestRank = () => import('./ACMContestRank.vue');
|
||||
const OIContestRank = () => import('./OIContestRank.vue');
|
||||
const NullComponent = {
|
||||
name: 'null-component',
|
||||
template: '<div></div>',
|
||||
|
|
|
@ -129,10 +129,9 @@
|
|||
</template>
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import ContestRankMixin from './contestRankMixin';
|
||||
import utils from '@/common/utils';
|
||||
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
export default {
|
||||
name: 'OIContestRank',
|
||||
components: {
|
||||
|
|
|
@ -202,14 +202,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import comment from '@/components/oj/comment/comment';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import Avatar from 'vue-avatar';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
|
||||
const Editor = () => import('@/components/admin/Editor.vue');
|
||||
const comment = () => import('@/components/oj/comment/comment');
|
||||
export default {
|
||||
components: {
|
||||
comment,
|
||||
|
|
|
@ -354,11 +354,12 @@
|
|||
</template>
|
||||
<script>
|
||||
import Avatar from 'vue-avatar';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import 'element-ui/lib/theme-chalk/display.css';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
const Editor = () => import('@/components/admin/Editor.vue');
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
|
|
|
@ -355,7 +355,6 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import CodeMirror from '@/components/oj/common/CodeMirror.vue';
|
||||
import storage from '@/common/storage';
|
||||
import utils from '@/common/utils';
|
||||
import {
|
||||
|
@ -370,6 +369,7 @@ import { pie, largePie } from './chartData';
|
|||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
const CodeMirror = () => import('@/components/oj/common/CodeMirror.vue');
|
||||
// 只显示这些状态的图形占用
|
||||
const filtedStatus = ['wa', 'ce', 'ac', 'pa', 'tle', 'mle', 're', 'pe'];
|
||||
|
||||
|
|
|
@ -262,9 +262,9 @@ import {
|
|||
JUDGE_STATUS_RESERVE,
|
||||
REMOTE_OJ,
|
||||
} from '@/common/constants';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import myMessage from '@/common/message';
|
||||
|
||||
import 'element-ui/lib/theme-chalk/display.css';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
export default {
|
||||
name: 'ProblemList',
|
||||
components: {
|
||||
|
|
|
@ -81,11 +81,10 @@
|
|||
|
||||
<script>
|
||||
import api from '@/common/api';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import utils from '@/common/utils';
|
||||
import { RULE_TYPE } from '@/common/constants';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
|
|
|
@ -80,11 +80,10 @@
|
|||
|
||||
<script>
|
||||
import api from '@/common/api';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import utils from '@/common/utils';
|
||||
import { RULE_TYPE } from '@/common/constants';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
const Pagination = () => import('@/components/oj/common/Pagination');
|
||||
export default {
|
||||
name: 'acm-rank',
|
||||
components: {
|
||||
|
|
|
@ -208,9 +208,11 @@
|
|||
import api from '@/common/api';
|
||||
import { JUDGE_STATUS, JUDGE_STATUS_RESERVE } from '@/common/constants';
|
||||
import utils from '@/common/utils';
|
||||
import Highlight from '@/components/oj/common/Highlight';
|
||||
import myMessage from '@/common/message';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
|
||||
const Highlight = () => import('@/components/oj/common/Highlight');
|
||||
|
||||
export default {
|
||||
name: 'submissionDetails',
|
||||
components: {
|
||||
|
|
|
@ -308,6 +308,7 @@ import {
|
|||
import utils from '@/common/utils';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import myMessage from '@/common/message';
|
||||
import 'element-ui/lib/theme-chalk/display.css';
|
||||
export default {
|
||||
name: 'submissionList',
|
||||
components: {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script>
|
||||
import Account from '@/components/oj/setting/Account';
|
||||
import UserInfo from '@/components/oj/setting/UserInfo';
|
||||
const Account = () => import('@/components/oj/setting/Account');
|
||||
const UserInfo = () => import('@/components/oj/setting/UserInfo');
|
||||
export default {
|
||||
components: {
|
||||
Account,
|
||||
|
|
Loading…
Reference in New Issue