Compare commits

...

2 Commits

Author SHA1 Message Date
Soohyeonl 3433586456 project refactoring 2022-08-21 15:51:54 +08:00
Soohyeonl 5f8c51f225 init 2022-08-21 15:50:00 +08:00
83 changed files with 4574 additions and 17951 deletions

View File

@ -6,12 +6,6 @@ import (
"github.com/open-ct/openitem/object"
)
type CreateNewQuestionRequest struct {
Body object.Task `json:"body"`
Answer object.Task `json:"answer"`
Solution object.Task `json:"solution"`
}
// CreateNewQuestion
// @Title CreateNewQuestion
// @Description 创建新的题目(临时题目)
@ -24,32 +18,13 @@ func (c *ApiController) CreateNewQuestion() {
return
}
var request CreateNewQuestionRequest
// var request object.TempQuestion
var request object.TempQuestion
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
tempQuestion := object.TempQuestion{
SourceProject: request.Body.SourceProject,
Author: request.Body.Owner,
Info: object.QuestionInfo{
Title: request.Body.Name,
Type: request.Body.Type,
Body: request.Body.Text,
Answer: request.Answer.Text,
Solution: request.Solution.Text,
},
BasicProps: object.QuestionBasicProps{},
SpecProps: object.QuestionSpecProps{},
ExtraProps: object.QuestionExtraProps{},
AdvancedProps: object.QuestionAdvancedProps{},
ApplyRecord: object.QuestionApplyRecord{},
}
if err != nil {
c.ResponseError(err.Error())
return
}
resp, err := object.CreateNewTempQuestion(&tempQuestion)
resp, err := object.CreateNewTempQuestion(&request)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -9,83 +9,58 @@ import (
)
type QuestionInfo struct {
Title string `json:"title"`
Type string `json:"type"`
Body string `json:"body"`
Answer string `json:"answer"`
Solution string `json:"solution"`
Title string `json:"title" bson:"title"`
Type string `json:"type" bson:"type"`
Body string `json:"body" bson:"body"`
Answer string `json:"answer" bson:"answer"`
Solution string `json:"solution" bson:"solution"`
}
type QuestionBasicProps struct {
Encode string `json:"encode"`
Subject string `json:"subject"`
DetailsDimension string `json:"details_dimension"`
SubDetailsDimension string `json:"sub_details_dimension"`
AbilityDimension string `json:"ability_dimension"`
SubAbilityDimension string `json:"sub_ability_dimension"`
Description string `json:"description"`
SubjectRequirements string `json:"subject_requirements"`
Details string `json:"details"`
Keywords []string `json:"keywords"`
Encode string `json:"encode" bson:"encode"`
Subject string `json:"subject" bson:"subject"`
DetailsDimension string `json:"details_dimension" bson:"details_dimension"`
SubDetailsDimension string `json:"sub_details_dimension" bson:"sub_details_dimension"`
AbilityDimension string `json:"ability_dimension" bson:"ability_dimension"`
SubAbilityDimension string `json:"sub_ability_dimension" bson:"sub_ability_dimension"`
Description string `json:"description" bson:"description"`
SubjectRequirements string `json:"subject_requirements" bson:"subject_requirements"`
Details string `json:"details" bson:"details"`
Keywords []string `json:"keywords" bson:"keywords"`
}
type QuestionSpecProps struct {
Topic string `json:"topic"`
ArticleType string `json:"article_type"`
Length string `json:"length"`
Topic string `json:"topic" bson:"topic"`
ArticleType string `json:"article_type" bson:"article_type"`
Length string `json:"length" bson:"length"`
}
type QuestionExtraProps struct {
IsScene bool `json:"is_scene"`
IsQuestionGroup bool `json:"is_question_group"`
ReadingMaterialTopic string `json:"reading_material_topic"`
MaterialLength int `json:"material_length"`
IsScene bool `json:"is_scene" bson:"is_scene"`
IsQuestionGroup bool `json:"is_question_group" bson:"is_question_group"`
ReadingMaterialTopic string `json:"reading_material_topic" bson:"reading_material_topic"`
MaterialLength int `json:"material_length" bson:"material_length"`
}
type QuestionAdvancedProps struct {
CttLevel float64 `json:"ctt_level"`
CttDiff_1 float64 `json:"ctt_diff_1"`
CttDiff_2 float64 `json:"ctt_diff_2"`
IrtLevel float64 `json:"irt_level"`
CttLevel float64 `json:"ctt_level" bson:"ctt_level"`
CttDiff_1 float64 `json:"ctt_diff_1" bson:"ctt_diff_1"`
CttDiff_2 float64 `json:"ctt_diff_2" bson:"ctt_diff_2"`
IrtLevel float64 `json:"irt_level" bson:"irt_level"`
}
type QuestionComment struct {
TimePoint time.Time `json:"time_point"`
Comment string `json:"comment"`
Author string `json:"author"`
TimePoint time.Time `json:"time_point" bson:"time_point"`
Comment string `json:"comment" bson:"comment"`
Author string `json:"author" bson:"author"`
}
type QuestionApplyRecord struct {
GradeFits string `json:"grade_fits"`
TestYear string `json:"test_year"`
TestRegion []string `json:"test_region"`
ParticipantCount int `json:"participant_count"`
TestCount int `json:"test_count"`
}
type Task struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Preface string `xorm:"mediumtext" json:"preface"`
Text string `xorm:"mediumtext" json:"text"`
Type string `xorm:"varchar(100)" json:"type"`
Canvas string `xorm:"mediumtext" json:"canvas"`
Basic string `xorm:"mediumtext" json:"basic"`
Video string `xorm:"mediumtext" json:"video"`
Slides string `xorm:"mediumtext" json:"slides"`
Sketch string `xorm:"mediumtext" json:"sketch"`
Frame string `xorm:"mediumtext" json:"frame"`
Three string `xorm:"mediumtext" json:"three"`
Game string `xorm:"mediumtext" json:"game"`
Deck string `xorm:"mediumtext" json:"deck"`
Extras []string `xorm:"varchar(1000)" json:"extras"`
Options []string `xorm:"varchar(100)" json:"options"`
Answer []string `xorm:"varchar(100)" json:"answer"`
SourceProject string `json:"source_project"` // 项目来源
GradeFits string `json:"grade_fits" bson:"grade_fits"`
TestYear string `json:"test_year" bson:"test_year"`
TestRegion []string `json:"test_region" bson:"test_region"`
ParticipantCount int `json:"participant_count" bson:"participant_count"`
TestCount int `json:"test_count" bson:"test_count"`
}
type TempQuestion struct {

BIN
swagger/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

BIN
swagger/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

93
swagger/index.html Normal file
View File

@ -0,0 +1,93 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>

View File

@ -0,0 +1,53 @@
<!doctype html>
<html lang="en-US">
<body onload="run()">
</body>
</html>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1);
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}
isValid = qp.state === sentState
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback(oauth2.auth);
} else {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: "Authorization failed: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid});
}
window.close();
}
</script>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;AAu/FA;AA6+FA;;;;;;;;;;;;;;;;;;;;;;;;;;AAyTA;;;;;;AAoIA;AAi7FA;AAmtCA;AAi0IA;AA0oJA;AAgwFA;AAyrGA;AA0lFA;AA4nFA;AA+9CA;AA+gDA;AAwrCA;AA60EA;;;;;AA6oCA;AAsyJA;;;;;;;;;;;;;;AA64EA;AA4mIA;AAquJA;AA2qHA;AA2mGA;AAiiEA;AAq4DA;AAg3DA;AAoPA;;;;;;AAk7FA;AA07FA;;;;;AAi8CA;AAgsFA;AAs2CA;AAglCA;AAu9CA;AAy8EA;AAsiCA;AA+yFA;;;;;;;;;AAgkDA;AA2zIA;AAu7FA;AAmrFA;AAu0EA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA8QA;AAmvGA;AAuxFA;;;;;;AAocA;AAkvFA;AAu+CA;AAo+CA;AAgrCA;AAuyEA","sourceRoot":""}

2
swagger/swagger-ui.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"swagger-ui.css","sources":[],"mappings":"","sourceRoot":""}

15
swagger/swagger-ui.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA0yCA;AAoyHA;AAmyHA;AAykGA;AA+9BA;AA6iCA;AAojCA;AAu5BA","sourceRoot":""}

2549
swagger/swagger.json Normal file

File diff suppressed because it is too large Load Diff

1694
swagger/swagger.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
node_modules
build

View File

@ -1,95 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"settings": {
"react": {
"version": "detect"
}
},
"plugins": ["unused-imports"],
"extends": ["eslint:recommended", "plugin:react/recommended"],
"rules": {
// "eqeqeq": "error",
"semi": ["error", "always"],
"indent": ["error", 2],
// follow antd's style guide
"quotes": ["error", "double"],
"jsx-quotes": ["error", "prefer-double"],
"space-in-parens": ["error", "never"],
"object-curly-spacing": ["error", "never"],
"array-bracket-spacing": ["error", "never"],
"comma-spacing": ["error", { "before": false, "after": true }],
"react/jsx-curly-spacing": [
"error",
{ "when": "never", "allowMultiline": true, "children": true }
],
"arrow-spacing": ["error", { "before": true, "after": true }],
"space-before-blocks": ["error", "always"],
"spaced-comment": ["error", "always"],
"react/jsx-tag-spacing": ["error", { "beforeSelfClosing": "always" }],
"block-spacing": ["error", "never"],
"space-before-function-paren": ["error", "never"],
"no-trailing-spaces": ["error", { "ignoreComments": true }],
"eol-last": ["error", "always"],
// "no-var": ["error"],
"curly": ["error", "all"],
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"no-mixed-spaces-and-tabs": "error",
"sort-imports": [
"error",
{
"ignoreDeclarationSort": true
}
],
"no-multiple-empty-lines": [
"error",
{ "max": 1, "maxBOF": 0, "maxEOF": 0 }
],
"space-unary-ops": ["error", { "words": true, "nonwords": false }],
"space-infix-ops": "error",
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
"comma-style": ["error", "last"],
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "never",
"exports": "never",
"functions": "never"
}
],
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"error",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "none",
"argsIgnorePattern": "^_"
}
],
"react/prop-types": "off",
"react/display-name": "off",
"react/react-in-jsx-scope": "off",
// don't use strict mod now, otherwise there are a lot of errors in the codebase
"no-unused-vars": "off",
"react/no-deprecated": "warn",
"no-case-declarations": "warn",
"react/jsx-key": "warn"
}
}

23
web/.gitignore vendored
View File

@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,70 +0,0 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

View File

@ -1,17 +0,0 @@
const CracoLessPlugin = require("craco-less");
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: {"@primary-color": "rgb(45,120,213)"},
javascriptEnabled: true,
},
},
},
},
],
};

View File

@ -1,53 +0,0 @@
{
"name": "web",
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "4.6.2",
"@craco/craco": "6.4.5",
"antd": "4.16.0",
"babel-eslint": "^10.1.0",
"braft-editor": "^2.3.9",
"casdoor-js-sdk": "^0.2.7",
"copy-to-clipboard": "^3.3.1",
"craco-less": "2.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"i18next": "^19.8.9",
"moment": "^2.29.1",
"react": "17.0.2",
"react-device-detect": "1.17.0",
"react-dom": "17.0.2",
"react-i18next": "^11.8.7",
"react-router-dom": "5.1.2",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "set PORT=8201 && craco start",
"build": "del build.zip 2>nul && set \"GENERATE_SOURCEMAP=false\" && set \"SKIP_PREFLIGHT_CHECK=true\" && craco build && 360zip.exe -ar build %cd%/build.zip || ECHO.",
"test": "craco test",
"eject": "craco eject",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"fix": "eslint --fix ."
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all",
"ie 9, ie 10, ie 11"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version",
"ie 9, ie 10, ie 11"
]
},
"devDependencies": {
"eslint": "^7.11.0",
"eslint-plugin-react": "^7.30.1"
}
}

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="https://cdn.open-ct.com/static/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="https://cdn.open-ct.com/static/favicon.ico" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>OpenItem</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -1,25 +0,0 @@
{
"short_name": "OpenItem",
"name": "OpenItem",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -1,372 +0,0 @@
import React, {Component} from "react";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import {Avatar, BackTop, Dropdown, Layout, Menu} from "antd";
import {DownOutlined, LogoutOutlined, SettingOutlined, createFromIconfontCN} from "@ant-design/icons";
import "./App.less";
import * as Setting from "./Setting";
import * as AccountBackend from "./backend/AccountBackend";
import AuthCallback from "./AuthCallback";
import * as Conf from "./Conf";
import HomePage from "./HomePage";
import DatasetListPage from "./DatasetListPage";
import DatasetEditPage from "./DatasetEditPage";
import SigninPage from "./SigninPage";
import i18next from "i18next";
import PendingTaskPage from "./PendingTaskPage";
import ProjectManagementPage from "./ProjectManagementPage";
import PropositionPaperPage from "./PropositionPaperPage";
const {Header, Footer} = Layout;
const IconFont = createFromIconfontCN({
scriptUrl: "//at.alicdn.com/t/font_2680620_ffij16fkwdg.js",
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
classes: props,
selectedMenuKey: 0,
account: undefined,
uri: null,
};
Setting.initServerUrl();
Setting.initCasdoorSdk(Conf.AuthConfig);
}
getRemSize = () => {
let whdef = 100 / 1920;
let wW = window.innerWidth;
let rem = wW * whdef;
document.documentElement.style.fontSize = rem + "px";
}
componentDidMount = () => {
window.resize = () => {
this.getRemSize();
};
this.getRemSize();
}
UNSAFE_componentWillMount() {
this.updateMenuKey();
this.getAccount();
}
componentDidUpdate() {
const uri = location.pathname;
if (this.state.uri !== uri) {
this.updateMenuKey();
}
}
updateMenuKey() {
const uri = location.pathname;
this.setState({
uri: uri,
});
if (uri === "/") {
this.setState({selectedMenuKey: "/"});
} else if (uri.includes("/datasets")) {
this.setState({selectedMenuKey: "/datasets"});
} else if (uri.includes("/pending-tasks")) {
this.setState({selectedMenuKey: "/pending-tasks"});
} else if (uri.includes("/project-management")) {
this.setState({selectedMenuKey: "/project-management"});
} else if (uri.includes("/proposition-paper")) {
this.setState({selectedMenuKey: "/proposition-paper"});
} else {
this.setState({selectedMenuKey: "null"});
}
}
onUpdateAccount(account) {
this.setState({
account: account,
});
}
setLanguage(account) {
// let language = account?.language;
let language = localStorage.getItem("language");
if (language !== "" && language !== i18next.language) {
Setting.setLanguage(language);
}
}
getAccount() {
AccountBackend.getAccount()
.then((res) => {
let account = res.data;
if (account !== null) {
this.setLanguage(account);
}
this.setState({
account: account,
});
});
}
signout() {
AccountBackend.signout()
.then((res) => {
if (res.status === "ok") {
this.setState({
account: null,
});
Setting.showMessage("success", "Successfully signed out, redirected to homepage");
Setting.goToLink("/");
// this.props.history.push("/");
} else {
Setting.showMessage("error", `Signout failed: ${res.msg}`);
}
});
}
handleRightDropdownClick(e) {
if (e.key === "/account") {
Setting.openLink(Setting.getMyProfileUrl(this.state.account));
} else if (e.key === "/logout") {
this.signout();
}
}
renderAvatar() {
if (this.state.account.avatar === "") {
return (
<Avatar style={{backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: "middle"}} size="large">
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
} else {
return (
<Avatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size="large">
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
}
}
renderRightDropdown() {
const menu = (
<Menu onClick={this.handleRightDropdownClick.bind(this)}>
<Menu.Item key="/account">
<SettingOutlined />
{i18next.t("account:My Account")}
</Menu.Item>
<Menu.Item key="/logout">
<LogoutOutlined />
{i18next.t("account:Sign Out")}
</Menu.Item>
</Menu>
);
return (
<Dropdown key="/rightDropDown" overlay={menu} className="rightDropDown">
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}>
&nbsp;
&nbsp;
{
this.renderAvatar()
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
</div>
</Dropdown>
);
}
renderAccount() {
let res = [];
if (this.state.account === undefined) {
return null;
} else if (this.state.account === null) {
res.push(
<Menu.Item key="/signup" style={{float: "right", marginRight: "20px"}}>
<a href={Setting.getSignupUrl()}>
{i18next.t("account:Sign Up")}
</a>
</Menu.Item>
);
res.push(
<Menu.Item key="/signin" style={{float: "right"}}>
<a href={Setting.getSigninUrl()}>
{i18next.t("account:Sign In")}
</a>
</Menu.Item>
);
res.push(
<Menu.Item key="/" style={{float: "right"}}>
<a href="/">
{i18next.t("general:Home")}
</a>
</Menu.Item>
);
} else {
res.push(this.renderRightDropdown());
}
return res;
}
renderMenu() {
let res = [];
if (this.state.account === null || this.state.account === undefined) {
return [];
}
res.push(
<Menu.Item key="/">
<a href="/">
{i18next.t("general:Home")}
</a>
{/* <Link to="/">*/}
{/* Home*/}
{/* </Link>*/}
</Menu.Item>
);
res.push(
<Menu.Item key="/datasets">
<Link to="/datasets">
{i18next.t("general:Datasets")}
</Link>
</Menu.Item>
);
res.push(
<Menu.Item key="/pending-tasks">
<Link to="/pending-tasks">
{i18next.t("general:Pending Tasks")}
</Link>
</Menu.Item>
);
res.push(
<Menu.Item key="/proposition-paper">
<Link to="/proposition-paper">
{i18next.t("general:Proposition Paper")}
</Link>
</Menu.Item>
);
return res;
}
renderHomeIfSignedIn(component) {
if (this.state.account !== null && this.state.account !== undefined) {
return <Redirect to="/" />;
} else {
return component;
}
}
renderSigninIfNotSignedIn(component) {
if (this.state.account === null) {
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/signin" />;
} else if (this.state.account === undefined) {
return null;
} else {
return component;
}
}
renderContent() {
return (
<div>
<Header style={{padding: "0", marginBottom: "3px"}}>
{
Setting.isMobile() ? null : <a className="logo" href={"/"} />
}
<Menu
// theme="dark"
mode={"horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]}
style={{lineHeight: "64px"}}
>
{
this.renderMenu()
}
{
this.renderAccount()
}
<Menu.Item key="en" className="rightDropDown" style={{float: "right", cursor: "pointer", marginLeft: "-10px", marginRight: "20px"}}>
<div className="rightDropDown" style={{float: "right", cursor: "pointer"}} onClick={() => {Setting.changeLanguage("en");}}>
&nbsp;&nbsp;&nbsp;&nbsp;<IconFont type="icon-en" />
&nbsp;
English
&nbsp;
&nbsp;
</div>
</Menu.Item>
<Menu.Item key="zh" className="rightDropDown" style={{float: "right", cursor: "pointer"}}>
<div className="rightDropDown" style={{float: "right", cursor: "pointer"}} onClick={() => {Setting.changeLanguage("zh");}}>
&nbsp;&nbsp;&nbsp;&nbsp;<IconFont type="icon-zh" />
&nbsp;
中文
&nbsp;
&nbsp;
</div>
</Menu.Item>
</Menu>
</Header>
<Switch>
<Route path="/callback" component={AuthCallback} />
<Route path="/signin" render={(props) => this.renderHomeIfSignedIn(<SigninPage {...props} />)} />
<Route path="/pending-tasks" render={(props) => this.renderSigninIfNotSignedIn(<PendingTaskPage account={this.state.account} {...props} />)} />
<Route path="/datasets" render={(props) => this.renderSigninIfNotSignedIn(<DatasetListPage account={this.state.account} {...props} />)} />
<Route path="/datasets/:datasetName" render={(props) => this.renderSigninIfNotSignedIn(<DatasetEditPage account={this.state.account} {...props} />)} />
<Route path="/project-management/:project_id/:role" render={(props) => this.renderSigninIfNotSignedIn(<ProjectManagementPage account={this.state.account} {...props} />)} />
<Route path="/proposition-paper" render={(props) => this.renderSigninIfNotSignedIn(<PropositionPaperPage account={this.state.account} {...props} />)} />
<Route path="/" render={(props) => <HomePage account={this.state.account} {...props} />} />
</Switch>
</div>
);
}
renderFooter() {
// How to keep your footer where it belongs ?
// https://www.freecodecamp.org/neyarnws/how-to-keep-your-footer-where-it-belongs-59c6aa05c59c/
return (
<Footer id="footer" style={
{
borderTop: "1px solid #e8e8e8",
backgroundColor: "white",
textAlign: "center",
}
}>
Made with <span style={{color: "rgb(255, 255, 255)"}}></span> by <a style={{fontWeight: "bold", color: "black"}} target="_blank" rel="noreferrer" href="https://item.open-ct.com">OpenItem</a>, {Setting.isMobile() ? "Mobile" : "Desktop"} View
</Footer>
);
}
render() {
return (
<div id="parent-area">
<BackTop />
<div id="content-wrap">
{
this.renderContent()
}
</div>
{
this.renderFooter()
}
</div>
);
}
}
export default withRouter(App);

View File

@ -1,64 +0,0 @@
@import '~antd/dist/antd.less';
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #09d3ac;
}
#parent-area {
position: relative;
min-height: 100vh;
}
#content-wrap {
padding-bottom: 70px; /* Footer height */
}
#footer {
position: absolute;
bottom: 0;
width: 100%;
height: 70px; /* Footer height */
}
.ant-table-body {
overflow-y: hidden !important
}
.language_box {
background: url("https://cdn.casbin.org/img/muti_language.svg");
background-size: 25px, 25px;
background-position: center;
background-repeat: no-repeat;
width: 45px;
height: 65px;
float: right;
cursor: pointer;
}
.language_box:hover {
background-color: #f5f5f5;
}
.rightDropDown:hover {
background-color: #f5f5f5;
}

View File

@ -1,71 +0,0 @@
import React from "react";
import {Button, Result, Spin} from "antd";
import {withRouter} from "react-router-dom";
import * as Setting from "./Setting";
class AuthCallback extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
msg: null,
};
}
UNSAFE_componentWillMount() {
this.login();
}
getFromLink() {
const from = sessionStorage.getItem("from");
if (from === null) {
return "/";
}
return from;
}
login() {
Setting.signin().then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", "Logged in successfully");
const link = this.getFromLink();
Setting.goToLink(link);
} else {
this.setState({
msg: res.msg,
});
}
});
}
render() {
return (
<div style={{textAlign: "center"}}>
{this.state.msg === null ? (
<Spin
size="large"
tip="Signing in..."
style={{paddingTop: "10%"}}
/>
) : (
<div style={{display: "inline"}}>
<Result
status="error"
title="Login Error"
subTitle={this.state.msg}
extra={[
<Button type="primary" key="details">
Details
</Button>,
<Button key="help">Help</Button>,
]}
/>
</div>
)}
</div>
);
}
}
export default withRouter(AuthCallback);

View File

@ -1,470 +0,0 @@
import React, {Component} from "react";
import {Button, Form, Input, Modal, Select, Space, Table, Tag, message} from "antd";
import {withRouter} from "react-router-dom";
import * as ProjectBackend from "./backend/ProjectBackend";
import ModalCard from "./ModulaCard";
import "./BuildTeam.less";
import {WarningTwoTone} from "@ant-design/icons";
const {Search, TextArea} = Input;
const {confirm} = Modal;
const {Option} = Select;
class index extends Component {
constructor(props) {
super(props);
this.state = {
classes: props,
loadingState: false,
createLoading: false,
roleChangeForm: {
show: false,
assignment_id: "",
new_role: "",
updateLoading: false,
},
addMemberForm: {
show: false,
loadingState: false,
role: 1,
user_id: null,
userInfo: null,
},
emailForm: {
show: false,
loadingState: false,
message: "",
send_time: "",
sender: "binary",
destination: [],
subject: "",
},
memberList: [],
};
}
columns = [{
title: "姓名",
align: "center",
render: (text, record) => (
<span>{record.info.name}</span>
),
}, {
title: "年龄",
align: "center",
render: (text, record) => (
<span>{record.info.birthday}</span>
),
}, {
title: "性别",
align: "center",
render: (text, record) => (
<span>{record.info.gender ? "男" : "女"}</span>
),
}, {
title: "专业",
align: "center",
render: (text, record) => (
<Tag color="processing">{record.info.owner}</Tag>
),
}, {
title: "职位",
align: "center",
render: (text, record) => (
<span>{record.info.type}</span>
),
}, {
title: "邮箱",
align: "center",
render: (text, record) => (
<Button type="link" onClick={() => {
this.setState({
emailForm: Object.assign(this.state.emailForm, {destination: [record.info.email], send_time: new Date().getTime().toString(), show: true}),
});
}}>
{record.info.email}
</Button>
),
}, {
title: "组织",
align: "center",
render: (text, record) => (
<span>{record.info.signupApplication}</span>
),
}, {
title: "位置",
align: "center",
render: (text, record) => (
<span>{record.info.region}</span>
),
}, {
title: "电话",
align: "center",
render: (text, record) => (
<span>{record.info.phone}</span>
),
}, {
title: "项目角色",
align: "center",
render: (text, record) => {
let roleList = ["管理员", "专家", "学科助理", "教师", "外审专家"];
return (
<Tag color="green">{roleList[record.role - 1]}</Tag>
);
},
}, {
title: "状态",
align: "center",
render: (text, record) => (
<>
{
record.info.isOnline ? (
<Tag color="#87d068">已确认 </Tag>
) : (
<Tag color="#f50">未确认 ×</Tag>
)
}
</>
),
}, {
title: "管理",
align: "center",
render: (text, record) => (
<Space size="middle">
<Button type="link" onClick={() => {
this.setState({
roleChangeForm: Object.assign(this.state.roleChangeForm, {
assignment_id: record.Id,
new_role: record.role,
show: true,
}),
});
}}>更改角色</Button>
<Button type="link" danger onClick={() => {
confirm({
icon: <WarningTwoTone />,
content: "该删除操作不可逆,是否继续?",
okText: "确认移除",
cancelText: "取消移除",
// onOk: () => {
// this.setState({
// loadingState: true
// });
// request({
// url: baseURL + `/review/proj/assign/${record.uuid}`,
// // url:`http://49.232.73.36:8081/review/proj/assign/${record.uuid}`,
// method: "DELETE"
// }).then(res => {
// this.setState({
// loadingState: false
// });
// message.success("删除成功!");
// this.getProjectMember();
// }).catch(err => {
// message.error(err.message || "请求错误!");
// this.setState({
// loadingState: false
// });
// });
// },
onOk: () => { },
onCancel() {
message.info("已取消移除");
},
});
}}>移除人员</Button>
</Space>
),
}]
emailFormRef = React.createRef()
componentDidMount() {
this.getProjectMember();
}
getProjectMember = () => {
this.setState({
loadingState: true,
});
ProjectBackend.GetProjectAssignments(this.props.match.params.project_id.split("_").join("/")).then(res => {
let memberList = [];
memberList = [...res.data.admins.map(item => {
item.roleName = "admin";
return item;
}), ...res.data.assistants.map(item => {
item.roleName = "assistant";
return item;
}), ...res.data.experts.map(item => {
item.roleName = "expert";
return item;
}), ...res.data.out_experts.map(item => {
item.roleName = "out_expert";
return item;
}), ...res.data.teachers.map(item => {
item.roleName = "teacher";
return item;
})];
let id_list = memberList.map(item => item.user_id);
ProjectBackend.GetUserList(id_list).then(res => {
let userInfo_list = Object.values(res.data);
memberList = memberList.map((item, index) => {
item.info = userInfo_list[index];
return item;
});
this.setState({
memberList,
loadingState: false,
});
});
});
}
render() {
return (
<div className="build-team-page" data-component="build-team-page">
<ModalCard
title="项目成员"
right={(
<Button type="primary" size="small" onClick={() => {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {show: true}),
});
}}>添加成员</Button>
)}
>
<div className="member-list">
<Table
key="admins"
columns={this.columns}
rowKey="Id"
pagination={false}
dataSource={this.state.memberList}
size="small"
loading={this.state.loadingState}
/>
</div>
</ModalCard>
<Modal
title="角色分配修改"
visible={this.state.roleChangeForm.show}
okText="确认修改"
cancelText="取消修改"
closable={!this.state.roleChangeForm.updateLoading}
keyboard={!this.state.roleChangeForm.updateLoading}
maskClosable={!this.state.roleChangeForm.updateLoading}
confirmLoading={this.state.roleChangeForm.updateLoading}
// onOk={() => {
// this.setState({
// roleChangeForm: Object.assign(this.state.roleChangeForm, {updateLoading: true})
// });
// request({
// url: baseURL + "/review/proj/assign",
// // url:"http://49.232.73.36:8081/review/proj/assign",
// method: "PATCH",
// data: {
// assignment_id: this.state.roleChangeForm.assignment_id,
// new_role: this.state.roleChangeForm.new_role,
// }
// }).then(res => {
// this.setState({
// roleChangeForm: Object.assign(this.state.roleChangeForm, {updateLoading: false, show: false})
// });
// this.getProjectMember();
// message.success("修改成功!");
// }).catch(err => {
// message.error(err.message || "请求错误!");
// this.setState({
// roleChangeForm: Object.assign(this.state.roleChangeForm, {updateLoading: false})
// });
// });
// }}
onOk={() => { }}
onCancel={() => {
if (this.state.roleChangeForm.updateLoading) {
message.error("修改中,操作不可中断!");
} else {
this.setState({
roleChangeForm: Object.assign(this.state.roleChangeForm, {show: false}),
});
}
}}
>
<label style={{lineHeight: ".6rem"}}>新的角色</label>
<Select value={this.state.roleChangeForm.new_role} style={{width: "100%"}} onChange={(e) => {
this.setState({
roleChangeForm: Object.assign(this.state.roleChangeForm, {new_role: e}),
});
}}>
<Option value={1}>管理员</Option>
<Option value={2}>专家</Option>
<Option value={3}>学科助理</Option>
<Option value={4}>教师</Option>
<Option value={5}>外审人员</Option>
</Select>
</Modal>
<Modal
title="添加成员"
visible={this.state.addMemberForm.show}
okText="确认添加"
cancelText="取消添加"
closable={!this.state.addMemberForm.loadingState}
keyboard={!this.state.addMemberForm.loadingState}
maskClosable={!this.state.addMemberForm.loadingState}
confirmLoading={this.state.addMemberForm.loadingState}
onOk={() => {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {loadingState: true}),
});
let data = new Object();
data.project_id = this.props.match.params.project_id.split("_").join("/");
data.user_id = this.state.addMemberForm.user_id;
data.role = this.state.addMemberForm.role;
ProjectBackend.MakeOneAssignment(data).then(res => {
message.success("success");
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {userInfo: null, loadingState: false, show: false}),
});
this.getProjectMember();
});
}}
onCancel={() => {
if (this.state.addMemberForm.loadingState) {
message.error("添加中,操作不可中断!");
} else {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {show: false}),
});
}
}}
>
<label style={{lineHeight: ".6rem"}}>查找用户</label>
<Search placeholder="请输入被添加用户账号" onSearch={(e) => {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {loadingState: true}),
});
let id_list = new Array();
id_list.push(e);
ProjectBackend.GetUserList(id_list).then(res => {
let userInfo = res.data[Object.keys(res.data)[0]];
if(userInfo) {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {user_id: userInfo.id, userInfo: userInfo, loadingState: false}),
});
}else{
message.warn("查无此人!");
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {loadingState: false}),
});
}
});
}} enterButton />
{
this.state.addMemberForm.userInfo != null ? (
<>
<label style={{lineHeight: ".6rem"}}>用户名</label>
<div>{this.state.addMemberForm.userInfo.name}</div>
<label style={{lineHeight: ".6rem"}}>角色分配</label>
<Select value={this.state.addMemberForm.role} style={{width: "100%"}} onChange={(e) => {
this.setState({
addMemberForm: Object.assign(this.state.addMemberForm, {role: e}),
});
}}>
<Option value={1}>管理员</Option>
<Option value={2}>专家</Option>
<Option value={3}>学科助理</Option>
<Option value={4}>教师</Option>
<Option value={5}>外审人员</Option>
</Select>
</>
) : (<></>)
}
</Modal>
<Modal
title="发送邮件"
visible={this.state.emailForm.show}
okText="确认发送"
cancelText="取消发送"
closable={!this.state.emailForm.loadingState}
keyboard={!this.state.emailForm.loadingState}
maskClosable={!this.state.emailForm.loadingState}
confirmLoading={this.state.emailForm.loadingState}
// onOk={() => {
// this.emailFormRef.current.validateFields().then(formData => {
// let data = {
// body: {
// message: formData.message,
// send_time: this.state.emailForm.send_time,
// sender: this.state.emailForm.sender
// },
// destination: this.state.emailForm.destination,
// subject: formData.subject
// };
// this.setState({
// emailForm: Object.assign(this.state.emailForm, {loadingState: true})
// });
// request({
// url: baseURL + "/review/noticer/email",
// // url:"http://49.232.73.36:8081/review/noticer/email",
// method: "POST",
// data
// }).then(res => {
// this.setState({
// emailForm: Object.assign(this.state.emailForm, {loadingState: false, show: false})
// });
// this.emailFormRef.current.resetFields();
// message.success("发送成功");
// }).catch(err => {
// this.setState({
// emailForm: Object.assign(this.state.emailForm, {loadingState: false})
// });
// message.error(err.message || "请求错误");
// });
// }).catch(err => {
// message.warning("请正确填写邮件内容");
// });
// }}
onOk={() => { }}
onCancel={() => {
if (this.state.emailForm.loadingState) {
message.error("发送中,操作不可中断!");
} else {
this.emailFormRef.current.resetFields();
this.setState({
emailForm: Object.assign(this.state.emailForm, {show: false}),
});
}
}}
>
<Form
name="emailForm"
labelCol={{span: 4}}
wrapperCol={{span: 20}}
initialValues={this.state.emailForm}
autoComplete="off"
ref={this.emailFormRef}
>
<Form.Item
label="邮件主题"
name="subject"
rules={[{required: true, message: "请输入邮件主题"}]}
>
<Input placeholder="请输入邮件主题" />
</Form.Item>
<Form.Item
label="邮件内容"
name="message"
rules={[{required: true, message: "请输入邮件内容"}]}
>
<TextArea placeholder="请输入邮件内容" autoSize={{minRows: 2, maxRows: 6}} />
</Form.Item>
</Form>
</Modal>
</div>
);
}
}
export default withRouter(index);

View File

@ -1,10 +0,0 @@
[data-component=build-team-page]{
width: 100%;
min-height: 6rem;
background-color: white;
.member-list{
width: 100%;
box-sizing: border-box;
padding: 0px .2rem;
}
}

View File

@ -1,106 +0,0 @@
import React, {Component} from "react";
import {Button, Calendar, Col, Row, Select} from "antd";
import "./CalendarButton.less";
/**
* @description: 参数
* @icon 按钮icon
* @label 按钮名称
*/
export default class index extends Component {
state = {
calendarVisible: false,
}
render() {
return (
<div className="calendar-button-box" data-component="calendar-button-box">
<Button size="small" icon={this.props.icon} onClick={() => {
this.setState({
calendarVisible: true,
});
}}>{this.props.label}</Button>
<div className="calendar-box" style={{maxHeight: this.state.calendarVisible ? "5rem" : 0}}>
<Calendar
fullscreen={false}
headerRender={({value, type, onChange, onTypeChange}) => {
const start = 0;
const end = 12;
const monthOptions = [];
const current = value.clone();
const localeData = value.localeData();
const months = [];
for (let i = 0; i < 12; i++) {
current.month(i);
months.push(localeData.monthsShort(current));
}
for (let index = start; index < end; index++) {
monthOptions.push(
<Select.Option className="month-item" key={`${index}`}>
{months[index]}
</Select.Option>
);
}
const month = value.month();
const year = value.year();
const options = [];
for (let i = year - 10; i < year + 10; i += 1) {
options.push(
<Select.Option key={i} value={i} className="year-item">
{i}
</Select.Option>
);
}
return (
<div style={{padding: 8}}>
<Row gutter={8} justify="end">
<Col>
<Select
size="small"
dropdownMatchSelectWidth={false}
className="my-year-select"
onChange={newYear => {
const now = value.clone().year(newYear);
onChange(now);
}}
value={String(year)}
>
{options}
</Select>
</Col>
<Col>
<Select
size="small"
dropdownMatchSelectWidth={false}
value={String(month)}
onChange={selectedMonth => {
const newValue = value.clone();
newValue.month(parseInt(selectedMonth, 10));
onChange(newValue);
}}
>
{monthOptions}
</Select>
</Col>
</Row>
</div>
);
}}
onSelect={(_date) => {
let date = new Date(_date._d);
this.setState({
calendarVisible: false,
});
this.props.onDateChange(`${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`);
}}
/>
</div>
</div>
);
}
}

View File

@ -1,21 +0,0 @@
[data-component=calendar-button-box]{
position: relative;
width: max-content;
height: max-content;
> .calendar-box{
width: auto;
max-height: 0;
overflow: hidden;
transition: all .2s;
position: absolute;
background-color: white;
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
top: .34rem;
left: 50%;
transform: translateX(-50%);
z-index: 600;
.ant-picker-calendar-date-value,th{
font-size: .14rem;
}
}
}

View File

@ -1,143 +0,0 @@
import React, {Component} from "react";
import {Input, Tag, Tooltip} from "antd";
import {PlusOutlined} from "@ant-design/icons";
import "./ChangeTags.less";
export default class index extends Component {
state = {
tags: [],
inputVisible: false,
inputValue: "",
editInputIndex: -1,
editInputValue: "",
};
handleClose = (removedTag) => {
const tags = this.state.tags.filter((tag) => tag !== removedTag);
this.props.onChange(tags);
this.setState({tags});
};
showInput = () => {
this.setState({inputVisible: true}, () => this.input.focus());
};
handleInputChange = (e) => {
this.setState({inputValue: e.target.value});
};
handleInputConfirm = () => {
const {inputValue} = this.state;
let {tags} = this.state;
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue];
}
this.props.onChange(tags);
this.setState({
tags,
inputVisible: false,
inputValue: "",
});
};
handleEditInputChange = (e) => {
this.setState({editInputValue: e.target.value});
};
handleEditInputConfirm = () => {
this.setState(({tags, editInputIndex, editInputValue}) => {
const newTags = [...tags];
newTags[editInputIndex] = editInputValue;
return {
tags: newTags,
editInputIndex: -1,
editInputValue: "",
};
});
};
saveInputRef = (input) => {
this.input = input;
};
saveEditInputRef = (input) => {
this.editInput = input;
};
render() {
const {tags, inputVisible, inputValue, editInputIndex, editInputValue} =
this.state;
return (
<>
{tags.map((tag, index) => {
if (editInputIndex === index) {
return (
<Input
ref={this.saveEditInputRef}
key={tag}
size="small"
className="tag-input"
value={editInputValue}
onChange={this.handleEditInputChange}
onBlur={this.handleEditInputConfirm}
onPressEnter={this.handleEditInputConfirm}
/>
);
}
const isLongTag = tag.length > 20;
const tagElem = (
<Tag
className="edit-tag"
key={tag}
closable={true}
onClose={() => this.handleClose(tag)}
>
<span
onDoubleClick={(e) => {
if (index !== 0) {
this.setState(
{editInputIndex: index, editInputValue: tag},
() => {
this.editInput.focus();
}
);
e.preventDefault();
}
}}
>
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
</span>
</Tag>
);
return isLongTag ? (
<Tooltip title={tag} key={tag}>
{tagElem}
</Tooltip>
) : (
tagElem
);
})}
{inputVisible && (
<Input
ref={this.saveInputRef}
type="text"
size="small"
className="tag-input"
value={inputValue}
onChange={this.handleInputChange}
onBlur={this.handleInputConfirm}
onPressEnter={this.handleInputConfirm}
/>
)}
{!inputVisible && (
<Tag className="site-tag-plus" onClick={this.showInput}>
<PlusOutlined /> 添加
</Tag>
)}
</>
);
}
}

View File

@ -1,14 +0,0 @@
.site-tag-plus {
background: #fff;
border-style: dashed;
}
.edit-tag {
user-select: none;
}
.tag-input {
width: 78px;
margin-right: 8px;
vertical-align: top;
}

View File

@ -1,190 +0,0 @@
import React, {Component} from "react";
import {withRouter} from "react-router-dom";
import {Button, Col, Row, Select, Slider, Spin, Tag, message} from "antd";
import BraftEditor from "braft-editor";
import "braft-editor/dist/index.css";
import "./ChoiceQuestionEditer.less";
import * as PropositionBackend from "./backend/PropositionBackend";
const {Option} = Select;
class ChoiceQuestionEditer extends Component {
state = {
editorState: BraftEditor.createEditorState(null),
loadingState: false,
questionParams: {
subject: "",
difficulty: 1,
answer: "",
},
}
upLoadQuestion = (uid) => {
this.setState({
loadingState: true,
});
let data = {
advanced_props: {
ctt_diff_1: this.state.questionParams.difficulty,
ctt_diff_2: this.state.questionParams.difficulty,
ctt_level: this.state.questionParams.difficulty,
irt_level: this.state.questionParams.difficulty,
},
apply_record: {
grade_fits: this.props.grade_range.join(","),
participant_count: 0,
test_count: 0,
test_region: [],
test_year: `${new Date().getFullYear()}`,
},
author: uid,
basic_props: {
ability_dimension: this.props.ability.join(","),
description: "暂无",
details: this.state.editorState.toHTML(),
details_dimension: this.props.content.join(","),
encode: "",
keywords: [],
sub_ability_dimension: "",
sub_details_dimension: "",
subject: this.state.questionParams.subject,
subject_requirements: "",
},
extra_props: {
is_question_group: false,
is_scene: true,
material_length: 0,
reading_material_topic: "",
},
info: {
answer: this.state.questionParams.answer,
body: this.state.editorState.toHTML(),
solution: "无",
title: "无",
type: "选择题",
},
source_project: this.props.projectId,
spec_props: {
article_type: "无",
length: "无",
topic: "无",
},
};
PropositionBackend.CreateNewQuestion(data).then(res => {
this.setState({
loadingState: false,
});
}).then(res => {
this.props.history.goBack();
message.success("上传成功");
}).catch(err => {
this.setState({
loadingState: false,
});
message.error(err.message || "请求错误");
});
}
componentDidMount() {
this.setState({
questionParams: Object.assign(this.state.questionParams, {subject: this.props.defaultSubjectValue}),
});
}
render() {
return (
<div className="choice-question-editer" data-component="choice-question-editer" id="choice-question-edit-box">
<Spin spinning={this.state.loadingState} tip="上传试题中">
<Row>
<BraftEditor
value={this.state.editorState}
onChange={(editorState) => {
this.setState({editorState});
}}
onSave={() => {
}}
/>
</Row>
<Row className="question-params">
<div className="title">
<span>参数编辑</span>
</div>
<Row className="param-item" style={{marginTop: ".17rem"}}>
<Col span="4" className="label">
<span>学科</span>
</Col>
<Col span="20" className="value">
<Select
placeholder="选择学科"
value={this.state.questionParams.subject}
defaultValue={this.props.defaultSubjectValue}
onSelect={(e) => {
let questionParams = Object.assign(this.state.questionParams, {subject: e});
this.setState({
questionParams,
});
}}
size="small"
>
{
this.props.subjectList.map((item, index) => (
<Option value={item} key={index + Math.random(100)}>{item}</Option>
))
}
</Select>
</Col>
</Row>
<Row className="param-item" style={{marginTop: ".17rem"}}>
<Col span="4" className="label">
<span>难度</span>
</Col>
<Col span="20" className="value">
<Slider marks={{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}} step={null} defaultValue={1} max={5} min={1} onChange={(e) => {
let questionParams = Object.assign(this.state.questionParams, {difficulty: e});
this.setState({
questionParams,
});
}} />
</Col>
</Row>
<Row className="param-item" style={{marginTop: ".3rem"}}>
<Col span="4" className="label">
<span>能力纬度</span>
</Col>
<Col span="20" className="value">
<div className="tag-list">
{
this.props.ability.map(item => (
<Tag key={item.id}>{item}</Tag>
))
}
</div>
</Col>
</Row>
<Row className="param-item" style={{marginTop: ".17rem"}}>
<Col span="4" className="label">
<span>内容纬度</span>
</Col>
<Col span="20" className="value">
<div className="tag-list">
{
this.props.content.map(item => (
<Tag key={item.id}>{item}</Tag>
))
}
</div>
</Col>
</Row>
</Row>
<div className="question-complete-box">
<Button type="primary" block onClick={() => {
this.upLoadQuestion(this.props.author);
}}>完成编辑</Button>
</div>
</Spin>
</div>
);
}
}
export default withRouter(ChoiceQuestionEditer);

View File

@ -1,48 +0,0 @@
[data-component=choice-question-editer]{
width: 100%;
height: auto;
.question-params{
> .title{
width: 100%;
height: .41rem;
background-color: #EEEEEE;
line-height: .41rem;
box-sizing: border-box;
padding-left: .1rem;
> span{
font-size: .14rem;
color: #000000;
}
}
> .param-item{
width: 100%;
height: .32rem;
.label{
box-sizing: border-box;
padding-left: .1rem;
line-height: .32rem;
> span{
font-size: .14rem;
color: #000000;
}
}
.value{
> .ant-select{
width: 100%;
}
> .tag-list{
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
}
}
}
.question-complete-box{
width: 100%;
margin-top: .77rem;
margin-bottom: .5rem;
}
}

View File

@ -1,102 +0,0 @@
import React, {Component} from "react";
import {Col, Row, Statistic} from "antd";
import ModulaCard from "./ModulaCard";
import "./CompletionStatus.less";
export default class index extends Component {
state = {
status: {},
loadingState: false,
}
getStatusList = () => {
this.setState({
loadingState: true,
});
// request({
// url:baseURL+`/review/proj/step/stat/${this.props.stepId}`,
// // url:`http://49.232.73.36:8081/review/proj/step/stat/${this.props.stepId}`,
// method:"GET"
// }).then(res => {
// this.setState({
// status:res.data,
// loadingState:false
// });
// }).catch(err => {
// this.setState({
// loadingState:false
// });
// });
var res = {
"operation_code": 1000,
"message": "",
"data": {
"pass": 0,
"pass_rate": 0,
"return": 0,
"to_audit": 0,
"to_correct": 0,
"to_upload": 0,
"total": 0,
},
};
this.setState({
status: res.data,
loadingState: false,
});
}
componentDidMount() {
this.getStatusList();
}
render() {
return (
<ModulaCard title="完成情况">
{
this.state.loadingState ? (
<></>
) : (
<div className="completion-status-box" data-component="completion-status-box">
<div className="left-box">
<div className="title">
<span>{this.props.title}</span>
</div>
<div className="value-list">
<Row gutter={16}>
<Col span={12}>
<Statistic title="材料总计" value={this.state.status.total} />
</Col>
<Col span={12}>
<Statistic title="通过率" value={this.state.status.pass_rate} suffix="%" />
</Col>
</Row>
</div>
</div>
<div className="right-box">
<Row gutter={16} style={{width: "4.2rem"}}>
<Col span={4}>
<Statistic title={<div className="statistic-item-title"><div className="circle" style={{backgroundColor: "#87D068"}}></div></div>} value={this.state.status.pass} />
</Col>
<Col span={4}>
<Statistic title={<div className="statistic-item-title"><div className="circle" style={{backgroundColor: "#FF5500"}}></div></div>} value={this.state.status.return} />
</Col>
<Col span={5}>
<Statistic title={<div className="statistic-item-title"><div className="circle" style={{backgroundColor: "#FF5500"}}></div></div>} value={this.state.status.to_upload} />
</Col>
<Col span={5}>
<Statistic title={<div className="statistic-item-title"><div className="circle" style={{backgroundColor: "#2DB7F5"}}></div></div>} value={this.state.status.to_audit} />
</Col>
<Col span={5}>
<Statistic title={<div className="statistic-item-title"><div className="circle" style={{backgroundColor: "#2DB7F5"}}></div></div>} value={this.state.status.to_correct} />
</Col>
</Row>
</div>
</div>
)
}
</ModulaCard>
);
}
}

View File

@ -1,51 +0,0 @@
[data-component=completion-status-box]{
width: 100%;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding-left: 0.47rem;
> .left-box{
> .title{
height: .24rem;
line-height: .24rem;
margin-bottom: .08rem;
> span{
font-size: .14rem;
color: #8C8C8C;
}
}
> .value-list{
.ant-statistic-title{
font-size: .14rem;
color: rgba(0, 0, 0, 0.45);
}
.ant-statistic-content-value-int,.ant-statistic-content-suffix{
font-size: .26rem;
color: rgba(0, 0, 0, 0.85);
}
}
}
> .right-box{
.statistic-item-title{
font-size: .14rem;
color: #000000D9;
line-height: .22rem;
box-sizing: border-box;
padding-left: .14rem;
position: relative;
> .circle{
width: .06rem;
height: .06rem;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-radius: 50%;
}
}
.ant-statistic-content-value-int,.ant-statistic-content-suffix{
font-size: .26rem;
color: rgba(0, 0, 0, 0.85);
}
}
}

View File

@ -1,11 +0,0 @@
export const AuthConfig = {
// serverUrl: "https://door.casdoor.com",
serverUrl: "http://localhost:7001",
clientId: "eb78c88fc0697611f3e3",
appName: "app-openitem",
organizationName: "openct",
redirectPath: "/callback",
};
export const ForceLanguage = "";
export const DefaultLanguage = "en";

View File

@ -1,477 +0,0 @@
import React, {Component} from "react";
import {Button, Col, Descriptions, Input, Modal, PageHeader, Pagination, Row, Select, Slider, Spin, Tag, message} from "antd";
import UpLoadQuestionModal from "./UpLoadQuestionModal";
import HistoryQuestion from "./HistoryQuestion";
import BraftEditor from "braft-editor";
import "braft-editor/dist/index.css";
import "./CreatePaper.less";
import * as ProjectBackend from "./backend/ProjectBackend";
import * as PropositionBackend from "./backend/PropositionBackend";
const {Option} = Select;
const {Search} = Input;
export default class CreatePaper extends Component {
state = {
initLoading: true,
createTime: 0,
projectInfo: {},
upLoadQuestionModalParams: {
show: false,
type: "update-paper",
},
questionList: [{
state: "edit", // 表示编辑中
subject: "",
difficulty: 1,
ability: this.props.match.params.ability,
content: this.props.match.params.content,
body: BraftEditor.createEditorState(null),
}],
paperViewVisible: false,
loadingState: false,
}
componentDidMount() {
let t = new Date();
this.setState({
createTime: `${t.getFullYear()}-${t.getMonth().toString().padStart(2, "0")}-${t.getDate().toString().padStart(2, "0")} ${t.getHours().toString().padStart(2, "0")}:${t.getMinutes().toString().padStart(2, "0")}:${t.getSeconds().toString().padStart(2, "0")}`,
});
this.getProjectInfo();
}
// upLoadQuestion = (info) => {
// this.setState({
// loadingState: true
// });
// return request({
// url: baseURL1 + "/qbank/question",
// // url:"http://49.232.73.36:8082/qbank/question",
// method: "POST",
// data: {
// advanced_props: {
// ctt_diff_1: info.difficulty,
// ctt_diff_2: info.difficulty,
// ctt_level: info.difficulty,
// irt_level: info.difficulty
// },
// apply_record: {
// grade_fits: this.state.projectInfo.basic_info.grade_range.join(","),
// participant_count: 0,
// test_count: 0,
// test_region: [],
// test_year: `${new Date().getFullYear()}`,
// },
// author: store.getState().userInfo.Id,
// basic_props: {
// ability_dimension: info.ability,
// description: "暂无",
// details: info.body.toHTML(),
// details_dimension: info.content,
// encode: "",
// keywords: [],
// sub_ability_dimension: "",
// sub_details_dimension: "",
// subject: info.subject,
// subject_requirements: ""
// },
// extra_props: {
// is_question_group: false,
// is_scene: true,
// material_length: 0,
// reading_material_topic: ""
// },
// info: {
// answer: "",
// body: info.body.toHTML(),
// solution: "无",
// title: "无",
// type: "选择题"
// },
// source_project: this.props.match.params.project,
// spec_props: {
// article_type: "无",
// length: "无",
// topic: "无"
// }
// }
// });
// }
upLoadQuestion = (info) => {
this.setState({
loadingState: true,
});
let data = {
advanced_props: {
ctt_diff_1: info.difficulty,
ctt_diff_2: info.difficulty,
ctt_level: info.difficulty,
irt_level: info.difficulty,
},
apply_record: {
grade_fits: this.state.projectInfo.basic_info.grade_range.join(","),
participant_count: 0,
test_count: 0,
test_region: [],
test_year: `${new Date().getFullYear()}`,
},
author: this.props.match.params.uid,
basic_props: {
ability_dimension: info.ability,
description: "暂无",
details: info.body.toHTML(),
details_dimension: info.content,
encode: "",
keywords: [],
sub_ability_dimension: "",
sub_details_dimension: "",
subject: info.subject,
subject_requirements: "",
},
extra_props: {
is_question_group: false,
is_scene: true,
material_length: 0,
reading_material_topic: "",
},
info: {
answer: "",
body: info.body.toHTML(),
solution: "无",
title: "无",
type: "选择题",
},
source_project: this.props.match.params.project,
spec_props: {
article_type: "无",
length: "无",
topic: "无",
},
};
return PropositionBackend.CreateNewQuestion(data);
}
getProjectInfo = () => {
let newParms = this.props.match.params;
ProjectBackend.GetDetailedInfo(newParms.uid + "/" + newParms.project).then(res => {
this.setState({
projectInfo: res.data.basic_info,
initLoading: false,
});
}).catch(err => {
this.setState({
initLoading: false,
});
this.props.history.goBack();
message.error("编辑器加载失败!");
});
}
render() {
return (
<div className="create-paper-page" data-component="create-paper-page">
<PageHeader
ghost={false}
onBack={() => this.props.history.goBack()}
title="命题组卷"
subTitle="上传试题"
extra={[
<Button key="1" onClick={() => {
this.setState({
upLoadQuestionModalParams: {
show: true,
type: "update-paper",
},
});
}}>编辑内容</Button>,
]}
>
{
this.state.initLoading ? (
<Spin spinning={true} tip="初始化中"></Spin>
) : (
<Descriptions size="small" column={3}>
<Descriptions.Item label="创建时间" key="createAt">{this.state.createTime}</Descriptions.Item>
<Descriptions.Item label="项目" key="peojects">{this.state.projectInfo.basic_info.name}</Descriptions.Item>
<Descriptions.Item label="学科" key="subjects">{
this.state.projectInfo.basic_info.subjects.map((item, index) => (
<span key={index}>{item}{index === this.state.projectInfo.basic_info.subjects.length - 1 ? "" : "、"}</span>
))
}</Descriptions.Item>
<Descriptions.Item label="内容纬度" key="content">{
this.props.match.params.content.split(",").map((item, index) => (
<span key={index}>{item}{index === this.props.match.params.content.split(",").length - 1 ? "" : "、"}</span>
))
}</Descriptions.Item>
<Descriptions.Item label="能力纬度" key="ability">
{
this.props.match.params.ability.split(",").map((item, index) => (
<span key={index}>{item}{index === this.props.match.params.ability.split(",").length - 1 ? "" : "、"}</span>
))
}
</Descriptions.Item>
</Descriptions>
)
}
</PageHeader>
<Spin spinning={this.state.loadingState} tip="上传中">
<Row className="container">
<Col span="8" className="left-box">
<div className="title-box">
<div className="title-value">
<div className="ver-line"></div>
<div className="value">
<span>相关题库</span>
</div>
</div>
<Search placeholder="input search text" style={{width: "2.64rem"}} size="middle" />
</div>
<HistoryQuestion />
<HistoryQuestion />
<HistoryQuestion />
<Pagination defaultCurrent={1} total={50} className="page-spare" />
</Col>
<Col span="16" className="right-box">
{
this.state.questionList.map((item, index) => (
<Row className="question-item" key={index + Math.random(100)} gutter={[8, 8]}>
<Col className="info-box" span="18">
<Row className="main-box">
{
item.state === "edit" ? (
<BraftEditor
value={item.body}
onChange={(editorState) => {
let questionList = [...this.state.questionList];
questionList[index].body = editorState;
this.setState({questionList});
}}
onSave={() => {
}}
/>
) : (
<div className="view-box" dangerouslySetInnerHTML={{__html: item.body.toHTML()}}>
</div>
)
}
</Row>
<Row className="btn-line">
<Button type="primary" size="small" style={{width: "1rem", marginRight: ".2rem"}} onClick={() => {
let questionList = [...this.state.questionList];
questionList[index].state = item.state === "edit" ? "complete" : "edit";
this.setState({
questionList,
});
}}>
{
item.state === "edit" ? "保存" : "编辑"
}
</Button>
<Button type="primary" danger size="small" style={{width: "1rem"}} onClick={() => {
let questionList = [...this.state.questionList];
questionList.splice(index, 1);
this.setState({
questionList,
});
}}>
删除
</Button>
</Row>
</Col>
<Col className="params-box" span="6">
<div className="title">
<span>参数编辑</span>
</div>
<Row className="params-item">
<Col className="label" span="8">
<span>学科</span>
</Col>
<Col className="value" span="16">
<Select
placeholder="选择学科"
value={item.subject}
defaultValue={this.props.match.params.subject}
onSelect={(e) => {
let questionList = [...this.state.questionList];
questionList[index].subject = e;
this.setState({
questionList,
});
}}
size="small"
>
{
this.state.initLoading ? "" : this.state.projectInfo.basic_info.subjects.map((item, index) => (
<Option value={item} key={index + Math.random(100)}>{item}</Option>
))
}
</Select>
</Col>
</Row>
<Row className="params-item" style={{marginTop: ".17rem"}}>
<Col span="8" className="label">
<span>难度</span>
</Col>
<Col span="16" className="value">
<Slider marks={{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}} step={null} value={item.difficulty} defaultValue={1} max={5} min={1} onChange={(e) => {
let questionList = [...this.state.questionList];
questionList[index].difficulty = e;
this.setState({
questionList,
});
}} />
</Col>
</Row>
<Row className="params-item" style={{marginTop: ".17rem"}}>
<Col span="8" className="label">
<span>能力纬度</span>
</Col>
<Col span="16" className="value">
<div className="tag-list">
{
item.ability.split(",").map(item => (
<Tag key={item.Id}>{item}</Tag>
))
}
</div>
</Col>
</Row>
<Row className="params-item" style={{marginTop: ".1rem"}}>
<Col span="8" className="label">
<span>内容纬度</span>
</Col>
<Col span="16" className="value">
<div className="tag-list">
{
item.content.split(",").map(item => (
<Tag Key={item.Id}>{item}</Tag>
))
}
</div>
</Col>
</Row>
</Col>
<Row gutter={[8, 8]} className="oper-btn-line">
<Col span="18" className="left">
{
index === this.state.questionList.length - 1 ? (
<Button type="primary" size="large" onClick={() => {
let questionList = [...this.state.questionList];
questionList.push({
state: "edit", // 表示编辑中
subject: "",
difficulty: 1,
ability: this.props.match.params.ability,
content: this.props.match.params.content,
body: BraftEditor.createEditorState(null),
});
this.setState({
questionList,
});
}}>
添加一题
</Button>
) : ""
}
</Col>
<Col span="6" className="right">
<Button size="large" style={{backgroundColor: "#EEEEEE"}} onClick={() => {
this.setState({
paperViewVisible: true,
});
}}>
预览试卷
</Button>
<Button type="primary" size="large" style={{width: "1.2rem"}} onClick={async() => {
let questionIdList = [];
for (let i = 0; i < this.state.questionList.length; i++) {
await this.upLoadQuestion(this.state.questionList[i]).then((res) => {
questionIdList.push({
comment: "暂无",
question_id: res.data,
score: 0,
});
}).catch(err => {
this.setState({
loadingState: false,
});
message.error(err.message || "试题上传失败,请重试");
});
}
let data = {
author: this.props.match.params.uid,
info: [{
description: "暂无",
question_list: questionIdList,
score: 0,
title: "无",
}],
props: {
difficulty: "1",
grade_range: this.state.projectInfo.basic_info.grade_range,
subjects: this.state.projectInfo.basic_info.subjects,
time_limit: "0",
},
source_project: this.props.match.params.project,
title: "无",
};
PropositionBackend.CreateNewTestpaper(data).then(res => {
this.setState({
loadingState: false,
});
this.props.history.goBack();
message.success("试题上传成功");
}).catch(err => {
this.setState({
loadingState: false,
});
message.error(err.message || "试题上传失败,请重试");
this.props.history.goBack();
});
}}>
保存
</Button>
</Col>
</Row>
</Row>
))
}
</Col>
</Row>
</Spin>
<UpLoadQuestionModal
{...this.state.upLoadQuestionModalParams}
onClose={() => {
this.setState({
upLoadQuestionModalParams: {
show: false,
type: "update-paper",
},
});
}}
/>
<Modal width={1200} title="试卷预览" visible={this.state.paperViewVisible} onOk={() => {
this.setState({
paperViewVisible: false,
});
}} onCancel={() => {
this.setState({
paperViewVisible: false,
});
}}>
<div className="view-box">
{
this.state.questionList.map(item => (
<div className="question-view-item" dangerouslySetInnerHTML={{__html: item.body.toHTML()}} key={item.Id} ></div>
))
}
</div>
</Modal>
</div>
);
}
}

View File

@ -1,120 +0,0 @@
[data-component=create-paper-page]{
width: 100%;
.container{
width: 100%;
box-sizing: border-box;
> .left-box{
background-color: white;
box-sizing: border-box;
padding: .22rem .1rem .4rem .22rem;
> .title-box{
width: 100%;
height: .32rem;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding-left: .1rem;
padding-right: .24rem;
margin-bottom: .2rem;
> .title-value{
height: 100%;
display: flex;
align-items: center;
> .ver-line{
height: .18rem;
width: .06rem;
background-color: #1890FF;
margin-right: .06rem;
}
> .value{
font-size: .16rem;
color: #000000D9;
}
}
}
.page-spare{
margin: .2rem;
.ant-pagination-item-link,.ant-pagination-item{
background: #91D5FF;
border: 1px solid #FFFEFE;
box-sizing: border-box;
border-radius: 2px;
.anticon{
color: white;
}
}
}
}
> .right-box{
background-color: #EEEEEE;
box-sizing: border-box;
padding: .16rem;
height: 100%;
overflow-y: scroll;
> .question-item{
width: 100%;
height: 100%;
background-color: white;
box-sizing: border-box;
padding: .62rem .73rem 0px .29rem;
overflow-y: scroll;
margin-top: .2rem;
> .params-box{
> .title{
width: 100%;
height: .41rem;
background-color: #EEEEEE;
line-height: .41rem;
box-sizing: border-box;
padding-left: .1rem;
> span{
font-size: .14rem;
color: #000000;
}
}
> .params-item{
width: 100%;
min-height: .32rem;
margin-top: .1rem;
> .label{
box-sizing: border-box;
padding-left: .1rem;
line-height: .32rem;
> span{
font-size: .14rem;
color: #000000;
}
}
> .value{
> .ant-select{
width: 100%;
}
> .tag-list{
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
}
}
}
> .oper-btn-line{
height: 1rem;
width: 100%;
> .right{
display: flex;
justify-content: space-around;
}
}
> .info-box{
> .btn-line{
height: 1rem;
}
}
}
}
}
}

View File

@ -1,433 +0,0 @@
import React, {Component} from "react";
import {Button, Modal, Pagination, Radio, Space, Table, Tag, Upload, message} from "antd";
import ModulaCard from "./ModulaCard";
import ModifyRecordModal from "./ModifyRecordModal";
import {FileTextTwoTone, UploadOutlined} from "@ant-design/icons";
import "./DataTable.less";
export default class index extends Component {
state = {
data: [],
loadingState: false,
reviewResultsVisible: false,
statusChangeLoading: false,
modifyRecordVisible: false,
selectedSubmitId: "",
statusChangeParams: {
value: 1,
submitId: "",
},
}
modifyRecordRef = React.createRef()
colums = [[{
title: "上传时间",
key: "CreateAt",
align: "center",
width: 150,
render: (text, record) => (
<span>{this.dateFilter(record.CreateAt)}</span>
),
}, {
title: "上传用户",
dataIndex: "user",
key: "user",
align: "center",
width: 120,
}, {
title: "评审材料",
key: "contents",
align: "center",
width: 220,
render: (text, record) => (
<Space size="middle">
{
record.contents ? (
<span style={{cursor: "pointer"}} onClick={this.downLoadFile.bind(this, record.contents[0].item_id)}>{record.contents[0].comment} <FileTextTwoTone /></span>
) : "无"
}
</Space>
),
}, {
title: "评审结果",
key: "status",
align: "center",
width: 100,
render: (text, record) => {
let levelList = [{
mode: "等待审核",
color: "default",
}, {
mode: "通过",
color: "#87D068",
}, {
mode: "再修改",
color: "#2DB7F5",
}, {
mode: "驳回",
color: "#FF5500",
}];
return (
<Space size="middle">
<Tag color={levelList[record.status].color} onClick={() => {
this.setState({
reviewResultsVisible: true,
statusChangeParams: {
submitId: record.uuid,
value: record.status,
},
});
}} style={{cursor: "pointer"}}>{levelList[record.status].mode}</Tag>
</Space>
);
},
}, {
title: "反馈批注材料",
dataIndex: "feedback-material",
key: "feedback-material",
align: "center",
render: (text, record) => {
if (!record.feedbackMaterial) {
return (
<Space size="middle">
<Upload>
<Button icon={<UploadOutlined />} size="small">上传批注</Button>
</Upload>
</Space>
);
}
return (
<Space size="middle">
<span>{record.feedbackMaterial}</span><FileTextTwoTone />
</Space>
);
},
}], [{
title: "材料编号",
dataIndex: "uuid",
key: "id",
align: "center",
}, {
title: "上传时间",
key: "date",
align: "center",
render: (text, record) => (
<Space size="middle">
<span>{this.dateFilter(record.CreateAt)}</span>{record.isDelay ? (<Tag color="error"></Tag>) : ""}
</Space>
),
}, {
title: "评审材料",
key: "review-materials",
width: 220,
align: "center",
render: (text, record) => (
<Space size="middle">
{
record.contents ? (
<span style={{cursor: "pointer"}} onClick={this.downLoadFile.bind(this, record.contents[0].item_id)}>{record.contents[0].comment} <FileTextTwoTone /></span>
) : "无"
}
</Space>
),
}, {
title: "评审结果",
key: "result",
align: "center",
render: (text, record) => {
let levelList = [{
mode: "等待评审",
color: "default",
}, {
mode: "通过",
color: "#87D068",
}, {
mode: "再修改",
color: "#2DB7F5",
}, {
mode: "驳回",
color: "#FF5500",
}];
return (
<Space size="middle">
<Tag color={levelList[record.status].color} style={{cursor: "pointer"}}>{levelList[record.status].mode}</Tag>
</Space>
);
},
}, {
title: "反馈意见",
key: "feedback",
align: "center",
render: (text, record) => {
if (record.feedback === "") {
return (
<Space size="middle">
<span style={{cursor: "pointer"}}>等待评审...</span>
</Space>
);
}
return (
<Space size="middle">
{/* <span style={{cursor:'pointer'}} onClick={this.downLoadFile}>{record.feedback}</span><FileTextTwoTone /> */}
<span></span>
</Space>
);
},
}, {
title: "修改记录",
key: "isModify",
align: "center",
width: 80,
render: (text, record) => (
<Space size="middle">
{record.contents ? (<Button type="link" onClick={() => {
this.setState({
selectedSubmitId: record.uuid,
modifyRecordVisible: true,
});
this.modifyRecordRef.current.getRecordList();
}}>查看</Button>) : (<span></span>)}
</Space>
),
}], [{
title: "上传时间",
dataIndex: "CreateAt",
key: "CreateAt",
align: "center",
width: 150,
}, {
title: "上传用户",
dataIndex: "user",
key: "user",
align: "center",
width: 120,
}, {
title: "评审材料",
key: "contents",
align: "center",
width: 220,
render: (text, record) => (
<Space size="middle">
{
record.contents ? (
<span style={{cursor: "pointer"}} onClick={this.downLoadFile.bind(this, record.contents[0].item_id)}>{record.contents[0].comment} <FileTextTwoTone /></span>
) : "无"
}
</Space>
),
}, {
title: "评审结果",
key: "status",
align: "center",
width: 100,
render: (text, record) => {
let levelList = [{
mode: "等待审核",
color: "default",
}, {
mode: "通过",
color: "#87D068",
}, {
mode: "再修改",
color: "#2DB7F5",
}, {
mode: "驳回",
color: "#FF5500",
}];
return (
<Space size="middle">
<Tag color={levelList[record.status].color} style={{cursor: "pointer"}}>{levelList[record.status].mode}</Tag>
</Space>
);
},
}, {
title: "反馈批注材料",
dataIndex: "feedback-material",
key: "feedback-material",
align: "center",
render: (text, record) => {
return (
<Space size="middle">
<span></span>
</Space>
);
},
}],
]
dateFilter(time) {
let date = new Date(time);
return `${date.getFullYear()}-${date.getMonth().toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
downLoadFile(file_id) {
message.info(`开始下载文件:${file_id}`);
// request({
// url:baseURL+`/review/file/${file_id}`,
// // url:`http://49.232.73.36:8081/review/file/${file_id}`,
// method: "GET",
// responseType:"blob"
// }).then(res => {
// const filename = res.headers["content-disposition"];
// const blob = new Blob([res.data]);
// var downloadElement = document.createElement("a");
// var href = window.URL.createObjectURL(blob);
// downloadElement.href = href;
// downloadElement.download = decodeURIComponent(filename.split("filename*=")[1].replace("utf-8''", ""));
// document.body.appendChild(downloadElement);
// downloadElement.click();
// document.body.removeChild(downloadElement);
// window.URL.revokeObjectURL(href);
// message.success("文件下载成功!");
// }).catch(err => {
// message.error("文件下载失败!");
// });
}
loadClumsIndex = () => {
if (this.props.role === "2") {
return 0;
} else if (this.props.role === "3") {
return 1;
} else if (this.props.role === "4" && this.props.stepName === "测试框架与论证报告") {
return 2;
} else if (this.props.role === "4" && this.props.stepName !== "测试框架与论证报告") {
return 1;
} else {
return 0;
}
}
componentDidMount() {
this.getDataList();
}
getDataList = () => {
this.setState({
loadingState: true,
});
var res = {
"operation_code": 1000,
"message": "",
"data": null,
};
this.setState({
data: res.data,
loadingState: false,
});
// request({method:"GET", url:baseURL+`/review/proj/submits/${this.props.stepId}`}).then(res => {
// // request({ method:'GET', url:`http://49.232.73.36:8081/review/proj/submits/${this.props.stepId}`}).then(res=>{
// this.setState({
// data:res.data,
// loadingState:false
// });
// }).catch(err => {
// message.error(err.message||"审查材料加载失败!");
// this.setState({
// loadingState:false
// });
// });
}
render() {
return (
<ModulaCard title={this.props.title}>
<div className="data-table-box" data-component="data-table-box">
<Table
dataSource={this.state.data}
columns={this.colums[this.loadClumsIndex()]}
size="small"
rowKey="Id"
pagination={false}
scroll={{y: "5.8rem"}}
loading={this.state.loadingState}
/>
<div className="footer">
<Pagination
total={85}
showTotal={total => `Total ${total} items`}
defaultPageSize={20}
defaultCurrent={1}
size="small"
/>
</div>
</div>
<Modal title="评审意见" visible={this.state.reviewResultsVisible}
cancelText="关闭"
okText="审核"
confirmLoading={this.state.statusChangeLoading}
closable={!this.state.statusChangeLoading}
maskClosable={!this.state.statusChangeLoading}
keyboard={!this.state.statusChangeLoading}
onOk={() => {
// this.setState({
// statusChangeLoading:true
// });
// request({
// url:baseURL+"/review/proj/submit",
// // url:`http://49.232.73.36:8081/review/proj/submit`,
// method: "PUT",
// data:{
// new_status:this.state.statusChangeParams.value,
// submit_id:this.state.statusChangeParams.submitId
// }
// }).then(res => {
// this.setState({
// statusChangeLoading:false,
// reviewResultsVisible:false
// });
// this.getDataList();
// message.success("审核成功");
// }).catch(err => {
// this.setState({
// statusChangeLoading:false
// });
// message.error(err.message||"审核失败");
// });
}}
onCancel={() => {
if (this.state.statusChangeLoading) {
message.warning("请等待");
} else {
this.setState({
reviewResultsVisible: false,
});
}
}}
>
<span>材料评审结果</span>
<Radio.Group name="radiogroup" value={this.state.statusChangeParams.value} onChange={(e) => {
let statusChangeParams = Object.assign(this.state.statusChangeParams, {
value: e.target.value,
});
this.setState({
statusChangeParams,
});
}}>
<Radio value={1}>通过</Radio>
<Radio value={2}>再修改</Radio>
<Radio value={3}>驳回</Radio>
</Radio.Group>
</Modal>
<ModifyRecordModal
show={this.state.modifyRecordVisible}
submitId={this.state.selectedSubmitId}
ref={this.modifyRecordRef}
onCancel={() => {
this.setState({
modifyRecordVisible: false,
});
}}
onComplete={() => {
this.setState({
modifyRecordVisible: false,
});
}}
/>
</ModulaCard>
);
}
}

View File

@ -1,24 +0,0 @@
[data-component=data-table-box]{
width: 100%;
height: calc(100% - .38rem);
box-sizing: border-box;
padding: 0rem .25rem 0rem .4rem;
position: relative;
.ant-table-thead,.ant-table-row{
height: .58rem;
}
.ant-table-cell{
font-size: .14rem;
}
> .footer{
width: 100%;
height: .40rem;
display: flex;
align-items: center;
position: absolute;
bottom: 0;
left: 0;
box-sizing: border-box;
padding-left: .4rem;
}
}

View File

@ -1,245 +0,0 @@
import React from "react";
import {Button, Card, Col, DatePicker, Input, Row, Select} from "antd";
import * as DatasetBackend from "./backend/DatasetBackend";
import * as Setting from "./Setting";
import moment from "moment";
import i18next from "i18next";
const {Option} = Select;
class DatasetEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
datasetName: props.match.params.datasetName,
dataset: null,
};
}
UNSAFE_componentWillMount() {
this.getDataset();
}
getDataset() {
DatasetBackend.getDataset(this.props.account.name, this.state.datasetName)
.then((dataset) => {
this.setState({
dataset: dataset,
});
});
}
parseDatasetField(key, value) {
if (["score"].includes(key)) {
value = Setting.myParseInt(value);
}
return value;
}
updateDatasetField(key, value) {
value = this.parseDatasetField(key, value);
let dataset = this.state.dataset;
dataset[key] = value;
this.setState({
dataset: dataset,
});
}
renderDataset() {
return (
<Card size="small" title={
<div>
{i18next.t("dataset:Edit Dataset")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" onClick={this.submitDatasetEdit.bind(this)}>{i18next.t("general:Save")}</Button>
</div>
} style={{marginLeft: "5px"}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("general:Name")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.name} onChange={e => {
this.updateDatasetField("name", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Start date")}:
</Col>
<Col span={5} >
<DatePicker defaultValue={moment(this.state.dataset.startDate, "YYYY-MM-DD")} onChange={(time, timeString) => {
this.updateDatasetField("startDate", timeString);
}} />
</Col>
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:End date")}:
</Col>
<Col span={10} >
<DatePicker defaultValue={moment(this.state.dataset.endDate, "YYYY-MM-DD")} onChange={(time, timeString) => {
this.updateDatasetField("endDate", timeString);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Full name")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.fullName} onChange={e => {
this.updateDatasetField("fullName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Organizer")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.organizer} onChange={e => {
this.updateDatasetField("organizer", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Location")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.location} onChange={e => {
this.updateDatasetField("location", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Address")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.address} onChange={e => {
this.updateDatasetField("address", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("general:Status")}:
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.dataset.status} onChange={(value => {this.updateDatasetField("status", value);})}>
{
[
{id: "Public", name: "Public (Everyone can see it)"},
{id: "Hidden", name: "Hidden (Only yourself can see it)"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Carousels")}:
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} placeholder="Please input"
value={this.state.dataset.carousels}
onChange={value => {
this.updateDatasetField("carousels", value);
}}
>
{
this.state.dataset.carousels.map((carousel, index) => <Option key={carousel}>{carousel}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Introduction text")}:
</Col>
<Col span={22} >
<Input value={this.state.dataset.introText} onChange={e => {
this.updateDatasetField("introText", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Default item")}:
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.dataset.defaultItem} onChange={value => {this.updateDatasetField("defaultItem", value);}}>
{
this.state.dataset.treeItems.filter(treeItem => treeItem.children.length === 0).map((treeItem, index) => <Option key={treeItem.title}>{`${treeItem.title} | ${treeItem.titleEn}`}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{i18next.t("dataset:Language")}:
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.dataset.language} onChange={(value => {this.updateDatasetField("language", value);})}>
{
[
{id: "zh", name: "zh"},
{id: "en", name: "en"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
</Card>
);
}
submitDatasetEdit() {
let dataset = Setting.deepCopy(this.state.dataset);
DatasetBackend.updateDataset(this.state.dataset.owner, this.state.datasetName, dataset)
.then((res) => {
if (res) {
Setting.showMessage("success", "Successfully saved");
this.setState({
datasetName: this.state.dataset.name,
});
this.props.history.push(`/datasets/${this.state.dataset.name}`);
} else {
Setting.showMessage("error", "failed to save: server side failure");
this.updateDatasetField("name", this.state.datasetName);
}
})
.catch(error => {
Setting.showMessage("error", `failed to save: ${error}`);
});
}
render() {
return (
<div>
<Row style={{width: "100%"}}>
<Col span={1}>
</Col>
<Col span={22}>
{
this.state.dataset !== null ? this.renderDataset() : null
}
</Col>
<Col span={1}>
</Col>
</Row>
<Row style={{margin: 10}}>
<Col span={2}>
</Col>
<Col span={18}>
<Button type="primary" size="large" onClick={this.submitDatasetEdit.bind(this)}>{i18next.t("general:Save")}</Button>
</Col>
</Row>
</div>
);
}
}
export default DatasetEditPage;

View File

@ -1,208 +0,0 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Col, Popconfirm, Row, Table} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as DatasetBackend from "./backend/DatasetBackend";
import i18next from "i18next";
class DatasetListPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
datasets: null,
};
}
UNSAFE_componentWillMount() {
this.getDatasets();
}
getDatasets() {
DatasetBackend.getDatasets(this.props.account.name)
.then((res) => {
this.setState({
datasets: res,
});
});
}
newDataset() {
return {
owner: this.props.account.name,
name: `dataset_${this.state.datasets.length}`,
createdTime: moment().format(),
startDate: moment().format("YYYY-MM-DD"),
endDate: moment().format("YYYY-MM-DD"),
fullName: `Dataset ${this.state.datasets.length}`,
organizer: "Casbin",
location: "Shanghai, China",
address: "3663 Zhongshan Road North",
status: "Public",
language: "zh",
carousels: [],
introText: "Introduction..",
defaultItem: "Home",
treeItems: [{key: "Home", title: "首页", titleEn: "Home", content: "内容", contentEn: "Content", children: []}],
};
}
addDataset() {
const newDataset = this.newDataset();
DatasetBackend.addDataset(newDataset)
.then((res) => {
Setting.showMessage("success", "Dataset added successfully");
this.setState({
datasets: Setting.prependRow(this.state.datasets, newDataset),
});
}
)
.catch(error => {
Setting.showMessage("error", `Dataset failed to add: ${error}`);
});
}
deleteDataset(i) {
DatasetBackend.deleteDataset(this.state.datasets[i])
.then((res) => {
Setting.showMessage("success", "Dataset deleted successfully");
this.setState({
datasets: Setting.deleteRow(this.state.datasets, i),
});
}
)
.catch(error => {
Setting.showMessage("error", `Dataset failed to delete: ${error}`);
});
}
renderTable(datasets) {
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "120px",
sorter: (a, b) => a.name.localeCompare(b.name),
render: (text, record, index) => {
return (
<Link to={`/datasets/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("dataset:Start date"),
dataIndex: "startDate",
key: "startDate",
width: "70px",
sorter: (a, b) => a.startDate.localeCompare(b.startDate),
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("dataset:End date"),
dataIndex: "endDate",
key: "endDate",
width: "70px",
sorter: (a, b) => a.endDate.localeCompare(b.endDate),
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("dataset:Full name"),
dataIndex: "fullName",
key: "fullName",
width: "200px",
sorter: (a, b) => a.fullName.localeCompare(b.fullName),
},
{
title: i18next.t("dataset:Organizer"),
dataIndex: "organizer",
key: "organizer",
width: "120px",
sorter: (a, b) => a.organizer.localeCompare(b.organizer),
},
{
title: i18next.t("dataset:Location"),
dataIndex: "location",
key: "location",
width: "120px",
sorter: (a, b) => a.location.localeCompare(b.location),
},
{
title: i18next.t("dataset:Address"),
dataIndex: "address",
key: "address",
width: "120px",
sorter: (a, b) => a.address.localeCompare(b.address),
},
{
title: i18next.t("general:Status"),
dataIndex: "status",
key: "status",
width: "80px",
sorter: (a, b) => a.status.localeCompare(b.status),
},
{
title: i18next.t("general:Action"),
dataIndex: "action",
key: "action",
width: "120px",
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/datasets/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Popconfirm
title={`Sure to delete dataset: ${record.name} ?`}
onConfirm={() => this.deleteDataset(index)}
okText="OK"
cancelText="Cancel"
>
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
</Popconfirm>
</div>
);
},
},
];
return (
<div>
<Table columns={columns} dataSource={datasets} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
title={() => (
<div>
{i18next.t("general:Datasets")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" size="small" onClick={this.addDataset.bind(this)}>{i18next.t("general:Add")}</Button>
</div>
)}
loading={datasets === null}
/>
</div>
);
}
render() {
return (
<div>
<Row style={{width: "100%"}}>
<Col span={1}>
</Col>
<Col span={22}>
{
this.renderTable(this.state.datasets)
}
</Col>
<Col span={1}>
</Col>
</Row>
</div>
);
}
}
export default DatasetListPage;

View File

@ -1,59 +0,0 @@
import React, {Component} from "react";
import {Button, Col, Row} from "antd";
import "./HistoryQuestion.less";
export default class HistoryQuestion extends Component {
render() {
return (
<div className="history-question-item" data-component="history-question-item">
<Row gutter={[16, 16]} className="queston-content">
<Col span="16" className="question-title">
<span>题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目......题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目.题目题目题目题目题目题目题目题目.(最多5行过多省略)题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目......题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目题目.题目题目题目题目题目题目题目题目.(最多5行过多省略)</span>
</Col>
<Col span="8" className="question-params">
<Row className="params-item">
<Col span="12" className="title">
<span>学科名称</span>
</Col>
<Col span="12" className="value" >
<span>数学</span>
</Col>
</Row>
<Row className="params-item">
<Col span="12" className="title">
<span>能力纬度</span>
</Col>
<Col span="12" className="value">
<span>参数1参数2参数3</span>
</Col>
</Row>
<Row className="params-item">
<Col span="12" className="title">
<span>内容纬度</span>
</Col>
<Col span="12" className="value">
<span>参数1参数2参数3</span>
</Col>
</Row>
<Row className="params-item">
<Col span="12" className="title">
<span>难度等级</span>
</Col>
<Col span="12" className="value">
<span>参数1</span>
</Col>
</Row>
</Col>
</Row>
<Row gutter={[16, 16]} className="question-footer">
<Col span="16" className="question-answer">
<span>答案xxxx</span>
</Col>
<Col span="8">
<Button type="primary" size="small" style={{backgroundColor: "#56BFFF", borderColor: "#56BFFF"}}>使用</Button>
</Col>
</Row>
</div>
);
}
}

View File

@ -1,63 +0,0 @@
[data-component=history-question-item]{
width: 100%;
box-sizing: border-box;
padding: .14rem .1rem .16rem .24rem;
background-color: #FFFEFE33;
border: .01rem solid #2DB7F5;
margin-top: .2rem;
> .queston-content{
width: 100%;
> .question-title{
width: 100%;
box-sizing: border-box;
padding-top: .11rem;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
text-align: justify;
line-height: .22rem;
> span{
font-size: .14rem;
color: #00000059;
}
}
> .question-params{
width: 100%;
> .params-item{
width: 100%;
> .title{
line-height: .22rem;
> span{
font-size: .1rem;
color: #1890FF;
}
}
> .value{
line-height: .22rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
> span{
font-size: .1rem;
color: #1890FF;
}
}
}
}
}
> .question-footer{
width: 100%;
margin-top: .25rem;
> .question-answer{
display: flex;
align-items: center;
> span{
font-size: .1rem;
color: #1890FF;
}
}
}
}

View File

@ -1,20 +0,0 @@
import React from "react";
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
};
}
render() {
return (
<div>
hello
</div>
);
}
}
export default HomePage;

View File

@ -1,167 +0,0 @@
// 封装的材料仓库组件
import React, {Component} from "react";
import {Empty, Input, Pagination, Upload, message} from "antd";
import ModulaCard from "./ModulaCard";
import UpLoadModal from "./UpLoadModal";
import {FileExcelFilled, FileMarkdownFilled, FilePptFilled, FileTextFilled, FileZipFilled, PlusOutlined} from "@ant-design/icons";
import "./MaterialWarehouse.less";
const {Search} = Input;
export default class inedx extends Component {
state = {
fileType: [<FileTextFilled key={1} />, <FileExcelFilled key={2} />, <FileZipFilled key={3} />, <FileMarkdownFilled key={4} />, <FilePptFilled key={5} />],
fileList: [],
upLoadVisible: false,
getFileLoading: false,
}
downLoadFile = (file_id) => {
message.info(`开始下载文件:${file_id}`);
// request({
// url:baseURL+`/review/file/${file_id}`,
// // url:`http://49.232.73.36:8081/review/file/${file_id}`,
// method: "GET",
// responseType:"blob"
// }).then(res => {
// const filename = res.headers["content-disposition"];
// const blob = new Blob([res.data]);
// var downloadElement = document.createElement("a");
// var href = window.URL.createObjectURL(blob);
// downloadElement.href = href;
// downloadElement.download = decodeURIComponent(filename.split("filename*=")[1].replace("utf-8''", ""));
// document.body.appendChild(downloadElement);
// downloadElement.click();
// document.body.removeChild(downloadElement);
// window.URL.revokeObjectURL(href);
// message.success("文件下载成功!");
// }).catch(err => {
// message.error("文件下载失败!");
// });
message.success("文件下载成功");
}
componentDidMount() {
this.getFileList();
}
getFileList = () => {
this.setState({
getFileLoading: true,
});
// request({
// url:baseURL+`/review/proj/detailed/${this.props.projectId}`,
// // url:`http://49.232.73.36:8081/review/proj/detailed/${this.props.projectId}`,
// method:"GET"
// }).then(res => {
// request({
// url:baseURL+"/review/query/file",
// // url:"http://49.232.73.36:8081/review/query/file",
// method:"POST",
// data:{
// id_list:res.data.materials.files
// }
// }).then(res => {
// this.setState({
// fileList:Object.values(res.data),
// getFileLoading:false
// });
// }).catch(err => {
this.setState({
getFileLoading: false,
});
// });
// }).catch(err => {
// this.setState({
// getFileLoading:false
// });
// });
}
fileViewLoader = () => {
if(this.props.role === "2" && this.state.fileList.length === 0) {
return (
<div className="empty-state-box">
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</div>
);
}else if((this.props.role === "2" && this.state.fileList.length !== 0) || this.props.role === "3" || this.props.role === "4") {
return this.state.fileList.map(item => (
<div className="file-item" key={item.Id} onClick={this.downLoadFile.bind(this, item.uuid)}>
<div className="icon">
<FileTextFilled key={item.Id} />
</div>
<div className="name">
<span>{item.name}</span>
</div>
</div>
));
}
return (
<></>
);
}
render() {
return (
<ModulaCard title="材料仓库" right={<Search placeholder="input search text" size="small" style={{width: 200}} />}>
<div className="material-warehouse-box" data-component="material-warehouse-box">
<div className="container">
{
this.props.role === "3" || (this.props.role === "4" && this.props.stepName !== "测试框架与论证报告") ? (
<div className="upload-download-box" onClick={() => {
this.setState({
upLoadVisible: true,
});
}}>
<Upload
name="avatar"
listType="picture-card"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
openFileDialogOnClick={false}
>
<div className="file-load-btn" onClick={() => {
this.setState({
upLoadVisible: true,
});
}}>
<PlusOutlined />
<div style={{marginTop: 8, width: "100%"}}>上传</div>
</div>
</Upload>
</div>
) : (
<></>
)
}
{this.fileViewLoader()}
<UpLoadModal
show={this.state.upLoadVisible}
projectId={this.props.projectId}
stepId = {this.props.stepId}
onClose={() => {
this.setState({
upLoadVisible: false,
});
}}
onUpdate = {() => {
this.getFileList();
}}
></UpLoadModal>
</div>
<div className="footer">
<Pagination
total={85}
showTotal={total => `Total ${total} items`}
defaultPageSize={20}
defaultCurrent={1}
size="small"
/>
</div>
</div>
</ModulaCard>
);
}
}

View File

@ -1,78 +0,0 @@
[data-component=material-warehouse-box]{
width: 100%;
height: calc(100% - .38rem);
box-sizing: border-box;
padding:0rem .21rem 0rem .23rem;
> .container{
width: 100%;
height: calc(100% - .4rem);
.empty-state-box{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
> .ant-empty{
margin: 0;
}
}
.file-item{
width: 1rem;
height: 1rem;
background-color: #F3F3F3;
float: left;
margin-right: .1rem;
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
cursor: pointer;
> .icon{
width: 100%;
height: .5rem;
display: flex;
align-items: center;
justify-content: center;
font-size: .32rem;
}
> .name{
text-align: center;
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis;
box-sizing: border-box;
padding: 0px .08rem;
> span{
font-size: .08rem;
}
}
}
> .upload-download-box{
float: left;
margin-right: .1rem;
.file-load-btn{
width: 100%;
height: 100%;
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
}
> .ant-upload-picture-card-wrapper{
width: 1rem;
height: 1rem;
> .ant-upload{
width: 100%;
height: 100%;
margin: 0;
}
}
}
}
> .footer{
display: flex;
align-items: flex-end;
width: 100%;
height: .4rem;
}
}

View File

@ -1,152 +0,0 @@
import React, {Component} from "react";
import {Modal, Space, Spin, Table} from "antd";
import {FileTextTwoTone} from "@ant-design/icons";
import "./ModifyRecordModal.less";
export default class index extends Component {
state = {
recordList: [{
key: "1",
date: "2021-08-11",
result: 0,
reviewMaterials: "待评审材料1",
feedback: "反馈意见",
}, {
key: "2",
date: "2021-08-11",
result: 1,
reviewMaterials: "待评审材料1",
feedback: "反馈意见",
}, {
key: "3",
date: "2021-08-11",
result: 2,
reviewMaterials: "待评审材料1",
feedback: "反馈意见",
}, {
key: "4",
date: "2021-08-11",
result: 3,
reviewMaterials: "待评审材料1",
feedback: "",
}],
submitInfo: {},
loadingState: false,
}
getRecordList() {
// this.setState({
// loadingState:true
// });
// request({
// url:baseURL+`/review/proj/submit/${this.props.submitId}`,
// method:"GET"
// }).then(res => {
// this.setState({
// submitInfo:res.data,
// loadingState:false
// });
// }).catch(err => {
// this.setState({
// loadingState:false
// });
// message.error("加载错误");
// this.props.onComplete();
// });
}
recordListColums = [{
title: "ID",
dataIndex: "uuid",
align: "center",
key: "id",
}, {
title: "评论",
dataIndex: "comment",
align: "center",
key: "comment",
}, {
title: "评审材料",
key: "review-materials",
align: "center",
width: 220,
render: (text, record) => (
<Space size="middle">
<span style={{cursor: "pointer"}} onClick={this.downLoadFile.bind(this, record.uuid)}>点击下载</span><FileTextTwoTone />
</Space>
),
}, {
title: "版本",
dataIndex: "version",
align: "center",
key: "version",
}]
downLoadFile(file_id) {
// message.info(`开始下载文件:${file_id}`);
// request({
// url:baseURL+`/review/file/${file_id}`,
// // url:`http://49.232.73.36:8081/review/file/${file_id}`,
// method: "GET",
// responseType:"blob"
// }).then(res => {
// const filename = res.headers["content-disposition"];
// const blob = new Blob([res.data]);
// var downloadElement = document.createElement("a");
// var href = window.URL.createObjectURL(blob);
// downloadElement.href = href;
// downloadElement.download = decodeURIComponent(filename.split("filename*=")[1].replace("utf-8''", ""));
// document.body.appendChild(downloadElement);
// downloadElement.click();
// document.body.removeChild(downloadElement);
// window.URL.revokeObjectURL(href);
// message.success("文件下载成功!");
// }).catch(err => {
// message.error("文件下载失败!");
// });
}
render() {
return (
<Modal title="修改记录"
width="9.51rem"
visible={this.props.show}
cancelText="关闭"
okText="确认"
confirmLoading={this.state.loadingState}
closable={!this.state.loadingState}
maskClosable={!this.state.loadingState}
keyboard={!this.state.loadingState}
onOk={() => {
this.props.onComplete();
}}
onCancel={() => {
this.props.onCancel();
}}
>
{
this.state.loadingState ? (
<Spin spinning={this.state.loadingState} tip="加载中"></Spin>
) : (
<div className="modify-record-box" data-component="modify-record-box">
<div className="basic">
<div className="id">
材料编号:{this.state.submitInfo.uuid}
</div>
<div className="date">
截止日期:未知
</div>
</div>
<Table
dataSource={this.state.submitInfo.contents || []}
columns={this.recordListColums} size="small"
pagination={false}
/>
</div>
)
}
</Modal>
);
}
}

View File

@ -1,31 +0,0 @@
[data-component=modify-record-box]{
width: 100%;
> .basic{
width: 100%;
height: 1.49rem;
box-sizing: border-box;
padding: .16rem .16rem 0px .16rem;
border: 1px solid #F0F0F0;
border-radius: .02rem;
margin-bottom: .32rem;
> .id{
font-size: .16rem;
line-height: .24rem;
font-weight: 400;
color: #000000;
height: .24rem;
}
> .date{
height: .22rem;
line-height: .22rem;
font-size: .14rem;
color: rgba(0, 0, 0, 0.45);
}
}
.ant-table-cell{
font-size: .14rem;
}
.ant-table-thead,.ant-table-row{
height: .56rem;
}
}

View File

@ -1,19 +0,0 @@
import React, {Component} from "react";
import "./ModulaCard.less";
export default class index extends Component {
render() {
return (
<div className="modula-card-box" data-component="modula-card-box">
<div className="title">
<div className="icon"></div>
<span>{this.props.title}</span>
<div className="right">
{this.props.right}
</div>
</div>
{this.props.children}
</div>
);
}
}

View File

@ -1,35 +0,0 @@
[data-component=modula-card-box]{
width: 100%;
height: 100%;
background-color: #fff;
box-sizing: border-box;
padding: .16rem 0rem ;
> .title{
width: 100%;
height: .22rem;
box-sizing: border-box;
padding: 0px .26rem;
display: flex;
align-items: center;
margin-bottom: .16rem;
position: relative;
> .icon{
display: block;
width: .06rem;
height: .18rem;
background-color: #1890FF;
margin-right: .16rem;
}
> span{
font-size: .16rem;
font-weight: 400;
line-height: .22rem;
}
> .right{
position: absolute;
height: 100%;
right: .14rem;
top: 0;
}
}
}

View File

@ -1,323 +0,0 @@
import React, {Component} from "react";
import {Button, Dropdown, Form, Input, Layout, Menu, Modal, Pagination, Space, Spin, Table, Tag, message} from "antd";
import {DownOutlined, PlusCircleOutlined} from "@ant-design/icons";
import * as ProjectBackend from "./backend/ProjectBackend";
import ChangeTags from "./ChangeTags";
import "./PendingTaskPage.less";
const {Header, Footer, Content} = Layout;
const {Search, TextArea} = Input;
export default class PeddingTasks extends Component {
constructor(props) {
super(props);
this.state = {
classes: props,
data: [],
tableHeight: 0,
loadingState: false,
isCreateProjectVisible: false,
createLoading: false,
form: {
current: 1,
pageSize: 10,
pageSizeOptions: [5, 10, 15, 20],
showSizeChanger: true,
total: 50,
},
createForm: {
name: "",
grade_range: [],
subjects: [],
description: "",
requirement: "",
summary: "",
target: "",
},
};
}
createFormRef = React.createRef()
columns = [
{
title: "项目名称",
key: "name",
align: "center",
width: 210,
render: (text, record) => (
// <Space size="middle">
// <Button type="link" onClick={this.seekProjectManagement.bind(this,record)}>{
// record.basic_info.name
// }</Button>
// </Space>
<span key={1}>{record.basic_info.name}</span>
),
},
{
title: "学科",
key: "subject",
dataIndex: "subject",
align: "center",
width: 210,
render: (text, record) => (
<>
{
record.basic_info.subjects.map((item, index) => (
<Tag key={index} color="green">
{
item
}
</Tag>
))
}
</>
),
},
{
title: "学段",
key: "period",
align: "center",
render: (text, record) => (
<>
{
record.basic_info.grade_range.map((item, index) => (
<Tag key={index} color="green">
{
item
}
</Tag>
))
}
</>
),
},
{
title: "试卷",
key: "paper",
align: "center",
width: 142,
render: record => (
<span>0</span>
),
},
{
title: "试题",
key: "questions",
align: "center",
width: 121,
render: record => (
<span>0</span>
),
},
{
title: "创建时间",
dataIndex: "created_time",
key: "create-time",
align: "center",
width: 342,
render: (text, record) => (
<span>{this.dateFilter(record.created_time)}</span>
),
},
{
title: "操作",
key: "title",
align: "center",
render: (text, record) => (
<Space size="middle">
<Button type="link" onClick={this.seekProjectManagement.bind(this, record)}>访问项目</Button>
<Button type="link" danger>删除</Button>
</Space>
),
},
]
pendingTaskMenu = () => {
return (
<Menu>
<Menu.Item key="1">任务1</Menu.Item>
<Menu.Item key="2">任务2</Menu.Item>
<Menu.Item disabled key="3">任务3</Menu.Item>
<Menu.Item key="4">任务4</Menu.Item>
</Menu>
);
}
componentDidMount = () => {
this.getProjectList(this.state.classes.account);
}
seekProjectManagement = (state) => {
let project_id = state.project_id.split("/").join("_");
this.props.history.push(`/project-management/${project_id}/${state.role}`);
}
dateFilter(time) {
let date = new Date(time);
return `${date.getFullYear()}-${date.getMonth().toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
getProjectList = (account) => {
ProjectBackend.GetUserAssignments(account.id).then(res => {
let id_list = res.data.map(item => item.project_id);
let role_lits = res.data.map(item => item.role);
let project_id_list = res.data.map(item => item.project_id);
ProjectBackend.GetProjectList(id_list).then(res => {
let data = Object.values(res.data);
data = data.map((item, index) => {
item.role = role_lits[index];
item.project_id = project_id_list[index];
return item;
});
this.setState({
data,
loadingState: false,
});
}).catch(err => {
message.error(err || "请求错误!");
}).catch(err => {
message.error(err || "请求错误!");
});
});
}
render() {
return (
<Layout className="pending-tasks-page" data-component="pending-tasks-page">
<Header>
<span className="title">项目列表/待处理任务</span>
<div className="right-box">
<Search placeholder="input search text" style={{width: "2.64rem", height: ".32rem"}} />
<Dropdown overlay={this.pendingTaskMenu()}>
<span>待处理任务<DownOutlined /></span>
</Dropdown>
<Button type="primary" icon={<PlusCircleOutlined />} onClick={() => {
this.setState({
isCreateProjectVisible: true,
});
}}>添加项目</Button>
</div>
</Header>
<Content>
<Table
loading={this.state.loadingState}
columns={this.columns}
dataSource={this.state.data}
rowSelection={{}}
size="small"
rowKey={record => record.num}
pagination={false}
scroll={{y: "calc(100vh - 2.2rem)"}}
/>
</Content>
<Footer>
<Pagination
size="small"
total={85}
showSizeChanger
showQuickJumper
/>
<Modal
title="创建项目"
visible={this.state.isCreateProjectVisible}
cancelText="取消创建"
okText="创建项目"
closable={!this.state.createLoading}
confirmLoading={this.state.createLoading}
maskClosable={!this.state.createLoading}
keyboard={!this.state.createLoading}
onOk={() => {
const form = this.createFormRef.current;
form.validateFields().then(data => {
this.setState({
createLoading: true,
});
ProjectBackend.CreatTemplateProject(data).then(res => {
if (res.status == "ok") {
message.success("项目创建成功!");
this.createFormRef.current.resetFields();
this.getProjectList(this.state.classes.account);
} else {
message.error("项目创建失败!");
}
this.setState({
createLoading: false,
isCreateProjectVisible: false,
});
}).catch(err => {
this.setState({
createLoading: false,
});
message.error(err.message || "未知错误");
});
}).catch(err => {
message.warning("请按要求填写表单项!");
});
}}
onCancel={() => {
if (this.state.createLoading) {
message.error("项目创建中,不可阻断!");
} else {
this.createFormRef.current.resetFields();
this.setState({
isCreateProjectVisible: false,
});
}
}}
>
<Spin spinning={this.state.createLoading} tip="项目创建中,请等待!">
<Form labelCol={{span: 4}} wrapperCol={{span: 20}} ref={this.createFormRef} name="createForm" initialValues={this.state.createForm}>
<Form.Item name="name" label="项目名称" rules={[{required: true, message: "项目名称不能为空!"}]}>
<Input placeholder="请输入项目名称" />
</Form.Item>
<Form.Item name="grade_range" label="年级范围" rules={[{required: true, message: "请至少创建一个年级"}]}>
<ChangeTags onChange={grade_range => {
let createForm = Object.assign(this.state.createForm, {
grade_range,
});
this.setState({
createForm,
});
}}></ChangeTags>
</Form.Item>
<Form.Item name="subjects" label="涉及学科" rules={[{required: true, message: "请至少创建一个学科!"}]}>
<ChangeTags onChange={subjects => {
let createForm = Object.assign(this.state.createForm, {
subjects,
});
this.setState({
createForm,
});
}}></ChangeTags>
</Form.Item>
<Form.Item name="target" label="目标说明" rules={[{required: true, message: "目标说明不能为空!"}]}>
<TextArea
placeholder="请输入项目目标说明,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="summary" label="项目摘要" rules={[{required: true, message: "项目摘要不能为空!"}]}>
<TextArea
placeholder="请输入项目摘要,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="description" label="项目描述" rules={[{required: true, message: "项目描述不能为空!"}]}>
<TextArea
placeholder="请输入项目描述,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="requirement" label="项目要求" rules={[{required: true, message: "项目要求不能为空!"}]}>
<TextArea
placeholder="请输入项目要求,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
</Form>
</Spin>
</Modal>
</Footer>
</Layout>
);
}
}

View File

@ -1,63 +0,0 @@
[data-component=pending-tasks-page]{
width: 100%;
// height: 100%;
height: 80vh;
> .ant-layout-header{
width: 100%;
min-width: 700px;
// height: .64rem;
height: 64px;
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 0rem .08rem;
// line-height: .64rem;
line-height: 64px;
> .title{
font-size: .16rem;
color: #000000D9;
}
> .right-box{
display: flex;
align-items: center;
height: .64rem;
> .ant-dropdown-trigger,.ant-input-search{
margin-right: .16rem;
}
> .ant-dropdown-trigger{
font-size: .14rem;
}
}
}
> .ant-layout-content{
height: 100%;
background-color: #FFFFFF;
.ant-table-header{
> .table{
height: .48rem;
}
.ant-table-cell::before{
display: none;
}
}
.ant-table-row{
height: .48rem;
> .ant-table-cell{
height: .48rem;
padding: 0;
font-size: 14px;
}
}
}
> .ant-layout-footer{
margin-bottom: 0rem;
padding: 0rem .2rem 0rem 0rem;
height: .5rem;
background-color: #ffffff;
display: flex;
align-items: center;
flex-direction: row-reverse;
}
}

View File

@ -1,239 +0,0 @@
import React, {Component} from "react";
import {Redirect, Route, Switch} from "react-router-dom";
import {Button, Descriptions, Form, Input, Modal, PageHeader, Spin, Tabs, message} from "antd";
import * as ProjectBackend from "./backend/ProjectBackend";
import ChangeTags from "./ChangeTags";
import Step from "./Step";
import BuildTeam from "./BuildTeam";
import "./ProjectManagementPage.less";
const {TabPane} = Tabs;
const {TextArea} = Input;
export default class ProjectManagementPage extends Component {
constructor(props) {
super(props);
this.state = {
demo: "Hello",
classes: props,
projectBaseInfo: {},
loadingState: true,
isCreateProjectVisible: false,
createLoading: false,
};
}
createFormRef = React.createRef();
componentDidMount() {
this.getProjectBaseInfo();
}
getProjectBaseInfo() {
this.setState({
loadingState: true,
});
ProjectBackend.GetDetailedInfo(this.props.match.params.project_id.split("_").join("/")).then(res => {
this.setState({
projectBaseInfo: res.data,
ProjectInfo: res.data.basic_info.basic_info,
loadingState: false,
});
}).catch(err => {
this.props.history.push("/home");
message.error(err.message || "项目信息加载失败,请重试!");
this.setState({
loadingState: false,
});
});
}
dateFilter(time) {
let date = new Date(time);
return `${date.getFullYear()}-${date.getMonth().toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
tabCruuent = () => {
let path_list = this.props.location.pathname.split("/");
return `${path_list[path_list.length - 1]}_${path_list[path_list.length - 2]}`;
}
render() {
return (
<div className="project-management-page" data-component="project-management-page" key="project-management-page">
<PageHeader
ghost={false}
onBack={() => this.props.history.push("/pending-tasks")}
title="项目管理"
subTitle={this.state.loadingState ? "加载中" : this.state.projectBaseInfo.basic_info.basic_info.name}
extra={[
<Button key="2" onClick={() => {
this.setState({
isCreateProjectVisible: true,
});
}}>编辑项目</Button>,
<Button key="1">导出成员</Button>,
]
}
footer = {
this.state.loadingState ? (
<Spin spinning={this.state.loadingState} tip="加载中" />
) : (
<Tabs defaultActiveKey={`${this.state.projectBaseInfo.steps[0].uuid}_${this.state.projectBaseInfo.steps[0].name}`} type="card" activeKey={this.tabCruuent()} onChange={(e) => {
this.props.history.push(`/project-management/${this.props.match.params.project_id}/${this.props.match.params.role}/${e.split("_")[1]}/${e.split("_")[0]}`, this.state.classes.account);
}}>
{
this.state.projectBaseInfo.steps.map(item => (
<TabPane key={`${item.uuid}_${item.name}`} tab={item.name}></TabPane>
))
}
</Tabs>
)
}
>
{
this.state.loadingState ? (
<Spin spinning={this.state.loadingState} tip="加载中..." />
) : (
<Descriptions size="small" column={3} style={{width: "auto"}}>
<Descriptions.Item label="创建时间">{this.dateFilter(this.state.projectBaseInfo.basic_info.created_time)}</Descriptions.Item>
<Descriptions.Item label="学科">
{
this.state.projectBaseInfo.basic_info.basic_info.subjects.map((item, index) => (
<span key={index}>{`${item}${index === this.state.projectBaseInfo.basic_info.basic_info.subjects.length - 1 ? "" : "、"}`}</span>
))
}
</Descriptions.Item>
<Descriptions.Item label="学段">
{
this.state.projectBaseInfo.basic_info.basic_info.grade_range.map((item, index) => (
<span key={index}>{`${item}${index === this.state.projectBaseInfo.basic_info.basic_info.grade_range.length - 1 ? "" : "、"}`}</span>
))
}
</Descriptions.Item>
<Descriptions.Item label="试卷">0</Descriptions.Item>
<Descriptions.Item label="试题">0</Descriptions.Item>
</Descriptions>
)
}
</PageHeader>
<div className="page-content-box">
{
this.state.loadingState ? (<></>) : (
<Switch>
{
this.state.projectBaseInfo.steps.map(item => (
<Route path={`/project-management/:project_id/:role/${item.name}/:step_id`} component={item.name === "组建团队" ? BuildTeam : Step} exact key={item.Id}></Route>
))
}
<Redirect from={`/project-management/${this.props.match.params.project_id}/${this.props.match.params.role}`} to={`/project-management/${this.props.match.params.project_id}/${this.props.match.params.role}/${this.state.projectBaseInfo.steps[0].name}/${this.state.projectBaseInfo.steps[0].uuid}`}></Redirect>
{/* <Route component={NotFound} key="404"></Route> */}
</Switch>
)
}
</div>
<Modal
title="编辑项目"
visible={this.state.isCreateProjectVisible}
cancelText="取消更改"
okText="确定更改"
closable={!this.state.createLoading}
confirmLoading={this.state.createLoading}
maskClosable={!this.state.createLoading}
keyboard={!this.state.createLoading}
onOk={() => {
const form = this.createFormRef.current;
form.validateFields().then(data => {
this.setState({
createLoading: true,
});
ProjectBackend.CreatTemplateProject(data).then(res => {
if (res.status == "ok") {
message.success("项目修改成功!");
this.createFormRef.current.resetFields();
this.getProjectList(this.state.classes.account);
} else {
message.error("项目修改失败!");
}
this.setState({
createLoading: false,
isCreateProjectVisible: false,
});
}).catch(err => {
message.error(err.message || "未知错误");
this.setState({
createLoading: false,
});
message.error(err.message || "未知错误");
});
}).catch(err => {
message.warning("请按要求填写表单项!");
});
}}
onCancel={() => {
if (this.state.createLoading) {
message.error("项目修改中,不可阻断!");
} else {
this.createFormRef.current.resetFields();
this.setState({
isCreateProjectVisible: false,
});
}
}}
>
<Spin spinning={this.state.createLoading} tip="项目修改中,请等待!">
<Form labelCol={{span: 4}} wrapperCol={{span: 20}} ref={this.createFormRef} name="createForm" initialValues={this.state.createForm}>
<Form.Item name="name" label="项目名称" rules={[{required: true, message: "项目名称不能为空!"}]}>
<Input placeholder="请输入项目名称" />
</Form.Item>
<Form.Item name="grade_range" label="年级范围" rules={[{required: true, message: "请至少创建一个年级"}]}>
<ChangeTags onChange={grade_range => {
let createForm = Object.assign(this.state.createForm, {
grade_range,
});
this.setState({
createForm,
});
}}></ChangeTags>
</Form.Item>
<Form.Item name="subjects" label="涉及学科" rules={[{required: true, message: "请至少创建一个学科!"}]}>
<ChangeTags onChange={subjects => {
let createForm = Object.assign(this.state.createForm, {
subjects,
});
this.setState({
createForm,
});
}}></ChangeTags>
</Form.Item>
<Form.Item name="target" label="目标说明" rules={[{required: true, message: "目标说明不能为空!"}]}>
<TextArea
placeholder="请输入项目目标说明,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="summary" label="项目摘要" rules={[{required: true, message: "项目摘要不能为空!"}]}>
<TextArea
placeholder="请输入项目摘要,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="description" label="项目描述" rules={[{required: true, message: "项目描述不能为空!"}]}>
<TextArea
placeholder="请输入项目描述,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
<Form.Item name="requirement" label="项目要求" rules={[{required: true, message: "项目要求不能为空!"}]}>
<TextArea
placeholder="请输入项目要求,若无,请填写无"
autoSize={{minRows: 3, maxRows: 5}}
/>
</Form.Item>
</Form>
</Spin>
</Modal>
</div >
);
}
}

View File

@ -1,49 +0,0 @@
[data-component=project-management-page]{
width: 100%;
height: 100%;
background-color: #EEEEEE;
overflow-y: scroll;
> .ant-page-header{
background-color: #FFFFFF;
padding: .16rem 0rem 0rem 0rem;
> .ant-page-header-heading{
box-sizing: border-box;
padding: 0rem .24rem;
}
> .ant-page-header-content{
padding-left: .564rem;
}
.ant-page-header-heading-title{
font-size: .2rem;
font-weight: 500;
line-height: .28rem;
color: #000000;
}
.ant-page-header-heading-sub-title{
font-size: .14rem;
line-height: .22rem;
color: rgba(0, 0, 0, 0.85);
}
.ant-tabs-nav-list{
padding-left: .564rem
}
.ant-descriptions{
width: 6.62rem;
}
.ant-tabs-tab-btn{
font-size: .14rem;
}
}
> .container{
width: 100%;
}
.ant-spin-nested-loading{
height: auto;
background-color: white;
}
.page-content-box{
width: 100%;
box-sizing: border-box;
padding: .3rem;
}
}

View File

@ -1,598 +0,0 @@
import React, {Component} from "react";
import {Button, Card, Col, Modal, Pagination, Popconfirm, Radio, Row, Space, Spin, Table, message} from "antd";
import UpLoadQuestionModal from "./UpLoadQuestionModal";
import {DeleteOutlined, EditOutlined, EllipsisOutlined} from "@ant-design/icons";
import "./PropositionPaperHome.less";
import * as PropositionBackend from "./backend/PropositionBackend";
const {Meta} = Card;
export default class PropositionPaperHome extends Component {
constructor(props) {
super(props);
this.state = {
paperShowConfirm: -1,
questionShowConfirm: -1,
account: props.location.state.account,
mode: "testpaper",
questionData: [],
testpaperData: [],
recordData: [{
key: "1",
date: "text",
content: "/",
param1: "/",
param2: "/",
param3: "/",
}, {
key: "2",
date: "text",
content: "内容修改版本的文字内容......",
param1: "内容纬度1内容纬度2",
param2: "/",
param3: "/",
}, {
key: "3",
date: "text",
content: "/",
param1: "/",
param2: "/",
param3: "/",
}, {
key: "4",
date: "text",
content: "/",
param1: "/",
param2: "/",
param3: "/",
}, {
key: "5",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "6",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "7",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "8",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "9",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "10",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "11",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "12",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "13",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}, {
key: "14",
date: "text",
content: "text",
param1: "text",
param2: "text",
param3: "text",
}],
upLoadQuestionModalParams: {
show: false,
type: "create",
},
questionRecord: {
Id: "",
viewIndex: 0,
recordList: [],
},
modifyRecordVisible: false,
upLoadQuestionVisible: false,
editQuestionVisible: false,
createPaperVisible: false,
loadingState: false,
recordLoading: true,
testpaperVisible: {
show: false,
id_list: [],
loadingState: false,
questionList: [],
},
};
}
columns = [{
title: "时间",
key: "date",
width: 110,
render: (text, record) => (
<span>{this.timeFilter(record.create_at)}</span>
),
}, {
title: "内容",
key: "content",
width: 110,
render: (text, record) => (
<span>{record.basic_props.description}</span>
),
}, {
title: "难度",
key: "param1",
width: 110,
render: (text, record) => (
<span>{record.advanced_props.irt_level}</span>
),
}, {
title: "阶段",
key: "param2",
width: 110,
render: (text, record) => (
<span>{record.apply_record.grade_fits}</span>
),
}, {
title: "学科",
key: "param3",
width: 110,
render: (text, record) => (
<span>{record.basic_props.subject}</span>
),
}, {
title: "操作",
key: "operate",
render: (text, record) => {
return (
<Button type="link" onClick={() => {
this.setState({
questionRecord: Object.assign(this.state.questionRecord, {viewIndex: this.state.questionRecord.recordList.findIndex(item => item.Id === record.Id)}),
});
}}>预览</Button>
);
},
}];
handleModeChange = e => {
const mode = e.target.value;
if (e.target.value === "questions") {
this.getUserQuestionList(this.state.account.id);
} else {
this.getUserTestpaperList(this.state.account.id);
}
this.setState({mode});
};
getQuestionRecord = (qid) => {
this.setState({
recordLoading: true,
});
PropositionBackend.TraceQuestionVersion(qid).then(res => {
this.setState({
questionRecord: Object.assign(this.state.questionRecord, {recordList: res.data, viewIndex: 0}),
recordLoading: false,
});
}).catch(err => {
this.setState({
recordLoading: false,
modifyRecordVisible: false,
});
message.error(err.message || "请求错误!");
});
}
getQuestionListInfo = () => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {loadingState: true}),
});
PropositionBackend.GetTempQuestionList(this.state.testpaperVisible.id_list).then(res => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {loadingState: false, questionList: Object.values(res.data)}),
});
}).catch(err => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {loadingState: false, show: false}),
});
message.error(err.message || "请求错误");
});
}
loadQuestionData = () => {
if (this.state.mode === "questions") {
return this.state.questionData.map((item, index) => (
<div key={index} style={{float: "left"}}>
<Popconfirm className="confirm"
title="Are you sure to delete this testpaper?"
onConfirm={(index) => {
// PropositionBackend.DeleteTempTestpaper(this.state.testpaperData[index].uuid).then(res => {
// });
}}
onCancel={() => {
this.setState({
questionShowConfirm: -1,
});
}}
okText="Yes"
cancelText="No"
visible={this.state.questionShowConfirm == index}
placement="rightTop"
>
</Popconfirm>
<Card key={index}
style={{width: 300}}
actions={[
<DeleteOutlined key="delete" onClick={() => {
this.setState({
questionShowConfirm: index,
});
}} />,
<EditOutlined key="edit" onClick={() => {
this.setState({
upLoadQuestionModalParams: {
type: "update",
show: true,
},
});
}} />,
<EllipsisOutlined key="ellipsis" onClick={() => {
this.setState({
modifyRecordVisible: true,
});
this.getQuestionRecord(item.uuid);
}} />,
]}
>
<Meta
title={(
<div className="header" style={{display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<span>{item.basic_props.subject || "未知"}</span>
<span>{this.timeFilter(item.create_at)}</span>
</div>
)}
description={item.info.title}
/>
</Card>
</div>
));
} else {
return this.state.testpaperData.map((item, index) => (
<div key={index}>
<Popconfirm
title="Are you sure to delete this task?"
onConfirm={() => {
PropositionBackend.DeleteTempTestpaper(item.uuid).then(res => {
if(res.status == "ok") {
message.success("删除成功!");
this.setState({paperShowConfirm: -1});
this.getUserTestpaperList(this.state.account.id);
}else{
message.success("删除失败");
this.setState({paperShowConfirm: -1});
this.getUserTestpaperList(this.state.account.id);
}
});
}}
onCancel={() => {
this.setState({
paperShowConfirm: -1,
});
}}
okText="Yes"
cancelText="No"
visible={this.state.paperShowConfirm == index}
placement="rightTop"
>
</Popconfirm>
<Card key={index}
style={{width: 300, float: "left"}}
actions={[
<DeleteOutlined key="delete" onClick={() => {
this.setState({
paperShowConfirm: index,
});
}} />,
<EditOutlined key="edit" onClick={() => {
this.setState({
upLoadQuestionModalParams: {
type: "update-paper",
show: true,
},
});
}} />,
<EllipsisOutlined key="ellipsis" onClick={() => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {id_list: item.info[0].question_list.map(item => item.question_id), show: true}),
});
this.getQuestionListInfo();
}} />,
]}
>
<Meta
title={(
<div className="header" style={{display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<span>{item.props.subjects[0] || "未知"}</span>
<span>{this.timeFilter(item.create_at)}</span>
</div>
)}
description={item.info[0].description}
/>
</Card>
</div>
));
}
}
timeFilter = (time) => {
let date = new Date(time);
// ${date.getHours().toString().padStart(2,'0')}:${date.getMinutes().toString().padStart(2,'0')}:${date.getSeconds().toString().padStart(2,'0')}
return `${date.getFullYear()}-${date.getMonth().toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
componentDidMount = () => {
this.state.mode === "testpaper" ? this.getUserTestpaperList(this.state.account.id) : this.getUserQuestionList(this.state.account.id);
}
getUserQuestionList = (uid) => {
this.setState({
loadingState: true,
});
PropositionBackend.GetUserTempQuestions(uid).then(res => {
this.setState({
questionData: res.data ? res.data : [],
loadingState: false,
});
}).catch(err => {
this.setState({
loadingState: false,
});
message.error(err.message || "加载失败");
});
}
getUserTestpaperList = (uid) => {
this.setState({
loadingState: true,
});
PropositionBackend.GetUserTempTestpaper(uid).then(res => {
this.setState({
testpaperData: res.data ? res.data : [],
loadingState: false,
});
}).catch(err => {
this.setState({
loadingState: false,
});
message.error(err.message || "加载失败");
});
}
render() {
return (
<div className="proposition-paper-home-page" data-component="proposition-paper-home-page">
<div className="header">
<div className="describe">
<div className="content">
<h1>实现高效命题组卷</h1>
<div className="context">
单题编辑上传试卷调用轻松快捷<br />
xxxxxxxxxx示例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言例宣传语言
</div>
</div>
<div className="btn-box">
<Button type="primary" onClick={() => {
this.setState({
upLoadQuestionModalParams: {
type: "create",
show: true,
},
});
}}>上传试题</Button>
<Button type="primary" style={{marginLeft: ".18rem"}} onClick={() => {
this.setState({
upLoadQuestionModalParams: {
type: "create-paper",
show: true,
},
});
}}>创建试卷</Button>
</div>
</div>
<img src="https://item-cdn.open-ct.com/img/proposition-paper-icon.png" alt="图片" className="icon"></img>
</div>
<div className="container">
<div className="category-list">
<Radio.Group onChange={this.handleModeChange} value={this.state.mode} >
<Radio.Button value="questions">我的试题</Radio.Button>
<Radio.Button value="testpaper" style={{marginLeft: 4}}>我的试卷库</Radio.Button>
</Radio.Group>
</div>
<div className="main">
<Spin spinning={this.state.loadingState} tip="加载中">
{
this.state.loadingState ? "" : this.loadQuestionData(this.state.account.id)
},
</Spin>
</div>
</div>
<UpLoadQuestionModal
{...this.state.upLoadQuestionModalParams}
uid={this.state.account.id}
onClose={() => {
let upLoadQuestionModalParams = Object.assign(this.state.upLoadQuestionModalParams, {show: false});
this.setState({
upLoadQuestionModalParams,
});
}}
/>
<Modal
title="修改记录"
visible={this.state.modifyRecordVisible}
footer={null}
onCancel={() => {
this.setState({
modifyRecordVisible: false,
});
}}
width="16.12rem"
>
<Spin spinning={this.state.recordLoading} tip="加载中">
<div className="question-modify-record-box">
<div className="preview-box">
<div className="header">
<span>题目效果预览窗</span>
</div>
<div className="view-box" dangerouslySetInnerHTML={{
__html: this.state.recordLoading ? "加载中" : (
this.state.questionRecord.recordList[this.state.questionRecord.viewIndex] ? this.state.questionRecord.recordList[this.state.questionRecord.viewIndex].info.body : "渲染失败"
),
}}>
</div>
</div>
<div className="record-box">
<Space align="center">
修改历史版本
</Space>
<div className="container">
<Table
dataSource={this.state.questionRecord.recordList}
columns={this.columns}
pagination={false}
scroll={{y: "calc(100% - .56rem)"}}
/>
</div>
<div className="footer">
<Pagination
total={85}
showTotal={total => `Total ${total} items`}
defaultPageSize={20}
defaultCurrent={1}
size="small"
/>
</div>
</div>
</div>
</Spin>
</Modal>
<Modal
title="试卷详情"
visible={this.state.testpaperVisible.show}
width="80%"
onOk={() => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {show: false}),
});
}}
onCancel={() => {
this.setState({
testpaperVisible: Object.assign(this.state.testpaperVisible, {show: false}),
});
}}
>
<Spin spinning={this.state.testpaperVisible.loadingState} tip="加载中">
{
this.state.testpaperVisible.loadingState ? "" : (
<>
{
this.state.testpaperVisible.questionList.map((item, index) => (
<div className="paper-question-item" key={item.id}>
<Row className="header">
<Col span="4">
<span>序号<span style={{fontWeight: "bold", color: "red"}}>{index + 1}</span></span>
</Col>
<Col span="6">
<span>
测试年份<span style={{fontWeight: "bold", color: "green"}}>{item.apply_record.test_year}</span>
</span>
</Col>
<Col span="4">
<span>
试题难度<span style={{fontWeight: "bold", color: "blue"}}>{item.advanced_props.irt_level}</span>
</span>
</Col>
<Col span="4">
<span>
试题类型{item.info.type}
</span>
</Col>
<Col span="4">
<span>
题目答案{item.info.answer || "无"}
</span>
</Col>
</Row>
<div className="body" dangerouslySetInnerHTML={{__html: item.info.body}}></div>
<div className="footer">
<Button type="primary" style={{float: "right"}} onClick={() => {
this.getQuestionRecord(item.uuid);
this.setState({
modifyRecordVisible: true,
});
}}>查看历史版本</Button>
</div>
<br />
<br />
</div>
))
}
</>
)
}
</Spin>
</Modal>
</div>
);
}
}

View File

@ -1,227 +0,0 @@
[data-component=proposition-paper-home-page]{
width: 100%;
height: 100%;
overflow-y: scroll;
> .header{
width: 100%;
height: 4.53rem;
background-color: #FFFFFF;
box-sizing: border-box;
padding: 0px 1.44rem;
display: flex;
align-items: center;
justify-content: space-between;
> .describe{
width: 4.27rem;
height: 100%;
box-sizing: border-box;
padding-top: 1.605rem;
> .content{
width: 4.27rem;
height: 1.32rem;
margin-bottom: .315rem;
> h1{
height: .32rem;
font-weight: 500;
font-size: .24rem;
line-height: .32rem;
margin-bottom: .16rem;
}
> .context{
font-size: .14rem;
line-height: .22rem;
color: #000000A6;
}
}
> .btn-box{
width: max-content;
height: .32rem;
display: flex;
align-items: center;
}
}
> .icon{
width: 4.04rem;
height: 4.04rem;
}
}
> .container{
width: 100%;
//height: calc(100% - 4.53rem);
box-sizing: border-box;
padding: .66rem 1.44rem .92rem 1.44rem;
> .category-list{
width: 100%;
height: .38rem;
margin-bottom: .19rem;
.ant-radio-button-wrapper {
background-color: #FAFAFA;
border: none;
box-shadow: none;
&::before{
display: none;
}
> .ant-radio-button-checked{
background-color: #FFFFFF;
}
}
}
> .main{
width: 100%;
height: 3.54rem;
.ant-spin{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
// display: flex;
// flex-wrap: wrap;
// align-content: space-between;
// justify-content: space-between;
}
}
.question-item{
width: 2.4rem;
height: 1.64rem;
background-color: #FFFFFF;
float: left;
margin-bottom: .1rem;
margin-right: .1rem;
> .header{
box-sizing: border-box;
padding: 0px .16rem;
width: 100%;
height: .38rem;
display: flex;
align-items: center;
justify-content: space-between;
> .category{
font-size: .14rem;
color: #000000D9;
}
> .date{
font-size: .14rem;
color: #D9D9D9;
}
}
> .container{
width: 100%;
height: .86rem;
box-sizing: border-box;
padding: .16rem;
border-bottom: 1px solid #ccc;
> .info{
width: 100%;
height: .24rem;
line-height: .24rem;
margin-bottom: .08rem;
> span{
font-size: .16rem;
color: #000000D9;
}
}
> .project{
width: 100%;
height: .22rem;
line-height: .22rem;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
> span{
font-size: .14rem;
color: #00000073;
}
}
}
> .footer{
width: 100%;
height: .4rem;
display: flex;
align-items: center;
justify-content: space-between;
> .b-right{
border-right: 1px solid #ccc;
}
> .action-item{
width: calc(100% / 3);
height: .16rem;
cursor: pointer;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
> .action{
color: #00000073;
}
}
}
}
}
.question-modify-record-box{
width: 100%;
height: 8.36rem;
box-sizing: border-box;
padding: .24rem .23rem .39rem 0rem;
display: flex;
align-items: center;
justify-content: space-between;
> .preview-box{
width: 5.53rem;
height: 100%;
box-sizing: border-box;
border: 1px solid #1890FF;
> .header{
width: 100%;
height: .35rem;
box-sizing: border-box;
padding-left: .46rem;
padding-top: .13rem;
line-height: .22rem;
}
> .view-box{
width: 100%;
box-sizing: border-box;
padding: 0rem .2rem;
height: calc(100% - .35rem);
overflow-y: scroll;
margin-top: .2rem;
}
}
> .record-box{
width: calc(100% - 5.88rem);
height: 100%;
.ant-space{
height: .32rem;
margin-bottom: .08rem;
}
.ant-table-thead,.ant-table-row{
height: .56rem;
}
> .container{
width: 100%;
height: calc(100% - .9rem);
.ant-table,.ant-table-wrapper,.ant-table-container {
width: 100%;
height: 100%;
}
}
> .footer{
width: 100%;
height: .5rem;
display: flex;
flex-direction: column-reverse;
}
}
}
.upLoad-question-title{
width: 100%;
margin-bottom: .27rem;
> span{
font-size: .14rem;
color: #898989;
}
}

View File

@ -1,34 +0,0 @@
import React, {Component} from "react";
import {Redirect, Route, Switch} from "react-router-dom";
import CreatePaper from "./CreatePaper";
import UploadQuestions from "./UploadQuestions";
import Home from "./PropositionPaperHome";
export default class PropositionPaperHome extends Component {
constructor(props) {
super(props);
this.state = {
classes: props,
};
}
componentDidMount() {
this.props.history.push({
path: "/proposition-paper/home",
state: {
account: this.props.account,
},
});
}
render() {
return (
<div className="proposition-paper-page" data-component="proposition-paper-page" style={{width: "100%", height: "100%"}}>
<Switch>
<Route path="/proposition-paper/home" component={Home} ></Route>
<Route path="/proposition-paper/create-paper/:project/:subject/:ability/:content/:type/:uid" component={CreatePaper} ></Route>
<Route path="/proposition-paper/upload-questions/:project/:subject/:ability/:content/:type/:uid" component={UploadQuestions}></Route>
<Redirect to="/proposition-paper/home" ></Redirect>
</Switch>
</div>
);
}
}

View File

@ -1,206 +0,0 @@
import {message} from "antd";
import {isMobile as isMobileDevice} from "react-device-detect";
import i18next from "i18next";
import Sdk from "casdoor-js-sdk";
export let ServerUrl = "";
export let CasdoorSdk;
export function initServerUrl() {
const hostname = window.location.hostname;
if (hostname === "localhost") {
ServerUrl = `http://${hostname}:8300`;
}
}
export function initCasdoorSdk(config) {
CasdoorSdk = new Sdk(config);
}
function getUrlWithLanguage(url) {
if (url.includes("?")) {
return `${url}&language=${getLanguage()}`;
} else {
return `${url}?language=${getLanguage()}`;
}
}
export function getSignupUrl() {
return getUrlWithLanguage(CasdoorSdk.getSignupUrl());
}
export function getSigninUrl() {
return getUrlWithLanguage(CasdoorSdk.getSigninUrl());
}
export function getUserProfileUrl(userName, account) {
return getUrlWithLanguage(CasdoorSdk.getUserProfileUrl(userName, account));
}
export function getMyProfileUrl(account) {
return getUrlWithLanguage(CasdoorSdk.getMyProfileUrl(account));
}
export function signin() {
return CasdoorSdk.signin(ServerUrl);
}
export function parseJson(s) {
if (s === "") {
return null;
} else {
return JSON.parse(s);
}
}
export function myParseInt(i) {
const res = parseInt(i);
return isNaN(res) ? 0 : res;
}
export function openLink(link) {
// this.props.history.push(link);
const w = window.open("about:blank");
w.location.href = link;
}
export function goToLink(link) {
window.location.href = link;
}
export function goToLinkSoft(ths, link) {
ths.props.history.push(link);
}
export function showMessage(type, text) {
if (type === "") {
return;
} else if (type === "success") {
message.success(text);
} else if (type === "error") {
message.error(text);
}
}
export function isAdminUser(account) {
return account?.isAdmin;
}
export function deepCopy(obj) {
return Object.assign({}, obj);
}
export function insertRow(array, row, i) {
return [...array.slice(0, i), row, ...array.slice(i)];
}
export function addRow(array, row) {
return [...array, row];
}
export function prependRow(array, row) {
return [row, ...array];
}
export function deleteRow(array, i) {
// return array = array.slice(0, i).concat(array.slice(i + 1));
return [...array.slice(0, i), ...array.slice(i + 1)];
}
export function swapRow(array, i, j) {
return [...array.slice(0, i), array[j], ...array.slice(i + 1, j), array[i], ...array.slice(j + 1)];
}
export function isMobile() {
// return getIsMobileView();
return isMobileDevice;
}
export function getFormattedDate(date) {
if (date === undefined || date === null) {
return null;
}
date = date.replace("T", " ");
date = date.replace("+08:00", " ");
return date;
}
export function getFormattedDateShort(date) {
return date.slice(0, 10);
}
export function getShortName(s) {
return s.split("/").slice(-1)[0];
}
export function getShortText(s, maxLength = 35) {
if (s.length > maxLength) {
return `${s.slice(0, maxLength)}...`;
} else {
return s;
}
}
function getRandomInt(s) {
let hash = 0;
if (s.length !== 0) {
for (let i = 0; i < s.length; i++) {
let char = s.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
}
return hash;
}
export function getAvatarColor(s) {
const colorList = ["#f56a00", "#7265e6", "#ffbf00", "#00a2ae"];
let random = getRandomInt(s);
if (random < 0) {
random = -random;
}
return colorList[random % 4];
}
export function getLanguage() {
return i18next.language;
}
export function setLanguage(language) {
localStorage.setItem("language", language);
changeMomentLanguage(language);
i18next.changeLanguage(language);
}
export function changeLanguage(language) {
localStorage.setItem("language", language);
changeMomentLanguage(language);
i18next.changeLanguage(language);
window.location.reload(true);
}
export function changeMomentLanguage(lng) {
return;
// if (lng === "zh") {
// moment.locale("zh", {
// relativeTime: {
// future: "%s内",
// past: "%s前",
// s: "几秒",
// ss: "%d秒",
// m: "1分钟",
// mm: "%d分钟",
// h: "1小时",
// hh: "%d小时",
// d: "1天",
// dd: "%d天",
// M: "1个月",
// MM: "%d个月",
// y: "1年",
// yy: "%d年",
// },
// });
// }
}

View File

@ -1,14 +0,0 @@
import React from "react";
import * as Setting from "./Setting";
class SigninPage extends React.Component {
componentDidMount() {
window.location.replace(Setting.getSigninUrl());
}
render() {
return "";
}
}
export default SigninPage;

View File

@ -1,52 +0,0 @@
import React, {Component} from "react";
import {Col, Row} from "antd";
import TaskRequirements from "./TaskRequirements";
import CompletionStatus from "./CompletionStatus";
import MaterialWarehouse from "./MaterialWarehouse";
import DataTable from "./DataTable";
import "./Step.less";
export default class index extends Component {
render() {
return (
<div className="step-page" data-component="step-page">
<Row style={{height: "7.48rem", marginBottom: ".22rem"}} gutter={24}>
<Col span={9}>
<Row style={{height: "1.9rem", marginBottom: ".22rem"}}>
<TaskRequirements
role={this.props.match.params.role}
stepId={this.props.match.params.step_id}
projectId={this.props.match.params.project_id}
></TaskRequirements>
</Row>
<Row style={{height: "1.9rem", marginBottom: ".12rem"}}>
<CompletionStatus
title="自项目创建截止今日情况"
stepId = {this.props.match.params.step_id}
/>
</Row>
<Row style={{height: "3.34rem"}}>
<MaterialWarehouse
role = {this.props.match.params.role}
projectId = {this.props.match.params.project_id}
stepId = {this.props.match.params.step_id}
stepName = {this.props.location.pathname.split("/")[this.props.location.pathname.split("/").length - 2]}
>
</MaterialWarehouse>
</Row>
</Col>
<Col span={15}>
<DataTable
title="材料评审"
stepName={this.props.location.pathname.split("/")[this.props.location.pathname.split("/").length - 2]}
role={this.props.match.params.role}
stepId={this.props.match.params.step_id}
>
</DataTable>
</Col>
</Row>
</div>
);
}
}

View File

@ -1,9 +0,0 @@
[data-component=step-page]{
width: 100%;
height: auto;
.addition-box{
width: 100%;
height: 100%;
background-color: white;
}
}

View File

@ -1,206 +0,0 @@
import React, {Component} from "react";
import {Button, Upload, message} from "antd";
import ModulaCard from "./ModulaCard";
import CalendarButton from "./CalendarButton";
import {FieldTimeOutlined, LinkOutlined} from "@ant-design/icons";
import "./TaskRequirements.less";
export default class index extends Component {
state = {
stepInfo: {},
loadingState: false,
upLoadState: false,
}
upLoadFile(info) {
this.setState({
upLoadState: true,
});
message.info("开始上传文件:" + info.file.name);
const formData = new FormData();
formData.append("file", info.file);
formData.append("source_project", this.props.projectId);
formData.append("tags", "无");
formData.append("description", "步骤附件");
// request({
// url:baseURL+"/review/file",
// // url:'http://49.232.73.36:8081/review/file',
// method:"POST",
// data:formData,
// headers:{
// "Content-Type":"multipart/form-data;"
// }
// }).then(res => {
// request({
// url:baseURL+"/review/proj/step/attachment",
// // url:'http://49.232.73.36:8081/review/proj/step/attachment',
// method:"POST",
// data:{
// files_ids:[res.data.uuid],
// step_id:this.props.stepId
// }
// }).then(res => {
// this.setState({
// upLoadState:false
// });
// this.getStepInfo();
// message.success("文件上传成功");
// }).catch(err => {
// this.setState({
// upLoadState:false
// });
// message.error("文件上传失败");
// });
// }).catch(err => {
// this.setState({
// upLoadState:false
// });
// message.error("文件上传失败");
// });
}
downLoadFile = () => {
if (this.state.stepInfo.attachments.length === 0) {
message.warning("暂无可下载附件");
} else {
message.info(`开始下载文件:${this.state.stepInfo.attachments[0]}`);
// request({
// url:baseURL+`/review/file/${this.state.stepInfo.attachments[0]}`,
// // url:`http://49.232.73.36:8081/review/file/${this.state.stepInfo.attachments[0]}`,
// method: "GET",
// responseType:"blob"
// }).then(res => {
// const filename = res.headers["content-disposition"];
// const blob = new Blob([res.data]);
// var downloadElement = document.createElement("a");
// var href = window.URL.createObjectURL(blob);
// downloadElement.href = href;
// downloadElement.download = decodeURIComponent(filename.split("filename*=")[1].replace("utf-8''", ""));
// document.body.appendChild(downloadElement);
// downloadElement.click();
// document.body.removeChild(downloadElement);
// window.URL.revokeObjectURL(href);
// message.success("文件下载成功!");
// }).catch(err => {
// message.error("文件下载失败!");
// });
}
}
// 根据不同身份加载不同操作情况
operationRender = () => {
let operationDomList = [() => (
<div className="choice-box">
<CalendarButton label="截止时间2021年8月20日" icon={<FieldTimeOutlined />} onDateChange={(date) => {
message.error("改操作暂不可用");
}} />
<Upload
name="filename"
showUploadList={false}
beforeUpload={() => {
if (this.state.upLoadState) {
message.error("当前存在上传中文件,请勿频繁上传!");
return false;
}
return true;
}}
customRequest={this.upLoadFile.bind(this)}
>
<Button size="small" icon={<LinkOutlined />} style={{marginLeft: ".1246rem"}}>{
this.state.stepInfo.attachments ? "已上传" : "暂无文件"
}</Button>
</Upload>
</div>
), () => (
<div className="choice-box">
<Button size="small" style={{marginRight: ".1246rem"}} icon={<FieldTimeOutlined />}>截止时间2021年8月20日</Button>
<Button size="small" icon={<LinkOutlined />} onClick={this.downLoadFile}>{
this.state.stepInfo.attachments ? "已上传" : "暂无文件"
}</Button>
</div>
), () => (
<div className="choice-box">
<Button size="small" style={{marginRight: ".1246rem"}} icon={<FieldTimeOutlined />}>截止时间2021年8月20日</Button>
<Button size="small" icon={<LinkOutlined />} onClick={this.downLoadFile}>{
this.state.stepInfo.attachments ? "已上传" : "暂无文件"
}</Button>
</div>
)];
return operationDomList[(this.props.role === "1" || this.props.role === "5") ? 0 : this.props.role - 2]();
}
componentDidMount() {
this.getStepInfo();
}
getStepInfo = () => {
this.setState({
loadingState: true,
});
// request({
// url:baseURL+`/review/proj/step/${this.props.stepId}`,
// // url:`http://49.232.73.36:8081/review/proj/step/${this.props.stepId}`,
// method:"GET"
// }).then(res => {
// this.setState({
// stepInfo:res.data,
// loadingState:false,
// });
// }).catch(err => {
// this.setState({
// loadingState:false,
// });
// });
var res = {
"operation_code": 1000,
"message": "",
"data": {
"Id": "62e4b170b686c0cf874cf17e",
"CreateAt": "2022-07-30T04:20:00.14Z",
"UpdateAt": "2022-07-30T04:20:00.14Z",
"uuid": "a17143e3-e428-4058-9ee7-7c1d7998fd97",
"project_id": "5677cb5a-e047-4be4-9d40-718a6c9371ef",
"index": 1,
"name": "测试框架与论证报告",
"description": "",
"requirement": "",
"status": 0,
"deadline": 0,
"timetable": null,
"creator": "2ab2770e-b6e7-476b-969c-2db815e878e6",
"attachments": [
"847ee0d9-a5bb-4186-9c59-852f9436f63e",
],
},
};
this.setState({
stepInfo: res.data,
loadingState: false,
});
}
render() {
return (
<ModulaCard title="任务要求">
{
this.state.loadingState ? (
<></>
) : (
<div className="task-requirements-box" data-component="task-requirements-box">
<div className="describe-box">
<div className="context">
<p>{this.state.stepInfo.requirement || "无"}</p>
</div>
<div className="btn-box">
{this.props.role === 2 ? (<Button type="link">编辑</Button>) : ""}
</div>
</div>
{this.operationRender()}
</div>
)
}
</ModulaCard>
);
}
}

View File

@ -1,37 +0,0 @@
[data-component=task-requirements-box]{
width: 100%;
height: calc(100% - .38rem);
> .describe-box{
width: 100%;
height: .66rem;
box-sizing: border-box;
padding: 0rem 0rem 0rem .5rem;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: .21rem;
> .context{
width: 5rem;
height: 100%;
> p{
font-size: .14rem;
line-height: .22rem;
margin: 0;
}
}
> .btn-box{
height: 100%;
}
}
> .choice-box{
width: 100%;
height: .32rem;
box-sizing: border-box;
padding: 0rem 0rem 0rem .5rem;
display: flex;
.ant-btn {
border-color: #1890FF;
color: #1890FF;
}
}
}

View File

@ -1,226 +0,0 @@
// 上传文件弹出框组件
import React, {Component} from "react";
import {Form, Input, Modal, Radio, Upload, message} from "antd";
import {InboxOutlined} from "@ant-design/icons";
import "./UpLoadModal.less";
export default class index extends Component {
state = {
upLoadFileList: [],
upLoadState: false, // 用于控制文件上传
createLoading: false,
submitForm: {
type: 1,
},
}
upLoadFormRef = React.createRef()
upLoadFile(info) {
this.setState({
upLoadState: true,
});
message.info("开始上传文件:" + info.file.name);
const formData = new FormData();
formData.append("file", info.file);
formData.append("source_project", this.props.projectId);
formData.append("tags", "无");
formData.append("description", "步骤附件");
// request({
// url:baseURL+"/review/file",
// // url:'http://49.232.73.36:8081/review/file',
// method:"POST",
// data:formData,
// headers:{
// "Content-Type":"multipart/form-data;"
// }
// }).then(res => {
// let upLoadFileList = Object.assign(this.state.upLoadFileList, {});
// upLoadFileList[upLoadFileList.length-1].status = "done";
// upLoadFileList[upLoadFileList.length-1].id = res.data.uuid;
// this.setState({
// upLoadFileList,
// upLoadState:false
// });
message.success("文件上传成功");
// }).catch(err => {
// let upLoadFileList = Object.assign(this.state.upLoadFileList, {});
// upLoadFileList[upLoadFileList.length-1].status = "error";
// this.setState({
// upLoadFileList,
// upLoadState:false
// });
// message.error("文件上传失败");
// });
}
createSubmit = () => {
if(this.state.upLoadFileList.length === 0) {
message.warning("请至少上传一个文件");
}else{
this.setState({
createLoading: true,
});
}
this.setState({
createLoading: false,
upLoadFileList: [],
});
// request({
// url:baseURL+"/review/proj/submit",
// // url:"http://49.232.73.36:8081/review/proj/submit",
// method:"POST",
// data:{
// description:"暂无",
// step_id:this.props.stepId,
// title:"一个新的提交"
// }
// }).then(res => {
// request({
// url:baseURL+"/review/proj/submit/content",
// // url:"http://49.232.73.36:8081/review/proj/submit/content",
// method:"POST",
// data:{
// comment:"一个新的文件",
// item_id:this.state.upLoadFileList[0].id,
// submit_id:res.data.uuid,
// type:this.state.submitForm.type,
// version:"1.0.0"
// }
// }).then(res => {
// this.upLoadFormRef.current.resetFields();
// this.setState({
// createLoading:false,
// upLoadFileList:[]
// });
// this.props.onClose();
// message.success("创建成功");
// this.props.onUpdate();
// }).catch(err => {
// this.setState({
// createLoading:false
// });
// message.error(err.message||"创建失败");
// });
// }).catch(err => {
// this.setState({
// createLoading:false
// });
// message.error(err.message||"创建失败");
// });
// }
}
render() {
return (
<Modal title="上传材料"
width="6.26rem"
okText="确认创建"
cancelText="放弃创建"
visible={this.props.show}
closable={!this.state.createLoading}
keyboard={!this.state.createLoading}
confirmLoading={this.state.createLoading}
onCancel={() => {
if(this.state.createLoading) {
message.info("请等待");
}else{
this.upLoadFormRef.current.resetFields();
this.setState({
upLoadFileList: [],
});
this.props.onClose();
}
}}
onOk={this.createSubmit}
>
<div className="up-load-modal-box" data-component="up-load-modal-box">
<Form
name="material"
labelCol={{span: 6}}
wrapperCol={{span: 18}}
initialValues = {this.state.submitForm}
ref={this.upLoadFormRef}
>
<Form.Item
label="材料内容"
name="type"
rules={[{required: true}]}
>
<Radio.Group size="small"
onChange={(e) => {
this.setState({
upLoadFormData: Object.assign({}, this.state.submitForm, {type: e.target.value}),
});
}}
>
<Radio value={1} key="option_1">新材料</Radio>
<Radio value={2} key="option_2">再修改材料</Radio>
<Radio value={3} key="option_3">驳回修改材料</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="材料编号"
name="material-id"
className="material__id"
rules={[{required: true}]}
extra="新材料自动生成材料编号,不可修改"
>
<Input placeholder="AK9800000123" disabled />
</Form.Item>
<Form.Item
label="上传"
rules={[{required: true}]}
labelCol={{span: 4}}
required={true}
>
<Upload.Dragger
name="filename"
fileList={this.state.upLoadFileList}
headers={{
// "Token":getCookie("token")
"Token": "demo",
}}
beforeUpload={(e) => {
if(this.state.upLoadState || this.state.createLoading) {
message.warning("当前存在上传中文件,请等待");
return false;
}else{
let upLoadFileList = [...this.state.upLoadFileList];
upLoadFileList.push({
name: e.name,
status: "uploading",
});
this.setState({
upLoadFileList,
});
return true;
}
}}
customRequest={this.upLoadFile.bind(this)}
onRemove={(e) => {
// uid
let upLoadFileList = Object.assign(this.state.upLoadFileList, {});
upLoadFileList = upLoadFileList.splice(upLoadFileList.findIndex(item => item.uid === e.uid), 1);
this.setState({
upLoadFileList,
});
}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint" style={{fontSize: ".1rem"}}>Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</p>
</Upload.Dragger>
</Form.Item>
</Form>
</div>
</Modal>
);
}
}

View File

@ -1,37 +0,0 @@
[data-component=up-load-modal-box]{
width: 100%;
.material__id{
display: block;
.ant-form-item-control{
box-sizing: border-box;
padding-left: 0.4008rem;
max-width: 91.66666667%;
.ant-form-item-control-input{
width: 100%;
max-width: 100%;
.ant-input {
background-color: #FFFFFF;
}
}
.ant-form-item-extra{
font-size: .14rem;
line-height: .22rem;
color: #00000073;
}
}
}
.upload-file-item{
width: 100%;
height: .22rem;
line-height: .22rem;
> .anticon{
color: #00000073;
font-size: .11rem;
}
> span{
font-size: .14rem;
color: #1890FF;
cursor: pointer;
}
}
}

View File

@ -1,283 +0,0 @@
import React, {Component} from "react";
import {Form, Modal, Select, Tag, message} from "antd";
import {withRouter} from "react-router-dom";
import {ExclamationCircleOutlined} from "@ant-design/icons";
import * as PorjectBackend from "./backend/ProjectBackend";
const {Option} = Select;
const {confirm} = Modal;
class index extends Component {
state = {
form: {
project: null,
subject: null,
content: [],
ability: [],
type: null,
},
projectLoading: false,
projectList: [],
}
contentOption = [{
value: "数与代数",
}, {
value: "历史认知",
}, {
value: "积累与运用",
}]
abilityOption = [{
value: "听力",
}, {
value: "了解",
}, {
value: "认识与知识",
}, {
value: "古诗文积累",
}]
formRef = React.createRef()
titleList=[{
type: "create",
value: "上传试题信息",
}, {
type: "update",
value: "编辑试题信息",
}, {
type: "edit",
value: "编辑试题信息",
}, {
type: "create-paper",
value: "上传试卷信息",
}, {
type: "update-paper",
value: "更新试卷信息",
}]
componentDidMount() {
this.getProjectList();
}
getProjectList = () => {
this.setState({
projectLoading: true,
});
let uid = this.props.uid ? this.props.uid : this.props.match.params.uid;
PorjectBackend.GetUserAssignments(uid).then(res => {
let id_list = [];
if(res.data) {
for(let i = 0;i < res.data.length;i++) {
id_list.push(res.data[i].project_id);
}
}
PorjectBackend.GetProjectList(id_list).then(res => {
this.setState({
projectList: Object.values(res.data),
projectLoading: false,
});
}).catch(err => {
message.error(err.message || "加载失败,请重试");
this.setState({
projectLoading: false,
});
});
}).catch(err => {
message.error(err.message || "加载失败,请重试");
this.setState({
projectLoading: false,
});
});
}
tagRender = (props) => {
let colorList = ["gold", "lime", "green", "cyan"];
const {label, closable, onClose} = props;
const onPreventMouseDown = event => {
event.preventDefault();
event.stopPropagation();
};
return (
<Tag
color={colorList[Math.floor(Math.random() * (3))]}
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{marginRight: 3}}
>
{label}
</Tag>
);
}
render() {
return (
<Modal
title={this.titleList[this.titleList.findIndex(item => item.type === this.props.type)].value}
cancelText="取消"
okText={this.props.type === "create" || this.props.type === "create-paper" ? "下一步" : "保存"}
visible={this.props.show}
onOk={() => {
this.formRef.current.validateFields().then(data => {
if(this.props.type === "create") {
this.props.history.push(`/proposition-paper/upload-questions/${data.project}/${data.subject}/${data.ability}/${data.content}/${data.type}/${this.props.uid}`);
}else if(this.props.type === "update") {
let that = this;
confirm({
icon: <ExclamationCircleOutlined />,
content: "此操作将覆盖当前撰写的试题,是否继续?",
onOk() {
that.props.onClose();
that.props.history.push(`/proposition-paper/upload-questions/${data.project}/${data.subject}/${data.ability}/${data.content}/${data.type}/${this.props.uid}`);
},
onCancel() {
that.props.onClose();
},
});
}else if(this.props.type === "edit") {
this.props.onClose();
}else if(this.props.type === "create-paper") {
this.props.history.push(`/proposition-paper/create-paper/${data.project}/${data.subject}/${data.ability}/${data.content}/${data.type}/${this.props.uid}`);
}else if(this.props.type === "update-paper") {
let that = this;
confirm({
icon: <ExclamationCircleOutlined />,
content: "此操作将覆盖当前撰写的试卷,是否继续?",
onOk() {
that.props.onClose();
that.props.history.push(`/proposition-paper/create-paper/${data.project}/${data.subject}/${data.ability}/${data.content}/${data.type}/${this.props.uid}`);
},
onCancel() {
that.props.onClose();
},
});
}else{
message.error("组件参数异常,失败!");
this.props.onClose();
}
}).catch(err => {
message.warning("请按要求选择选项");
});
}}
onCancel={() => {
this.formRef.current.resetFields();
this.props.onClose();
}}
>
<div className="upLoad-question-title">
<span>试题编号编号:absnahghj自动生成</span>
</div>
<Form
labelCol={{span: 5}}
wrapperCol={{span: 19}}
labelAlign="left"
ref={this.formRef}
initialValues={this.state.form}
>
<Form.Item
name="project"
label="项目"
rules={[{required: true, message: "请选择项目名称"}]}
>
<Select placeholder="选择项目名称" loading={this.state.projectLoading} onFocus={this.getProjectList} onSelect={(e) => {
let from = Object.assign(this.state.form, {project: e});
this.setState({
from,
});
}}>
{
this.state.projectLoading ? (
<></>
) : this.state.projectList.map(item => (
<Option key={item.basic_info.owner + item.basic_info.name} value={item.basic_info.name}>{item.basic_info.name}</Option>
))
}
</Select>
</Form.Item>
<Form.Item
name="subject"
label="学科"
rules={[{required: true, message: "请选择学科"}]}
>
<Select placeholder="选择学科" loading={this.state.projectLoading} onFocus={() => {
if(!this.state.form.project) {
message.warning("请先选择项目");
}
}} onSelect={(e) => {
let form = Object.assign(this.state.form, {subject: e});
this.setState({
form,
});
}}>
{
!this.state.form.project ? (
<></>
) : this.state.projectList[this.state.projectList.findIndex(item => item.name === this.state.form.project)].basic_info.subjects.map((item, index) => (
<Option key={index} value={item}>{item}</Option>
))
}
</Select>
</Form.Item>
<Form.Item
name="content"
label="内容纬度"
rules={[{required: true, message: "请选择内容纬度"}]}
>
<Select
mode="multiple"
showArrow
tagRender={this.tagRender}
style={{width: "100%"}}
options={this.contentOption}
onChange={(e) => {
let form = Object.assign(this.state.form, {content: e});
this.setState({
form,
});
}}
/>
</Form.Item>
<Form.Item
name="ability"
label="能力维度"
rules={[{required: true, message: "请选择能力维度"}]}
>
<Select
mode="multiple"
showArrow
tagRender={this.tagRender}
style={{width: "100%"}}
options={this.abilityOption}
onChange={(e) => {
let form = Object.assign(this.state.form, {ability: e});
this.setState({
form,
});
}}
/>
</Form.Item>
<Form.Item
name="type"
label="题型"
rules={[{required: true, message: "请选择题型"}]}
>
<Select placeholder="选择题型" onSelect={(e) => {
let form = Object.assign(this.state.form, {type: e});
this.setState({
form,
});
}}>
<Option value="1">选择题</Option>
<Option value="2">填空题</Option>
<Option value="3">简答题</Option>
</Select>
</Form.Item>
</Form>
</Modal>
);
}
}
export default withRouter(index);

View File

@ -1,159 +0,0 @@
import React, {Component} from "react";
import {Button, Descriptions, Input, Layout, Menu, PageHeader, Pagination, Spin, message} from "antd";
import ChoiceQuestionEditer from "./ChoiceQuestionEditer";
import HistoryQuestion from "./HistoryQuestion";
import UpLoadQuestionModal from "./UpLoadQuestionModal";
import "./UploadQuestions.less";
import * as ProjectBackend from "./backend/ProjectBackend";
const {Search} = Input;
const {Sider, Content} = Layout;
export default class UploadQuestions extends Component {
state = {
difficultyValue: 4,
createTime: 0,
projectInfo: {},
initLoading: true,
upLoadQuestionModalParams: {
show: false,
type: "update",
},
}
componentDidMount() {
let t = new Date();
this.setState({
createTime: `${t.getFullYear()}-${t.getMonth().toString().padStart(2, "0")}-${t.getDate().toString().padStart(2, "0")} ${t.getHours().toString().padStart(2, "0")}:${t.getMinutes().toString().padStart(2, "0")}:${t.getSeconds().toString().padStart(2, "0")}`,
});
this.getProjectInfo();
}
getProjectInfo=() => {
let newParms = this.props.match.params;
ProjectBackend.GetDetailedInfo(newParms.uid + "/" + newParms.project).then(res => {
this.setState({
projectInfo: res.data.basic_info,
initLoading: false,
});
}).catch(err => {
this.setState({
initLoading: false,
});
this.props.history.goBack();
message.error("编辑器加载失败!");
});
}
render() {
return (
<div className="upLoad-question-page" data-component="upLoad-question-page">
<PageHeader
ghost={false}
onBack={() => this.props.history.goBack()}
title="命题组卷"
subTitle="上传试题"
extra={[
<Button key="1" onClick={() => {
this.setState({
upLoadQuestionModalParams: {
show: true,
type: "update",
},
});
}}>编辑内容</Button>,
]}
>
{
this.state.initLoading ? (
<Spin spinning={true} tip="初始化中"></Spin>
) : (
<Descriptions size="small" column={3}>
<Descriptions.Item label="创建时间" key="createAt">{this.state.createTime}</Descriptions.Item>
<Descriptions.Item label="项目" key="peojects">{this.state.projectInfo.basic_info.name}</Descriptions.Item>
<Descriptions.Item label="学科" key="subjects">{
this.state.projectInfo.basic_info.subjects.map((item, index) => (
<span key={index}>{item}{index === this.state.projectInfo.basic_info.subjects.length - 1 ? "" : "、"}</span>
))
}</Descriptions.Item>
<Descriptions.Item label="内容纬度" key="content">{
this.props.match.params.content.split(",").map((item, index) => (
<span key={index}>{item}{index === this.props.match.params.content.split(",").length - 1 ? "" : "、"}</span>
))
}</Descriptions.Item>
<Descriptions.Item label="能力纬度" key="ability">
{
this.props.match.params.ability.split(",").map((item, index) => (
<span key={index}>{item}{index === this.props.match.params.ability.split(",").length - 1 ? "" : "、"}</span>
))
}
</Descriptions.Item>
</Descriptions>
)
}
</PageHeader>
<div className="main">
<Layout className="container">
<Sider theme="light" width="2.4rem" style={{backgroundColor: "#FAFAFA"}}>
<Menu
style={{width: "2.4rem"}}
defaultSelectedKeys={[this.props.match.params.type]}
defaultOpenKeys={[this.props.match.params.type]}
mode="vertical"
theme="light"
onClick = {(e) => {
let data = this.props.match.params;
this.props.history.push(`/proposition-paper/upload-questions/${data.project}/${data.subject}/${data.ability}/${data.content}/${e.key}`);
}}
>
<Menu.Item key="1">选择题</Menu.Item>
<Menu.Item key="2">填空题</Menu.Item>
<Menu.Item key="3">阅读题</Menu.Item>
</Menu>
</Sider>
<Content style={{backgroundColor: "white"}} className="content">
<ChoiceQuestionEditer
author={this.props.match.params.uid}
defaultSubjectValue={this.props.match.params.subject}
subjectList={this.state.initLoading ? [] : this.state.projectInfo.basic_info.subjects}
ability={this.props.match.params.ability.split(",")}
content={this.props.match.params.content.split(",")}
grade_range={this.state.initLoading ? [] : this.state.projectInfo.basic_info.grade_range}
projectId={this.props.match.params.project}
/>
</Content>
<Sider theme="light" width="7.47rem" className="question-box">
<div className="question-content-box">
<div className="title">相关题目</div>
<div className="filter-box">
<span>筛选</span>
<Search placeholder="input search text" style={{width: 200}} size="small" />
</div>
<HistoryQuestion
/>
<HistoryQuestion
/>
<HistoryQuestion
/>
<Pagination defaultCurrent={1} total={50} className="page-spare" />
</div>
</Sider>
</Layout>
</div>
<UpLoadQuestionModal
{...this.state.upLoadQuestionModalParams}
onClose={() => {
let upLoadQuestionModalParams = Object.assign(this.state.upLoadQuestionModalParams, {show: false});
this.setState({
upLoadQuestionModalParams,
});
}}
/>
</div>
);
}
}

View File

@ -1,289 +0,0 @@
[data-component=upLoad-question-page]{
width: 100%;
height: 100%;
.main{
background-color: white;
> .container{
width: 100%;
height: 100%;
}
}
.ant-menu-inline{
border-right: none;
}
.ant-menu-item{
padding-left: .7rem;
box-sizing: border-box;
}
.ant-menu-item-selected{
border-right: 3px solid #1890ff;
}
.content{
box-sizing: border-box;
padding: .3rem .3rem 0rem .3rem;
> .title{
width: 100%;
> p{
font-size: .12rem;
font-weight: 400;
}
}
.line{
width: 100%;
height: .01rem;
background-color: #D9D9D9;
margin: .15rem 0px;
}
.edit-box{
width: 100%;
min-height: 4.67rem;
margin-top: .2rem;
> .edit-core-box{
width: 100%;
height: 5.27rem;
}
}
.header-title{
width: 100%;
height: .41rem;
background-color: #EEEEEE;
box-sizing: border-box;
padding: .13rem .1rem 0rem .1rem;
display: flex;
justify-content: space-between;
> .title{
font-size: .14rem;
}
> svg{
width: .19rem;
}
> .right-box{
width: 1.92rem;
height: 100%;
margin-right: .57rem;
display: flex;
justify-content: space-between;
> span{
font-size: .1rem;
color: #00000066;
}
}
}
.subject-edit-box,.params-edit-box{
width: 100%;
height: calc(100% - .45rem);
box-sizing: border-box;
border: .01rem solid #D9D9D9;
margin-top: .04rem;
border-radius: .02rem;
padding: .1rem .05rem 0rem .08rem;
}
.subject-edit-box{
> .ant-input{
border: none;
box-shadow: none;
}
}
.difficulty{
width: 100%;
height: .32rem;
display: flex;
align-items: center;
justify-content: center;
> .content-box{
width: 100%;
height: 4px;
background-color: #F5F5F5;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
> .value-box{
position: absolute;
height: 100%;
background-color: #91D5FF;
transition: all .2s;
}
> .value-item{
width: .08rem;
height: .08rem;
background-color: #F5F5F5;
border-radius: .08rem;
cursor: pointer;
position: relative;
> .value{
width: .16rem;
height: .16rem;
font-size: .14rem;
position: absolute;
bottom: -0.18rem;
left: -0.04rem;
text-align: center;
}
&::before{
content:"";
display: block;
width: .04rem;
height: .04rem;
background-color: white;
border-radius: .04rem;
position: absolute;
top: .02rem;
left: .02rem;
z-index: 2;
}
&::after{
content:"";
display: block;
width: .08rem;
height: .08rem;
background-color: #69C0FF;
border-radius: .08rem;
position: absolute;
top: 0rem;
left: 0rem;
z-index: 1;
}
}
> .select-value{
width: .15rem;
height: .15rem;
border-radius: .15rem;
background-color: white;
&::before{
display: block;
content: "";
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: .15rem;
background-color: #91D5FF;
z-index: 1;
}
&::after{
display: block;
content: "";
width: .09rem;
height: .09rem;
position: absolute;
left: .03rem;
top: .03rem;
border-radius: .09rem;
background-color: white;
z-index: 2;
}
}
> .big-value{
&::after{
display: none;
}
}
}
}
.params-item{
height: .22rem;
float: left;
min-width: .5rem;
background-color: #FAFAFA;
box-sizing: border-box;
border: .01rem solid #D9D9D9;
font-size: .11rem;
color: #000000A6;
margin-right: .04rem;
margin-top: .09rem;
display: flex;
align-items: center;
justify-content: center;
}
.option-box{
padding: 0px;
margin-top: .15rem;
margin-bottom: .5rem;
}
.option-item{
width: 100%;
height: .49rem;
margin-top: .04rem;
box-sizing: border-box;
padding-right: .57rem;
display: flex;
align-items: center;
justify-content: space-between;
> .oper-box{
width: 2rem;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.oper-icon{
font-size: .26rem;
}
}
> .value-box{
width: calc(100% - 2.5rem);
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
> .oper-box{
width:.7rem;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
margin-left: .2rem;
> .oper-icon{
font-size: .26rem;
}
}
}
}
.btn-box{
width: 100%;
margin: .8rem 0rem;
}
}
.question-box{
box-sizing: border-box;
padding:0rem 1.03rem .57rem .27rem;
.question-content-box{
height: 100%;
box-sizing: border-box;
background-color: #F5FBFF;
padding: .35rem .16rem 0px .1rem;
border: 1px solid #1890FF;
> .title{
width: 100%;
text-align: center;
font-size: .14rem;
}
> .filter-box{
display: flex;
justify-content: center;
align-items: center;
margin-top: .27rem;
> span{
font-size: .14rem;
margin-right: .2rem;
}
}
.page-spare{
margin: .2rem;
.ant-pagination-item-link,.ant-pagination-item{
background: #91D5FF;
border: 1px solid #FFFEFE;
box-sizing: border-box;
border-radius: 2px;
.anticon{
color: white;
}
}
}
}
}
}

View File

@ -1,22 +0,0 @@
import * as Setting from "../Setting";
export function getAccount() {
return fetch(`${Setting.ServerUrl}/api/get-account`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function signin(code, state) {
return fetch(`${Setting.ServerUrl}/api/signin?code=${code}&state=${state}`, {
method: "POST",
credentials: "include",
}).then(res => res.json());
}
export function signout() {
return fetch(`${Setting.ServerUrl}/api/signout`, {
method: "POST",
credentials: "include",
}).then(res => res.json());
}

View File

@ -1,49 +0,0 @@
import * as Setting from "../Setting";
export function getGlobalDatasets() {
return fetch(`${Setting.ServerUrl}/api/get-global-datasets`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function getDatasets(owner) {
return fetch(`${Setting.ServerUrl}/api/get-datasets?owner=${owner}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function getDataset(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-dataset?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function updateDataset(owner, name, dataset) {
let newDataset = Setting.deepCopy(dataset);
return fetch(`${Setting.ServerUrl}/api/update-dataset?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newDataset),
}).then(res => res.json());
}
export function addDataset(dataset) {
let newDataset = Setting.deepCopy(dataset);
return fetch(`${Setting.ServerUrl}/api/add-dataset`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newDataset),
}).then(res => res.json());
}
export function deleteDataset(dataset) {
let newDataset = Setting.deepCopy(dataset);
return fetch(`${Setting.ServerUrl}/api/delete-dataset`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newDataset),
}).then(res => res.json());
}

View File

@ -1,81 +0,0 @@
import * as Setting from "../Setting";
export function GetDetailedInfo(pid) {
return fetch(`${Setting.ServerUrl}/api/review/proj/detailed?:pid=${pid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function GetBasicInfo(pid) {
return fetch(`${Setting.ServerUrl}/api/review/proj/basic?:pid=${pid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function UpdateProjectInfo() {
return fetch(`${Setting.ServerUrl}/api/review/proj`, {
method: "PUT",
credentials: "include",
}).then(res => res.json());
}
export function CreatTemplateProject(data) {
let project = new Object();
project.basic_info = data;
project.name = data.name;
return fetch(`${Setting.ServerUrl}/api/review/proj/template`, {
method: "POST",
credentials: "include",
body: JSON.stringify(project),
}).then(res => res.json());
}
export function CreateEmptyProject() {
return fetch(`${Setting.ServerUrl}/api/review/proj`, {
method: "POST",
credentials: "include",
}).then(res => res.json());
}
export function GetUserAssignments(uid) {
return fetch(`${Setting.ServerUrl}/api/review/proj/user?:uid=${uid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function GetProjectList(id_list) {
let data = new Object();
data.id_list = id_list;
return fetch(`${Setting.ServerUrl}/api/review/query/proj`, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
}).then(res => res.json());
}
export function GetProjectAssignments(pid) {
return fetch(`${Setting.ServerUrl}/api/review/proj/assign?:pid=${pid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function GetUserList(id_list) {
let data = new Object();
data.id_list = id_list;
return fetch(`${Setting.ServerUrl}/api/review/query/user`, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
}).then(res => res.json());
}
export function MakeOneAssignment(data) {
return fetch(`${Setting.ServerUrl}/api/review/proj/assign`, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
}).then(res => res.json());
}

View File

@ -1,56 +0,0 @@
import * as Setting from "../Setting";
export function TraceQuestionVersion(qid) {
return fetch(`${Setting.ServerUrl}/api/qbank/question/trace?:qid=${qid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function GetTempQuestionList(id_list) {
let data = new Object();
data.id_list = id_list;
return fetch(`${Setting.ServerUrl}/api/qbank/query/t_question`, {
method: "POST",
credentials: "include",
body: JSON.stringify(data),
}).then(res => res.json());
}
export function GetUserTempQuestions(uid) {
return fetch(`${Setting.ServerUrl}/api/qbank/question/user_t?:uid=${uid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function GetUserTempTestpaper(uid) {
return fetch(`${Setting.ServerUrl}/api/qbank/testpaper/user_t?:uid=${uid}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function CreateNewQuestion(data) {
let newData = Setting.deepCopy(data);
return fetch(`${Setting.ServerUrl}/api/qbank/question`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newData),
}).then(res => res.json());
}
export function CreateNewTestpaper(data) {
let newData = Setting.deepCopy(data);
return fetch(`${Setting.ServerUrl}/api/qbank/testpaper`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newData),
}).then(res => res.json());
}
export function DeleteTempTestpaper(tid) {
return fetch(`${Setting.ServerUrl}/api/qbank/testpaper/temp?:tid=${tid}`, {
method: "DELETE",
credentials: "include",
}).then(res => res.json());
}

View File

@ -1,77 +0,0 @@
/* 默认字体 具体可能需要修改 */
#editor {
font-family: 'Microsoft-YaHei';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
content: "宋体";
font-family: "SimSun";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
content: "黑体";
font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
content: "微软雅黑";
font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=consolas]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=consolas]::before {
content: "consolas";
font-family: "consolas";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
content: "楷体";
font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
content: "仿宋";
font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
content: "Arial";
font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
content: "New Roman";
font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
content: "sans-serif";
font-family: "sans-serif";
}
.ql-font-SimSun {
font-family: "SimSun";
}
.ql-font-SimHei {
font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
font-family: "KaiTi";
}
.ql-font-FangSong {
font-family: "FangSong";
}
.ql-font-Arial {
font-family: "Arial";
}
.ql-font-Times-New-Roman {
font-family: "Times New Roman";
}
.ql-font-sans-serif {
font-family: "sans-serif";
}
.ql-font-consolas {
font-family: "consolas";
}

View File

@ -1,57 +0,0 @@
import i18n from "i18next";
import zh from "./locales/zh/data.json";
import en from "./locales/en/data.json";
import * as Conf from "./Conf";
import * as Setting from "./Setting";
const resources = {
en: en,
zh: zh,
};
function initLanguage() {
let language = localStorage.getItem("language");
if (language === undefined || language == null) {
if (Conf.ForceLanguage !== "") {
language = Conf.ForceLanguage;
} else {
let userLanguage;
userLanguage = navigator.language;
switch (userLanguage) {
case "zh-CN":
language = "zh";
break;
case "zh":
language = "zh";
break;
case "en":
language = "en";
break;
case "en-US":
language = "en";
break;
default:
language = Conf.DefaultLanguage;
}
}
}
Setting.changeMomentLanguage(language);
return language;
}
i18n.init({
lng: initLanguage(),
resources: resources,
keySeparator: false,
interpolation: {
escapeValue: false,
},
// debug: true,
saveMissing: true,
});
export default i18n;

View File

@ -1,80 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
.logo {
background: url("https://cdn.open-ct.com/logo/openct_logo_1082x328.png");
background-size: 108px, 33px;
width: 108px;
height: 33px;
/*background: rgba(0, 0, 0, 0.2);*/
margin: 17px 10px 16px 20px;
float: left;
}
.ant-table.ant-table-middle .ant-table-title, .ant-table.ant-table-middle .ant-table-footer, .ant-table.ant-table-middle thead > tr > th, .ant-table.ant-table-middle tbody > tr > td {
padding: 1px 8px !important;
}
.ant-list-sm .ant-list-item {
padding: 2px !important;
}
.highlight-row {
background: rgb(249,198,205);
}
.alert-row {
background: #ffccc7;
}
/*http://react-china.org/t/topic/33846/3*/
.ant-table-header {
scrollbar-color:transparent transparent
}
.ant-table-header::-webkit-scrollbar {
background-color:transparent
}
.import-output {
height: 500px;
}
.CodeMirror {
height: 100% !important;
}
.App {
padding: 20px;
}
.red-row {
background: #ffccc7;
}
.yellow-row {
background: #ffffb8;
}
.green-row {
background: #d9f7be;
}
/*https://stackoverflow.com/questions/64961752/how-do-i-change-the-color-of-selected-menu-item-in-ant-design*/
/*.conferenceMenu > .ant-menu-item-selected {*/
/* background-color: rgb(230,247,255) !important;*/
/*}*/
.conferenceMenu {
background-color: rgb(242,242,242) !important;
}

View File

@ -1,28 +0,0 @@
// create-react-app + IE9
// https://www.cnblogs.com/xuexia/p/12092768.html
// react-app-polyfill
// https://www.npmjs.com/package/react-app-polyfill
import "react-app-polyfill/ie9";
import "react-app-polyfill/stable";
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "./font.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import "antd/dist/antd.min.css";
import {BrowserRouter} from "react-router-dom";
import "./i18n";
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
),
document.getElementById("root"));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

View File

@ -1,34 +0,0 @@
{
"account": {
"My Account": "My Account",
"Sign In": "Sign In",
"Sign Out": "Sign Out",
"Sign Up": "Sign Up"
},
"dataset": {
"Address": "Address",
"Carousels": "Carousels",
"Default item": "Default item",
"Edit Dataset": "Edit Dataset",
"End date": "End date",
"Full name": "Full name",
"Introduction text": "Introduction text",
"Language": "Language",
"Location": "Location",
"Organizer": "Organizer",
"Start date": "Start date"
},
"general": {
"Action": "Action",
"Add": "Add",
"Datasets": "Datasets",
"Delete": "Delete",
"Edit": "Edit",
"Home": "Home",
"Name": "Name",
"Save": "Save",
"Status": "Status",
"Pendingtasks":"Pending Tasks",
"Proposition Paper":"Proposition Paper"
}
}

View File

@ -1,34 +0,0 @@
{
"account": {
"My Account": "我的账户",
"Sign In": "登录",
"Sign Out": "登出",
"Sign Up": "注册"
},
"dataset": {
"Address": "Address",
"Carousels": "Carousels",
"Default item": "Default item",
"Edit Dataset": "Edit Dataset",
"End date": "End date",
"Full name": "Full name",
"Introduction text": "Introduction text",
"Language": "Language",
"Location": "Location",
"Organizer": "Organizer",
"Start date": "Start date"
},
"general": {
"Action": "操作",
"Add": "添加",
"Datasets": "数据集",
"Delete": "删除",
"Edit": "编辑",
"Home": "首页",
"Name": "名称",
"Save": "保存",
"Status": "状态",
"Pending Tasks":"项目管理",
"Proposition Paper":"命题组卷"
}
}

View File

@ -1,135 +0,0 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
"This web app is being served cache-first by a service " +
"worker. To learn more, visit https://bit.ly/CRA-PWA"
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
"New content is available and will be used when all " +
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log("Content is cached for offline use.");
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error("Error during service worker registration:", error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get("content-type");
if (
response.status === 404 ||
(contentType != null && contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
"No internet connection found. App is running in offline mode."
);
});
}
export function unregister() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

File diff suppressed because it is too large Load Diff