Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-lab
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-lab
Commits
f450256c
提交
f450256c
authored
9月 13, 2023
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: 新增大赛监控
上级
a7cbd6a2
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
430 行增加
和
55 行删除
+430
-55
Index.vue
src/modules/admin/contest/check/views/Index.vue
+55
-55
api.ts
src/modules/admin/contest/dashboard/api.ts
+21
-0
index.ts
src/modules/admin/contest/dashboard/index.ts
+10
-0
Index.vue
src/modules/admin/contest/dashboard/views/Index.vue
+343
-0
menu.ts
src/stores/menu.ts
+1
-0
没有找到文件。
src/modules/admin/contest/check/views/Index.vue
浏览文件 @
f450256c
...
@@ -73,48 +73,48 @@ const listOptions = $computed(() => {
...
@@ -73,48 +73,48 @@ const listOptions = $computed(() => {
{
label
:
'已评分'
,
prop
:
'checked_flag_name'
},
{
label
:
'已评分'
,
prop
:
'checked_flag_name'
},
{
label
:
'得分'
,
prop
:
'score_name'
,
slots
:
'table-score'
},
{
label
:
'得分'
,
prop
:
'score_name'
,
slots
:
'table-score'
},
{
label
:
'更新时间'
,
prop
:
'updated_time'
},
{
label
:
'更新时间'
,
prop
:
'updated_time'
},
{
//
{
label
:
'模块一'
,
//
label: '模块一',
prop
:
'module_1'
,
//
prop: 'module_1',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
0
)
//
return getModuleStatus(row, 0)
}
//
}
},
//
},
{
//
{
label
:
'模块二'
,
//
label: '模块二',
prop
:
'module_2'
,
//
prop: 'module_2',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
1
)
//
return getModuleStatus(row, 1)
}
//
}
},
//
},
{
//
{
label
:
'模块三'
,
//
label: '模块三',
prop
:
'module_3'
,
//
prop: 'module_3',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
2
)
//
return getModuleStatus(row, 2)
}
//
}
},
//
},
{
//
{
label
:
'模块四'
,
//
label: '模块四',
prop
:
'module_4'
,
//
prop: 'module_4',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
3
)
//
return getModuleStatus(row, 3)
}
//
}
},
//
},
{
//
{
label
:
'模块五'
,
//
label: '模块五',
prop
:
'module_5'
,
//
prop: 'module_5',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
4
)
//
return getModuleStatus(row, 4)
}
//
}
},
//
},
{
//
{
label
:
'模块六'
,
//
label: '模块六',
prop
:
'module_6'
,
//
prop: 'module_6',
computed
({
row
}:
{
row
:
any
})
{
//
computed({ row }: { row: any }) {
return
getModuleStatus
(
row
,
5
)
//
return getModuleStatus(row, 5)
}
//
}
},
//
},
{
label
:
'操作'
,
slots
:
'table-x'
,
width
:
100
}
{
label
:
'操作'
,
slots
:
'table-x'
,
width
:
100
}
]
]
}
}
...
@@ -128,19 +128,19 @@ function onUpdateSuccess() {
...
@@ -128,19 +128,19 @@ function onUpdateSuccess() {
appList
?.
refetch
()
appList
?.
refetch
()
}
}
function
getModuleStatus
(
row
:
any
,
index
:
number
)
{
//
function getModuleStatus(row: any, index: number) {
const
[
first
]
=
row
.
student_module_status_list
//
const [first] = row.student_module_status_list
try
{
//
try {
const
data
:
any
=
JSON
.
parse
(
first
.
data
)
//
const data: any = JSON.parse(first.data)
const
status
:
number
=
data
[
index
].
status
//
const status: number = data[index].status
if
(
status
==
0
)
return
'未开始'
//
if (status == 0) return '未开始'
if
(
status
==
1
)
return
'<span class="is-inProgress">进行中</span>'
//
if (status == 1) return '
<
span
class
=
"is-inProgress"
>
进行中
<
/span>
'
if
(
status
==
2
)
return
'<span class="is-completed">已完成</span>'
//
if (status == 2) return '
<
span
class
=
"is-completed"
>
已完成
<
/span>
'
}
catch
(
error
)
{
//
} catch (error) {
// console.log(error)
//
// console.log(error)
}
//
}
return
'未开始'
//
return '未开始'
}
//
}
</
script
>
</
script
>
<
template
>
<
template
>
...
...
src/modules/admin/contest/dashboard/api.ts
0 → 100644
浏览文件 @
f450256c
import
httpRequest
from
'@/utils/axios'
// 获取赛项列表
export
function
getCompetitionItems
()
{
return
httpRequest
.
get
(
'/api/lab/v1/expert/competition/items'
)
}
// 获取赛项的统计详情
export
function
getCompetitionStatistics
(
params
:
{
competition_id
:
string
;
platform_key
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/expert/competition/statistics'
,
{
params
})
}
// 获取所有的参赛学员列表
export
function
getCompetitionCompetitors
(
params
:
{
competition_id
:
string
;
platform_key
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/expert/competition/competitors'
,
{
params
})
}
// 获取平台标识列表
export
function
getPlatformKeys
(
params
?:
{
competition_id
:
string
})
{
return
httpRequest
.
get
(
'/api/resource/v1/backend/competition-book/platform-keys'
,
{
params
})
}
src/modules/admin/contest/dashboard/index.ts
0 → 100644
浏览文件 @
f450256c
import
type
{
RouteRecordRaw
}
from
'vue-router'
import
AppLayout
from
'@/components/layout/Index.vue'
export
const
routes
:
Array
<
RouteRecordRaw
>
=
[
{
path
:
'/admin/contest/dashboard'
,
component
:
AppLayout
,
children
:
[{
path
:
''
,
component
:
()
=>
import
(
'./views/Index.vue'
)
}]
}
]
src/modules/admin/contest/dashboard/views/Index.vue
0 → 100644
浏览文件 @
f450256c
<
script
setup
lang=
"ts"
>
import
AppList
from
'@/components/base/AppList.vue'
import
{
getCompetitionItems
,
getCompetitionStatistics
,
getCompetitionCompetitors
}
from
'../api'
import
{
useNow
}
from
'@vueuse/core'
import
dayjs
from
'dayjs'
import
duration
from
'dayjs/plugin/duration'
dayjs
.
extend
(
duration
)
const
appList
=
ref
<
InstanceType
<
typeof
AppList
>
|
null
>
(
null
)
const
competitionList
=
ref
<
Array
<
any
>>
([])
const
currentCompetition
=
ref
()
const
countdown
=
computed
(()
=>
{
const
timestamp
=
currentCompetition
.
value
?.
end_at
*
1000
-
useNow
().
value
.
getTime
()
if
(
timestamp
>
0
)
{
return
dayjs
.
duration
(
timestamp
).
format
(
'HH:mm:ss'
)
}
return
'00:00:00'
})
async
function
fetchCompetition
()
{
const
res
=
await
getCompetitionItems
()
competitionList
.
value
=
res
.
data
.
items
if
(
res
.
data
.
items
.
length
)
{
currentCompetition
.
value
=
res
.
data
.
items
[
0
]
}
}
onMounted
(()
=>
{
fetchCompetition
()
})
const
platformKey
=
ref
(
'career_data_analysis'
)
const
platformKeys
=
ref
([
{
platform_key
:
'career_data_analysis'
,
name
:
'商业数据分析实验'
},
{
platform_key
:
'data_marketing'
,
name
:
'数据营销实操'
}
])
// async function fetchPlatformKeys(competition_id: string) {
// const res = await getPlatformKeys({ competition_id })
// platformKeys.value = res.data.items
// }
// watchEffect(() => {
// if (currentCompetition.value?.id) fetchPlatformKeys(currentCompetition.value.id)
// })
function
fetchInfo
()
{
if
(
currentCompetition
.
value
?.
id
&&
platformKey
.
value
)
{
fetchStatistics
(
currentCompetition
.
value
.
id
,
platformKey
.
value
)
fetchCompetitors
(
currentCompetition
.
value
.
id
,
platformKey
.
value
)
}
}
let
timer
:
null
|
number
=
null
onMounted
(()
=>
{
timer
=
setInterval
(()
=>
{
fetchInfo
()
},
5000
)
})
onUnmounted
(()
=>
{
timer
&&
clearInterval
(
timer
)
})
const
statistics
=
reactive
({
competitor_count
:
0
,
complete_answer_competitor_count
:
0
,
starting_answer_competitor_count
:
0
})
async
function
fetchStatistics
(
competition_id
:
string
,
platform_key
=
'career_data_analysis'
)
{
const
res
=
await
getCompetitionStatistics
({
competition_id
,
platform_key
})
Object
.
assign
(
statistics
,
res
.
data
.
detail
)
}
const
competitorsList
=
ref
([])
async
function
fetchCompetitors
(
competition_id
:
string
,
platform_key
=
'career_data_analysis'
)
{
const
res
=
await
getCompetitionCompetitors
({
competition_id
,
platform_key
})
competitorsList
.
value
=
res
.
data
.
items
}
watchEffect
(()
=>
{
fetchInfo
()
})
// 已提交选手
const
submittedListOptions
=
computed
(()
=>
{
return
{
columns
:
[
{
label
:
'序号'
,
type
:
'index'
,
width
:
60
},
{
label
:
'选手姓名'
,
prop
:
'student.name'
},
{
label
:
'参赛ID'
,
prop
:
'login_id'
},
{
label
:
'所在学校'
,
prop
:
'student.organ_name'
},
{
label
:
'所在专业'
,
prop
:
'student.specialty_name'
},
{
label
:
'所在班级'
,
prop
:
'student.class_name'
},
{
label
:
'提交时间'
,
prop
:
'commit_time'
,
computed
({
row
}:
{
row
:
any
})
{
return
dayjs
.
unix
(
row
.
commit_time
).
format
(
'YYYY-MM-DD HH:mm:ss'
)
}
},
{
label
:
'作答用时'
,
prop
:
'commit_time'
,
computed
({
row
}:
{
row
:
any
})
{
const
timestamp
=
(
row
.
commit_time
*
1000
||
useNow
().
value
.
getTime
())
-
row
.
start_answer_time
*
1000
const
duration
=
dayjs
.
duration
(
timestamp
)
return
duration
.
hours
()
===
0
?
duration
.
format
(
"m[']s"
)
:
duration
.
format
(
"H[h]m[']s"
)
}
}
],
data
:
competitorsList
.
value
.
filter
((
item
:
any
)
=>
item
.
commit_status
==
3
)
}
})
// 正在作答选手
const
answeringListOptions
=
computed
(()
=>
{
return
{
columns
:
[
{
label
:
'序号'
,
type
:
'index'
,
width
:
60
},
{
label
:
'选手姓名'
,
prop
:
'student.name'
},
{
label
:
'参赛ID'
,
prop
:
'login_id'
},
{
label
:
'所在学校'
,
prop
:
'student.organ_name'
},
{
label
:
'所在专业'
,
prop
:
'student.specialty_name'
},
{
label
:
'所在班级'
,
prop
:
'student.class_name'
},
{
label
:
'提交时间'
,
prop
:
'commit_time'
,
computed
({
row
}:
{
row
:
any
})
{
return
dayjs
.
unix
(
row
.
commit_time
).
format
(
'YYYY-MM-DD HH:mm:ss'
)
}
},
{
label
:
'作答用时'
,
prop
:
'commit_time'
,
computed
({
row
}:
{
row
:
any
})
{
const
timestamp
=
(
row
.
commit_time
*
1000
||
useNow
().
value
.
getTime
())
-
row
.
start_answer_time
*
1000
const
duration
=
dayjs
.
duration
(
timestamp
)
return
duration
.
hours
()
===
0
?
duration
.
format
(
"m[']s"
)
:
duration
.
format
(
"H[h]m[']s"
)
}
}
],
data
:
competitorsList
.
value
.
filter
((
item
:
any
)
=>
item
.
commit_status
==
2
||
item
.
commit_status
==
4
)
}
})
// 选手答题情况
const
listOptions
=
computed
(()
=>
{
return
{
columns
:
[
{
label
:
'序号'
,
type
:
'index'
,
width
:
60
},
{
label
:
'选手姓名'
,
prop
:
'student.name'
},
{
label
:
'参赛ID'
,
prop
:
'login_id'
},
{
label
:
'性别'
,
prop
:
'student.gender'
},
{
label
:
'所在学校'
,
prop
:
'student.organ_name'
},
{
label
:
'所在专业'
,
prop
:
'student.specialty_name'
},
{
label
:
'所在班级'
,
prop
:
'student.class_name'
},
{
label
:
'模块一'
,
prop
:
'module_1'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
0
)
}
},
{
label
:
'模块二'
,
prop
:
'module_2'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
1
)
}
},
{
label
:
'模块三'
,
prop
:
'module_3'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
2
)
}
},
{
label
:
'模块四'
,
prop
:
'module_4'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
3
)
}
},
{
label
:
'模块五'
,
prop
:
'module_5'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
4
)
}
},
{
label
:
'模块六'
,
prop
:
'module_6'
,
computed
({
row
}:
{
row
:
any
})
{
return
getModuleStatus
(
row
,
5
)
}
}
],
data
:
competitorsList
.
value
}
})
function
getModuleStatus
(
row
:
any
,
index
:
number
)
{
try
{
const
[
first
]
=
JSON
.
parse
(
row
.
module_status_data
)
||
[]
const
status
:
number
=
first
[
index
].
status
if
(
status
==
0
)
return
'<span class="not-started">未开始</span>'
if
(
status
==
1
)
return
'<span class="is-inProgress">进行中</span>'
if
(
status
==
2
)
return
'<span class="is-completed">已完成</span>'
}
catch
(
error
)
{
// console.log(error)
}
return
'<span class="not-started">未开始</span>'
}
</
script
>
<
template
>
<AppCard>
<h2
class=
"end-countdown"
>
结束倒计时:
<span>
{{
countdown
}}
</span>
</h2>
<el-row
justify=
"center"
>
<el-select
v-model=
"currentCompetition"
value-key=
"id"
size=
"large"
style=
"margin-right: 20px"
>
<el-option
v-for=
"item in competitionList"
:key=
"item.id"
:value=
"item"
:label=
"item.name"
></el-option>
</el-select>
<el-select
v-model=
"platformKey"
size=
"large"
>
<el-option
v-for=
"item in platformKeys"
:key=
"item.platform_key"
:value=
"item.platform_key"
:label=
"item.name"
></el-option>
</el-select>
</el-row>
<ul
class=
"statistics"
>
<li>
<h6>
<span>
{{
statistics
.
competitor_count
}}
</span>
<em>
人
</em>
</h6>
<p>
参赛人数
</p>
</li>
<li>
<h6>
<span>
{{
statistics
.
starting_answer_competitor_count
}}
</span>
<em>
人
</em>
</h6>
<p>
正在答题人数
</p>
</li>
<li>
<h6>
<span>
{{
statistics
.
complete_answer_competitor_count
}}
</span>
<em>
人
</em>
</h6>
<p>
已完成人数
</p>
</li>
</ul>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<h2
class=
"h2-title"
>
已提交选手
</h2>
<AppList
border
v-bind=
"submittedListOptions"
ref=
"appList"
></AppList
></el-col>
<el-col
:span=
"12"
>
<h2
class=
"h2-title"
>
正在作答选手
</h2>
<AppList
border
v-bind=
"answeringListOptions"
ref=
"appList"
></AppList
></el-col>
</el-row>
<h2
class=
"h2-title"
>
选手答题情况
</h2>
<AppList
border
v-bind=
"listOptions"
ref=
"appList"
></AppList>
</AppCard>
</
template
>
<
style
lang=
"scss"
>
.statistics
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
column-gap
:
140px
;
margin
:
90px
0
;
li
{
width
:
212px
;
height
:
212px
;
background-color
:
rgba
(
247
,
247
,
247
,
1
);
border-radius
:
50%
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
h6
{
color
:
rgba
(
178
,
15
,
60
,
1
);
span
{
font-size
:
40px
;
}
em
{
font-size
:
18px
;
}
}
p
{
margin-top
:
20px
;
font-size
:
18px
;
color
:
rgba
(
96
,
96
,
96
,
1
);
}
}
}
.is-completed
{
display
:
inline-block
;
width
:
60px
;
height
:
24px
;
border-radius
:
4px
;
font-size
:
12px
;
line-height
:
24px
;
text-align
:
center
;
background-color
:
rgba
(
211
,
242
,
216
,
1
);
color
:
rgba
(
32
,
176
,
48
,
1
);
}
.is-inProgress
{
display
:
inline-block
;
width
:
60px
;
height
:
24px
;
border-radius
:
4px
;
font-size
:
12px
;
line-height
:
24px
;
text-align
:
center
;
background-color
:
rgba
(
251
,
219
,
225
,
1
);
color
:
rgba
(
171
,
42
,
65
,
1
);
}
.not-started
{
display
:
inline-block
;
width
:
60px
;
height
:
24px
;
border-radius
:
4px
;
font-size
:
12px
;
line-height
:
24px
;
text-align
:
center
;
color
:
rgba
(
144
,
144
,
144
,
1
);
background-color
:
rgba
(
228
,
228
,
230
,
1
);
}
.h2-title
{
padding-left
:
5px
;
font-size
:
18px
;
font-weight
:
500
;
line-height
:
1
;
margin
:
20px
0
;
border-left
:
3px
solid
#aa1941
;
}
.end-countdown
{
float
:
right
;
color
:
rgba
(
109
,
110
,
111
,
1
);
font-size
:
14px
;
span
{
color
:
rgba
(
65
,
80
,
88
,
1
);
font-size
:
30px
;
}
}
</
style
>
src/stores/menu.ts
浏览文件 @
f450256c
...
@@ -40,6 +40,7 @@ const adminMenus: IMenuItem[] = [
...
@@ -40,6 +40,7 @@ const adminMenus: IMenuItem[] = [
{
name
:
'参赛选手管理'
,
path
:
'/admin/contest/contestants'
,
tag
:
'competition-competitor'
},
{
name
:
'参赛选手管理'
,
path
:
'/admin/contest/contestants'
,
tag
:
'competition-competitor'
},
{
name
:
'评分专家管理'
,
path
:
'/admin/contest/experts'
,
tag
:
'expert'
},
{
name
:
'评分专家管理'
,
path
:
'/admin/contest/experts'
,
tag
:
'expert'
},
{
name
:
'大赛训练答疑'
,
path
:
'/admin/contest/discuss'
,
tag
:
'v1-teacher-train-discussion'
},
{
name
:
'大赛训练答疑'
,
path
:
'/admin/contest/discuss'
,
tag
:
'v1-teacher-train-discussion'
},
{
name
:
'大赛监控'
,
path
:
'/admin/contest/dashboard'
,
tag
:
'v1-expert-statistic'
},
{
name
:
'大赛评分'
,
path
:
'/admin/contest/check'
,
tag
:
'v1-expert-check'
},
{
name
:
'大赛评分'
,
path
:
'/admin/contest/check'
,
tag
:
'v1-expert-check'
},
{
name
:
'大赛发布成绩'
,
path
:
'/admin/contest/score'
,
tag
:
'v1-expert-score'
}
{
name
:
'大赛发布成绩'
,
path
:
'/admin/contest/score'
,
tag
:
'v1-expert-score'
}
]
]
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论