Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xiaomai-cloud-class-web
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xiaomai-cloud-class
xiaomai-cloud-class-web
Commits
4a8eb900
Commit
4a8eb900
authored
Jun 08, 2021
by
guomingpang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:新增答题详情与
parent
7becb496
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2931 additions
and
1131 deletions
+2931
-1131
src/common/js/user.ts
+48
-43
src/core/page.less
+9
-9
src/modules/course-manage/video-course/index.jsx
+33
-34
src/modules/teach-tool/components/AnswerDescPage.jsx
+701
-0
src/modules/teach-tool/components/AnswerDescPage.less
+415
-0
src/modules/teach-tool/examination-manager/Index.tsx
+449
-411
src/modules/teach-tool/examination-manager/TestDetailPage.jsx
+314
-0
src/modules/teach-tool/examination-manager/TestDetailPage.less
+313
-0
src/modules/teach-tool/examination-manager/UserData.tsx
+365
-302
src/modules/teach-tool/examination-manager/userData.less
+79
-79
src/modules/teach-tool/question-manage/components/QuestionList.jsx
+136
-191
src/routes/config/mainRoutes.tsx
+69
-62
No files found.
src/common/js/user.ts
View file @
4a8eb900
...
...
@@ -3,7 +3,7 @@
* @Date: 2020-08-31 09:34:25
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-05-12 17:27:08
* @Description:
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
...
...
@@ -11,32 +11,34 @@ import Storage from './storage';
import
{
PREFIX
,
USER_PREFIX
}
from
'@/domains/basic-domain/constants'
;
class
User
{
getStoreId
(){
return
Storage
.
get
(
`
${
PREFIX
}
_storeId`
)
getStoreId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_storeId`
);
}
getEnterpriseId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_enterpriseId`
)
return
Storage
.
get
(
`
${
PREFIX
}
_enterpriseId`
)
;
}
getStoreName
(){
return
Storage
.
get
(
`
${
PREFIX
}
_storeName`
)
getStoreName
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_storeName`
)
;
}
getStoreType
(){
return
Storage
.
get
(
`
${
PREFIX
}
_storeType`
)
getStoreType
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_storeType`
)
;
}
getStoreUserId
(){
return
Storage
.
get
(
`
${
PREFIX
}
_storeUserId`
)
getStoreUserId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_storeUserId`
)
;
}
get
UserId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_
userId`
)
get
CustomerId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_
customerId`
);
}
getUserRole
(){
return
Storage
.
get
(
`
${
PREFIX
}
_userRole`
)
getUserId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_userId`
);
}
getUserRole
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_userRole`
);
}
getToken
()
{
...
...
@@ -47,67 +49,71 @@ class User {
return
Storage
.
get
(
`
${
PREFIX
}
_isAdmin`
);
}
setStoreId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_storeId`
,
value
)
setStoreId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_storeId`
,
value
);
}
setEnterpriseId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_enterpriseId`
,
value
)
return
Storage
.
set
(
`
${
PREFIX
}
_enterpriseId`
,
value
);
}
setStoreName
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_storeName`
,
value
);
}
setStore
Name
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_store
Name`
,
value
)
setStore
Type
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_store
Type`
,
value
);
}
setStore
Type
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_store
Type`
,
value
)
setStore
UserId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_store
UserId`
,
value
);
}
set
StoreUserId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_
storeUserId`
,
value
)
set
CustomerId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_
customerId`
,
value
);
}
setUserId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_userId`
,
value
)
setUserId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_userId`
,
value
);
}
setUserRole
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_userRole`
,
value
)
setUserRole
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_userRole`
,
value
);
}
setToken
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_token`
,
value
);
setToken
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_token`
,
value
);
}
setIsAdmin
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_isAdmin`
,
value
);
}
removeToken
(){
removeToken
()
{
return
Storage
.
remove
(
`
${
PREFIX
}
_token`
);
}
removeUserId
(){
removeUserId
()
{
return
Storage
.
remove
(
`
${
PREFIX
}
_userId`
);
}
removeEnterpriseId
()
{
return
Storage
.
remove
(
`
${
PREFIX
}
_enterpriseId`
)
return
Storage
.
remove
(
`
${
PREFIX
}
_enterpriseId`
)
;
}
getCustomerStoreId
(){
getCustomerStoreId
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_customerStoreId`
);
}
setCustomerStoreId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_customerStoreId`
,
value
);
setCustomerStoreId
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_customerStoreId`
,
value
);
}
setIdentifier
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_identifier`
,
value
);
setIdentifier
(
value
:
any
)
{
return
Storage
.
set
(
`
${
PREFIX
}
_identifier`
,
value
);
}
getIdentifier
(){
getIdentifier
()
{
return
Storage
.
get
(
`
${
PREFIX
}
_identifier`
);
}
clearUserInfo
(){
clearUserInfo
()
{
Storage
.
remove
(
`
${
USER_PREFIX
}
_token`
);
Storage
.
remove
(
`
${
USER_PREFIX
}
_userId`
);
Storage
.
remove
(
`
${
USER_PREFIX
}
_userPhone`
);
...
...
@@ -119,4 +125,4 @@ class User {
}
}
export
default
new
User
();
\ No newline at end of file
export
default
new
User
();
src/core/page.less
View file @
4a8eb900
...
...
@@ -7,7 +7,7 @@
left: 0px;
right: 0;
bottom: 0;
z-index:3;
z-index:
3;
background-color: #fff;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
...
...
@@ -20,12 +20,12 @@
bottom: 0;
z-index: 102;
overflow: auto;
margin:0 16px;
.box{
&:first-child{
margin:
0 16px;
.box
{
&:first-child
{
margin-bottom: 8px;
}
&:last-child{
&:last-child
{
margin-bottom: 16px;
}
}
...
...
@@ -48,10 +48,10 @@
.content-header {
padding: 16px 16px 8px 16px;
line-height: 30px;
font-size:24px;
color:#333;
font-weight:bold;
background: #
FFF
;
font-size:
24px;
color:
#333;
font-weight:
bold;
background: #
fff
;
h1 {
font-weight: normal;
display: inline-block;
...
...
src/modules/course-manage/video-course/index.jsx
View file @
4a8eb900
import
React
from
'react'
import
{
Tabs
}
from
'antd'
import
VideoCourseFilter
from
'./components/VideoCourseFilter'
import
VideoCourseOpt
from
'./components/VieoCourseOpt'
import
VideoCourseList
from
'./components/VideoCourseList'
import
CourseService
from
'@/domains/course-domain/CourseService'
import
User
from
'@/common/js/user'
const
{
TabPane
}
=
Tabs
import
React
from
'react'
;
import
{
Tabs
}
from
'antd'
;
import
VideoCourseFilter
from
'./components/VideoCourseFilter'
;
import
VideoCourseOpt
from
'./components/VieoCourseOpt'
;
import
VideoCourseList
from
'./components/VideoCourseList'
;
import
CourseService
from
'@/domains/course-domain/CourseService'
;
import
User
from
'@/common/js/user'
;
const
{
TabPane
}
=
Tabs
;
class
VideoCourse
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
)
super
(
props
)
;
this
.
state
=
{
query
:
{
size
:
10
,
...
...
@@ -18,45 +18,43 @@ class VideoCourse extends React.Component {
dataSource
:
[],
// 视频课列表
totalCount
:
0
,
// 视频课数据总条数
currentTabKey
:
'internal'
,
}
}
;
}
componentWillMount
()
{
// 获取视频课列表
this
.
handleFetchScheduleList
()
this
.
handleFetchScheduleList
()
;
}
// 获取视频课列表
handleFetchScheduleList
=
(
_query
=
{})
=>
{
const
{
currentTabKey
}
=
this
.
state
const
{
currentTabKey
}
=
this
.
state
;
const
query
=
{
...
this
.
state
.
query
,
...
_query
,
courseDivision
:
currentTabKey
===
'external'
?
1
:
null
,
}
// 更新请求参数
this
.
setState
({
query
})
};
CourseService
.
videoSchedulePage
(
query
).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
||
{}
const
{
records
=
[],
total
=
0
}
=
result
const
{
result
=
{}
}
=
res
||
{}
;
const
{
records
=
[],
total
=
0
}
=
result
;
if
(
Number
(
total
)
&&
query
.
size
*
(
query
.
current
-
1
)
>=
Number
(
total
))
{
this
.
handleFetchScheduleList
({
...
query
,
current
:
1
,
})
return
})
;
return
;
}
this
.
setState
({
query
,
dataSource
:
records
,
totalCount
:
Number
(
total
),
})
})
}
})
;
})
;
}
;
currenTabChange
=
(
currentTabKey
)
=>
{
const
{
query
}
=
this
.
state
const
{
query
}
=
this
.
state
;
this
.
setState
(
{
currentTabKey
,
...
...
@@ -66,20 +64,21 @@ class VideoCourse extends React.Component {
},
},
()
=>
{
this
.
handleFetchScheduleList
()
console
.
log
(
'this.state.query===>'
,
this
.
state
.
query
);
// this.handleFetchScheduleList()
}
)
}
)
;
}
;
changeShelfState
=
(
index
,
shelfState
)
=>
{
const
{
dataSource
}
=
this
.
state
dataSource
[
index
].
shelfState
=
shelfState
const
{
dataSource
}
=
this
.
state
;
dataSource
[
index
].
shelfState
=
shelfState
;
this
.
setState
({
dataSource
,
})
}
})
;
}
;
render
()
{
const
{
dataSource
,
totalCount
,
query
,
currentTabKey
}
=
this
.
state
const
{
dataSource
,
totalCount
,
query
,
currentTabKey
}
=
this
.
state
;
return
(
<
div
className=
'page video-course-page'
>
<
div
className=
'content-header'
>
视频课
</
div
>
...
...
@@ -107,8 +106,8 @@ class VideoCourse extends React.Component {
/>
</
div
>
</
div
>
)
)
;
}
}
export
default
VideoCourse
export
default
VideoCourse
;
src/modules/teach-tool/components/AnswerDescPage.jsx
0 → 100644
View file @
4a8eb900
/*
* @Author: yuananting
* @Date: 2021-04-08 15:50:52
* @LastEditors: wufan
* @LastEditTime: 2021-04-24 15:55:19
* @Description: 助学工具-考试-答案详情
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'react'
;
import
{
Route
,
withRouter
}
from
'react-router-dom'
;
import
User
from
'@/common/js/user'
;
import
Service
from
'@/common/js/service'
;
import
'./AnswerDescPage.less'
;
import
{
message
}
from
'antd'
;
import
XMAudio
from
'./XMAudio'
;
// import ScanFileModal from '@/component/ScanFileModal';
const
NUM_TO_WORD_MAP
=
[
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
,
'H'
,
'I'
,
'J'
,
'K'
,
'L'
,
'M'
,
'N'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
];
function
AnswerDescPage
(
props
)
{
const
examId
=
props
.
match
.
params
.
testId
.
replace
(
/
\?
.+/
,
''
);
const
paperId
=
window
.
getParameterByName
(
'paperId'
);
const
[
customerId
,
setCustomerId
]
=
useState
(
''
);
const
[
examDetail
,
setExamDetail
]
=
useState
({
examDesc
:
''
,
examDuration
:
0
,
examEndTime
:
''
,
examName
:
''
,
examStartTime
:
''
,
paperId
:
0
,
passRate
:
0
,
passScore
:
0
,
resultContent
:
''
,
resultShow
:
''
,
totalQuestionCount
:
0
,
totalScore
:
0
,
userCorrectQuestion
:
0
,
userExamDuration
:
0
,
userExamState
:
''
,
userScore
:
0
,
});
const
[
paperDetail
,
setPaperDetail
]
=
useState
({});
// 试卷详情
const
[
userAnswerList
,
setUserAnswerList
]
=
useState
([]);
// 用户当前答案列表
const
[
allUserAnswerList
,
setAllUserAnswerList
]
=
useState
([]);
// 用户所有答案列表
const
[
errorUserAnswerList
,
setErrorUserAnswerList
]
=
useState
([]);
// 用户错误答案列表
const
[
questionList
,
setQuestionList
]
=
useState
([]);
// 用户当前题目列表
const
[
allQuestionList
,
setAllQuestionList
]
=
useState
([]);
// 用户所有题目列表
const
[
errorQuestionList
,
setErrorQuestionList
]
=
useState
([]);
// 用户错误题目列表
const
[
errorCount
,
setErrorCount
]
=
useState
(
0
);
const
[
onlyError
,
setOnlyError
]
=
useState
(
true
);
// 只显示错题
const
[
activeIndex
,
setActiveIndex
]
=
useState
(
0
);
// 当前答题索引
const
[
activeOrderIndex
,
setActiveOrderIndex
]
=
useState
(
0
);
// 当前答题序号
const
[
isShowQuestionCard
,
setIsShowQuestionCard
]
=
useState
(
false
);
// 答题卡是否展开
const
[
isShowErrorPage
,
setIsShowErrorPage
]
=
useState
(
false
);
// 是否展示异常提示页面
// 是否打开预览文件弹框
const
[
showScanFile
,
setShowScanFile
]
=
useState
(
false
);
const
[
scanFileAddress
,
setScanFileAddress
]
=
useState
(
false
);
const
[
scanFileType
,
setScanFileType
]
=
useState
(
false
);
const
questionTypeList
=
{
SINGLE_CHOICE
:
'单选题'
,
MULTI_CHOICE
:
'多选题'
,
JUDGE
:
'判断题'
,
GAP_FILLING
:
'填空题'
,
INDEFINITE_CHOICE
:
'不定项选择题'
,
};
useEffect
(()
=>
{
getPaperDetail
();
getExamDetail
();
},
[]);
// 获取考试详情
function
getExamDetail
()
{
const
params
=
{
examId
,
source
:
0
,
tenantId
:
User
.
getStoreId
()
||
window
.
getParameterByName
(
'id'
),
userId
:
User
.
getCustomerId
(),
};
Service
.
Hades
(
'public/customerHades/queryUserExamResult'
,
params
,
{
reject
:
true
,
})
.
then
((
res
)
=>
{
const
data
=
{
...
res
.
result
};
setExamDetail
(
data
);
})
.
catch
((
res
)
=>
{
if
(
res
.
code
===
'EXAM_IS_NOT_EXIST'
)
{
handleChangeShowErrorPage
();
}
else
{
message
.
error
(
res
.
message
);
}
});
}
// 获取试卷详情
function
getPaperDetail
()
{
Service
.
Hades
(
'public/customerHades/queryUserExamAnswer'
,
{
examId
,
paperId
,
readAnswer
:
true
,
source
:
0
,
tenantId
:
User
.
getStoreId
()
||
window
.
getParameterByName
(
'id'
),
userId
:
User
.
getCustomerId
(),
}).
then
((
res
)
=>
{
const
{
paperDetailVO
,
userExamAnswerVO
}
=
res
.
result
;
const
paperDetail
=
{
...
paperDetailVO
};
const
allUserAnswerList
=
[...
userExamAnswerVO
];
let
allQuestionList
=
[...
paperDetail
.
questionList
];
allQuestionList
=
allQuestionList
.
map
((
item
,
index
)
=>
{
item
.
orderIndex
=
index
;
return
item
;
});
const
errorUserAnswerList
=
_
.
filter
(
allUserAnswerList
,
(
item
)
=>
{
return
item
.
isCorrect
===
0
;
});
let
errorQuestionList
=
[];
let
userAnswerMap
=
{};
errorUserAnswerList
.
forEach
((
item
)
=>
{
userAnswerMap
[
item
.
questionId
]
=
true
;
});
allQuestionList
.
forEach
((
item
)
=>
{
if
(
userAnswerMap
[
item
.
questionId
])
{
errorQuestionList
.
push
(
item
);
}
});
setActiveOrderIndex
(
errorQuestionList
.
length
>
0
?
errorQuestionList
[
0
].
orderIndex
:
0
);
setPaperDetail
(
paperDetail
);
setAllUserAnswerList
(
allUserAnswerList
);
setErrorUserAnswerList
(
errorUserAnswerList
);
setErrorCount
(
errorUserAnswerList
.
length
);
setUserAnswerList
(
errorUserAnswerList
);
setAllQuestionList
(
allQuestionList
);
setErrorQuestionList
(
errorQuestionList
);
setQuestionList
(
errorQuestionList
);
});
}
function
handleChangeShowErrorPage
()
{
setIsShowErrorPage
(
true
);
}
function
handleChangeActiveIndex
(
isPre
)
{
if
(
onlyError
)
{
if
(
isPre
&&
activeOrderIndex
!==
errorQuestionList
[
0
].
orderIndex
)
{
setActiveOrderIndex
(
errorQuestionList
[
activeIndex
-
1
].
orderIndex
);
setActiveIndex
(
activeIndex
-
1
);
}
else
if
(
!
isPre
&&
activeOrderIndex
!==
errorQuestionList
[
errorCount
-
1
].
orderIndex
)
{
setActiveOrderIndex
(
errorQuestionList
[
activeIndex
+
1
].
orderIndex
);
setActiveIndex
(
activeIndex
+
1
);
}
}
else
{
if
(
isPre
&&
activeOrderIndex
!==
0
)
{
setActiveOrderIndex
(
activeIndex
-
1
);
setActiveIndex
(
activeIndex
-
1
);
}
else
if
(
!
isPre
&&
activeOrderIndex
!==
questionList
.
length
-
1
)
{
setActiveOrderIndex
(
activeIndex
+
1
);
setActiveIndex
(
activeIndex
+
1
);
}
}
}
function
renderFooterText
()
{
if
(
onlyError
&&
errorCount
>
0
)
{
// 只看错题
return
(
<
div
className=
'footer-btn'
>
<
div
className=
'pre-next'
>
<
div
className=
{
`${activeOrderIndex === (errorQuestionList.length > 0 ? errorQuestionList[0].orderIndex : 0) ? 'disabled' : ''} pre`
}
onClick=
{
()
=>
handleChangeActiveIndex
(
true
)
}
>
<
span
className=
'icon iconfont'
>

</
span
>
<
div
className=
'text'
>
上一题
</
div
>
</
div
>
<
div
className=
{
`${activeOrderIndex === (errorQuestionList.length > 0 ? errorQuestionList[errorCount - 1].orderIndex : 0) ? 'disabled' : ''} next`
}
onClick=
{
()
=>
handleChangeActiveIndex
(
false
)
}
>
<
div
className=
'text'
>
下一题
</
div
>
<
span
className=
'icon iconfont'
>

</
span
>
</
div
>
</
div
>
</
div
>
);
}
else
if
(
!
onlyError
)
{
return
(
<
div
className=
'footer-btn'
>
<
div
className=
'pre-next'
>
<
div
className=
{
`${activeOrderIndex === 0 ? 'disabled' : ''} pre`
}
onClick=
{
()
=>
handleChangeActiveIndex
(
true
)
}
>
<
span
className=
'icon iconfont'
>

</
span
>
<
div
className=
'text'
>
上一题
</
div
>
</
div
>
<
div
className=
{
`${activeOrderIndex === questionList.length - 1 ? 'disabled' : ''} next`
}
onClick=
{
()
=>
handleChangeActiveIndex
(
false
)
}
>
<
div
className=
'text'
>
下一题
</
div
>
<
span
className=
'icon iconfont'
>

</
span
>
</
div
>
</
div
>
</
div
>
);
}
}
function
handleRenderQuestionItem
()
{
return
_
.
map
(
questionList
,
(
questionItem
,
questionIndex
)
=>
{
const
{
questionStemList
,
optionList
,
gapFillingAnswerList
,
questionAnswerDescList
,
questionType
,
score
,
questionId
,
orderIndex
}
=
questionItem
;
return
(
<
div
className=
{
`question-info-item`
}
>
{
renderStem
(
questionItem
,
questionStemList
,
questionType
,
score
,
orderIndex
,
questionId
,
gapFillingAnswerList
)
}
{
questionType
!==
'GAP_FILLING'
&&
_
.
map
(
optionList
,
(
optionItem
,
optionIndex
)
=>
{
return
renderOption
(
optionItem
,
optionIndex
,
questionId
);
})
}
{
renderAnswerCompare
(
questionId
,
questionType
,
optionList
,
gapFillingAnswerList
)
}
{
renderAnswerDesc
(
questionAnswerDescList
)
}
</
div
>
);
});
}
// 查看图片或视频
function
handleScanFile
(
scanFileType
,
scanFileAddress
)
{
setShowScanFile
(
true
);
setScanFileAddress
(
scanFileAddress
);
setScanFileType
(
scanFileType
);
}
// 渲染多媒体内容
function
renderMediaContent
(
mediaContent
)
{
return
(
<
div
className=
'media-container'
>
{
_
.
map
(
mediaContent
,
(
mediaItem
,
mediaIndex
)
=>
{
let
dom
=
''
;
let
{
type
,
content
,
size
}
=
mediaItem
;
switch
(
type
)
{
case
'PICTURE'
:
dom
=
(
<
div
key=
{
mediaIndex
+
1
}
className=
'picture-box'
>
<
img
src=
{
content
}
onClick=
{
()
=>
handleScanFile
(
'JPG'
,
content
)
}
/>
</
div
>
);
break
;
case
'VOICE'
:
dom
=
(
<
div
key=
{
mediaIndex
+
1
}
className=
'voice-box'
>
<
XMAudio
url=
{
content
}
getDuration=
{
(
durationSize
)
=>
{
size
=
durationSize
;
}
}
index=
{
mediaIndex
+
1
}
size=
{
size
||
1000
}
/>
</
div
>
);
break
;
case
'AUDIO'
:
dom
=
(
<
div
key=
{
mediaIndex
}
className=
'voice-box'
>
<
XMAudio
url=
{
content
}
getDuration=
{
(
durationSize
)
=>
{
size
=
durationSize
;
}
}
index=
{
mediaIndex
}
size=
{
size
||
1000
}
/>
</
div
>
);
break
;
}
return
dom
;
})
}
</
div
>
);
}
// 渲染题干
function
renderStem
(
questionItem
,
questionStemList
,
questionType
,
score
,
orderIndex
,
questionId
,
gapFillingAnswerList
)
{
const
textContent
=
_
.
filter
(
questionStemList
,
(
item
)
=>
{
return
item
.
type
===
'RICH_TEXT'
;
});
const
mediaContent
=
_
.
filter
(
questionStemList
,
(
item
)
=>
{
return
item
.
type
!==
'RICH_TEXT'
;
});
let
content
=
textContent
.
length
>
0
?
textContent
[
0
].
content
:
''
;
const
userAnswerItem
=
_
.
filter
(
userAnswerList
,
(
item
)
=>
{
return
item
.
questionId
===
questionId
;
});
// 填空题题干渲染
if
(
questionType
===
'GAP_FILLING'
&&
userAnswerItem
.
length
>
0
)
{
let
userAnswer
=
[];
if
(
userAnswerItem
[
0
].
answer
)
{
userAnswer
=
userAnswerItem
[
0
].
answer
;
}
else
{
gapFillingAnswerList
.
forEach
((
item
,
index
)
=>
{
userAnswer
.
push
(
''
);
});
}
gapFillingAnswerList
.
map
((
gapItem
,
gapIndex
)
=>
{
let
gapValue
=
gapItem
.
correctAnswerList
.
includes
(
userAnswer
[
gapIndex
])
?
`<img src="https://image.xiaomaiketang.com/xm/FwZa2Kaypc.png" />`
:
`<img src="https://image.xiaomaiketang.com/xm/7tRHDf6ysA.png" />`
;
content
=
content
.
replace
(
/
(
_
)
|
(
<input.*
?
>
)
/
,
`<div class="gap-line"><span>
${
userAnswer
[
gapIndex
]
===
'@X#$'
?
''
:
userAnswer
[
gapIndex
]}
</span>
${
gapValue
}
</div>`
);
});
}
let
textDom
=
(
<
div
key=
{
0
}
className=
'text-dom'
dangerouslySetInnerHTML=
{
{
__html
:
content
,
}
}
/>
);
return
(
<
div
className=
'stem-line__item'
id=
{
questionItem
.
questionId
}
>
<
div
className=
'text'
>
<
img
className=
'answer-icon'
src=
{
userAnswerItem
[
0
].
isCorrect
===
1
?
'https://image.xiaomaiketang.com/xm/FwZa2Kaypc.png'
:
'https://image.xiaomaiketang.com/xm/7tRHDf6ysA.png'
}
/>
<
span
className=
'question-index'
>
第
{
orderIndex
+
1
}
题
</
span
>
<
span
className=
'steam-line_type'
>
(
{
`${questionTypeList[questionType]} | ${score}分`
}
)
</
span
>
{
textDom
}
</
div
>
{
renderMediaContent
(
mediaContent
)
}
</
div
>
);
}
// 渲染选项
function
renderOption
(
optionItem
,
optionIndex
,
questionId
)
{
const
{
questionOptionContentList
,
optionSort
,
isCorrectAnswer
,
id
}
=
optionItem
;
const
textContent
=
_
.
filter
(
questionOptionContentList
,
(
item
)
=>
{
return
item
.
type
===
'RICH_TEXT'
;
});
const
mediaContent
=
_
.
filter
(
questionOptionContentList
,
(
item
)
=>
{
return
item
.
type
!==
'RICH_TEXT'
;
});
let
content
=
textContent
.
length
>
0
?
textContent
[
0
].
content
:
''
;
let
textDom
=
(
<
span
key=
{
0
}
className=
'text-dom'
dangerouslySetInnerHTML=
{
{
__html
:
content
,
}
}
/>
);
const
userAnswerItem
=
_
.
filter
(
userAnswerList
,
(
item
)
=>
{
return
item
.
questionId
===
questionId
;
});
let
userAnswer
=
[];
if
(
userAnswerItem
.
length
>
0
)
{
if
(
userAnswerItem
[
0
].
answer
)
{
userAnswer
=
userAnswerItem
[
0
].
answer
;
}
}
let
optionStatus
=
''
;
if
(
userAnswer
.
includes
(
id
))
{
// 选中
optionStatus
=
'answered'
;
}
let
answerStatusIcon
=
'default'
;
if
(
isCorrectAnswer
)
{
answerStatusIcon
=
'https://image.xiaomaiketang.com/xm/FwZa2Kaypc.png'
;
}
else
if
(
userAnswer
.
includes
(
id
))
{
answerStatusIcon
=
'https://image.xiaomaiketang.com/xm/7tRHDf6ysA.png'
;
}
else
{
answerStatusIcon
=
'default'
;
}
return
(
<
div
className=
'option-line__item'
>
<
div
className=
{
`text ${optionStatus}`
}
>
<
div
className=
'option-sort'
>
{
NUM_TO_WORD_MAP
[
optionSort
]
}
</
div
>
<
div
className=
'option-content'
>
<
div
className=
'text-dom'
>
{
textDom
}
</
div
>
{
mediaContent
.
length
>
0
&&
renderMediaContent
(
mediaContent
)
}
</
div
>
</
div
>
{
answerStatusIcon
!==
'default'
&&
<
img
className=
'icon'
src=
{
answerStatusIcon
}
/>
}
</
div
>
);
}
// 渲染答案对比
function
renderAnswerCompare
(
questionId
,
questionType
,
optionList
,
gapFillingAnswerList
)
{
const
userAnswerItem
=
_
.
filter
(
userAnswerList
,
(
item
)
=>
{
return
item
.
questionId
===
questionId
;
});
const
userAnswer
=
userAnswerItem
.
length
>
0
&&
userAnswerItem
[
0
].
answer
;
if
(
questionType
===
'GAP_FILLING'
)
{
return
(
<
div
className=
'answer-compare-box'
>
<
div
className=
'answer-info'
style=
{
{
marginBottom
:
'0.16rem'
}
}
>
<
div
className=
'title'
>
考试作答是
</
div
>
{
userAnswer
&&
userAnswer
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
'content'
>
<
span
>
[填空
{
index
+
1
}
]
</
span
>
<
span
dangerouslySetInnerHTML=
{
{
__html
:
item
===
'@X#$'
?
''
:
item
,
}
}
/>
<
span
>
;
</
span
>
</
div
>
);
})
}
</
div
>
<
div
className=
'answer-info'
>
<
div
className=
'title'
>
正确答案是
</
div
>
{
gapFillingAnswerList
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
'content'
style=
{
{
color
:
'#20CECD'
}
}
>
<
span
>
[填空
{
index
+
1
}
]
</
span
>
{
_
.
map
(
item
.
correctAnswerList
,
(
childItem
,
childIndex
)
=>
{
return
(
<
span
dangerouslySetInnerHTML=
{
{
__html
:
childItem
,
}
}
/>
);
})
}
<
span
>
;
</
span
>
</
div
>
);
})
}
</
div
>
</
div
>
);
}
else
{
// 正确答案序号
const
rightOptionSort
=
_
.
filter
(
optionList
,
(
item
)
=>
{
return
item
.
isCorrectAnswer
===
1
;
}).
map
((
sortItem
)
=>
{
return
sortItem
.
optionSort
;
});
const
userAnswerSort
=
_
.
filter
(
optionList
,
(
item
)
=>
{
if
(
userAnswer
)
{
return
userAnswer
.
includes
(
item
.
id
);
}
}).
map
((
sortItem
)
=>
{
return
sortItem
.
optionSort
;
});
return
(
<
div
className=
'answer-compare-box'
>
<
div
className=
'answer-info'
>
<
div
className=
'title'
>
考试作答是
</
div
>
<
div
className=
'content'
>
{
userAnswerSort
.
map
((
item
)
=>
{
return
<
span
>
{
NUM_TO_WORD_MAP
[
item
]
}
</
span
>;
})
}
</
div
>
</
div
>
<
div
className=
'answer-info'
>
<
div
className=
'title'
>
正确答案是
</
div
>
<
div
className=
'content'
style=
{
{
color
:
'#20CECD'
}
}
>
{
rightOptionSort
.
map
((
item
)
=>
{
return
<
span
>
{
NUM_TO_WORD_MAP
[
item
]
}
</
span
>;
})
}
</
div
>
</
div
>
</
div
>
);
}
}
// 渲染答案解析
function
renderAnswerDesc
(
questionAnswerDescList
)
{
const
textContent
=
_
.
filter
(
questionAnswerDescList
,
(
item
)
=>
{
return
item
.
type
===
'RICH_TEXT'
;
});
const
mediaContent
=
_
.
filter
(
questionAnswerDescList
,
(
item
)
=>
{
return
item
.
type
!==
'RICH_TEXT'
;
});
let
content
=
textContent
.
length
>
0
?
`
${
textContent
[
0
].
content
}
:`
:
''
;
let
textDom
=
(
<
div
key=
{
0
}
className=
'text-dom'
dangerouslySetInnerHTML=
{
{
__html
:
content
,
}
}
/>
);
return
(
<
div
className=
'desc-line__item'
>
{
textDom
}
{
renderAnswerDescMedia
(
mediaContent
)
}
</
div
>
);
}
// 渲染答案解析的多媒体
function
renderAnswerDescMedia
(
mediaContent
)
{
const
pictureMediaList
=
_
.
filter
(
mediaContent
,
(
mediaItem
)
=>
{
return
mediaItem
.
type
===
'PICTURE'
;
});
const
voiceMediaList
=
_
.
filter
(
mediaContent
,
(
mediaItem
)
=>
{
return
mediaItem
.
type
===
'VOICE'
;
});
const
audioMediaList
=
_
.
filter
(
mediaContent
,
(
mediaItem
)
=>
{
return
mediaItem
.
type
===
'AUDIO'
;
});
const
videoMediaList
=
_
.
filter
(
mediaContent
,
(
mediaItem
)
=>
{
return
mediaItem
.
type
===
'VIDEO'
;
});
return
(
<
div
className=
'desc-media-container'
>
{
pictureMediaList
.
length
>
0
&&
(
<
div
className=
'desc-picture-box'
>
{
_
.
map
(
pictureMediaList
,
(
pictureItem
,
pictureIndex
)
=>
{
let
{
content
}
=
pictureItem
;
return
(
<
div
className=
'picture-box'
key=
{
pictureIndex
}
>
<
img
className=
'img-box'
src=
{
content
}
onClick=
{
()
=>
handleScanFile
(
'JPG'
,
content
)
}
/>
</
div
>
);
})
}
</
div
>
)
}
{
audioMediaList
.
length
>
0
&&
(
<
div
className=
'desc-audio-box'
>
{
_
.
map
(
audioMediaList
,
(
audioItem
,
audioIndex
)
=>
{
let
{
content
,
size
}
=
audioItem
;
return
(
<
div
className=
'audio-box'
key=
{
audioIndex
}
>
<
XMAudio
forbidParse
url=
{
content
}
getDuration=
{
(
durationSize
)
=>
{
size
=
durationSize
;
}
}
index=
{
audioIndex
}
size=
{
size
||
1000
}
/>
</
div
>
);
})
}
</
div
>
)
}
{
voiceMediaList
.
length
>
0
&&
(
<
div
className=
'desc-audio-box'
>
{
_
.
map
(
voiceMediaList
,
(
voiceItem
,
voiceIndex
)
=>
{
let
{
content
,
size
}
=
voiceItem
;
return
(
<
div
className=
'audio-box'
key=
{
voiceIndex
}
>
<
XMAudio
forbidParse
url=
{
content
}
getDuration=
{
(
durationSize
)
=>
{
size
=
durationSize
;
}
}
index=
{
voiceIndex
}
size=
{
size
||
1000
}
/>
</
div
>
);
})
}
</
div
>
)
}
{
videoMediaList
.
length
>
0
&&
(
<
div
className=
'desc-video-box'
>
{
_
.
map
(
videoMediaList
,
(
videoItem
,
videoIndex
)
=>
{
let
{
content
}
=
videoItem
;
return
(
<
div
className=
'video-box'
key=
{
videoIndex
}
>
<
img
className=
'video-box_content'
src=
{
`${content}?x-oss-process=video/snapshot,t_0,m_fast`
}
/>
<
img
className=
'video-box_btn'
src=
'https://image.xiaomaiketang.com/xm/r5H8cYm4ch.png'
onClick=
{
()
=>
handleScanFile
(
'MP4'
,
content
)
}
/>
</
div
>
);
})
}
</
div
>
)
}
</
div
>
);
}
// 答题卡展开和收起
function
handleToggleQuestionCardShow
()
{
setIsShowQuestionCard
(
!
isShowQuestionCard
);
}
// 快速跳转题目
function
handleQuickActiveQuestion
(
orderIndex
,
answerIndex
)
{
setActiveOrderIndex
(
orderIndex
);
setActiveIndex
(
answerIndex
);
setIsShowQuestionCard
(
false
);
}
// 只选错题
function
chooseErrorAnswer
()
{
setOnlyError
(
!
onlyError
);
setActiveOrderIndex
(
!
onlyError
?
(
errorCount
>
0
?
errorQuestionList
[
0
].
orderIndex
:
0
)
:
0
);
setActiveIndex
(
0
);
setUserAnswerList
(
!
onlyError
?
errorUserAnswerList
:
allUserAnswerList
);
setQuestionList
(
!
onlyError
?
errorQuestionList
:
allQuestionList
);
}
const
{
totalQuestionCount
,
userCorrectQuestion
}
=
examDetail
;
let
sortedAnswerList
=
[];
let
userAnswerMap
=
{};
userAnswerList
.
forEach
((
item
)
=>
{
userAnswerMap
[
item
.
questionId
]
=
item
;
});
questionList
.
forEach
((
item
)
=>
{
if
(
userAnswerMap
[
item
.
questionId
])
{
sortedAnswerList
.
push
({
...
userAnswerMap
[
item
.
questionId
],
orderIndex
:
item
.
orderIndex
,
});
}
});
return
(
<
div
className=
'answer-desc-page'
>
<
div
className=
'center'
>
<
div
className=
'box'
>
<
div
className=
'box-content'
>
<
div
className=
'answer-desc-header'
>
<
div
className=
'desc-header-container'
>
<
div
className=
'desc-title'
>
答案解析
</
div
>
<
div
className=
'choose-error-box'
>
<
img
onClick=
{
()
=>
{
chooseErrorAnswer
();
}
}
src=
{
onlyError
?
'https://image.xiaomaiketang.com/xm/FwZa2Kaypc.png'
:
'https://image.xiaomaiketang.com/xm/crtyKFjcAm.png'
}
/>
<
span
>
只看错题
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
{
errorCount
===
0
&&
onlyError
?
(
<
div
className=
'empty'
>
<
div
className=
'img-box'
>
<
img
src=
{
'https://image.xiaomaiketang.com/xm/sz3CMdQKQE.png'
}
alt=
''
/>
</
div
>
<
div
className=
'empty-text'
>
本次考试无错题
</
div
>
</
div
>
)
:
(
<
div
className=
'answer-desc-content'
>
<
div
className=
'question-list-box'
>
{
handleRenderQuestionItem
()
}
</
div
>
</
div
>
)
}
{
/* {renderFooterText()} */
}
</
div
>
</
div
>
{
/* {showScanFile && (
<ScanFileModal
fileType={scanFileType}
item={{
ossAddress: scanFileAddress,
}}
close={() => {
setShowScanFile(false);
}}
/>
)} */
}
</
div
>
);
}
export
default
withRouter
(
AnswerDescPage
);
src/modules/teach-tool/components/AnswerDescPage.less
0 → 100644
View file @
4a8eb900
.answer-desc-page {
background-color: #fff;
p {
margin: 0 !important;
}
.answer-desc-header {
.desc-header-container {
display: flex;
justify-content: space-between;
align-items: center;
.desc-title {
font-size: 20px;
font-weight: 500;
color: #333333;
position: relative;
margin-left: 8px;
line-height: 28px;
&::before {
position: absolute;
content: '';
left: -8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background: #336dff;
}
}
.choose-error-box {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 2px;
}
span {
font-size: 15px;
color: #999999;
}
}
}
}
.answer-desc-content {
width: 840px;
margin: 0 auto;
text-align: left;
.question-list-box {
padding: 21px 0;
.question-info-item {
.stem-line__item {
margin-bottom: 16px;
.text {
line-height: 1.8;
.answer-icon {
width: 20px;
height: 20px;
margin-right: 12px;
display: inline-block;
margin-bottom: 5px;
}
.question-index {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
}
.steam-line_type {
font-size: 13px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
}
.text-dom {
color: #333333;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
font-size: 17px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
* {
display: inline;
}
.gap-line {
border-bottom: 1px solid;
margin: 0 8px;
padding: 0 4px;
img {
margin-left: 4px;
width: 16px;
height: 16px;
}
}
}
}
}
.option-line__item {
margin-bottom: 12px;
display: flex;
align-items: center;
.icon {
margin-left: 12px;
width: 20px;
height: 20px;
}
.text {
width: 100%;
background: #f4f6fa;
border-radius: 5px;
display: flex;
align-items: center;
padding: 16px;
.option-sort {
font-size: 15px;
line-height: 21px;
position: relative;
&::after {
position: absolute;
left: 22px;
content: '';
width: 1px;
height: 20px;
background-color: rgba(238, 238, 238, 1);
}
}
.option-content {
margin-left: 32px;
}
.text-dom {
font-size: 15px;
color: #333333;
line-height: 21px;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
}
&.answered {
background: rgba(41, 102, 255, 0.05);
border: 1px solid #2966ff;
color: #2966ff;
.text-dom {
color: #2966ff;
}
}
&.right {
background: rgba(21, 217, 177, 0.05);
border: 1px solid #15d9b1;
color: #15d9b1;
}
&.wrong {
background: rgba(255, 79, 79, 0.05);
border: 1px solid #ff4f4f;
color: #ff4f4f;
}
}
}
.desc-line__item {
margin-top: 20px;
.desc-title {
font-size: 20px;
font-weight: 500;
color: #333333;
position: relative;
margin-left: 8px;
line-height: 36px;
&::before {
position: absolute;
content: '';
left: -8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background: #336dff;
}
}
.text-dom {
font-size: 14px;
color: #999999;
line-height: 20px;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
* {
display: inline;
}
}
}
.media-container {
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
align-items: center;
justify-content: center;
margin-top: 12px;
margin-right: 12px;
position: relative;
display: inline-flex;
border: 1px solid #e8e8e8;
img {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.voice-box {
width: 27px;
height: 40px;
margin-top: 12px;
margin-right: 12px;
}
}
.desc-media-container {
margin: 12px 0;
.desc-picture-box {
display: inline-flex;
margin-bottom: 12px;
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
align-items: center;
justify-content: center;
margin-right: 12px;
position: relative;
display: inline-flex;
border: 1px solid #e8e8e8;
.img-box {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
.desc-audio-box {
margin-bottom: 12px;
.audio-box {
margin-bottom: 12px;
border-radius: 5px;
height: 40px;
}
}
.desc-video-box {
.video-box {
position: relative;
display: inline-block;
width: 208px;
height: calc(208px * 9 / 16);
position: relative;
background-color: #000;
margin: 0px 12px 12px 0;
&_content {
max-width: 100%;
max-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&_btn {
width: 32px;
height: 32px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -16px;
margin-left: -16px;
}
.icon_arrow {
position: absolute;
top: -12px;
right: -8px;
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
}
}
}
}
.answer-compare-box {
margin-top: 16px;
padding-top: 12px;
padding-bottom: 12px;
font-size: 15px;
.answer-info {
margin-bottom: 8px;
.title {
color: #999999;
display: inline-block;
margin-right: 12px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
}
.content {
font-weight: 500;
color: #333333;
display: inline;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
span {
margin-right: 4px;
}
}
}
}
}
}
}
.footer-btn {
width: 100%;
height: 64px;
background: #ffffff;
position: fixed;
bottom: 0;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid rgba(238, 238, 238, 1);
padding: 10px 16px;
.hand-in-paper {
display: flex;
.icon {
width: 15px;
line-height: 18px;
color: rgba(255, 183, 20, 1);
margin-right: 6px;
}
}
.pre-next {
width: 343px;
height: 44px;
border-radius: 5px;
border: 2px solid #eeeeee;
display: flex;
justify-content: space-between;
.pre,
.next {
width: 50%;
display: flex;
align-items: center;
justify-content: center;
.icon {
width: 17px;
line-height: 11px;
margin-left: 5px;
color: #ccc;
}
.text {
font-size: 15px;
color: #666;
line-height: 21px;
}
&.disabled {
pointer-events: none;
.text {
color: #cccccc;
}
}
}
}
}
}
.empty {
z-index: 10;
width: 100%;
height: 360px;
background-color: #fff;
.img-box {
margin: 0 auto;
width: 120px;
}
img {
height: 120px;
display: inline-block;
margin-top: 100px;
margin-bottom: 18px;
}
.empty-text {
width: 250px;
height: 18px;
font-size: 13px;
color: #333;
line-height: 18px;
margin: 0 auto;
text-align: center;
}
}
src/modules/teach-tool/examination-manager/Index.tsx
View file @
4a8eb900
import
React
,
{
useState
,
useRef
,
useEffect
,
useContext
}
from
'react'
import
React
,
{
useState
,
useRef
,
useEffect
,
useContext
}
from
'react'
;
import
{
Input
,
Select
,
DatePicker
,
Tooltip
,
Button
,
Table
,
Dropdown
,
Menu
,
Modal
}
from
'antd'
;
import
TeacherSelect
from
'@/modules/common/TeacherSelect'
;
import
{
Route
,
withRouter
}
from
'react-router-dom'
;
import
Service
from
"@/common/js/service"
;
import
Service
from
'@/common/js/service'
;
import
moment
from
'moment'
;
import
{
PageControl
}
from
"@/components"
;
import
{
PageControl
}
from
'@/components'
;
import
AddExam
from
'./AddExam'
;
import
User
from
"@/common/js/user"
;
import
{
XMContext
}
from
"@/store/context"
;
import
ExamShareModal
from
'./ExamShareModal'
import
DataAnalysic
from
'./DataAnalysic'
import
'./index.less'
import
User
from
'@/common/js/user'
;
import
{
XMContext
}
from
'@/store/context'
;
import
ExamShareModal
from
'./ExamShareModal'
;
import
DataAnalysic
from
'./DataAnalysic'
;
import
'./index.less'
;
const
{
RangePicker
}
=
DatePicker
;
const
{
Search
}
=
Input
;
const
{
Option
}
=
Select
;
interface
sortType
{
type
:
"ascend"
|
"descend"
|
null
|
undefined
type
:
'ascend'
|
'descend'
|
null
|
undefined
;
}
interface
fixType
{
left
:
boolean
|
"right"
|
"left"
|
undefined
,
right
:
"right"
|
"left"
,
left
:
boolean
|
'right'
|
'left'
|
undefined
;
right
:
'right'
|
'left'
;
}
const
fixStr
:
fixType
=
{
left
:
'left'
,
right
:
'right'
}
const
fixStr
:
fixType
=
{
left
:
'left'
,
right
:
'right'
,
}
;
function
ExaminationManager
(
props
:
any
)
{
const
queryInit
:
any
=
{
examName
:
''
,
current
:
1
,
size
:
10
,
order
:
'EXAM_START_TIME_DESC'
}
const
sortStatus
:
sortType
=
{
type
:
undefined
}
const
sortEnum
=
{
}
const
{
match
}
=
props
;
const
sortState
:
any
=
false
;
const
ctx
:
any
=
useContext
(
XMContext
);
const
[
query
,
setQuery
]
=
useState
(
queryInit
);
const
[
expandFilter
,
setExpandFilter
]
=
useState
(
false
);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
list
,
setList
]
=
useState
([]);
const
[
field
,
setfield
]
=
useState
(
''
);
const
[
order
,
setOrder
]
=
useState
(
sortStatus
.
type
);
const
[
modal
,
setModal
]
=
useState
(
null
);
const
[
questionCntSort
,
setQuestionCntSort
]
=
useState
(
sortState
)
const
queryRef
=
useRef
({});
const
orderEnum
=
{
userCnt
:
{
ascend
:
'USER_CNT_ASC'
,
descend
:
'USER_CNT_DESC'
},
passCnt
:
{
ascend
:
'PASS_CNT_ASC'
,
descend
:
'PASS_CNT_DESC'
},
examCreateTime
:
{
ascend
:
'EXAM_START_TIME_ASC'
,
descend
:
'EXAM_START_TIME_DESC'
const
queryInit
:
any
=
{
examName
:
''
,
current
:
1
,
size
:
10
,
order
:
'EXAM_START_TIME_DESC'
,
};
const
sortStatus
:
sortType
=
{
type
:
undefined
,
};
const
sortEnum
=
{};
const
{
match
}
=
props
;
const
sortState
:
any
=
false
;
const
ctx
:
any
=
useContext
(
XMContext
);
const
[
query
,
setQuery
]
=
useState
(
queryInit
);
const
[
expandFilter
,
setExpandFilter
]
=
useState
(
false
);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
list
,
setList
]
=
useState
([]);
const
[
field
,
setfield
]
=
useState
(
''
);
const
[
order
,
setOrder
]
=
useState
(
sortStatus
.
type
);
const
[
modal
,
setModal
]
=
useState
(
null
);
const
[
questionCntSort
,
setQuestionCntSort
]
=
useState
(
sortState
);
const
queryRef
=
useRef
({});
const
orderEnum
=
{
userCnt
:
{
ascend
:
'USER_CNT_ASC'
,
descend
:
'USER_CNT_DESC'
,
},
passCnt
:
{
ascend
:
'PASS_CNT_ASC'
,
descend
:
'PASS_CNT_DESC'
,
},
examCreateTime
:
{
ascend
:
'EXAM_START_TIME_ASC'
,
descend
:
'EXAM_START_TIME_DESC'
,
},
};
const
columns
=
[
{
title
:
'考试'
,
// fixed:fixStr.left,
width
:
320
,
dataIndex
:
'examName'
,
render
:
(
text
:
any
,
record
:
any
)
=>
{
var
_text
=
'未开始'
,
_color
=
'rgba(255, 183, 20, 1)'
;
if
(
moment
().
valueOf
()
>
record
.
examEndTime
)
{
_text
=
'已结束'
;
_color
=
'rgba(153, 153, 153, 1)'
;
}
else
if
(
moment
().
valueOf
()
>
record
.
examStartTime
)
{
_text
=
'进行中'
;
_color
=
'rgba(59, 189, 170, 1)'
;
}
}
const
columns
=
[
{
title
:
"考试"
,
// fixed:fixStr.left,
width
:
320
,
dataIndex
:
"examName"
,
render
:
(
text
:
any
,
record
:
any
)
=>
{
var
_text
=
'未开始'
,
_color
=
'rgba(255, 183, 20, 1)'
;
if
(
moment
().
valueOf
()
>
record
.
examEndTime
)
{
_text
=
'已结束'
;
_color
=
'rgba(153, 153, 153, 1)'
;
}
else
if
(
moment
().
valueOf
()
>
record
.
examStartTime
)
{
_text
=
'进行中'
;
_color
=
'rgba(59, 189, 170, 1)'
;
}
return
<
div
style=
{
{
width
:
320
}
}
>
<
div
className=
'oneLineText'
style=
{
{
width
:
320
,
color
:
'#333'
,
fontWeight
:
'bold'
}
}
>
{
text
}
</
div
>
<
div
>
<
span
>
{
moment
(
record
.
examStartTime
).
format
(
"YYYY-MM-DD HH:mm"
)
}
~
{
moment
(
record
.
examEndTime
).
format
(
"YYYY-MM-DD HH:mm"
)
}
</
span
>
<
div
className=
"status"
style=
{
{
border
:
`1px solid ${_color}`
,
borderRadius
:
2
,
color
:
_color
}
}
>
{
_text
}
</
div
></
div
>
<
div
>
创建人:
{
record
.
examCreator
}
</
div
>
</
div
>
},
},
{
title
:
"考试时长"
,
dataIndex
:
"examDuration"
,
render
:
(
text
:
any
)
=>
<
span
>
{
(
text
||
0
)
/
60
/
1000
}
分钟
</
span
>,
},
{
title
:
"及格分/总分"
,
dataIndex
:
"totalScore"
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
`${record.passScore || 0}/${record.totalScore || 0}`
}
</
span
>,
},
{
title
:
"题目数量"
,
align
:
fixStr
.
right
,
dataIndex
:
"questionCnt"
,
},
{
title
:
"参与人数"
,
dataIndex
:
"userCnt"
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
"userCnt"
?
order
:
sortStatus
.
type
,
},
{
title
:
"及格数"
,
dataIndex
:
"passCnt"
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
"passCnt"
?
order
:
sortStatus
.
type
,
},
{
title
:
"创建时间"
,
dataIndex
:
"examCreateTime"
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
"examCreateTime"
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
moment
(
text
).
format
(
"YYYY-MM-DD HH:mm"
)
}
</
span
>,
},
{
title
:
"操作"
,
fixed
:
fixStr
.
right
,
dataIndex
:
"operate"
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
div
className=
"table_operate"
>
{
ctx
.
xmState
?.
userPermission
?.
SeeExamData
()
&&
[<
div
key=
"data"
className=
"operate__item"
onClick=
{
()
=>
{
props
.
history
.
push
({
pathname
:
`${match.url}/analysic/${record.examId}`
})
}
}
>
数据
</
div
>,
<
span
className=
"operate__item split"
>
|
</
span
>]
}
<
div
key=
"share"
className=
"operate__item"
onClick=
{
()
=>
{
shareModal
(
record
)
}
}
>
分享
</
div
>
{
((
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
(
moment
().
valueOf
()
<
record
.
examStartTime
))
||
(
ctx
.
xmState
?.
userPermission
?.
DelExam
()
&&
(
moment
().
valueOf
()
+
30
*
60
*
1000
<
record
.
examStartTime
)))
&&
[<
span
className=
"operate__item split"
>
|
</
span
>,
<
Dropdown
overlay=
{
getOpe
(
record
)
}
>
<
span
className=
'more'
>
更多
</
span
>
</
Dropdown
>]
}
return
(
<
div
style=
{
{
width
:
320
}
}
>
<
div
className=
'oneLineText'
style=
{
{
width
:
320
,
color
:
'#333'
,
fontWeight
:
'bold'
}
}
>
{
text
}
</
div
>
<
div
>
{
' '
}
<
span
>
{
moment
(
record
.
examStartTime
).
format
(
'YYYY-MM-DD HH:mm'
)
}
~
{
moment
(
record
.
examEndTime
).
format
(
'YYYY-MM-DD HH:mm'
)
}{
' '
}
</
span
>
{
' '
}
<
div
className=
'status'
style=
{
{
border
:
`1px solid ${_color}`
,
borderRadius
:
2
,
color
:
_color
}
}
>
{
_text
}
</
div
>
</
div
>
<
div
>
创建人:
{
record
.
examCreator
}
</
div
>
</
div
>
);
},
},
{
title
:
'考试时长'
,
dataIndex
:
'examDuration'
,
render
:
(
text
:
any
)
=>
<
span
>
{
(
text
||
0
)
/
60
/
1000
}
分钟
</
span
>,
},
{
title
:
'及格分/总分'
,
dataIndex
:
'totalScore'
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
`${record.passScore || 0}/${record.totalScore || 0}`
}
</
span
>,
},
{
title
:
'题目数量'
,
align
:
fixStr
.
right
,
dataIndex
:
'questionCnt'
,
},
{
title
:
'参与人数'
,
dataIndex
:
'userCnt'
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
'userCnt'
?
order
:
sortStatus
.
type
,
},
{
title
:
'及格数'
,
dataIndex
:
'passCnt'
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
'passCnt'
?
order
:
sortStatus
.
type
,
},
{
title
:
'创建时间'
,
dataIndex
:
'examCreateTime'
,
align
:
fixStr
.
right
,
sorter
:
true
,
sortOrder
:
field
===
'examCreateTime'
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
moment
(
text
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
span
>,
},
{
title
:
'操作'
,
fixed
:
fixStr
.
right
,
dataIndex
:
'operate'
,
render
:
(
text
:
any
,
record
:
any
)
=>
(
<
div
className=
'table_operate'
>
{
ctx
.
xmState
?.
userPermission
?.
SeeExamData
()
&&
[
<
div
key=
'data'
className=
'operate__item'
onClick=
{
()
=>
{
props
.
history
.
push
({
pathname
:
`${match.url}/analysic/${record.examId}`
,
});
}
}
>
数据
</
div
>,
},
];
function
shareModal
(
record
:
any
)
{
const
modal
=
<
ExamShareModal
data=
{
record
}
close=
{
()
=>
{
setModal
(
null
)
}
}
/>
setModal
(
modal
as
any
)
}
function
getOpe
(
item
:
any
)
{
return
<
Menu
>
{
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
(
moment
().
valueOf
()
<
item
.
examStartTime
)
&&
<
Menu
.
Item
key=
"0"
>
<
span
onClick=
{
()
=>
{
if
(
moment
().
valueOf
()
+
5
*
60
*
1000
>
item
.
examStartTime
)
{
Modal
.
info
({
title
:
'无法编辑'
,
content
:
'离考试开始时间小于5分钟,为保证答题数据的准确性,不能再进行编辑了'
,
})
}
else
{
props
.
history
.
push
({
pathname
:
`${match.url}/edit/${item.examId}`
})
}
}
}
>
编辑
</
span
>
</
Menu
.
Item
>
}
{
ctx
.
xmState
?.
userPermission
?.
DelExam
()
&&
(
moment
().
valueOf
()
+
30
*
60
*
1000
<
item
.
examStartTime
)
&&
<
Menu
.
Item
key=
"1"
>
<
span
onClick=
{
()
=>
{
deleteExam
(
item
)
}
}
>
删除
</
span
>
</
Menu
.
Item
>
}
</
Menu
>
}
function
deleteExam
(
item
:
any
)
{
Modal
.
confirm
({
title
:
'删除考试'
,
content
:
'确定删除该考试吗?'
,
okText
:
'删除'
,
cancelText
:
'取消'
,
icon
:
<
span
className=
"icon iconfont default-confirm-icon"
>

</
span
>,
onOk
:
()
=>
{
Service
.
Hades
(
"public/hades/deleteExam"
,
{
"examId"
:
item
.
examId
,
userId
:
User
.
getStoreUserId
(),
tenantId
:
User
.
getStoreId
(),
source
:
0
}).
then
(()
=>
{
getList
()
})
}
})
}
function
getList
()
{
const
_query
=
{
...
queryRef
.
current
};
// if(_query.examCreator){
// _query.examCreator =parseInt(_query.examCreator)
// }
Service
.
Hades
(
"public/hades/queryExamPageList"
,
{
...
_query
,
userId
:
User
.
getStoreUserId
(),
tenantId
:
User
.
getStoreId
(),
source
:
0
}).
then
((
res
)
=>
{
setList
(
res
.
result
?.
records
||
[])
setTotal
(
parseInt
(
res
.
result
.
total
))
})
}
useEffect
(()
=>
{
queryRef
.
current
=
query
;
getList
();
},
[
query
])
function
onShowSizeChange
(
current
:
any
,
size
:
any
)
{
(
queryRef
.
current
as
any
).
size
=
size
}
function
onChange
(
pagination
:
any
,
filters
:
any
,
sorter
:
any
,
extra
:
any
)
{
setfield
(
sorter
.
field
);
setOrder
(
sorter
.
order
)
console
.
log
(
sorter
.
field
,
sorter
.
order
,
(
orderEnum
as
any
)[
sorter
.
field
])
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
order
=
(
orderEnum
as
any
)[
sorter
.
field
][
sorter
.
order
]
||
'EXAM_START_TIME_DESC'
setQuery
(
_query
)
}
return
<
div
className=
"page examination-manager"
>
<
div
className=
"content-header"
>
考试
</
div
>
<
div
className=
"box content-body"
>
<
div
className=
"xm-search-filter"
>
<
div
style=
{
{
display
:
'flex'
}
}
>
<
div
className=
"search-condition"
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
考试名称:
</
span
>
<
Search
value=
{
query
.
examName
}
className=
'search-input'
placeholder=
"搜索考试名称"
onChange=
{
(
e
)
=>
{
const
_query
=
{
...
query
}
_query
.
examName
=
e
.
target
.
value
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
onSearch=
{
()
=>
{
}
}
enterButton=
{
<
span
className=
"icon iconfont"
>

</
span
>
}
/>
</
div
>
<
TeacherSelect
val=
{
query
.
examCreator
}
onChange=
{
(
examCreator
:
any
)
=>
{
const
_query
=
{
...
query
}
_query
.
examCreator
=
examCreator
;
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
roleCodes=
{
[
"CloudManager"
,
'StoreManager'
]
}
></
TeacherSelect
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
考试时间:
</
span
>
<
RangePicker
className=
'search-input'
value=
{
[
query
.
examStartTime
?
moment
(
Number
(
query
.
examStartTime
))
:
null
,
query
.
examStartTime
?
moment
(
Number
(
query
.
examEndTime
))
:
null
]
}
onChange=
{
(
date
:
any
)
=>
{
const
_query
=
{
...
query
}
_query
.
examStartTime
=
date
&&
date
[
0
]?.
startOf
(
'day'
).
valueOf
();
_query
.
examEndTime
=
date
&&
date
[
1
]?.
endOf
(
'day'
).
valueOf
();
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
/>
</
div
>
{
!!
expandFilter
&&
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
创建时间:
</
span
>
<
RangePicker
className=
'search-input'
value=
{
[
query
.
createStartTime
?
moment
(
Number
(
query
.
createStartTime
))
:
null
,
query
.
createStartTime
?
moment
(
Number
(
query
.
createEndTime
))
:
null
]
}
onChange=
{
(
date
:
any
)
=>
{
const
_query
=
{
...
query
}
_query
.
createStartTime
=
date
&&
date
[
0
]?.
startOf
(
'day'
).
valueOf
();
_query
.
createEndTime
=
date
&&
date
[
1
]?.
endOf
(
'day'
).
valueOf
();
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
/>
</
div
>
}
</
div
>
<
div
className=
"reset-fold-area"
>
<
Tooltip
title=
"清空筛选"
><
span
className=
"resetBtn iconfont icon"
onClick=
{
()
=>
{
setfield
(
''
)
setQuery
({
current
:
1
,
size
:
10
,
order
:
'EXAM_START_TIME_DESC'
});
}
}
>

</
span
></
Tooltip
>
<
span
style=
{
{
cursor
:
'pointer'
}
}
className=
"fold-btn"
onClick=
{
()
=>
{
setExpandFilter
(
!
expandFilter
)
}
}
>
{
expandFilter
?
<
span
><
span
>
收起
</
span
><
span
className=
"iconfont icon fold-icon"
>

</
span
>
</
span
>
:
<
span
>
展开
<
span
className=
"iconfont icon fold-icon"
>

</
span
></
span
>
}
</
span
>
</
div
>
<
span
className=
'operate__item split'
key=
{
'split-1'
}
>
{
' '
}
|
{
' '
}
</
span
>,
]
}
<
div
key=
'share'
className=
'operate__item'
onClick=
{
()
=>
{
shareModal
(
record
);
}
}
>
分享
</
div
>
{
((
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
moment
().
valueOf
()
<
record
.
examStartTime
)
||
(
ctx
.
xmState
?.
userPermission
?.
DelExam
()
&&
moment
().
valueOf
()
+
30
*
60
*
1000
<
record
.
examStartTime
))
&&
[
<
span
className=
'operate__item split'
key=
{
'split-2'
}
>
{
' '
}
|
{
' '
}
</
span
>,
<
Dropdown
overlay=
{
getOpe
(
record
)
}
>
<
span
className=
'more'
>
更多
</
span
>
</
Dropdown
>,
]
}
</
div
>
),
},
];
function
shareModal
(
record
:
any
)
{
const
modal
=
(
<
ExamShareModal
data=
{
record
}
close=
{
()
=>
{
setModal
(
null
);
}
}
/>
);
setModal
(
modal
as
any
);
}
function
getOpe
(
item
:
any
)
{
return
(
<
Menu
>
{
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
moment
().
valueOf
()
<
item
.
examStartTime
&&
(
<
Menu
.
Item
key=
'0'
>
<
span
onClick=
{
()
=>
{
if
(
moment
().
valueOf
()
+
5
*
60
*
1000
>
item
.
examStartTime
)
{
Modal
.
info
({
title
:
'无法编辑'
,
content
:
'离考试开始时间小于5分钟,为保证答题数据的准确性,不能再进行编辑了'
,
});
}
else
{
props
.
history
.
push
({
pathname
:
`${match.url}/edit/${item.examId}`
,
});
}
}
}
>
编辑
</
span
>
</
Menu
.
Item
>
)
}
{
ctx
.
xmState
?.
userPermission
?.
DelExam
()
&&
moment
().
valueOf
()
+
30
*
60
*
1000
<
item
.
examStartTime
&&
(
<
Menu
.
Item
key=
'1'
>
<
span
onClick=
{
()
=>
{
deleteExam
(
item
);
}
}
>
删除
</
span
>
</
Menu
.
Item
>
)
}
</
Menu
>
);
}
function
deleteExam
(
item
:
any
)
{
Modal
.
confirm
({
title
:
'删除考试'
,
content
:
'确定删除该考试吗?'
,
okText
:
'删除'
,
cancelText
:
'取消'
,
icon
:
<
span
className=
'icon iconfont default-confirm-icon'
>

</
span
>,
onOk
:
()
=>
{
Service
.
Hades
(
'public/hades/deleteExam'
,
{
examId
:
item
.
examId
,
userId
:
User
.
getStoreUserId
(),
tenantId
:
User
.
getStoreId
(),
source
:
0
,
}).
then
(()
=>
{
getList
();
});
},
});
}
function
getList
()
{
const
_query
=
{
...
queryRef
.
current
};
// if(_query.examCreator){
// _query.examCreator =parseInt(_query.examCreator)
// }
Service
.
Hades
(
'public/hades/queryExamPageList'
,
{
...
_query
,
userId
:
User
.
getStoreUserId
(),
tenantId
:
User
.
getStoreId
(),
source
:
0
,
}).
then
((
res
)
=>
{
setList
(
res
.
result
?.
records
||
[]);
setTotal
(
parseInt
(
res
.
result
.
total
));
});
}
useEffect
(()
=>
{
queryRef
.
current
=
query
;
getList
();
},
[
query
]);
function
onShowSizeChange
(
current
:
any
,
size
:
any
)
{
(
queryRef
.
current
as
any
).
size
=
size
;
}
function
onChange
(
pagination
:
any
,
filters
:
any
,
sorter
:
any
,
extra
:
any
)
{
setfield
(
sorter
.
field
);
setOrder
(
sorter
.
order
);
console
.
log
(
sorter
.
field
,
sorter
.
order
,
(
orderEnum
as
any
)[
sorter
.
field
]);
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
order
=
(
orderEnum
as
any
)[
sorter
.
field
][
sorter
.
order
]
||
'EXAM_START_TIME_DESC'
;
setQuery
(
_query
);
}
return
(
<
div
className=
'page examination-manager'
>
<
div
className=
'content-header'
>
考试
</
div
>
<
div
className=
'box content-body'
>
<
div
className=
'xm-search-filter'
>
<
div
style=
{
{
display
:
'flex'
}
}
>
<
div
className=
'search-condition'
>
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
考试名称:
</
span
>
<
Search
value=
{
query
.
examName
}
className=
'search-input'
placeholder=
'搜索考试名称'
onChange=
{
(
e
)
=>
{
const
_query
=
{
...
query
};
_query
.
examName
=
e
.
target
.
value
;
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
onSearch=
{
()
=>
{}
}
enterButton=
{
<
span
className=
'icon iconfont'
>

</
span
>
}
/>
</
div
>
<
TeacherSelect
val=
{
query
.
examCreator
}
onChange=
{
(
examCreator
:
any
)
=>
{
const
_query
=
{
...
query
};
_query
.
examCreator
=
examCreator
;
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
roleCodes=
{
[
'CloudManager'
,
'StoreManager'
]
}
></
TeacherSelect
>
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
考试时间:
</
span
>
<
RangePicker
className=
'search-input'
value=
{
[
query
.
examStartTime
?
moment
(
Number
(
query
.
examStartTime
))
:
null
,
query
.
examStartTime
?
moment
(
Number
(
query
.
examEndTime
))
:
null
]
}
onChange=
{
(
date
:
any
)
=>
{
const
_query
=
{
...
query
};
_query
.
examStartTime
=
date
&&
date
[
0
]?.
startOf
(
'day'
).
valueOf
();
_query
.
examEndTime
=
date
&&
date
[
1
]?.
endOf
(
'day'
).
valueOf
();
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
/>
</
div
>
{
!!
expandFilter
&&
(
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
创建时间:
</
span
>
<
RangePicker
className=
'search-input'
value=
{
[
query
.
createStartTime
?
moment
(
Number
(
query
.
createStartTime
))
:
null
,
query
.
createStartTime
?
moment
(
Number
(
query
.
createEndTime
))
:
null
,
]
}
onChange=
{
(
date
:
any
)
=>
{
const
_query
=
{
...
query
};
_query
.
createStartTime
=
date
&&
date
[
0
]?.
startOf
(
'day'
).
valueOf
();
_query
.
createEndTime
=
date
&&
date
[
1
]?.
endOf
(
'day'
).
valueOf
();
_query
.
current
=
1
;
setQuery
(
_query
);
}
}
/>
</
div
>
)
}
</
div
>
{
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
<
Button
type=
'primary'
onClick=
{
()
=>
{
props
.
history
.
push
({
pathname
:
`${match.url}/add`
})
}
}
style=
{
{
margin
:
'4px 0 16px'
}
}
>
新建考试
</
Button
>
}
<
div
className=
"content"
>
<
Table
bordered
size=
"small"
columns=
{
columns
}
dataSource=
{
list
}
scroll=
{
{
x
:
1150
}
}
onChange=
{
onChange
}
pagination=
{
false
}
style=
{
{
margin
:
'0px 0 16px'
}
}
>
</
Table
>
{
total
>
0
&&
<
PageControl
size=
"small"
current=
{
query
.
current
-
1
}
pageSize=
{
query
.
size
}
total=
{
total
}
onShowSizeChange=
{
onShowSizeChange
}
toPage=
{
(
page
:
any
)
=>
{
console
.
log
(
page
)
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
current
=
page
+
1
;
setQuery
(
_query
)
}
}
/>
}
<
div
className=
'reset-fold-area'
>
<
Tooltip
title=
'清空筛选'
>
<
span
className=
'resetBtn iconfont icon'
onClick=
{
()
=>
{
setfield
(
''
);
setQuery
({
current
:
1
,
size
:
10
,
order
:
'EXAM_START_TIME_DESC'
});
}
}
>

{
' '
}
</
span
>
</
Tooltip
>
<
span
style=
{
{
cursor
:
'pointer'
}
}
className=
'fold-btn'
onClick=
{
()
=>
{
setExpandFilter
(
!
expandFilter
);
}
}
>
{
expandFilter
?
(
<
span
>
<
span
>
收起
</
span
>
<
span
className=
'iconfont icon fold-icon'
>

</
span
>
{
' '
}
</
span
>
)
:
(
<
span
>
展开
<
span
className=
'iconfont icon fold-icon'
>

</
span
>
</
span
>
)
}
</
span
>
</
div
>
</
div
>
</
div
>
<
Route
path=
{
`${match.url}/add`
}
render=
{
()
=>
{
return
<
AddExam
freshList=
{
()
=>
{
{
ctx
.
xmState
?.
userPermission
?.
AddExam
()
&&
(
<
Button
type=
'primary'
onClick=
{
()
=>
{
props
.
history
.
push
({
pathname
:
`${match.url}/add`
,
});
}
}
style=
{
{
margin
:
'4px 0 16px'
}
}
>
新建考试
</
Button
>
)
}
<
div
className=
'content'
>
<
Table
bordered
size=
'small'
columns=
{
columns
}
dataSource=
{
list
}
scroll=
{
{
x
:
1150
}
}
onChange=
{
onChange
}
pagination=
{
false
}
rowKey=
{
(
record
)
=>
record
.
examId
}
style=
{
{
margin
:
'0px 0 16px'
}
}
></
Table
>
{
total
>
0
&&
(
<
PageControl
size=
'small'
current=
{
query
.
current
-
1
}
pageSize=
{
query
.
size
}
total=
{
total
}
onShowSizeChange=
{
onShowSizeChange
}
toPage=
{
(
page
:
any
)
=>
{
console
.
log
(
page
);
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
current
=
page
+
1
;
setQuery
(
_query
);
}
}
/>
)
}
</
div
>
</
div
>
<
Route
path=
{
`${match.url}/add`
}
render=
{
()
=>
{
return
(
<
AddExam
freshList=
{
()
=>
{
let
_query
:
any
=
{
...
queryRef
.
current
};
if
(
_query
.
current
!=
1
)
{
_query
.
current
=
1
;
setQuery
(
_query
)
_query
.
current
=
1
;
setQuery
(
_query
);
}
else
{
getList
()
getList
();
}
}
}
/>;
}
}
/>
<
Route
path=
{
`${match.url}/edit/:id`
}
render=
{
()
=>
{
return
<
AddExam
type=
'edit'
freshList=
{
()
=>
{
getList
()
}
}
/>;
}
}
/>
<
Route
path=
{
`${match.url}/analysic/:id`
}
render=
{
()
=>
{
return
<
DataAnalysic
/>;
}
}
/>
{
modal
}
}
}
/>
);
}
}
/>
<
Route
path=
{
`${match.url}/edit/:id`
}
render=
{
()
=>
{
return
(
<
AddExam
type=
'edit'
freshList=
{
()
=>
{
getList
();
}
}
/>
);
}
}
/>
<
Route
path=
{
`${match.url}/analysic/:id`
}
render=
{
()
=>
{
return
<
DataAnalysic
/>;
}
}
/>
{
modal
}
</
div
>
);
}
export
default
withRouter
(
ExaminationManager
);
\ No newline at end of file
export
default
withRouter
(
ExaminationManager
);
src/modules/teach-tool/examination-manager/TestDetailPage.jsx
0 → 100644
View file @
4a8eb900
/*
* @Author: yuananting
* @Date: 2021-04-07 16:10:21
* @LastEditors: wufan
* @LastEditTime: 2021-04-26 10:21:19
* @Description: 助学工具-考试-考试结果
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
{
Route
,
withRouter
}
from
'react-router-dom'
;
import
User
from
'@/common/js/user'
;
import
'./TestDetailPage.less'
;
import
Service
from
'@/common/js/service'
;
import
{
message
,
Empty
}
from
'antd'
;
import
moment
from
'moment'
;
import
AnswerDescPage
from
'../components/AnswerDescPage'
;
import
Breadcrumbs
from
'@/components/Breadcrumbs'
;
function
TestDetailPage
(
props
)
{
const
examId
=
props
.
match
.
params
.
testId
.
replace
(
/
\?
.+/
,
''
);
const
paperId
=
window
.
getParameterByName
(
'paperId'
);
const
[
customerId
,
setCustomerId
]
=
useState
(
''
);
const
[
examDetail
,
setExamDetail
]
=
useState
({
answerAnalysis
:
''
,
resultShow
:
''
,
examDesc
:
''
,
examDuration
:
0
,
examEndTime
:
''
,
examName
:
''
,
examStartTime
:
''
,
paperId
:
0
,
passRate
:
0
,
passScore
:
0
,
resultContent
:
''
,
totalQuestionCount
:
0
,
totalScore
:
0
,
userCorrectQuestion
:
0
,
userExamDuration
:
0
,
userExamState
:
'INIT'
,
userScore
:
0
,
});
// 考试详情
const
[
testState
,
setTestState
]
=
useState
({
isDelete
:
false
,
});
// 考试状态
const
[
paperDetail
,
setPaperDetail
]
=
useState
({});
// 试卷详情
const
[
questionList
,
setQuestionList
]
=
useState
([]);
// 试卷题目列表
const
[
userAnswerList
,
setUserAnswerList
]
=
useState
([]);
// 用户答案列表
const
[
isScrollShow
,
setIsScrollShow
]
=
useState
(
false
);
// 是否展示回到顶部按钮
const
{
answerAnalysis
,
resultContent
,
resultShow
,
examName
,
examStartTime
,
examEndTime
,
totalScore
,
totalQuestionCount
,
passScore
,
examDuration
,
examDesc
,
userExamDuration
,
userExamState
,
userScore
,
userCorrectQuestion
,
}
=
examDetail
;
useEffect
(()
=>
{
bindScroll
();
getPaperDetail
();
getExamDetail
();
},
[]);
function
bindScroll
()
{
window
.
addEventListener
(
'scroll'
,
handleScroll
,
true
);
}
function
getExamDetail
()
{
const
params
=
{
examId
,
source
:
0
,
tenantId
:
User
.
getStoreId
()
||
window
.
getParameterByName
(
'id'
),
userId
:
User
.
getCustomerId
(),
};
Service
.
Hades
(
'public/customerHades/queryUserExamResult'
,
params
,
{
reject
:
true
,
})
.
then
((
res
)
=>
{
const
data
=
{
...
res
.
result
};
setExamDetail
(
data
);
})
.
catch
((
res
)
=>
{
if
(
res
.
code
===
'EXAM_IS_NOT_EXIST'
)
{
handleChangeShowErrorPage
();
}
else
{
message
.
error
(
res
.
message
);
}
});
}
function
getPaperDetail
()
{
Service
.
Hades
(
'public/customerHades/queryUserExamAnswer'
,
{
examId
,
paperId
,
source
:
0
,
readAnswer
:
true
,
tenantId
:
User
.
getStoreId
()
||
window
.
getParameterByName
(
'id'
),
userId
:
User
.
getCustomerId
(),
}).
then
((
res
)
=>
{
if
(
res
.
success
)
{
const
{
paperDetailVO
,
userExamAnswerVO
}
=
res
.
result
;
const
userAnswerList
=
[...
userExamAnswerVO
];
const
{
questionList
}
=
paperDetailVO
;
setPaperDetail
(
paperDetail
);
setUserAnswerList
(
userAnswerList
);
setQuestionList
(
questionList
||
[]);
}
});
}
function
handleChangeShowErrorPage
()
{
setIsShowErrorPage
(
true
);
setTestState
({
isDelete
:
true
,
});
}
// 用户时长转换
function
formatTime
(
msTime
)
{
let
time
=
msTime
/
1000
;
let
hour
=
Math
.
floor
(
time
/
60
/
60
)
%
24
>
9
?
Math
.
floor
(
time
/
60
/
60
)
%
24
:
'0'
+
(
Math
.
floor
(
time
/
60
/
60
)
%
24
);
let
minute
=
Math
.
floor
(
time
/
60
)
%
60
>
9
?
Math
.
floor
(
time
/
60
)
%
60
:
'0'
+
(
Math
.
floor
(
time
/
60
)
%
60
);
let
second
=
time
%
60
>
9
?
Math
.
round
(
time
%
60
)
:
'0'
+
Math
.
round
(
time
%
60
);
return
`
${
hour
}
:
${
minute
}
:
${
second
}
`
;
}
// 快速跳转题目
function
handleQuickActiveQuestion
(
questionId
)
{
let
selectDom
=
document
.
getElementById
(
`
${
questionId
}
`
);
selectDom
.
scrollIntoView
({
block
:
'center'
,
});
}
// 回到顶部
function
handleGoTop
()
{
window
.
scrollTo
(
0
,
0
);
}
// 监听滚动,200以后出现回到顶部按钮
function
handleScroll
()
{
if
(
window
.
pageYOffset
>
200
)
{
setIsScrollShow
(
true
);
}
else
{
setIsScrollShow
(
false
);
}
}
function
renderResultInfo
()
{
console
.
log
(
'userExamState====>'
,
userExamState
);
if
(
userExamState
!==
'INIT'
)
{
if
(
userExamState
===
'FINISH_EXAM'
)
{
if
(
resultShow
===
'IMMEDIATELY'
||
(
resultShow
===
'AFTER_EXAM_END'
&&
Date
.
now
()
>
examEndTime
))
{
return
(
<
div
>
<
div
className=
'exam-info'
>
{
[
'PASS_AND_SCORE'
,
'ONLY_SCORE'
].
includes
(
resultContent
)
&&
(
<
div
className=
'info-score item'
>
<
div
className=
'current-score'
>
{
userScore
}
<
img
src=
'https://image.xiaomaiketang.com/xm/TsaApiPyxA.png'
/>
</
div
>
<
div
className=
'origin-data'
>
总分
{
totalScore
}
</
div
>
</
div
>
)
}
{
[
'PASS_AND_SCORE'
,
'ONLY_PASS'
].
includes
(
resultContent
)
&&
(
<
div
className=
'info-level item'
>
<
div
className=
'current-level'
>
{
userScore
<
passScore
?
'不及格'
:
'及格'
}
</
div
>
<
div
className=
'origin-data'
>
及格分
{
passScore
}
</
div
>
</
div
>
)
}
<
div
className=
'info-correct item'
>
<
div
className=
'current-correct'
>
{
userCorrectQuestion
}
<
div
className=
'text'
>
答对题数
</
div
>
</
div
>
<
div
className=
'origin-data'
>
共
{
totalQuestionCount
}
题
</
div
>
</
div
>
<
div
className=
'info-time item'
>
<
div
className=
'current-time'
>
{
formatTime
(
Number
(
userExamDuration
>
examDuration
?
examDuration
:
userExamDuration
||
0
))
}
<
div
className=
'text'
>
用时
</
div
>
</
div
>
<
div
className=
'origin-data'
>
考试时长
{
(
examDuration
||
0
)
/
60
/
1000
}
分钟
</
div
>
</
div
>
</
div
>
{
[
'ANALYSE_AND_RIGHT_OR_WRONG'
,
'RIGHT_OR_WRONG'
].
includes
(
answerAnalysis
)
&&
(
<
div
className=
'exam-result'
>
<
div
className=
'result-title'
>
<
div
className=
'left-title'
>
答题情况
</
div
>
<
div
className=
'right-tip'
>
<
span
className=
'correct-num'
>
{
userCorrectQuestion
}
</
span
>
<
span
className=
'incorrect-num'
>
/
{
totalQuestionCount
}
</
span
>
道正确
</
div
>
</
div
>
<
div
className=
'result-content'
>
{
sortedAnswerList
.
map
((
item
,
index
)
=>
{
return
(
<
div
className=
'result-content__item'
onClick=
{
()
=>
{
console
.
log
(
'item'
,
item
);
handleQuickActiveQuestion
(
item
.
questionId
);
}
}
>
<
img
className=
'icon'
src=
{
item
.
isCorrect
===
1
?
'https://image.xiaomaiketang.com/xm/FwZa2Kaypc.png'
:
'https://image.xiaomaiketang.com/xm/7tRHDf6ysA.png'
}
/>
<
div
className=
'result-content-box'
>
{
index
+
1
}
</
div
>
</
div
>
);
})
}
</
div
>
</
div
>
)
}
{
!
testState
.
isDelete
&&
userExamState
!==
'LACK_EXAM'
&&
(
resultShow
===
'IMMEDIATELY'
||
(
resultShow
===
'AFTER_EXAM_END'
&&
Date
.
now
()
>
examEndTime
))
&&
answerAnalysis
===
'ANALYSE_AND_RIGHT_OR_WRONG'
&&
<
AnswerDescPage
/>
}
</
div
>
);
}
else
if
(
resultShow
===
'AFTER_EXAM_END'
)
{
return
customizeRenderEmpty
(
'after'
);
}
}
else
{
return
customizeRenderEmpty
(
'lack'
);
}
}
}
function
customizeRenderEmpty
(
status
)
{
return
(
<
div
className=
'empty-result'
>
<
Empty
image=
{
status
===
'lack'
?
'https://image.xiaomaiketang.com/xm/7xi4YTXmHK.png'
:
'https://image.xiaomaiketang.com/xm/bhGtST27Hf.png'
}
imageStyle=
{
{
height
:
'100px'
,
}
}
description=
{
status
===
'lack'
?
(
<
div
className=
'lack-desc'
>
<
div
className=
'title'
>
缺考
</
div
>
<
div
className=
'content'
>
你没有成功交卷哦
</
div
>
</
div
>
)
:
(
<
div
className=
'after-desc'
>
<
div
className=
'title'
>
恭喜你完成考试!
</
div
>
<
div
className=
'after-show-box'
>
<
div
>
本次考试用时
{
window
.
formatHourTime
(
Number
(
userExamDuration
||
0
))
}
</
div
>
<
div
>
考试结果会在
<
span
style=
{
{
color
:
'#333333'
}
}
>
{
moment
(
Number
(
examEndTime
)).
format
(
'YYYY.MM.DD HH:mm'
)
}
公布哦
</
span
>
</
div
>
</
div
>
</
div
>
)
}
></
Empty
>
</
div
>
);
}
let
sortedAnswerList
=
[];
questionList
.
map
((
item
)
=>
{
userAnswerList
&&
userAnswerList
.
map
((
answerItem
)
=>
{
if
(
item
.
questionId
===
answerItem
.
questionId
)
{
sortedAnswerList
.
push
(
answerItem
);
}
});
});
return
(
<
div
className=
'exam-result-page page'
>
<
Breadcrumbs
navList=
{
'答题详情'
}
goBack=
{
props
.
history
.
goBack
}
/>
<
div
className=
'center'
>
<
div
className=
'box'
>
<
div
className=
'box-content'
>
<
div
className=
'exam-head'
style=
{
{
padding
:
examName
.
length
>
20
?
'8px 0 14px'
:
'16px 0 29px'
,
}
}
>
<
div
className=
{
`exam-name ${examName.length > 20 ? 'many' : 'few'}`
}
>
{
examName
}
</
div
>
</
div
>
{
renderResultInfo
()
}
</
div
>
</
div
>
</
div
>
{
isScrollShow
&&
<
div
className=
'go-top'
onClick=
{
handleGoTop
}
></
div
>
}
</
div
>
);
}
export
default
withRouter
(
TestDetailPage
);
src/modules/teach-tool/examination-manager/TestDetailPage.less
0 → 100644
View file @
4a8eb900
.exam-result-page {
margin: 0 auto;
.go-top {
cursor: pointer;
position: fixed;
width: 48px;
height: 48px;
background-image: url('https://image.xiaomaiketang.com/xm/jWix2xDm4t.png');
background-size: 100%;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
border-radius: 4px;
bottom: 125px;
right: calc(~'(100vw - 1232px)/2');
display: flex;
justify-content: center;
align-items: center;
&:hover {
background-image: url('https://image.xiaomaiketang.com/xm/GHBBNDtTDd.png');
}
}
.box {
.box-content {
position: relative;
width: 840px;
margin: 0 auto;
.exam-head {
color: #333;
text-align: center;
.exam-name {
width: 600px;
font-weight: 500;
margin: 0 auto;
font-size: 24px;
line-height: 33px;
}
.many {
font-size: 21px;
line-height: 29px;
}
.few {
font-size: 25px;
line-height: 36px;
}
}
.empty-result {
box-sizing: border-box;
margin: 0 16px;
background: #ffffff;
box-shadow: 0 -15px 10px 0 rgba(0, 34, 121, 0.1);
border-radius: 4px 4px 0 0;
padding-top: 56px;
.lack-desc {
margin-top: 16px;
.title {
font-size: 17px;
font-weight: 500;
color: #333333;
line-height: 24px;
margin-bottom: 4px;
}
.content {
font-size: 15px;
color: #999999;
line-height: 21px;
}
}
.after-desc {
margin-top: 16px;
.title {
font-size: 17px;
font-weight: 500;
color: #333333;
line-height: 24px;
margin-bottom: 20px;
}
.after-show-box {
// margin: 0 37px;
padding: 11px 21px;
background: #f4f6fa;
border-radius: 4px;
font-size: 15px;
color: #999999;
line-height: 21px;
text-align: center;
}
}
}
.exam-info {
width: 600px;
box-sizing: border-box;
margin: 0 auto;
padding: 24px 22px 12px 22px;
height: 130px;
background: #ffffff;
box-shadow: 0px -10px 10px 0px rgba(0, 61, 214, 0.1);
border-radius: 4px;
display: flex;
flex: 1;
justify-content: center;
.item {
display: flex;
flex-direction: column;
width: 150px;
// height: 130px;
align-items: center;
justify-content: space-between;
position: relative;
.origin-data {
text-align: center;
font-size: 14px;
color: #999999;
line-height: 20px;
}
.line {
&::before {
width: 1px;
height: 40px;
content: '';
position: absolute;
right: 0;
top: 0;
background-color: rgba(232, 232, 232, 1);
}
}
&.info-score {
.current-score {
font-size: 40px;
font-weight: 500;
color: #ff4f4f;
text-align: center;
line-height: 42px;
img {
display: block;
margin: 0 auto;
width: 27px;
height: 12px;
padding-left: 4px;
}
}
}
&.info-level {
.current-level {
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 33px;
text-align: center;
}
}
&.info-correct {
.current-correct {
display: flex;
flex-direction: column;
padding-bottom: 20px;
justify-content: space-between;
align-items: center;
font-size: 20px;
font-weight: 500;
color: #333333;
line-height: 28px;
.text {
margin-top: 4px;
width: 48px;
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #9b9b9b;
line-height: 17px;
text-align: center;
}
}
}
&.info-time {
.current-time {
display: flex;
flex-direction: column;
padding-bottom: 20px;
justify-content: space-between;
align-items: center;
font-size: 20px;
font-weight: 500;
color: #333333;
line-height: 28px;
}
.text {
margin-top: 4px;
width: 48px;
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #9b9b9b;
line-height: 17px;
text-align: center;
}
}
}
}
.left-title {
font-size: 20px;
font-weight: 500;
color: #333333;
position: relative;
margin-left: 8px;
line-height: 28px;
&::before {
position: absolute;
content: '';
left: -8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background: #336dff;
}
}
.exam-result {
padding: 44px 0 24px 0;
vertical-align: middle;
.result-title {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
line-height: 24px;
.right-tip {
font-size: 15px;
font-weight: 500;
color: #999999;
.correct-num {
color: #16e0b7;
}
.incorrect-num {
font-size: 11px;
}
}
}
.result-content {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
&__item {
position: relative;
margin: 0 16px 16px 0;
width: 36px;
height: 36px;
background: #f4f6fa;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:nth-child(5n) {
margin-right: 50px;
}
&:nth-child(15n) {
margin-right: 0px;
}
.icon {
width: 16px;
height: 16px;
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -50%);
}
.result-content-box {
width: 36px;
height: 36px;
font-size: 15px;
line-height: 36px;
background: #f4f6fa;
border-radius: 4px;
text-align: center;
}
}
}
.result-content::after {
content: '';
flex: auto;
}
}
}
}
.footer-btn {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
height: 64px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid rgba(238, 238, 238, 1);
box-sizing: border-box;
.text {
cursor: pointer;
background: #ffb714;
border-radius: 5px;
width: 342px;
font-size: 15px;
color: #ffffff;
line-height: 44px;
text-align: center;
&.disabled {
background-color: #ccc;
}
}
}
}
src/modules/teach-tool/examination-manager/UserData.tsx
View file @
4a8eb900
import
React
,
{
useState
,
useRef
,
useEffect
}
from
'react'
import
Service
from
"@/common/js/service"
;
import
{
PageControl
}
from
"@/components"
;
import
React
,
{
useState
,
useRef
,
useEffect
}
from
'react'
;
import
Service
from
'@/common/js/service'
;
import
{
PageControl
}
from
'@/components'
;
import
{
Input
,
Select
,
Tooltip
,
Table
,
Button
}
from
'antd'
;
import
User
from
"@/common/js/user"
;
import
{
ColumnsType
}
from
'antd/es/table'
;
import
User
from
'@/common/js/user'
;
import
moment
from
'moment'
;
import
'./userData.less'
import
'./userData.less'
;
const
{
Search
}
=
Input
;
const
{
Option
}
=
Select
;
declare
var
window
:
any
;
interface
sortType
{
type
:
"ascend"
|
"descend"
|
null
|
undefined
type
:
'ascend'
|
'descend'
|
null
|
undefined
;
}
interface
User
{
key
:
number
;
name
:
string
;
}
function
DataAnalysic
(
props
:
any
)
{
const
sortStatus
:
sortType
=
{
type
:
undefined
}
const
useDataInit
:
any
=
{};
const
queryInit
:
any
=
{
current
:
1
,
size
:
10
,
};
const
[
useData
,
setUserData
]
=
useState
(
useDataInit
);
const
[
list
,
setList
]
=
useState
([]);
const
[
query
,
setQuery
]
=
useState
(
queryInit
);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
field
,
setfield
]
=
useState
(
''
);
const
[
allData
,
setAllData
]
=
useState
(
0
);
const
[
order
,
setOrder
]
=
useState
(
sortStatus
.
type
);
const
userTypeEnum
=
{
WORK_WE_CHAT
:
'企业微信'
,
WE_CHAT
:
'微信'
}
const
userExamStateEnum
=
{
EXAM
:
'进行中'
,
LACK_EXAM
:
'缺考'
,
FINISH_EXAM
:
'已考试'
}
const
ExamPassColorEnum
=
{
EXAM_FAIL
:
'rgba(255, 79, 79, 1)'
,
EXAM_PASS
:
'rgba(59, 189, 170, 1)'
,
}
const
ExamPassEnum
=
{
EXAM_FAIL
:
'不及格'
,
EXAM_PASS
:
'及格'
,
}
const
userExamStateColorEnum
=
{
EXAM
:
'rgba(35, 143, 255, 1)'
,
LACK_EXAM
:
'rgba(204, 204, 204, 1)'
,
FINISH_EXAM
:
'rgba(47, 200, 60, 1)'
}
const
orderEnum
=
{
score
:
{
ascend
:
'EXAM_SCORE_ASC'
,
descend
:
'EXAM_SCORE_DESC'
},
userDuration
:
{
ascend
:
'USER_DURATION_ASC'
,
descend
:
'USER_DURATION_DESC'
},
}
const
queryRef
=
useRef
({});
useEffect
(()
=>
{
queryExamUserData
();
},
[])
useEffect
(()
=>
{
queryRef
.
current
=
query
;
queryExamUserDataList
();
},
[
query
])
function
queryExamUserData
()
{
Service
.
Hades
(
'public/hades/queryExamUserData'
,
{
examId
:
props
.
examId
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
}).
then
((
res
)
=>
{
setUserData
(
res
.
result
)
})
}
function
queryExamUserDataList
()
{
Service
.
Hades
(
'public/hades/queryExamUserDataList'
,
{
...
query
,
examId
:
props
.
examId
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
}).
then
((
res
)
=>
{
setList
(
res
.
result
.
records
);
setTotal
(
parseInt
(
res
.
result
.
total
))
if
(
!
allData
)
{
setAllData
(
parseInt
(
res
.
result
.
total
))
}
})
}
const
columns
=
[
{
title
:
"学员"
,
dataIndex
:
"userName"
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
text
}
<
span
style=
{
{
color
:
record
.
userSource
===
'WORK_WE_CHAT'
?
'rgba(255, 157, 20, 1)'
:
'rgba(29, 204, 101, 1)'
}
}
>
@
{
(
userTypeEnum
as
any
)[
record
.
userSource
]
}
</
span
></
span
>,
},
{
title
:
"手机号"
,
dataIndex
:
"phone"
,
},
{
title
:
"考试状态"
,
dataIndex
:
"userExamState"
,
render
:
(
text
:
any
)
=>
<
span
>
<
span
className=
'exstatus'
style=
{
{
background
:
(
userExamStateColorEnum
as
any
)[
text
]
}
}
></
span
>
{
(
userExamStateEnum
as
any
)[
text
]
}
</
span
>,
},
{
title
:
"考试成绩"
,
dataIndex
:
"score"
,
sorter
:
true
,
sortOrder
:
field
===
"score"
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
text
}
<
span
style=
{
{
border
:
`1px solid ${(ExamPassColorEnum as any)[record.examPass]}`
,
fontSize
:
12
,
color
:
(
ExamPassColorEnum
as
any
)[
record
.
examPass
],
display
:
'inline-block'
,
padding
:
'0px 2px'
}
}
>
{
(
ExamPassEnum
as
any
)[
record
.
examPass
]
}
</
span
></
span
>,
},
{
title
:
"进入考试时间"
,
dataIndex
:
"examStartTime"
,
render
:
(
text
:
any
)
=>
<
span
>
{
moment
(
text
).
format
(
"YYYY-MM-DD HH:mm"
)
}
</
span
>,
},
{
title
:
"考试用时"
,
dataIndex
:
"userDuration"
,
sorter
:
true
,
sortOrder
:
field
===
"userDuration"
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
record
.
userExamState
===
'FINISH_EXAM'
?
window
.
formatHourTime
(
text
):
'-'
}
</
span
>,
},
];
function
onChange
(
pagination
:
any
,
filters
:
any
,
sorter
:
any
,
extra
:
any
)
{
setfield
(
sorter
.
field
);
setOrder
(
sorter
.
order
)
console
.
log
(
sorter
.
field
,
sorter
.
order
,
(
orderEnum
as
any
)[
sorter
.
field
])
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
order
=
(
orderEnum
as
any
)[
sorter
.
field
][
sorter
.
order
]
setQuery
(
_query
)
}
function
download
()
{
Service
.
Hades
(
'public/hades/exportExamData'
,
{
// ...query,
examId
:
props
.
examId
,
exportDataType
:
'EXAM_USER_DATA'
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
}).
then
((
res
)
=>
{
const
dom
=
(
document
as
any
).
getElementById
(
"load-play-back-excel"
)
dom
.
setAttribute
(
'href'
,
res
.
result
);
dom
.
click
();
})
}
return
<
div
className=
"rr"
>
<
a
target=
"_blank"
download
id=
"load-play-back-excel"
style=
{
{
position
:
"absolute"
,
left
:
"-10000px"
}
}
>
111
</
a
>
<
div
className=
"dataPanal"
>
<
div
className=
"item"
>
<
div
className=
"num"
>
{
useData
.
joinCnt
||
0
}
</
div
>
<
div
className=
"percent"
></
div
>
<
div
className=
"subTitle"
>
参与人数
</
div
>
</
div
>
<
div
className=
"item"
>
<
div
className=
"num"
>
{
useData
.
finishCnt
||
0
}
</
div
>
<
div
className=
"percent"
>
占比
{
parseInt
(((
useData
.
finishCnt
||
0
)
/
(
useData
.
joinCnt
||
1
))
*
100
+
''
)
}
%
</
div
>
<
div
className=
"subTitle"
>
完成考试数 (人)
</
div
>
</
div
>
<
div
className=
"item"
>
<
div
className=
"num"
>
{
useData
.
passCnt
||
0
}
</
div
>
<
div
className=
"percent"
>
占比
{
parseInt
(((
useData
.
passCnt
||
0
)
/
(
useData
.
finishCnt
||
1
))
*
100
+
''
)
}
%
</
div
>
<
div
className=
"subTitle"
>
及格数 (人)
</
div
>
</
div
>
<
div
className=
"item"
>
<
div
className=
"num"
>
{
useData
.
averageScore
||
0
}
</
div
>
<
div
className=
"percent"
>
总分
{
props
.
examDetail
?.
examPaper
?.
totalScore
}
</
div
>
<
div
className=
"subTitle"
>
平均分
</
div
>
</
div
>
<
div
className=
"item"
>
<
div
className=
"num"
>
{
window
.
formatHourTime
(
useData
.
averageDuration
||
0
)
}
</
div
>
<
div
className=
"percent"
></
div
>
<
div
className=
"subTitle"
>
平均用时
</
div
>
</
div
>
const
sortStatus
:
sortType
=
{
type
:
undefined
,
};
const
useDataInit
:
any
=
{};
const
queryInit
:
any
=
{
current
:
1
,
size
:
10
};
const
[
useData
,
setUserData
]
=
useState
(
useDataInit
);
const
[
list
,
setList
]
=
useState
([]);
const
[
query
,
setQuery
]
=
useState
(
queryInit
);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
field
,
setfield
]
=
useState
(
''
);
const
[
allData
,
setAllData
]
=
useState
(
0
);
const
[
order
,
setOrder
]
=
useState
(
sortStatus
.
type
);
const
userTypeEnum
=
{
WORK_WE_CHAT
:
'企业微信'
,
WE_CHAT
:
'微信'
,
};
const
userExamStateEnum
=
{
EXAM
:
'进行中'
,
LACK_EXAM
:
'缺考'
,
FINISH_EXAM
:
'已考试'
,
};
const
ExamPassColorEnum
=
{
EXAM_FAIL
:
'rgba(255, 79, 79, 1)'
,
EXAM_PASS
:
'rgba(59, 189, 170, 1)'
,
};
const
ExamPassEnum
=
{
EXAM_FAIL
:
'不及格'
,
EXAM_PASS
:
'及格'
,
};
const
userExamStateColorEnum
=
{
EXAM
:
'rgba(35, 143, 255, 1)'
,
LACK_EXAM
:
'rgba(204, 204, 204, 1)'
,
FINISH_EXAM
:
'rgba(47, 200, 60, 1)'
,
};
const
orderEnum
=
{
score
:
{
ascend
:
'EXAM_SCORE_ASC'
,
descend
:
'EXAM_SCORE_DESC'
,
},
userDuration
:
{
ascend
:
'USER_DURATION_ASC'
,
descend
:
'USER_DURATION_DESC'
,
},
};
const
queryRef
=
useRef
({});
useEffect
(()
=>
{
queryExamUserData
();
},
[]);
useEffect
(()
=>
{
queryRef
.
current
=
query
;
queryExamUserDataList
();
getCustomerId
();
},
[
query
]);
function
queryExamUserData
()
{
Service
.
Hades
(
'public/hades/queryExamUserData'
,
{
examId
:
props
.
examId
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
,
}).
then
((
res
)
=>
{
setUserData
(
res
.
result
);
});
}
function
queryExamUserDataList
()
{
Service
.
Hades
(
'public/hades/queryExamUserDataList'
,
{
...
query
,
examId
:
props
.
examId
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
,
}).
then
((
res
)
=>
{
setList
(
res
.
result
.
records
);
setTotal
(
parseInt
(
res
.
result
.
total
));
if
(
!
allData
)
{
setAllData
(
parseInt
(
res
.
result
.
total
));
}
});
}
const
columns
:
ColumnsType
<
User
>
=
[
{
title
:
'学员'
,
dataIndex
:
'userName'
,
render
:
(
text
:
any
,
record
:
any
)
=>
(
<
span
>
{
text
}
<
span
style=
{
{
color
:
record
.
userSource
===
'WORK_WE_CHAT'
?
'rgba(255, 157, 20, 1)'
:
'rgba(29, 204, 101, 1)'
}
}
>
@
{
(
userTypeEnum
as
any
)[
record
.
userSource
]
}
</
span
>
</
span
>
),
},
{
title
:
'手机号'
,
dataIndex
:
'phone'
,
},
{
title
:
'考试状态'
,
dataIndex
:
'userExamState'
,
render
:
(
text
:
any
)
=>
(
<
span
>
{
' '
}
<
span
className=
'exstatus'
style=
{
{
background
:
(
userExamStateColorEnum
as
any
)[
text
]
}
}
></
span
>
{
(
userExamStateEnum
as
any
)[
text
]
}
</
span
>
),
},
{
title
:
'考试成绩'
,
dataIndex
:
'score'
,
sorter
:
true
,
sortOrder
:
field
===
'score'
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
(
<
span
>
{
' '
}
{
text
}{
' '
}
<
span
style=
{
{
border
:
`1px solid ${(ExamPassColorEnum as any)[record.examPass]}`
,
fontSize
:
12
,
color
:
(
ExamPassColorEnum
as
any
)[
record
.
examPass
],
display
:
'inline-block'
,
padding
:
'0px 2px'
,
}
}
>
{
(
ExamPassEnum
as
any
)[
record
.
examPass
]
}
</
span
>
</
span
>
),
},
{
title
:
'进入考试时间'
,
dataIndex
:
'examStartTime'
,
render
:
(
text
:
any
)
=>
<
span
>
{
moment
(
text
).
format
(
'YYYY-MM-DD HH:mm'
)
}
</
span
>,
},
{
title
:
'考试用时'
,
dataIndex
:
'userDuration'
,
sorter
:
true
,
sortOrder
:
field
===
'userDuration'
?
order
:
sortStatus
.
type
,
render
:
(
text
:
any
,
record
:
any
)
=>
<
span
>
{
record
.
userExamState
===
'FINISH_EXAM'
?
window
.
formatHourTime
(
text
)
:
'-'
}
</
span
>,
},
//TODO:
{
title
:
'操作'
,
key
:
''
,
dataIndex
:
'edit'
,
width
:
'30%'
,
align
:
'right'
,
render
:
(
value
:
any
,
record
:
any
)
=>
{
return
(
<
Choose
>
<
When
condition=
{
record
.
userExamState
===
'FINISH_EXAM'
}
>
<
div
className=
'answer-detail'
onClick=
{
()
=>
{
checkAnswerDetail
(
record
);
}
}
>
答题详情
</
div
>
</
When
>
<
Otherwise
>
-
</
Otherwise
>
</
Choose
>
);
},
},
];
function
onChange
(
pagination
:
any
,
filters
:
any
,
sorter
:
any
,
extra
:
any
)
{
setfield
(
sorter
.
field
);
setOrder
(
sorter
.
order
);
console
.
log
(
sorter
.
field
,
sorter
.
order
,
(
orderEnum
as
any
)[
sorter
.
field
]);
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
order
=
(
orderEnum
as
any
)[
sorter
.
field
][
sorter
.
order
];
setQuery
(
_query
);
}
function
download
()
{
Service
.
Hades
(
'public/hades/exportExamData'
,
{
// ...query,
examId
:
props
.
examId
,
exportDataType
:
'EXAM_USER_DATA'
,
tenantId
:
User
.
getStoreId
(),
userId
:
User
.
getStoreUserId
(),
source
:
0
,
}).
then
((
res
)
=>
{
const
dom
=
(
document
as
any
).
getElementById
(
'load-play-back-excel'
);
dom
.
setAttribute
(
'href'
,
res
.
result
);
dom
.
click
();
});
}
//查看答题详情
function
checkAnswerDetail
(
record
:
object
)
{
const
{
paperId
,
userExamState
}
=
props
.
examDetail
?.
examPaper
;
window
.
RCHistory
.
push
({
pathname
:
`/test-detail/
${
props
.
examId
}
?paperId=
${
paperId
}
&userExamState=
${
userExamState
}
`
,
});
// console.log(record);
}
//C端 的学员学院列表
function
getCustomerId
()
{
let
params
=
{
userId
:
User
.
getUserId
(),
};
Service
.
Hades
(
'public/customerHades/getStoreListCustomer'
,
params
).
then
((
res
)
=>
{
res
.
result
.
map
((
item
:
any
)
=>
{
if
(
User
.
getStoreId
()
===
item
.
storeId
)
{
User
.
setCustomerId
(
item
.
storeCustomerId
);
}
});
});
}
return
(
<
div
className=
'rr'
>
<
a
target=
'_blank'
download
id=
'load-play-back-excel'
style=
{
{
position
:
'absolute'
,
left
:
'-10000px'
}
}
>
111
</
a
>
<
div
className=
'dataPanal'
>
<
div
className=
'item'
>
<
div
className=
'num'
>
{
useData
.
joinCnt
||
0
}
</
div
>
<
div
className=
'percent'
></
div
>
<
div
className=
'subTitle'
>
参与人数
</
div
>
</
div
>
<
div
className=
"xm-search-filter"
style=
{
{
marginTop
:
12
}
}
>
<
div
style=
{
{
display
:
'flex'
}
}
>
<
div
className=
"search-condition"
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
学员:
</
span
>
<
Search
value=
{
query
.
examName
}
className=
'search-input'
placeholder=
"搜索学员名或手机号"
onChange=
{
(
e
)
=>
{
const
_query
=
{
...
query
}
_query
.
searchKey
=
e
.
target
.
value
setQuery
(
_query
);
}
}
onSearch=
{
()
=>
{
}
}
enterButton=
{
<
span
className=
"icon iconfont"
>

</
span
>
}
/>
</
div
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
学员类型:
</
span
>
<
Select
value=
{
query
.
userSource
}
placeholder=
"请选择学员类型"
onChange=
{
(
val
)
=>
{
const
_query
=
{
...
query
}
_query
.
userSource
=
val
setQuery
(
_query
);
}
}
className=
'search-input'
allowClear
>
{
Object
.
keys
(
userTypeEnum
).
map
((
key
:
any
)
=>
{
return
<
Option
value=
{
key
}
>
{
(
userTypeEnum
as
any
)[
key
]
}
</
Option
>
})
}
</
Select
>
</
div
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
考试状态:
</
span
>
<
Select
value=
{
query
.
userExamState
}
placeholder=
"请选择考试状态"
onChange=
{
(
val
)
=>
{
const
_query
=
{
...
query
}
_query
.
userExamState
=
val
setQuery
(
_query
);
}
}
className=
'search-input'
allowClear
>
{
Object
.
keys
(
userExamStateEnum
).
map
((
key
:
any
)
=>
{
return
<
Option
value=
{
key
}
>
{
(
userExamStateEnum
as
any
)[
key
]
}
</
Option
>
})
}
</
Select
>
</
div
>
</
div
>
<
div
className=
"reset-fold-area"
>
<
Tooltip
title=
"清空筛选"
><
span
className=
"resetBtn iconfont icon"
onClick=
{
()
=>
{
setfield
(
''
)
setQuery
({
current
:
1
,
size
:
10
});
}
}
>

</
span
></
Tooltip
>
</
div
>
</
div
>
<
div
className=
'item'
>
<
div
className=
'num'
>
{
useData
.
finishCnt
||
0
}
</
div
>
<
div
className=
'percent'
>
占比
{
parseInt
(((
useData
.
finishCnt
||
0
)
/
(
useData
.
joinCnt
||
1
))
*
100
+
''
)
}
%
</
div
>
<
div
className=
'subTitle'
>
完成考试数 (人)
</
div
>
</
div
>
{
!!
allData
&&
<
Button
style=
{
{
marginBottom
:
12
}
}
onClick=
{
download
}
>
导出
</
Button
>
}
<
div
className=
'item'
>
<
div
className=
'num'
>
{
useData
.
passCnt
||
0
}
</
div
>
<
div
className=
'percent'
>
占比
{
parseInt
(((
useData
.
passCnt
||
0
)
/
(
useData
.
finishCnt
||
1
))
*
100
+
''
)
}
%
</
div
>
<
div
className=
'subTitle'
>
及格数 (人)
</
div
>
</
div
>
<
div
className=
'item'
>
<
div
className=
'num'
>
{
useData
.
averageScore
||
0
}
</
div
>
<
div
className=
'percent'
>
总分
{
props
.
examDetail
?.
examPaper
?.
totalScore
}
</
div
>
<
div
className=
'subTitle'
>
平均分
</
div
>
</
div
>
<
div
className=
'item'
>
<
div
className=
'num'
>
{
window
.
formatHourTime
(
useData
.
averageDuration
||
0
)
}
</
div
>
<
div
className=
'percent'
></
div
>
<
div
className=
'subTitle'
>
平均用时
</
div
>
</
div
>
</
div
>
<
div
className=
'xm-search-filter'
style=
{
{
marginTop
:
12
}
}
>
<
div
style=
{
{
display
:
'flex'
}
}
>
<
div
className=
'search-condition'
>
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
学员:
</
span
>
<
Search
value=
{
query
.
examName
}
className=
'search-input'
placeholder=
'搜索学员名或手机号'
onChange=
{
(
e
)
=>
{
const
_query
=
{
...
query
};
_query
.
searchKey
=
e
.
target
.
value
;
setQuery
(
_query
);
}
}
onSearch=
{
()
=>
{}
}
enterButton=
{
<
span
className=
'icon iconfont'
>

</
span
>
}
/>
</
div
>
<
div
className=
"content"
>
<
Table
bordered
size=
"small"
columns=
{
columns
}
dataSource=
{
list
}
onChange=
{
onChange
}
pagination=
{
false
}
>
</
Table
>
{
total
>
0
&&
<
PageControl
size=
"small"
current=
{
query
.
current
-
1
}
pageSize=
{
query
.
size
}
total=
{
total
}
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
学员类型:
</
span
>
<
Select
value=
{
query
.
userSource
}
placeholder=
'请选择学员类型'
onChange=
{
(
val
)
=>
{
const
_query
=
{
...
query
};
_query
.
userSource
=
val
;
setQuery
(
_query
);
}
}
className=
'search-input'
allowClear
>
{
Object
.
keys
(
userTypeEnum
).
map
((
key
:
any
)
=>
{
return
(
<
Option
value=
{
key
}
key=
{
key
}
>
{
(
userTypeEnum
as
any
)[
key
]
}
</
Option
>
);
})
}
</
Select
>
</
div
>
toPage=
{
(
page
:
any
)
=>
{
console
.
log
(
page
)
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
current
=
page
+
1
;
setQuery
(
_query
)
}
}
/>
}
<
div
className=
'search-condition__item'
>
<
span
className=
'search-name'
>
考试状态:
</
span
>
<
Select
value=
{
query
.
userExamState
}
placeholder=
'请选择考试状态'
onChange=
{
(
val
)
=>
{
const
_query
=
{
...
query
};
_query
.
userExamState
=
val
;
setQuery
(
_query
);
}
}
className=
'search-input'
allowClear
>
{
Object
.
keys
(
userExamStateEnum
).
map
((
key
:
any
)
=>
{
return
(
<
Option
value=
{
key
}
key=
{
key
}
>
{
(
userExamStateEnum
as
any
)[
key
]
}
</
Option
>
);
})
}
</
Select
>
</
div
>
</
div
>
<
div
className=
'reset-fold-area'
>
<
Tooltip
title=
'清空筛选'
>
<
span
className=
'resetBtn iconfont icon'
onClick=
{
()
=>
{
setfield
(
''
);
setQuery
({
current
:
1
,
size
:
10
});
}
}
>

{
' '
}
</
span
>
</
Tooltip
>
</
div
>
</
div
>
</
div
>
{
!!
allData
&&
(
<
Button
style=
{
{
marginBottom
:
12
}
}
onClick=
{
download
}
>
导出
</
Button
>
)
}
<
div
className=
'content'
>
<
Table
bordered
size=
'small'
columns=
{
columns
}
dataSource=
{
list
}
onChange=
{
onChange
}
pagination=
{
false
}
></
Table
>
{
total
>
0
&&
(
<
PageControl
size=
'small'
current=
{
query
.
current
-
1
}
pageSize=
{
query
.
size
}
total=
{
total
}
toPage=
{
(
page
:
any
)
=>
{
console
.
log
(
page
);
let
_query
:
any
=
{
...
queryRef
.
current
};
_query
.
current
=
page
+
1
;
setQuery
(
_query
);
}
}
/>
)
}
</
div
>
</
div
>
);
}
export
default
DataAnalysic
;
\ No newline at end of file
export
default
DataAnalysic
;
src/modules/teach-tool/examination-manager/userData.less
View file @
4a8eb900
.dataPanal{
border-radius: 4px;
border: 1px solid #E8E8E8;
display: flex;
.item{
text-align: center;
// width: 29.9%;
.dataPanal {
border-radius: 4px;
border: 1px solid #e8e8e8;
display: flex;
.item {
text-align: center;
// width: 29.9%;
position: relative;
flex: 1;
.num {
font-size: 26px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 26px;
margin-top: 12px;
}
.percent {
margin-top: 6px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 17px;
height: 20px;
margin-bottom: 18px;
}
.subTitle {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 12px;
}
.type {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
span {
color: rgba(153, 153, 153, 1);
}
.icon {
color: rgba(204, 204, 204, 1);
font-size: 16px;
margin-right: 4px;
position: relative;
flex: 1;
.num{
font-size: 26px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 26px;
margin-top: 12px;
}
.percent{
margin-top: 6px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 17px;
height: 20px;
margin-bottom: 18px;
}
.subTitle{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 12px;
}
.type{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
span{
color: rgba(153, 153, 153, 1);
}
.icon{
color: rgba(204, 204, 204, 1);
font-size: 16px;
margin-right: 4px;
position: relative;
top: 1px;
}
}
&:after{
content: '';
width: 0px;
height: 40px;
position: absolute;
width: 1px;
background-color: rgba(232, 232, 232, 1);
top: 40px;
right: 0px;
}
&:last-child{
&:after{
display: none;
}
}
top: 1px;
}
}
&:after {
content: '';
width: 0px;
height: 40px;
position: absolute;
width: 1px;
background-color: rgba(232, 232, 232, 1);
top: 40px;
right: 0px;
}
.exstatus{
width: 4px;
height: 4px;
background: rgb(35, 143, 255);
display: inline-block;
border-radius: 50%;
position: relative;
top: -4px;
&:last-child {
&:after {
display: none;
}
}
}
\ No newline at end of file
}
.exstatus {
width: 4px;
height: 4px;
background: rgb(35, 143, 255);
display: inline-block;
border-radius: 50%;
position: relative;
top: -4px;
}
}
.answer-detail {
color: rgb(35, 143, 255);
}
src/modules/teach-tool/question-manage/components/QuestionList.jsx
View file @
4a8eb900
...
...
@@ -6,61 +6,49 @@
* @Description: 助学工具-题库-题目列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import
React
,
{
Component
}
from
"react"
;
import
{
Table
,
ConfigProvider
,
Empty
,
Row
,
Input
,
Select
,
Tooltip
,
Space
,
Button
,
Modal
,
message
,
}
from
"antd"
;
import
{
PageControl
}
from
"@/components"
;
import
"./QuestionList.less"
;
import
User
from
"@/common/js/user"
;
import
AidToolService
from
"@/domains/aid-tool-domain/AidToolService"
;
import
_
from
"underscore"
;
import
PreviewQuestionModal
from
"../modal/PreviewQuestionModal"
;
import
BatchImportQuestionModal
from
"../modal/BatchImportQuestionModal"
;
import
{
Route
,
withRouter
}
from
"react-router-dom"
;
import
OperateQuestion
from
"../OperateQuestion"
;
import
Bus
from
"@/core/bus"
;
import
React
,
{
Component
}
from
'react'
;
import
{
Table
,
ConfigProvider
,
Empty
,
Row
,
Input
,
Select
,
Tooltip
,
Space
,
Button
,
Modal
,
message
}
from
'antd'
;
import
{
PageControl
}
from
'@/components'
;
import
'./QuestionList.less'
;
import
User
from
'@/common/js/user'
;
import
AidToolService
from
'@/domains/aid-tool-domain/AidToolService'
;
import
_
from
'underscore'
;
import
PreviewQuestionModal
from
'../modal/PreviewQuestionModal'
;
import
BatchImportQuestionModal
from
'../modal/BatchImportQuestionModal'
;
import
{
Route
,
withRouter
}
from
'react-router-dom'
;
import
OperateQuestion
from
'../OperateQuestion'
;
import
Bus
from
'@/core/bus'
;
const
{
Search
}
=
Input
;
const
questionTypeEnum
=
{
SINGLE_CHOICE
:
"单选题"
,
MULTI_CHOICE
:
"多选题"
,
JUDGE
:
"判断题"
,
GAP_FILLING
:
"填空题"
,
INDEFINITE_CHOICE
:
"不定项选择题"
,
SINGLE_CHOICE
:
'单选题'
,
MULTI_CHOICE
:
'多选题'
,
JUDGE
:
'判断题'
,
GAP_FILLING
:
'填空题'
,
INDEFINITE_CHOICE
:
'不定项选择题'
,
};
const
questionTypeList
=
[
{
label
:
"单选题"
,
value
:
"SINGLE_CHOICE"
,
label
:
'单选题'
,
value
:
'SINGLE_CHOICE'
,
},
{
label
:
"多选题"
,
value
:
"MULTI_CHOICE"
,
label
:
'多选题'
,
value
:
'MULTI_CHOICE'
,
},
{
label
:
"判断题"
,
value
:
"JUDGE"
,
label
:
'判断题'
,
value
:
'JUDGE'
,
},
{
label
:
"填空题"
,
value
:
"GAP_FILLING"
,
label
:
'填空题'
,
value
:
'GAP_FILLING'
,
},
{
label
:
"不定项选择题"
,
value
:
"INDEFINITE_CHOICE"
,
label
:
'不定项选择题'
,
value
:
'INDEFINITE_CHOICE'
,
},
];
...
...
@@ -71,7 +59,7 @@ class QuestionList extends Component {
query
:
{
current
:
1
,
size
:
10
,
order
:
"UPDATED_DESC"
,
// 排序规则[ ACCURACY_DESC, ACCURACY_ASC, CREATED_DESC, CREATED_ASC, UPDATED_DESC, UPDATED_ASC ]
order
:
'UPDATED_DESC'
,
// 排序规则[ ACCURACY_DESC, ACCURACY_ASC, CREATED_DESC, CREATED_ASC, UPDATED_DESC, UPDATED_ASC ]
categoryId
:
null
,
// 当前题库分类Id
questionName
:
null
,
// 题目名称
questionType
:
null
,
// 题目类型
...
...
@@ -86,15 +74,14 @@ class QuestionList extends Component {
}
componentDidMount
()
{
this
.
queryQuestionPageList
();
Bus
.
bind
(
"queryQuestionPageList"
,
(
selectedCategoryId
)
=>
{
selectedCategoryId
=
selectedCategoryId
===
"null"
?
null
:
selectedCategoryId
;
Bus
.
bind
(
'queryQuestionPageList'
,
(
selectedCategoryId
)
=>
{
selectedCategoryId
=
selectedCategoryId
===
'null'
?
null
:
selectedCategoryId
;
this
.
InitSearch
(
selectedCategoryId
);
});
}
componentWillUnmount
()
{
Bus
.
unbind
(
"queryQuestionPageList"
,
this
.
queryQuestionPageList
);
Bus
.
unbind
(
'queryQuestionPageList'
,
this
.
queryQuestionPageList
);
}
// 初始化列表查询
...
...
@@ -103,7 +90,7 @@ class QuestionList extends Component {
...
this
.
state
.
query
,
categoryId
,
current
:
1
,
order
:
"UPDATED_DESC"
,
// 排序规则
order
:
'UPDATED_DESC'
,
// 排序规则
questionName
:
null
,
// 题目名称
questionType
:
null
,
// 题目类型
};
...
...
@@ -123,7 +110,7 @@ class QuestionList extends Component {
},
},
()
=>
{
if
(
searchType
===
"questionName"
)
return
;
if
(
searchType
===
'questionName'
)
return
;
this
.
queryQuestionPageList
();
}
);
...
...
@@ -134,7 +121,7 @@ class QuestionList extends Component {
const
_query
=
{
...
this
.
state
.
query
,
current
:
1
,
order
:
"UPDATED_DESC"
,
// 排序规则
order
:
'UPDATED_DESC'
,
// 排序规则
questionName
:
null
,
// 题目名称
questionType
:
null
,
// 题目类型
};
...
...
@@ -156,31 +143,28 @@ class QuestionList extends Component {
const
{
categoryId
}
=
this
.
state
.
query
;
return
(
<
Empty
image=
"https://image.xiaomaiketang.com/xm/emptyTable.png"
image=
'https://image.xiaomaiketang.com/xm/emptyTable.png'
imageStyle=
{
{
height
:
100
,
}
}
description=
{
<
span
>
<
span
>
还没有题目
</
span
>
{
[
"CloudManager"
,
"StoreManager"
].
includes
(
User
.
getUserRole
())
&&
categoryId
&&
(
<
span
>
,快去
<
span
className=
"empty-list-tip"
onClick=
{
()
=>
{
this
.
handleCreateQuestion
();
}
}
>
新建一个
</
span
>
吧!
{
[
'CloudManager'
,
'StoreManager'
].
includes
(
User
.
getUserRole
())
&&
categoryId
&&
(
<
span
>
,快去
<
span
className=
'empty-list-tip'
onClick=
{
()
=>
{
this
.
handleCreateQuestion
();
}
}
>
新建一个
</
span
>
)
}
吧!
</
span
>
)
}
</
span
>
}
></
Empty
>
}
></
Empty
>
);
};
...
...
@@ -188,59 +172,56 @@ class QuestionList extends Component {
handleChangeTable
=
(
pagination
,
filters
,
sorter
)
=>
{
const
{
columnKey
,
order
}
=
sorter
;
let
sort
=
null
;
if
(
columnKey
===
"accuracy"
&&
order
===
"ascend"
)
{
sort
=
"ACCURACY_ASC"
;
if
(
columnKey
===
'accuracy'
&&
order
===
'ascend'
)
{
sort
=
'ACCURACY_ASC'
;
}
if
(
columnKey
===
"accuracy"
&&
order
===
"descend"
)
{
sort
=
"ACCURACY_DESC"
;
if
(
columnKey
===
'accuracy'
&&
order
===
'descend'
)
{
sort
=
'ACCURACY_DESC'
;
}
if
(
columnKey
===
"updateTime"
&&
order
===
"ascend"
)
{
sort
=
"UPDATED_ASC"
;
if
(
columnKey
===
'updateTime'
&&
order
===
'ascend'
)
{
sort
=
'UPDATED_ASC'
;
}
if
(
columnKey
===
"updateTime"
&&
order
===
"descend"
)
{
sort
=
"UPDATED_DESC"
;
if
(
columnKey
===
'updateTime'
&&
order
===
'descend'
)
{
sort
=
'UPDATED_DESC'
;
}
const
_query
=
this
.
state
.
query
;
_query
.
order
=
sort
||
"UPDATED_DESC"
;
_query
.
order
=
sort
||
'UPDATED_DESC'
;
this
.
setState
({
query
:
_query
},
()
=>
this
.
queryQuestionPageList
());
};
// 表头设置
parseColumns
=
()
=>
{
// 权限判断
const
isPermiss
=
[
"CloudManager"
,
"StoreManager"
].
includes
(
User
.
getUserRole
()
);
const
isPermiss
=
[
'CloudManager'
,
'StoreManager'
].
includes
(
User
.
getUserRole
());
const
columns
=
[
{
title
:
"题目"
,
key
:
"questionStem"
,
dataIndex
:
"questionStem"
,
title
:
'题目'
,
key
:
'questionStem'
,
dataIndex
:
'questionStem'
,
ellipsis
:
{
showTitle
:
false
,
},
render
:
(
val
)
=>
{
var
handleVal
=
val
;
handleVal
=
handleVal
.
replace
(
/<
(?!
img|input
)
.*
?
>/g
,
""
);
handleVal
=
handleVal
.
replace
(
/<
\s?
input
[^
>
]
*>/gi
,
"_、"
);
handleVal
=
handleVal
.
replace
(
/
\&
nbsp
\;
/gi
,
" "
);
handleVal
=
handleVal
.
replace
(
/style
\s
*
?
=
\s
*
?([
‘"
])[\s\S]
*
?\1
/gi
,
""
);
handleVal
=
handleVal
.
replace
(
/<
(?!
img|input
)
.*
?
>/g
,
''
);
handleVal
=
handleVal
.
replace
(
/<
\s?
input
[^
>
]
*>/gi
,
'_、'
);
handleVal
=
handleVal
.
replace
(
/
\&
nbsp
\;
/gi
,
' '
);
handleVal
=
handleVal
.
replace
(
/style
\s
*
?
=
\s
*
?([
‘"
])[\s\S]
*
?\1
/gi
,
''
);
return
(
<
Tooltip
overlayClassName=
"aid-tool-list"
overlayClassName=
'aid-tool-list'
title=
{
<
div
style=
{
{
maxWidth
:
700
,
width
:
"auto"
}
}
style=
{
{
maxWidth
:
700
,
width
:
'auto'
}
}
dangerouslySetInnerHTML=
{
{
__html
:
handleVal
,
}
}
/>
}
placement=
"topLeft"
overlayStyle=
{
{
maxWidth
:
700
}
}
>
placement=
'topLeft'
overlayStyle=
{
{
maxWidth
:
700
}
}
>
<
div
className=
"one-line-text"
className=
'one-line-text'
dangerouslySetInnerHTML=
{
{
__html
:
handleVal
,
}
}
...
...
@@ -250,71 +231,56 @@ class QuestionList extends Component {
},
},
{
title
:
"题型"
,
key
:
"questionTypeEnum"
,
dataIndex
:
"questionTypeEnum"
,
width
:
"16%"
,
title
:
'题型'
,
key
:
'questionTypeEnum'
,
dataIndex
:
'questionTypeEnum'
,
width
:
'16%'
,
render
:
(
val
)
=>
{
return
questionTypeEnum
[
val
];
},
},
{
title
:
"正确率"
,
key
:
"accuracy"
,
dataIndex
:
"accuracy"
,
title
:
'正确率'
,
key
:
'accuracy'
,
dataIndex
:
'accuracy'
,
sorter
:
true
,
showSorterTooltip
:
false
,
width
:
"14%"
,
width
:
'14%'
,
render
:
(
val
)
=>
{
return
parseInt
(
val
*
100
)
+
"%"
;
return
parseInt
(
val
*
100
)
+
'%'
;
},
},
{
title
:
"更新时间"
,
key
:
"updateTime"
,
dataIndex
:
"updateTime"
,
title
:
'更新时间'
,
key
:
'updateTime'
,
dataIndex
:
'updateTime'
,
sorter
:
true
,
showSorterTooltip
:
false
,
width
:
"24%"
,
width
:
'24%'
,
render
:
(
val
)
=>
{
return
formatDate
(
"YYYY-MM-DD H:i:s"
,
val
);
return
formatDate
(
'YYYY-MM-DD H:i:s'
,
val
);
},
},
{
title
:
"操作"
,
key
:
"operate"
,
dataIndex
:
"operate"
,
width
:
"24%"
,
title
:
'操作'
,
key
:
'operate'
,
dataIndex
:
'operate'
,
width
:
'24%'
,
render
:
(
val
,
record
)
=>
{
return
(
<
div
className=
"record-operate"
>
<
div
className=
"record-operate__item"
onClick=
{
()
=>
this
.
previewQuestion
(
record
.
id
)
}
>
<
div
className=
'record-operate'
>
<
div
className=
'record-operate__item'
onClick=
{
()
=>
this
.
previewQuestion
(
record
.
id
)
}
>
预览
</
div
>
{
isPermiss
&&
<
span
className=
'record-operate__item split'
>
|
</
span
>
}
{
isPermiss
&&
(
<
span
className=
"record-operate__item split"
>
|
</
span
>
)
}
{
isPermiss
&&
(
<
div
className=
"record-operate__item"
onClick=
{
()
=>
this
.
editQuestion
(
record
.
id
,
record
.
questionTypeEnum
)
}
>
<
div
className=
'record-operate__item'
onClick=
{
()
=>
this
.
editQuestion
(
record
.
id
,
record
.
questionTypeEnum
)
}
>
编辑
</
div
>
)
}
{
isPermiss
&&
<
span
className=
'record-operate__item split'
>
|
</
span
>
}
{
isPermiss
&&
(
<
span
className=
"record-operate__item split"
>
|
</
span
>
)
}
{
isPermiss
&&
(
<
div
className=
"record-operate__item"
onClick=
{
()
=>
this
.
delQuestionConfirm
(
record
)
}
>
<
div
className=
'record-operate__item'
onClick=
{
()
=>
this
.
delQuestionConfirm
(
record
)
}
>
删除
</
div
>
)
}
...
...
@@ -363,13 +329,11 @@ class QuestionList extends Component {
// 删除题目确认弹窗
delQuestionConfirm
(
record
)
{
return
Modal
.
confirm
({
title
:
"提示"
,
content
:
"确定要删除此题目吗?"
,
icon
:
(
<
span
className=
"icon iconfont default-confirm-icon"
>

</
span
>
),
okText
:
"删除"
,
cancelText
:
"取消"
,
title
:
'提示'
,
content
:
'确定要删除此题目吗?'
,
icon
:
<
span
className=
'icon iconfont default-confirm-icon'
>

</
span
>,
okText
:
'删除'
,
cancelText
:
'取消'
,
onOk
:
()
=>
{
this
.
deleteQuestion
(
record
);
},
...
...
@@ -386,7 +350,7 @@ class QuestionList extends Component {
};
AidToolService
.
deleteQuestion
(
params
).
then
((
res
)
=>
{
if
(
res
.
success
)
{
message
.
success
(
"删除成功"
);
message
.
success
(
'删除成功'
);
const
{
query
,
total
}
=
this
.
state
;
const
{
size
,
current
}
=
query
;
const
_query
=
query
;
...
...
@@ -397,7 +361,7 @@ class QuestionList extends Component {
}
this
.
setState
({
query
:
_query
},
()
=>
{
this
.
queryQuestionPageList
();
Bus
.
trigger
(
"queryCategoryTree"
,
"remain"
);
Bus
.
trigger
(
'queryCategoryTree'
,
'remain'
);
});
}
});
...
...
@@ -419,7 +383,7 @@ class QuestionList extends Component {
close=
{
()
=>
{
this
.
setState
({
batchImportQuestionModal
:
null
},
()
=>
{
this
.
queryQuestionPageList
();
Bus
.
trigger
(
"queryCategoryTree"
,
"remain"
);
Bus
.
trigger
(
'queryCategoryTree'
,
'remain'
);
});
}
}
categoryId=
{
categoryId
}
...
...
@@ -429,60 +393,50 @@ class QuestionList extends Component {
};
render
()
{
const
{
dataSource
=
[],
total
,
query
,
previewQuestionModal
,
batchImportQuestionModal
,
}
=
this
.
state
;
const
{
dataSource
=
[],
total
,
query
,
previewQuestionModal
,
batchImportQuestionModal
}
=
this
.
state
;
const
{
current
,
size
,
categoryId
,
questionName
,
questionType
}
=
query
;
const
{
match
}
=
this
.
props
;
return
(
<
div
className=
"question-list"
>
<
div
className=
"question-list-filter"
>
<
Row
type=
"flex"
justify=
"space-between"
align=
"top"
>
<
div
className=
"search-condition"
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-label"
>
题目:
</
span
>
<
div
className=
'question-list'
>
<
div
className=
'question-list-filter'
>
<
Row
type=
'flex'
justify=
'space-between'
align=
'top'
>
<
div
className=
'search-condition'
>
<
div
className=
'search-condition__item'
>
<
span
className=
'search-label'
>
题目:
</
span
>
<
Search
placeholder=
"搜索题目名称"
placeholder=
'搜索题目名称'
value=
{
questionName
}
style=
{
{
width
:
178
}
}
onChange=
{
(
e
)
=>
{
this
.
handleChangeQuery
(
"questionName"
,
e
.
target
.
value
);
this
.
handleChangeQuery
(
'questionName'
,
e
.
target
.
value
);
}
}
onSearch=
{
()
=>
{
this
.
queryQuestionPageList
();
}
}
enterButton=
{
<
span
className=
"icon iconfont"
>

</
span
>
}
enterButton=
{
<
span
className=
'icon iconfont'
>

</
span
>
}
/>
</
div
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-label"
>
题型:
</
span
>
<
div
className=
'search-condition__item'
>
<
span
className=
'search-label'
>
题型:
</
span
>
<
Select
placeholder=
"请选择题目类型"
placeholder=
'请选择题目类型'
value=
{
questionType
}
style=
{
{
width
:
178
}
}
showSearch
allowClear
enterButton=
{
<
span
className=
"icon iconfont"
>

</
span
>
}
filterOption=
{
(
inputVal
,
option
)
=>
option
.
props
.
children
.
includes
(
inputVal
)
}
filterOption=
{
(
inputVal
,
option
)
=>
option
.
props
.
children
.
includes
(
inputVal
)
}
onChange=
{
(
value
)
=>
{
if
(
_
.
isEmpty
(
value
))
{
this
.
handleChangeQuery
(
"questionType"
,
value
);
this
.
handleChangeQuery
(
'questionType'
,
value
);
}
}
}
onSelect=
{
(
value
)
=>
{
this
.
handleChangeQuery
(
"questionType"
,
value
);
}
}
>
this
.
handleChangeQuery
(
'questionType'
,
value
);
}
}
>
{
_
.
map
(
questionTypeList
,
(
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
value
}
key=
{
item
.
key
}
>
<
Select
.
Option
value=
{
item
.
value
}
key=
{
item
.
value
}
>
{
item
.
label
}
</
Select
.
Option
>
);
...
...
@@ -491,28 +445,24 @@ class QuestionList extends Component {
</
div
>
</
div
>
<
div
className=
"reset-fold-area"
>
<
Tooltip
title=
"清空筛选"
>
<
span
className=
"resetBtn iconfont icon"
onClick=
{
this
.
handleReset
}
>

{
" "
}
<
div
className=
'reset-fold-area'
>
<
Tooltip
title=
'清空筛选'
>
<
span
className=
'resetBtn iconfont icon'
onClick=
{
this
.
handleReset
}
>

{
' '
}
</
span
>
</
Tooltip
>
</
div
>
</
Row
>
</
div
>
{
[
"CloudManager"
,
"StoreManager"
].
includes
(
User
.
getUserRole
())
&&
categoryId
&&
(
<
Space
size=
{
16
}
>
<
Button
type=
"primary"
onClick=
{
this
.
handleCreateQuestion
}
>
新建题目
</
Button
>
<
Button
onClick=
{
this
.
batchImportQuestion
}
>
批量导入
</
Button
>
</
Space
>
)
}
<
div
className=
"question-list-content"
>
{
[
'CloudManager'
,
'StoreManager'
].
includes
(
User
.
getUserRole
())
&&
categoryId
&&
(
<
Space
size=
{
16
}
>
<
Button
type=
'primary'
onClick=
{
this
.
handleCreateQuestion
}
>
新建题目
</
Button
>
<
Button
onClick=
{
this
.
batchImportQuestion
}
>
批量导入
</
Button
>
</
Space
>
)
}
<
div
className=
'question-list-content'
>
<
ConfigProvider
renderEmpty=
{
this
.
customizeRenderEmpty
}
>
<
Table
rowKey=
{
(
record
)
=>
record
.
id
}
...
...
@@ -524,16 +474,14 @@ class QuestionList extends Component {
/>
</
ConfigProvider
>
{
total
>
0
&&
(
<
div
className=
"box-footer"
>
<
div
className=
'box-footer'
>
<
PageControl
current=
{
current
-
1
}
pageSize=
{
size
}
total=
{
total
}
toPage=
{
(
page
)
=>
{
const
_query
=
{
...
query
,
current
:
page
+
1
};
this
.
setState
({
query
:
_query
},
()
=>
this
.
queryQuestionPageList
()
);
this
.
setState
({
query
:
_query
},
()
=>
this
.
queryQuestionPageList
());
}
}
showSizeChanger=
{
true
}
onShowSizeChange=
{
this
.
onShowSizeChange
}
...
...
@@ -543,10 +491,7 @@ class QuestionList extends Component {
{
previewQuestionModal
}
{
batchImportQuestionModal
}
</
div
>
<
Route
path=
{
`${match.url}/question-operate-page`
}
component=
{
OperateQuestion
}
/>
<
Route
path=
{
`${match.url}/question-operate-page`
}
component=
{
OperateQuestion
}
/>
</
div
>
);
}
...
...
src/routes/config/mainRoutes.tsx
View file @
4a8eb900
...
...
@@ -14,13 +14,13 @@ import UserManage from '@/modules/college-manage/UserManagePage';
import
StoreDecorationPage
from
'@/modules/store-manage/StoreDecorationPage'
;
import
CourseCatalogPage
from
'@/modules/store-manage/CourseCatalogPage'
;
import
LiveCoursePage
from
'@/modules/course-manage/LiveCoursePage'
;
import
AddLivePage
from
'@/modules/course-manage/AddLive'
import
VideoCoursePage
from
'@/modules/course-manage/video-course'
import
GraphicsCoursePage
from
'@/modules/course-manage/graphics-course'
import
OfflineCoursePage
from
'@/modules/course-manage/offline-course'
import
AddVideoCoursePage
from
'@/modules/course-manage/video-course/AddVideoCourse'
import
AddGraphicsCoursePage
from
'@/modules/course-manage/graphics-course/AddGraphicsCourse'
import
AddOfflineCoursePage
from
'@/modules/course-manage/offline-course/AddOfflineCourse'
import
AddLivePage
from
'@/modules/course-manage/AddLive'
;
import
VideoCoursePage
from
'@/modules/course-manage/video-course'
;
import
GraphicsCoursePage
from
'@/modules/course-manage/graphics-course'
;
import
OfflineCoursePage
from
'@/modules/course-manage/offline-course'
;
import
AddVideoCoursePage
from
'@/modules/course-manage/video-course/AddVideoCourse'
;
import
AddGraphicsCoursePage
from
'@/modules/course-manage/graphics-course/AddGraphicsCourse'
;
import
AddOfflineCoursePage
from
'@/modules/course-manage/offline-course/AddOfflineCourse'
;
// import DataList from '@/modules/course-manage/DataList/DataList';
// import ClassBook from '@/modules/resource-disk';
import
ResourceDisk
from
'@/modules/resource-disk'
;
...
...
@@ -33,145 +33,152 @@ import CourseCategoryManage from '@/modules/teach-tool/components/CourseCategory
import
QuestionManageIndex
from
'@/modules/teach-tool/question-manage/Index'
;
import
PaperManageIndex
from
'@/modules/teach-tool/paper-manage/Index'
;
import
ExaminationManagerIndex
from
'@/modules/teach-tool/examination-manager/Index'
;
import
KnowledgeBase
from
"@/modules/knowledge-base/index"
;
import
ExaminationManagerTestDetail
from
'@/modules/teach-tool/examination-manager/TestDetailPage'
;
import
KnowledgeBase
from
'@/modules/knowledge-base/index'
;
import
CollegeInfoPage
from
'@/modules/college-manage/CollegeInfoPage'
;
const
mainRoutes
=
[
{
path
:
"/home"
,
path
:
'/home'
,
component
:
Home
,
name
:
"中心首页"
,
name
:
'中心首页'
,
},
{
path
:
"/employees-manage"
,
path
:
'/employees-manage'
,
component
:
EmployeesManagePage
,
name
:
"员工管理"
,
name
:
'员工管理'
,
},
{
path
:
'/college-employee'
,
component
:
EmployeeManage
,
name
:
'员工管理'
component
:
EmployeeManage
,
name
:
'员工管理'
,
},
{
path
:
'/personal-info'
,
component
:
personalInfoPage
,
name
:
'个人信息'
component
:
personalInfoPage
,
name
:
'个人信息'
,
},
{
path
:
"/user-manage"
,
path
:
'/user-manage'
,
component
:
UserManagePage
,
name
:
"学员管理"
,
name
:
'学员管理'
,
},
{
path
:
'/college-user'
,
component
:
UserManage
,
name
:
'学员管理'
component
:
UserManage
,
name
:
'学员管理'
,
},
{
path
:
'/store-decoration'
,
component
:
StoreDecorationPage
,
name
:
'学院装修'
component
:
StoreDecorationPage
,
name
:
'学院装修'
,
},
{
path
:
"/live-course"
,
path
:
'/live-course'
,
component
:
LiveCoursePage
,
name
:
"直播课"
,
name
:
'直播课'
,
},
{
path
:
"/video-course"
,
path
:
'/video-course'
,
component
:
VideoCoursePage
,
name
:
"视频课"
,
name
:
'视频课'
,
},
{
path
:
"/graphics-course"
,
path
:
'/graphics-course'
,
component
:
GraphicsCoursePage
,
name
:
"图文课"
,
name
:
'图文课'
,
},
{
path
:
"/offline-course"
,
path
:
'/offline-course'
,
component
:
OfflineCoursePage
,
name
:
"线下课"
,
name
:
'线下课'
,
},
{
path
:
"/create-live-course"
,
path
:
'/create-live-course'
,
component
:
AddLivePage
,
name
:
"创建直播课"
,
name
:
'创建直播课'
,
},
{
path
:
"/create-video-course"
,
path
:
'/create-video-course'
,
component
:
AddVideoCoursePage
,
name
:
"创建视频课"
,
name
:
'创建视频课'
,
},
{
path
:
"/knowledge-base"
,
path
:
'/knowledge-base'
,
// component:ResourceDisk,
component
:
KnowledgeBase
,
name
:
"知识库"
,
name
:
'知识库'
,
},
{
path
:
"/create-graphics-course"
,
path
:
'/create-graphics-course'
,
component
:
AddGraphicsCoursePage
,
name
:
"创建图文课"
,
name
:
'创建图文课'
,
},
{
path
:
"/create-offline-course"
,
path
:
'/create-offline-course'
,
component
:
AddOfflineCoursePage
,
name
:
"创建线下课"
,
name
:
'创建线下课'
,
},
{
path
:
"/resource-disk"
,
path
:
'/resource-disk'
,
component
:
ResourceDisk
,
name
:
"资料云盘"
,
name
:
'资料云盘'
,
},
{
path
:
'/question-manage-index'
,
component
:
QuestionManageIndex
,
name
:
'题库'
component
:
QuestionManageIndex
,
name
:
'题库'
,
},
{
path
:
'/paper-manage-index'
,
component
:
PaperManageIndex
,
name
:
'试卷'
component
:
PaperManageIndex
,
name
:
'试卷'
,
},
{
path
:
'/examination-manage-index'
,
component
:
ExaminationManagerIndex
,
name
:
'考试'
component
:
ExaminationManagerIndex
,
name
:
'考试'
,
},
{
path
:
'/test-detail/:testId'
,
component
:
ExaminationManagerTestDetail
,
// () => import('@/modules/teach-tool/examination-manager/TestDetailPage'),
name
:
'答题详情'
,
},
{
path
:
'/course-category-manage'
,
component
:
CourseCategoryManage
,
name
:
'分类管理'
component
:
CourseCategoryManage
,
name
:
'分类管理'
,
},
{
path
:
"/switch-route"
,
path
:
'/switch-route'
,
component
:
SwitchRoute
,
name
:
"登录后跳转承载页"
,
name
:
'登录后跳转承载页'
,
},
{
path
:
"/plan"
,
path
:
'/plan'
,
component
:
PlanPage
,
name
:
"培训计划"
,
name
:
'培训计划'
,
},
{
path
:
"/create-plan"
,
path
:
'/create-plan'
,
component
:
AddPlanPage
,
name
:
"创建视频课"
,
name
:
'创建视频课'
,
},
{
path
:
'/store-info'
,
component
:
StoreInfoPage
,
name
:
'学院信息'
component
:
StoreInfoPage
,
name
:
'学院信息'
,
},
{
path
:
'/college-info'
,
component
:
CollegeInfoPage
,
name
:
'学院信息'
component
:
CollegeInfoPage
,
name
:
'学院信息'
,
},
{
path
:
"/learning-data"
,
path
:
'/learning-data'
,
component
:
LearningDataPage
,
name
:
"学习数据"
,
name
:
'学习数据'
,
},
];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment