Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-lab
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-lab
Commits
e685420a
提交
e685420a
authored
6月 19, 2023
作者:
lhh
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
update
上级
3418f93e
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
688 行增加
和
101 行删除
+688
-101
api.ts
src/modules/admin/lab/experiment/api.ts
+21
-0
GradeRulesDialog.vue
...ules/admin/lab/experiment/components/GradeRulesDialog.vue
+25
-7
GroupQuestion.vue
...min/lab/experiment/components/Questions/GroupQuestion.vue
+99
-0
MaterialQuestion.vue
.../lab/experiment/components/Questions/MaterialQuestion.vue
+163
-0
PreviewDialog.vue
...min/lab/experiment/components/Questions/PreviewDialog.vue
+55
-0
QuestionsOrder.vue
...in/lab/experiment/components/Questions/QuestionsOrder.vue
+57
-5
TagQuestion.vue
...admin/lab/experiment/components/Questions/TagQuestion.vue
+99
-0
UserQuestion.vue
...dmin/lab/experiment/components/Questions/UserQuestion.vue
+47
-61
Questions.vue
src/modules/admin/lab/experiment/views/Questions.vue
+108
-15
dictionary.ts
src/utils/dictionary.ts
+2
-1
vite.config.ts
vite.config.ts
+12
-12
没有找到文件。
src/modules/admin/lab/experiment/api.ts
浏览文件 @
e685420a
...
...
@@ -134,3 +134,23 @@ export function getConnectionList(params: { experiment_id: string }) {
export
function
getMaterialList
(
params
:
{
experiment_id
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/marketing-material/all'
,
{
params
})
}
// 创建题
export
function
updateQuestions
(
data
:
{
experiment_id
:
string
;
module
:
string
;
questions
:
any
})
{
return
httpRequest
.
post
(
'/api/resource/v1/backend/experiment-question/save'
,
data
)
}
// 获取题
export
function
getQuestions
(
data
:
{
experiment_id
:
string
;
types
?:
any
})
{
return
httpRequest
.
post
(
'/api/resource/v1/backend/experiment-question/list'
,
data
)
}
// 获取老师创建的标签
export
function
getQuestionTags
(
params
:
{
experiment_id
:
string
})
{
return
httpRequest
.
get
(
'/api/resource/v1/backend/experiment-question/tags'
,
{
params
})
}
// 获取群组
export
function
getQuestionGroup
(
params
:
{
experiment_id
:
string
})
{
return
httpRequest
.
get
(
'/api/resource/v1/backend/experiment-question/groups'
,
{
params
})
}
\ No newline at end of file
src/modules/admin/lab/experiment/components/GradeRulesDialog.vue
浏览文件 @
e685420a
...
...
@@ -59,7 +59,7 @@ function handleRemove(index: number) {
form
.
rule_list
.
splice
(
index
,
1
)
}
// 提交
function
handleSubmit
()
{
function
handleSubmit
(
call
?:
any
)
{
for
(
let
i
=
0
;
i
<
form
.
rule_list
.
length
;
i
++
)
{
const
item
=
form
.
rule_list
[
i
]
if
(
!
item
.
name
||
!
item
.
type
)
{
...
...
@@ -83,9 +83,12 @@ function handleSubmit() {
formRef
?.
validate
().
then
(()
=>
{
const
params
=
{
...
form
,
rule_list
:
JSON
.
stringify
(
form
.
rule_list
)
}
updateExperimentGradeRule
(
params
).
then
(()
=>
{
ElMessage
({
message
:
'保存成功'
,
type
:
'success'
})
emit
(
'update'
)
emit
(
'update:modelValue'
,
false
)
call
()
if
(
!
call
)
{
ElMessage
({
message
:
'保存成功'
,
type
:
'success'
})
emit
(
'update'
)
emit
(
'update:modelValue'
,
false
)
}
})
})
}
...
...
@@ -98,7 +101,7 @@ function currentRuleNames(value: number) {
})
if
(
props
.
data
.
type
===
'4'
)
{
// 数字营销实验
return
tempList
.
filter
(
item
=>
[
1
,
5
,
6
,
7
,
8
,
9
].
includes
(
item
.
value
as
number
))
return
tempList
.
filter
(
item
=>
[
1
,
5
,
6
,
7
,
8
,
9
,
10
].
includes
(
item
.
value
as
number
))
}
else
{
return
tempList
.
filter
((
item
:
any
)
=>
item
.
value
<=
5
)
}
...
...
@@ -127,6 +130,15 @@ function rowScore(percent = 0) {
const
score
=
parseFloat
(
props
.
data
.
score
)
||
0
return
(
percent
/
100
)
*
score
}
// 编辑题
const
handleEdit
=
function
(
type
:
number
)
{
handleSubmit
(
function
()
{
window
.
open
(
`/admin/lab/experiment/questions?id=
${
props
.
data
.
id
}
&type=
${
type
}
&name=
${
props
.
data
.
name
}
&type_name=
${
props
.
data
.
type_name
}
&score=
${
props
.
data
.
score
}
`
)
})
}
</
script
>
<
template
>
...
...
@@ -181,7 +193,7 @@ function rowScore(percent = 0) {
<el-radio-group
v-model=
"row.rule_mode"
size=
"small"
>
<el-radio
:label=
"1"
>
人工评分
</el-radio>
<!--
<el-radio
:label=
"2"
v-if=
"[2, 3].includes(row.type)"
>
自动评分
</el-radio>
-->
<el-radio
:label=
"2"
>
自动评分
</el-radio>
<el-radio
:label=
"2"
:disabled=
"row.type === 1 || row.type === 8"
>
自动评分
</el-radio>
</el-radio-group>
</
template
>
</el-table-column>
...
...
@@ -191,7 +203,13 @@ function rowScore(percent = 0) {
<el-table-column
align=
"right"
>
<
template
#
default=
"{ $index, row }"
>
<div
class=
"btn-box"
>
<el-button
style=
"padding: 0"
text
type=
"primary"
@
click=
"handleRemove($index)"
v-if=
"row.type !== 1"
<el-button
:disabled=
"row.type === 1 || row.type === 8"
style=
"padding: 0"
text
type=
"primary"
@
click=
"handleEdit(row.type)"
v-if=
"row.type !== 1"
>
编辑
</el-button
>
<el-button
style=
"padding: 0"
text
type=
"primary"
@
click=
"handleRemove($index)"
v-if=
"row.type !== 1"
...
...
src/modules/admin/lab/experiment/components/Questions/GroupQuestion.vue
0 → 100644
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
import
type
{
FormInstance
}
from
'element-plus'
import
{
CircleCloseFilled
}
from
'@element-plus/icons-vue'
import
{
getQuestionGroup
}
from
'../../api'
const
route
=
useRoute
()
const
modelValue
:
any
=
defineModel
()
const
ruleFormRef
=
ref
<
FormInstance
>
()
const
removeQuestion
=
(
index
:
number
)
=>
{
modelValue
.
value
.
splice
(
index
,
1
)
}
let
options
=
$ref
<
{
id
:
string
;
name
:
string
}[]
>
()
onMounted
(()
=>
{
const
dom
:
any
=
document
.
querySelectorAll
(
'.app-main'
)[
0
]
dom
.
style
.
overflow
=
'visible'
getQuestionGroup
({
experiment_id
:
route
.
query
.
id
as
string
}).
then
(
res
=>
{
options
=
res
.
data
.
items
})
})
</
script
>
<
template
>
<el-card
:id=
"`site-card$
{index}`" class="box-card" v-for="(item, index) in modelValue" :key="item">
<el-icon
@
click=
"removeQuestion(index)"
v-if=
"modelValue.length > 1"
class=
"close"
size=
"28"
color=
"#c01c40"
><CircleCloseFilled
/></el-icon>
<div
class=
"head-box"
>
<el-tabs
v-model=
"item.type"
type=
"card"
class=
"demo-tabs"
>
<el-tab-pane
label=
"静待群组"
:name=
"301"
></el-tab-pane>
<el-tab-pane
label=
"动态群组"
:name=
"302"
></el-tab-pane>
</el-tabs>
<el-form-item
label=
"本题分值"
class=
"head-r"
>
<el-input-number
v-model=
"item.score"
:min=
"1"
:max=
"100"
controls-position=
"right"
/>
</el-form-item>
</div>
<el-form
ref=
"ruleFormRef"
label-width=
"120px"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"题目标题"
:required=
"true"
>
<el-input
v-model=
"item.title"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"题干内容"
>
<el-input
v-model=
"item.content"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"正确答案"
:required=
"true"
>
<span
v-if=
"item.type === 301"
>
创建静态群组成功
</span>
<el-select
v-else
v-model=
"item.answer"
class=
"m-2"
placeholder=
"请选择"
size=
"large"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"答案解析"
>
<el-input
v-model=
"item.answer_analysis"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
</el-form>
</el-card>
</
template
>
<
style
lang=
"scss"
scoped
>
.close
{
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
cursor
:
pointer
;
}
.box-card
{
padding-top
:
20px
;
position
:
relative
;
margin-bottom
:
30px
;
overflow
:
visible
;
}
.head-box
{
display
:
flex
;
margin-bottom
:
30px
;
.head-r
{
margin-left
:
auto
;
}
}
.upload-box
{
display
:
flex
;
.upload-loading
{
width
:
300px
;
display
:
flex
;
align-items
:
center
;
&
:hover
{
.succ-icon2
{
display
:
block
;
}
.succ-icon1
{
display
:
none
;
}
}
.succ-icon2
{
display
:
none
;
cursor
:
pointer
;
}
}
}
</
style
>
src/modules/admin/lab/experiment/components/Questions/MaterialQuestion.vue
0 → 100644
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
import
type
{
FormInstance
}
from
'element-plus'
import
{
Document
,
CircleCheck
,
CircleClose
,
CircleCloseFilled
}
from
'@element-plus/icons-vue'
import
AppUpload
from
'@/components/base/AppUpload.vue'
import
{
getQuestionTags
}
from
'../../api'
import
type
{
UploadFile
}
from
'element-plus'
const
route
=
useRoute
()
const
modelValue
:
any
=
defineModel
()
const
ruleFormRef
=
ref
<
FormInstance
>
()
// 移除上传文件
const
handleRemove
=
(
file
:
UploadFile
)
=>
{
if
(
file
)
{
modelValue
.
value
.
forEach
((
item
:
any
)
=>
{
const
index
=
item
.
files
.
findIndex
((
cItem
:
{
url
:
string
})
=>
cItem
.
url
===
file
.
url
)
item
.
files
.
splice
(
index
,
1
)
})
}
}
const
handleDownload
=
(
file
:
any
)
=>
{
if
(
file
)
{
modelValue
.
value
.
forEach
((
i
:
any
)
=>
{
const
item
:
any
=
i
.
files
.
find
((
item
:
any
)
=>
item
.
url
===
file
.
url
)
if
(
item
)
item
.
is_download
=
file
.
is_download
})
}
}
const
removeQuestion
=
(
index
:
number
)
=>
{
modelValue
.
value
.
splice
(
index
,
1
)
}
const
options
=
$ref
<
{
id
:
number
;
name
:
string
}[]
>
([
{
id
:
401
,
name
:
'文本资料'
},
{
id
:
402
,
name
:
'图片资料'
},
{
id
:
403
,
name
:
'语音资料'
},
{
id
:
404
,
name
:
'视频资料'
},
{
id
:
405
,
name
:
'H5资料'
},
{
id
:
406
,
name
:
'二维码资料'
},
{
id
:
407
,
name
:
'小程序资料'
},
{
id
:
408
,
name
:
'卡券资料'
}
])
onMounted
(()
=>
{
const
dom
:
any
=
document
.
querySelectorAll
(
'.app-main'
)[
0
]
dom
.
style
.
overflow
=
'visible'
})
const
getTips
=
function
(
n
:
number
)
{
const
tipText
:
any
=
{
402
:
'试题文件支持格式包含:png jpg jpeg ,大小不超过5M'
,
403
:
'试题文件支持格式包含:mp3 wav,大小不超过5M'
,
404
:
'试题文件支持格式包含:帧率为25fps/输出码率为4M/输出格式为mp4,建议采用格式工厂等工具处理后上传。'
,
405
:
'试题文件支持格式包含:png jpg jpeg ,大小不超过5M'
,
406
:
'试题文件支持格式包含:png jpg jpeg ,大小不超过5M'
,
407
:
'试题文件支持格式包含:png jpg jpeg ,大小不超过5M'
,
508
:
'试题文件支持格式包含:png jpg jpeg ,大小不超过5M'
}
return
tipText
[
n
]
}
</
script
>
<
template
>
<el-card
:id=
"`site-card$
{index}`" class="box-card" v-for="(item, index) in modelValue" :key="item">
<el-icon
@
click=
"removeQuestion(index)"
v-if=
"modelValue.length > 1"
class=
"close"
size=
"28"
color=
"#c01c40"
><CircleCloseFilled
/></el-icon>
<div
class=
"head-box"
>
<el-select
v-model=
"item.type"
class=
"m-2"
placeholder=
"请选择"
size=
"large"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
<el-form-item
label=
"本题分值"
class=
"head-r"
>
<el-input-number
v-model=
"item.score"
:min=
"1"
:max=
"100"
controls-position=
"right"
/>
</el-form-item>
</div>
<el-form
ref=
"ruleFormRef"
label-width=
"120px"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"题目标题"
:required=
"true"
>
<el-input
v-model=
"item.title"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"题干内容"
>
<el-input
v-model=
"item.content"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"正确答案"
:required=
"true"
>
<span>
上传成功
</span>
</el-form-item>
<el-form-item
label=
"答案解析"
>
<el-input
v-model=
"item.answer_analysis"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"试题文件"
v-if=
"item.type !== 401"
>
<AppUpload
v-model=
"item.files"
>
<template
#
tip
>
{{
getTips
(
item
.
type
)
}}
</
template
>
<
template
#
file=
"{ file }"
>
<div
class=
"upload-box"
>
<div
class=
"upload-loading"
>
<el-icon
style=
"margin-right: 5px"
><Document
/></el-icon>
<p
style=
"margin-right: 5px"
>
{{
file
.
name
}}
</p>
<p
v-if=
"file.status === 'uploading'"
>
{{
file
.
percentage
}}
%
</p>
<template
v-if=
"file.status === 'success'"
>
<el-icon
class=
"succ-icon1"
color=
"#67c23a"
size=
"18"
style=
"margin-left: 30px"
><CircleCheck
/></el-icon>
<el-icon
class=
"succ-icon2"
size=
"18"
style=
"margin-left: 30px"
@
click=
"handleRemove(file)"
><CircleClose
/></el-icon>
</
template
>
</div>
<el-switch
@
change=
"handleDownload(file)"
v-model=
"file.is_download"
size=
"large"
active-text=
"能否下载"
/>
</div>
</template>
</AppUpload>
</el-form-item>
</el-form>
</el-card>
</template>
<
style
lang=
"scss"
scoped
>
.close
{
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
cursor
:
pointer
;
}
.box-card
{
padding-top
:
20px
;
position
:
relative
;
margin-bottom
:
30px
;
overflow
:
visible
;
}
.head-box
{
display
:
flex
;
margin-bottom
:
30px
;
.head-r
{
margin-left
:
auto
;
}
}
.upload-box
{
display
:
flex
;
.upload-loading
{
width
:
300px
;
display
:
flex
;
align-items
:
center
;
&
:hover
{
.succ-icon2
{
display
:
block
;
}
.succ-icon1
{
display
:
none
;
}
}
.succ-icon2
{
display
:
none
;
cursor
:
pointer
;
}
}
}
</
style
>
src/modules/admin/lab/experiment/components/Questions/PreviewDialog.vue
0 → 100644
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
import
{
getQuestions
}
from
'../../api'
const
route
:
any
=
useRoute
()
onMounted
(()
=>
{
getCurrentQuestions
()
})
// 获取题
let
list
:
any
=
$ref
()
const
getCurrentQuestions
=
function
()
{
getQuestions
({
experiment_id
:
route
.
query
.
id
}).
then
(
res
=>
{
list
=
res
.
data
.
items
})
}
</
script
>
<
template
>
<el-dialog
title=
"2023商业数据分析大赛决赛试题"
:close-on-click-modal=
"false"
width=
"50%"
@
update:modelValue=
"$emit('update:modelValue')"
>
<el-card
class=
"box-card"
v-for=
"(item, index) in list"
:key=
"item.id"
>
<template
#
header
>
<div
class=
"card-header"
>
<span>
{{
index
+
1
}}
.
{{
item
.
title
}}
</span>
<el-form-item
label=
"本题分值"
style=
"margin: 0"
>
{{
item
.
score
}}
</el-form-item>
</div>
</
template
>
<div
class=
"text item"
>
正确答案:{{ item.answer || '上传成功' }}
</div>
</el-card>
</el-dialog>
</template>
<
style
lang=
"scss"
scoped
>
.card-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.text
{
font-size
:
14px
;
}
.item
{
// margin-bottom: 18px;
}
.box-card
{
margin-bottom
:
20px
;
}
</
style
>
src/modules/admin/lab/experiment/components/Questions/QuestionsOrder.vue
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
></
script
>
<
script
setup
lang=
"ts"
>
const
route
=
useRoute
()
const
props
=
defineProps
<
{
data
:
any
}
>
()
const
score
=
computed
(()
=>
{
return
props
.
data
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
=
b
.
score
+
a
return
a
},
0
)
})
const
orderSite
=
ref
(
0
)
// 点击序号滚动左侧
const
handleOrder
=
(
n
:
number
)
=>
{
const
dom
:
any
=
document
.
querySelector
(
`#site-card
${
n
}
`
)
if
(
dom
)
{
orderSite
.
value
=
n
const
top
=
dom
.
getBoundingClientRect
().
top
+
document
.
documentElement
.
scrollTop
window
.
scrollTo
(
0
,
top
-
80
)
}
}
// 页面滚动改变序号高亮
window
.
onscroll
=
function
()
{
const
dom
:
any
=
document
.
querySelectorAll
(
'.box-card'
)
if
(
dom
)
{
for
(
let
i
=
0
;
i
<
dom
.
length
;
i
++
)
{
const
top
=
dom
[
i
].
getBoundingClientRect
().
top
if
(
top
<
dom
[
0
].
offsetHeight
/
2
&&
top
>
0
)
{
orderSite
.
value
=
i
}
}
}
}
const
typeName
=
computed
(()
=>
{
const
name
:
any
=
{
'10'
:
'用户/事件数据'
,
'6'
:
'标签管理'
,
'7'
:
'群组管理'
,
'9'
:
'营销资料管理'
}
return
name
[(
route
.
query
?.
type
as
string
)
||
'10'
]
})
</
script
>
<
template
>
<div
class=
"order-box"
>
<div
class=
"order-score"
>
<div
class=
"order-score_flex"
>
<span
class=
"el-tooltip question-total-number"
>
0
</span>
<span
class=
"el-tooltip question-total-number"
>
{{
score
}}
</span>
<span
class=
"el-tooltip paper-total-score"
>
/100
</span>
<em>
分
</em>
</div>
<div
class=
"tip"
>
注:每模块满分为100,请注意每题分值。
</div>
</div>
<div
class=
"order-list"
>
<div
class=
"title"
>
用户/事件数据
</div>
<div
class=
"title"
>
{{
typeName
}}
</div>
<div
class=
"list"
>
<div
class=
"li active"
>
1
</div>
<div
class=
"li"
>
2
</div>
<div
@
click=
"handleOrder(index)"
:class=
"orderSite === index ? 'li active' : 'li'"
v-for=
"(item, index) in props.data"
:key=
"item"
>
{{
index
+
1
}}
</div>
</div>
</div>
</div>
...
...
src/modules/admin/lab/experiment/components/Questions/TagQuestion.vue
0 → 100644
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
import
type
{
FormInstance
}
from
'element-plus'
import
{
CircleCloseFilled
}
from
'@element-plus/icons-vue'
import
{
getQuestionTags
}
from
'../../api'
const
route
=
useRoute
()
const
modelValue
:
any
=
defineModel
()
const
ruleFormRef
=
ref
<
FormInstance
>
()
const
removeQuestion
=
(
index
:
number
)
=>
{
modelValue
.
value
.
splice
(
index
,
1
)
}
let
options
=
$ref
<
{
id
:
string
;
name
:
string
}[]
>
()
onMounted
(()
=>
{
const
dom
:
any
=
document
.
querySelectorAll
(
'.app-main'
)[
0
]
dom
.
style
.
overflow
=
'visible'
getQuestionTags
({
experiment_id
:
route
.
query
.
id
as
string
}).
then
(
res
=>
{
options
=
res
.
data
.
items
})
})
</
script
>
<
template
>
<el-card
:id=
"`site-card$
{index}`" class="box-card" v-for="(item, index) in modelValue" :key="item">
<el-icon
@
click=
"removeQuestion(index)"
v-if=
"modelValue.length > 1"
class=
"close"
size=
"28"
color=
"#c01c40"
><CircleCloseFilled
/></el-icon>
<div
class=
"head-box"
>
<el-tabs
v-model=
"item.type"
type=
"card"
class=
"demo-tabs"
>
<el-tab-pane
label=
"标签类型"
:name=
"201"
></el-tab-pane>
<el-tab-pane
label=
"标签"
:name=
"202"
></el-tab-pane>
</el-tabs>
<el-form-item
label=
"本题分值"
class=
"head-r"
>
<el-input-number
v-model=
"item.score"
:min=
"1"
:max=
"100"
controls-position=
"right"
/>
</el-form-item>
</div>
<el-form
ref=
"ruleFormRef"
label-width=
"120px"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"题目标题"
:required=
"true"
>
<el-input
v-model=
"item.title"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"题干内容"
>
<el-input
v-model=
"item.content"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"正确答案"
:required=
"true"
>
<span
v-if=
"item.type === 201"
>
创建标签类型成功
</span>
<el-select
v-else
v-model=
"item.answer"
class=
"m-2"
placeholder=
"请选择"
size=
"large"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"答案解析"
>
<el-input
v-model=
"item.answer_analysis"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
</el-form>
</el-card>
</
template
>
<
style
lang=
"scss"
scoped
>
.close
{
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
cursor
:
pointer
;
}
.box-card
{
padding-top
:
20px
;
position
:
relative
;
margin-bottom
:
30px
;
overflow
:
visible
;
}
.head-box
{
display
:
flex
;
margin-bottom
:
30px
;
.head-r
{
margin-left
:
auto
;
}
}
.upload-box
{
display
:
flex
;
.upload-loading
{
width
:
300px
;
display
:
flex
;
align-items
:
center
;
&
:hover
{
.succ-icon2
{
display
:
block
;
}
.succ-icon1
{
display
:
none
;
}
}
.succ-icon2
{
display
:
none
;
cursor
:
pointer
;
}
}
}
</
style
>
src/modules/admin/lab/experiment/components/Questions/User.vue
→
src/modules/admin/lab/experiment/components/Questions/User
Question
.vue
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
import
type
{
FormInstance
,
FormRules
}
from
'element-plus'
import
{
Document
,
CircleCheck
,
CircleClose
}
from
'@element-plus/icons-vue'
import
type
{
FormInstance
}
from
'element-plus'
import
{
Document
,
CircleCheck
,
CircleClose
,
CircleCloseFilled
}
from
'@element-plus/icons-vue'
import
AppUpload
from
'@/components/base/AppUpload.vue'
import
type
{
UploadFile
}
from
'element-plus'
const
activeName
=
ref
(
'1'
)
const
modelValue
:
any
=
defineModel
(
)
const
ruleFormRef
=
ref
<
FormInstance
>
()
const
ruleForm
=
reactive
({
title
:
''
,
content
:
''
,
answer
:
'导入成功'
,
answer_analysis
:
''
,
files
:
[]
})
const
rules
=
reactive
<
FormRules
>
({
title
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}],
answer
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}]
})
const
submitForm
=
async
(
formEl
:
FormInstance
|
undefined
)
=>
{
if
(
!
formEl
)
return
await
formEl
.
validate
((
valid
,
fields
)
=>
{
if
(
valid
)
{
console
.
log
(
ruleForm
,
'ruleForm'
)
console
.
log
(
'submit!'
)
}
else
{
console
.
log
(
'error submit!'
,
fields
)
}
})
}
const
resetForm
=
(
formEl
:
FormInstance
|
undefined
)
=>
{
if
(
!
formEl
)
return
formEl
.
resetFields
()
}
const
num
=
ref
(
0
)
// 移除上传文件
const
handleRemove
=
(
file
:
UploadFile
)
=>
{
if
(
file
)
{
const
index
=
ruleForm
.
files
.
findIndex
((
item
:
any
)
=>
item
.
url
===
file
.
url
)
if
(
index
!=
-
1
)
ruleForm
.
files
.
splice
(
index
,
1
)
modelValue
.
value
.
forEach
((
item
:
any
)
=>
{
const
index
=
item
.
files
.
findIndex
((
cItem
:
{
url
:
string
})
=>
cItem
.
url
===
file
.
url
)
item
.
files
.
splice
(
index
,
1
)
})
}
}
const
handleDownload
=
(
file
:
any
)
=>
{
if
(
file
)
{
const
item
:
any
=
ruleForm
.
files
.
find
((
item
:
any
)
=>
item
.
url
===
file
.
url
)
if
(
item
)
item
.
is_download
=
file
.
is_download
modelValue
.
value
.
forEach
((
i
:
any
)
=>
{
const
item
:
any
=
i
.
files
.
find
((
item
:
any
)
=>
item
.
url
===
file
.
url
)
if
(
item
)
item
.
is_download
=
file
.
is_download
})
}
}
const
removeQuestion
=
(
index
:
number
)
=>
{
modelValue
.
value
.
splice
(
index
,
1
)
}
onMounted
(()
=>
{
const
dom
:
any
=
document
.
querySelectorAll
(
'.app-main'
)[
0
]
dom
.
style
.
overflow
=
'visible'
})
</
script
>
<
template
>
<el-card
class=
"box-card"
>
<el-card
:id=
"`site-card$
{index}`" class="box-card" v-for="(item, index) in modelValue" :key="item">
<el-icon
@
click=
"removeQuestion(index)"
v-if=
"modelValue.length > 1"
class=
"close"
size=
"28"
color=
"#c01c40"
><CircleCloseFilled
/></el-icon>
<div
class=
"head-box"
>
<el-tabs
v-model=
"
activeNam
e"
type=
"card"
class=
"demo-tabs"
>
<el-tab-pane
label=
"用户数据"
name=
"
1"
></el-tab-pane>
<el-tab-pane
label=
"事件数据"
name=
"
2"
></el-tab-pane>
<el-tabs
v-model=
"
item.typ
e"
type=
"card"
class=
"demo-tabs"
>
<el-tab-pane
label=
"用户数据"
:name=
"10
1"
></el-tab-pane>
<el-tab-pane
label=
"事件数据"
:name=
"10
2"
></el-tab-pane>
</el-tabs>
<el-form-item
label=
"本题分值"
class=
"head-r"
>
<el-input-number
v-model=
"
num"
:min=
"1"
:max=
"1
0"
controls-position=
"right"
/>
<el-input-number
v-model=
"
item.score"
:min=
"1"
:max=
"10
0"
controls-position=
"right"
/>
</el-form-item>
</div>
<el-form
ref=
"ruleFormRef"
:model=
"ruleForm"
:rules=
"rules"
label-width=
"120px"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"题目标题"
prop=
"titl
e"
>
<el-input
v-model=
"
ruleForm.title"
placeholder=
"请输入"
/>
<el-form
ref=
"ruleFormRef"
label-width=
"120px"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"题目标题"
:required=
"tru
e"
>
<el-input
v-model=
"
item.title"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"题干内容"
>
<el-input
v-model=
"
ruleFor
m.content"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
<el-input
v-model=
"
ite
m.content"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"正确答案"
prop=
"answer
"
>
导入成功
</el-form-item>
<el-form-item
label=
"正确答案"
:required=
"true
"
>
导入成功
</el-form-item>
<el-form-item
label=
"答案解析"
>
<el-input
v-model=
"
ruleFor
m.answer_analysis"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
<el-input
v-model=
"
ite
m.answer_analysis"
:rows=
"5"
type=
"textarea"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"试题文件"
>
<AppUpload
v-model=
"
ruleFor
m.files"
>
<AppUpload
v-model=
"
ite
m.files"
>
<template
#
tip
>
试题文件支持格式包含:png jpg doc docx xls xlsx pdf ppt pptx,大小不超过50M
</
template
>
<
template
#
file=
"{ file }"
>
<!--
{{
file
}}
-->
<div
class=
"upload-box"
>
<div
class=
"upload-loading"
>
<el-icon
style=
"margin-right: 5px"
><Document
/></el-icon>
...
...
@@ -105,21 +89,23 @@ const handleDownload = (file: any) => {
</div>
</template>
</AppUpload>
<!-- <el-input v-model="ruleForm.textarea" :rows="5" type="textarea" placeholder="Please input" /> -->
</el-form-item>
<!-- <el-form-item style="display: flex">
<div>
<el-button @click="resetForm(ruleFormRef)">删除</el-button>
</div>
<div style="margin-left: auto">
<el-button type="primary" @click="submitForm(ruleFormRef)"> 保存 </el-button>
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
</div>
</el-form-item> -->
</el-form>
</el-card>
</template>
<
style
lang=
"scss"
scoped
>
.close
{
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
cursor
:
pointer
;
}
.box-card
{
padding-top
:
20px
;
position
:
relative
;
margin-bottom
:
30px
;
overflow
:
visible
;
}
.head-box
{
display
:
flex
;
margin-bottom
:
30px
;
...
...
src/modules/admin/lab/experiment/views/Questions.vue
浏览文件 @
e685420a
<
script
setup
lang=
"ts"
>
const
User
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/User.vue'
))
import
{
updateQuestions
,
getQuestions
}
from
'../api'
import
{
ElMessage
}
from
'element-plus'
const
PreviewDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/PreviewDialog.vue'
))
const
QuestionsOrder
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/QuestionsOrder.vue'
))
const
UserQuestion
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/UserQuestion.vue'
))
const
TagQuestion
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/TagQuestion.vue'
))
const
GroupQuestion
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/GroupQuestion.vue'
))
const
MaterialQuestion
=
defineAsyncComponent
(()
=>
import
(
'../components/Questions/MaterialQuestion.vue'
))
const
route
:
any
=
useRoute
()
const
router
=
useRouter
()
const
getQuestionTypeInitValue
=
function
()
{
const
type
:
any
=
{
10
:
101
,
6
:
201
,
7
:
301
,
9
:
401
}
return
type
[
parseInt
(
route
.
query
?.
type
||
'10'
)]
}
let
data
=
$ref
([
{
type
:
getQuestionTypeInitValue
(),
score
:
1
,
title
:
''
,
content
:
''
,
answer_analysis
:
''
,
answer
:
''
,
files
:
[]
}
])
const
addQuestion
=
()
=>
{
data
.
push
({
type
:
getQuestionTypeInitValue
(),
score
:
1
,
title
:
''
,
content
:
''
,
answer_analysis
:
''
,
answer
:
''
,
files
:
[]
})
}
const
previewDialogVisible
=
$ref
(
false
)
const
handleSubmit
=
function
()
{
const
type
:
any
=
{
10
:
'1'
,
6
:
'2'
,
7
:
'3'
,
9
:
'4'
}
const
params
=
{
experiment_id
:
route
.
query
.
id
,
module
:
type
[
route
.
query
.
type
],
questions
:
data
}
//判断必填不能为空
function
findItem
(
value
:
string
)
{
return
data
.
findIndex
((
item
:
any
)
=>
item
[
value
]
===
''
)
}
if
(
findItem
(
'title'
)
!==
-
1
)
{
ElMessage
.
error
(
`第
${
findItem
(
'title'
)
+
1
}
题,题目不能为空`
)
return
false
}
updateQuestions
(
params
).
then
(
res
=>
{
if
(
res
.
data
.
status
)
{
ElMessage
({
message
:
'保存成功'
,
type
:
'success'
})
router
.
go
(
0
)
}
})
}
onMounted
(()
=>
{
getCurrentQuestions
()
})
// 获取题
const
getCurrentQuestions
=
function
()
{
const
typesJson
:
any
=
{
10
:
[
101
,
102
],
6
:
[
201
,
202
],
7
:
[
301
,
302
],
9
:
[
401
,
402
,
403
,
404
,
405
,
406
,
407
,
408
]
}
getQuestions
({
experiment_id
:
route
.
query
.
id
,
types
:
typesJson
[
route
.
query
.
type
]
}).
then
(
res
=>
{
if
(
res
.
data
?.
items
)
{
if
(
res
.
data
.
items
?.
length
)
{
data
=
res
.
data
.
items
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
b
.
type
=
parseInt
(
b
.
type
)
b
.
files
.
map
((
item
:
any
)
=>
{
item
.
is_download
===
'true'
?
(
item
.
is_download
=
true
)
:
(
item
.
is_download
=
false
)
return
item
})
a
.
push
(
b
)
return
a
},
[])
}
}
})
}
</
script
>
<
template
>
<AppCard>
<div
class=
"content-top"
>
<div
class=
"info"
style=
"display: flex"
>
<p>
实验名称:
2023商业数据分析大赛决赛
</p>
<p>
实验类型:
数字营销实验
</p>
<p>
实验总成绩:
100
</p>
<p>
实验名称:
{{
route
.
query
.
name
}}
</p>
<p>
实验类型:
{{
route
.
query
.
type_name
}}
</p>
<p>
实验总成绩:
{{
route
.
query
.
score
}}
</p>
</div>
<el-button>
预览
</el-button>
<el-button
@
click=
"previewDialogVisible = true"
>
预览
</el-button>
</div>
</AppCard>
<div
class=
"content-bottom"
>
<AppCard
class=
"l-card"
>
<UserQuestion
v-model=
"data"
v-if=
"route.query.type === '10'"
></UserQuestion>
<TagQuestion
v-model=
"data"
v-if=
"route.query.type === '6'"
></TagQuestion>
<GroupQuestion
v-model=
"data"
v-if=
"route.query.type === '7'"
></GroupQuestion>
<MaterialQuestion
v-model=
"data"
v-if=
"route.query.type === '9'"
></MaterialQuestion>
<div
class=
"btn-box"
>
<div
class=
"btn-l"
>
<el-button
type=
"primary"
>
添加试题
</el-button>
<
el-button>
取消
</el-button
>
<el-button
type=
"primary"
@
click=
"addQuestion"
>
添加试题
</el-button>
<
!--
<el-button>
取消
</el-button>
--
>
</div>
<div
class=
"btn-r"
>
<el-button
type=
"primary"
>
保存
</el-button>
<el-button
type=
"primary"
@
click=
"handleSubmit"
>
保存
</el-button>
</div>
</div>
<User></User>
</AppCard>
<AppCard
class=
"r-card"
>
<QuestionsOrder></QuestionsOrder>
<QuestionsOrder
:data=
"data"
></QuestionsOrder>
</AppCard>
</div>
<PreviewDialog
v-model=
"previewDialogVisible"
></PreviewDialog>
</
template
>
<
style
lang=
"scss"
scoped
>
.btn-box
{
...
...
@@ -40,9 +138,6 @@ const QuestionsOrder = defineAsyncComponent(() => import('../components/Question
margin-left
:
auto
;
}
}
.app-main
{
overflow
:
inherit
;
}
.content-top
{
display
:
flex
;
...
...
@@ -66,14 +161,12 @@ const QuestionsOrder = defineAsyncComponent(() => import('../components/Question
margin-top
:
0
;
height
:
fit-content
;
:deep
(
.app-card-bd.has-background
)
{
// height: 100%;
box-sizing
:
border-box
;
}
}
.l-card
{
margin-top
:
0
;
flex
:
1
;
height
:
10000px
;
}
}
</
style
>
src/utils/dictionary.ts
浏览文件 @
e685420a
...
...
@@ -30,7 +30,8 @@ export const gradeRule: Record<number, any> = {
6
:
'用户标签'
,
7
:
'用户群组'
,
8
:
'用户旅程'
,
9
:
'营销资料'
9
:
'营销资料'
,
10
:
'用户/事件数据'
}
// 参赛模式列表
export
const
gradeRuleList
=
json2Array
(
gradeRule
)
...
...
vite.config.ts
浏览文件 @
e685420a
...
...
@@ -10,7 +10,7 @@ import AutoImport from 'unplugin-auto-import/vite'
export
default
defineConfig
(({
mode
})
=>
({
base
:
mode
===
'prod'
?
'https://webapp-pub.ezijing.com/website/prod/saas-lab/'
:
'/'
,
plugins
:
[
vue
({
reactivityTransform
:
true
}),
vue
({
reactivityTransform
:
true
,
script
:
{
defineModel
:
true
}
}),
AutoImport
({
imports
:
[
'vue'
,
'vue/macros'
,
'vue-router'
,
'@vueuse/core'
,
'@vueuse/math'
],
dts
:
true
,
...
...
@@ -26,17 +26,17 @@ export default defineConfig(({ mode }) => ({
cert
:
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'./https/ezijing.com.pem'
))
},
proxy
:
{
// '/api/resource': {
// target: 'http://com-resource-admin-test.ezijing.com',
// changeOrigin: true
,
// rewrite: path => path.replace(/^\/api\/resource/, '')
// },
// '/api/lab': {
// target: 'http://com-resource-api-test.ezijing.com',
// changeOrigin: true
,
// rewrite: path => path.replace(/^\/api\/lab/, '')
// },
'/api'
:
'https://saas-lab.ezijing.com'
'/api'
:
'https://saas-lab.ezijing.com'
,
'/api/resource'
:
{
target
:
'http://com-resource-admin-test.ezijing.com'
,
changeOrigin
:
true
,
rewrite
:
path
=>
path
.
replace
(
/^
\/
api
\/
resource/
,
''
)
},
'/api/lab'
:
{
target
:
'http://com-resource-api-test.ezijing.com'
,
changeOrigin
:
true
,
rewrite
:
path
=>
path
.
replace
(
/^
\/
api
\/
lab/
,
''
)
}
}
},
resolve
:
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论