Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-dml
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-dml
Commits
db1e84fd
提交
db1e84fd
authored
3月 19, 2024
作者:
lhh
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
链接管理,学生端老师端开发
上级
4e3f32ad
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
1334 行增加
和
50 行删除
+1334
-50
EventRule.vue
src/components/rule/EventRule.vue
+84
-16
api.ts
src/modules/connect/api.ts
+79
-0
DataProgressDialog.vue
src/modules/connect/components/DataProgressDialog.vue
+74
-0
EventDataDialog.vue
src/modules/connect/components/EventDataDialog.vue
+168
-0
ListItem.vue
src/modules/connect/components/ListItem.vue
+136
-24
OtherFields.vue
src/modules/connect/components/OtherFields.vue
+173
-0
StudentFollowDialog.vue
src/modules/connect/components/StudentFollowDialog.vue
+308
-0
UserDataDialog.vue
src/modules/connect/components/UserDataDialog.vue
+179
-0
types.ts
src/modules/connect/types.ts
+44
-0
Index.vue
src/modules/connect/views/Index.vue
+84
-5
vite.config.ts
vite.config.ts
+5
-5
没有找到文件。
src/components/rule/EventRule.vue
浏览文件 @
db1e84fd
...
@@ -2,7 +2,13 @@
...
@@ -2,7 +2,13 @@
import
type
{
EventRule
,
EventRuleItem
,
RuleAttr
}
from
'@/types'
import
type
{
EventRule
,
EventRuleItem
,
RuleAttr
}
from
'@/types'
import
{
Operation
,
Plus
,
CloseBold
}
from
'@element-plus/icons-vue'
import
{
Operation
,
Plus
,
CloseBold
}
from
'@element-plus/icons-vue'
import
{
useMetaEvent
}
from
'@/composables/useAllData'
import
{
useMetaEvent
}
from
'@/composables/useAllData'
import
{
stringOperatorList
,
numberOperatorList
,
dateOperatorList
,
happenInfoList
,
triggerInfoList
}
from
'@/utils/dictionary'
import
{
stringOperatorList
,
numberOperatorList
,
dateOperatorList
,
happenInfoList
,
triggerInfoList
}
from
'@/utils/dictionary'
import
{
searchEventAttrs
}
from
'@/api/base'
import
{
searchEventAttrs
}
from
'@/api/base'
const
{
limit
=
Infinity
}
=
defineProps
<
{
limit
?:
number
}
>
()
const
{
limit
=
Infinity
}
=
defineProps
<
{
limit
?:
number
}
>
()
...
@@ -113,7 +119,12 @@ function handleTriggerOperateChange(value: string, item: EventRuleItem) {
...
@@ -113,7 +119,12 @@ function handleTriggerOperateChange(value: string, item: EventRuleItem) {
function
querySearch
(
rule
:
EventRuleItem
,
attr
:
RuleAttr
,
search
:
string
,
cb
:
(
arg
:
any
)
=>
void
)
{
function
querySearch
(
rule
:
EventRuleItem
,
attr
:
RuleAttr
,
search
:
string
,
cb
:
(
arg
:
any
)
=>
void
)
{
const
found
=
getEventAttrList
(
rule
.
happen_info
.
event_id
).
find
(
item
=>
item
.
id
===
attr
.
attr_id
)
const
found
=
getEventAttrList
(
rule
.
happen_info
.
event_id
).
find
(
item
=>
item
.
id
===
attr
.
attr_id
)
const
experiment_meta_event_id
=
found
?.
experiment_meta_event_id
||
''
const
experiment_meta_event_id
=
found
?.
experiment_meta_event_id
||
''
searchEventAttrs
({
search
,
experiment_meta_event_id
:
experiment_meta_event_id
,
experiment_meta_event_attr_id
:
attr
.
attr_id
,
per_page
:
1000
}).
then
(
res
=>
{
searchEventAttrs
({
search
,
experiment_meta_event_id
:
experiment_meta_event_id
,
experiment_meta_event_attr_id
:
attr
.
attr_id
,
per_page
:
1000
}).
then
(
res
=>
{
const
list
:
string
[]
=
[]
const
list
:
string
[]
=
[]
res
.
data
.
list
.
forEach
((
item
:
any
)
=>
{
res
.
data
.
list
.
forEach
((
item
:
any
)
=>
{
try
{
try
{
...
@@ -134,7 +145,12 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -134,7 +145,12 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
const
found
=
getEventAttrList
(
rule
.
happen_info
.
event_id
).
find
(
item
=>
item
.
id
===
attr
.
attr_id
)
const
found
=
getEventAttrList
(
rule
.
happen_info
.
event_id
).
find
(
item
=>
item
.
id
===
attr
.
attr_id
)
const
experiment_meta_event_id
=
found
?.
experiment_meta_event_id
||
''
const
experiment_meta_event_id
=
found
?.
experiment_meta_event_id
||
''
loading
.
value
=
true
loading
.
value
=
true
searchEventAttrs
({
search
,
experiment_meta_event_id
:
experiment_meta_event_id
,
experiment_meta_event_attr_id
:
attr
.
attr_id
,
per_page
:
1000
}).
then
(
res
=>
{
searchEventAttrs
({
search
,
experiment_meta_event_id
:
experiment_meta_event_id
,
experiment_meta_event_attr_id
:
attr
.
attr_id
,
per_page
:
1000
}).
then
(
res
=>
{
const
list
:
string
[]
=
[]
const
list
:
string
[]
=
[]
res
.
data
.
list
.
forEach
((
item
:
any
)
=>
{
res
.
data
.
list
.
forEach
((
item
:
any
)
=>
{
try
{
try
{
...
@@ -166,24 +182,47 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -166,24 +182,47 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
<el-row>
<el-row>
<!-- 发生 -->
<!-- 发生 -->
<el-form-item>
<el-form-item>
<el-select
v-model=
"rule.happen_info.is_happened"
@
change=
"value => handleHappenOperateChange(value, rule)"
>
<el-select
v-model=
"rule.happen_info.is_happened"
@
change=
"value => handleHappenOperateChange(value, rule)"
>
<el-option
v-for=
"option in happenInfoList"
:key=
"option.label"
v-bind=
"option"
></el-option>
<el-option
v-for=
"option in happenInfoList"
:key=
"option.label"
v-bind=
"option"
></el-option>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item>
<el-form-item>
<el-select
v-model=
"rule.happen_info.event_id"
@
change=
"value => handleEventChange(value, rule)"
>
<el-select
v-model=
"rule.happen_info.event_id"
@
change=
"value => handleEventChange(value, rule)"
>
<el-option
v-for=
"option in currentMetaEventList"
:key=
"option.id"
:label=
"option.name"
:value=
"option.id"
></el-option>
<el-option
v-for=
"option in currentMetaEventList"
:key=
"option.id"
:label=
"option.name"
:value=
"option.id"
></el-option>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-button
text
:icon=
"Plus"
@
click=
"handleAttrAdd(rule.happen_info.attr_list)"
>
添加条件
</el-button>
<el-button
text
:icon=
"Plus"
@
click=
"handleAttrAdd(rule.happen_info.attr_list)"
>
添加条件
</el-button>
<el-button
text
:icon=
"CloseBold"
@
click=
"handleRemove(eventAttrRule.items, index)"
v-if=
"limit !== 1"
></el-button>
<el-button
text
:icon=
"CloseBold"
@
click=
"handleRemove(eventAttrRule.items, index)"
v-if=
"limit !== 1"
></el-button>
</el-row>
</el-row>
<!-- 属性条件 -->
<!-- 属性条件 -->
<el-row
justify=
"space-between"
class=
"rule-item"
v-for=
"(attr, index) in rule.happen_info.attr_list"
:key=
"index"
>
<el-row
justify=
"space-between"
class=
"rule-item"
v-for=
"(attr, index) in rule.happen_info.attr_list"
:key=
"index"
>
<div>
<div>
<el-form-item>
<el-form-item>
<el-select
v-model=
"attr.attr_id"
@
change=
"value => handleAttrChange(value, attr, rule)"
>
<el-select
v-model=
"attr.attr_id"
@
change=
"value => handleAttrChange(value, attr, rule)"
>
<el-option
v-for=
"option in getEventAttrList(rule.happen_info.event_id)"
:key=
"option.id"
:label=
"option.name"
:value=
"option.id"
></el-option>
<el-option
v-for=
"option in getEventAttrList(rule.happen_info.event_id)"
:key=
"option.id"
:label=
"option.name"
:value=
"option.id"
></el-option>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item>
<el-form-item>
...
@@ -192,7 +231,8 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -192,7 +231,8 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
v-for=
"option in getOperatorList(attr.attr_type)"
v-for=
"option in getOperatorList(attr.attr_type)"
:key=
"option.value"
:key=
"option.value"
:label=
"option.alias || option.label"
:label=
"option.alias || option.label"
:value=
"option.value"
></el-option>
:value=
"option.value"
></el-option>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item
v-if=
"!['null', 'not null'].includes(attr.operate)"
>
<el-form-item
v-if=
"!['null', 'not null'].includes(attr.operate)"
>
...
@@ -208,14 +248,29 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -208,14 +248,29 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</
template
>
</
template
>
<!-- 时间区间 -->
<!-- 时间区间 -->
<
template
v-else-if=
"attr.attr_type === '5' && attr.operate === 'range'"
>
<
template
v-else-if=
"attr.attr_type === '5' && attr.operate === 'range'"
>
<el-date-picker
v-model=
"attr.value.start"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
<el-date-picker
<el-date-picker
v-model=
"attr.value.end"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
v-model=
"attr.value.start"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
<el-date-picker
v-model=
"attr.value.end"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
</
template
>
</
template
>
<
template
v-else-if=
"attr.attr_type === '4' && (attr.operate === 'after' || attr.operate === 'before')"
>
<
template
v-else-if=
"attr.attr_type === '4' && (attr.operate === 'after' || attr.operate === 'before')"
>
<el-date-picker
v-model=
"attr.value"
type=
"date"
value-format=
"YYYY-MM-DD"
/>
<el-date-picker
v-model=
"attr.value"
type=
"date"
value-format=
"YYYY-MM-DD"
/>
</
template
>
</
template
>
<
template
v-else-if=
"attr.attr_type === '5' && (attr.operate === 'after' || attr.operate === 'before')"
>
<
template
v-else-if=
"attr.attr_type === '5' && (attr.operate === 'after' || attr.operate === 'before')"
>
<el-date-picker
v-model=
"attr.value"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
<el-date-picker
v-model=
"attr.value"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
style=
"width: 180px"
/>
</
template
>
</
template
>
<
template
v-else
>
<
template
v-else
>
<el-select
<el-select
...
@@ -227,10 +282,16 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -227,10 +282,16 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
:remote-method=
"(query:string) => remoteMethod(rule, attr, query)"
:remote-method=
"(query:string) => remoteMethod(rule, attr, query)"
:loading=
"loading"
:loading=
"loading"
style=
"width: 320px"
style=
"width: 320px"
v-if=
"['in', 'not in'].includes(attr.operate)"
>
v-if=
"['in', 'not in'].includes(attr.operate)"
>
<el-option
v-for=
"item in options"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in options"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</el-select>
<el-autocomplete
v-model=
"attr.value"
:fetch-suggestions=
"(query, cb) => querySearch(rule, attr, query, cb)"
style=
"width: 320px"
v-else
/>
<el-autocomplete
v-model=
"attr.value"
:fetch-suggestions=
"(query, cb) => querySearch(rule, attr, query, cb)"
style=
"width: 320px"
v-else
/>
</
template
>
</
template
>
</el-form-item>
</el-form-item>
</div>
</div>
...
@@ -245,7 +306,12 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -245,7 +306,12 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</el-form-item>
</el-form-item>
<el-form-item>
<el-form-item>
<el-select
v-model=
"rule.trigger_info.operate"
@
change=
"value => handleTriggerOperateChange(value, rule)"
>
<el-select
v-model=
"rule.trigger_info.operate"
@
change=
"value => handleTriggerOperateChange(value, rule)"
>
<el-option
v-for=
"option in triggerNumberOperatorList"
:key=
"option.value"
:label=
"option.alias || option.label"
:value=
"option.value"
/>
<el-option
v-for=
"option in triggerNumberOperatorList"
:key=
"option.value"
:label=
"option.alias || option.label"
:value=
"option.value"
/>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item>
<el-form-item>
...
@@ -255,7 +321,9 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
...
@@ -255,7 +321,9 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</section>
</section>
</div>
</div>
</div>
</div>
<el-button
text
:icon=
"Plus"
@
click=
"handleAdd(eventAttrRule.items)"
v-if=
"eventAttrRule.items.length < limit"
>
添加条件
</el-button>
<el-button
text
:icon=
"Plus"
@
click=
"handleAdd(eventAttrRule.items)"
v-if=
"eventAttrRule.items.length < limit"
>
添加条件
</el-button
>
<slot
name=
"footer"
></slot>
<slot
name=
"footer"
></slot>
</el-card>
</el-card>
</template>
</template>
...
...
src/modules/connect/api.ts
浏览文件 @
db1e84fd
...
@@ -54,3 +54,82 @@ export function asyncOfficialAccountInfo(params: { connection_id: string; appid:
...
@@ -54,3 +54,82 @@ export function asyncOfficialAccountInfo(params: { connection_id: string; appid:
export
function
asyncOfficialAccountUsers
(
params
:
{
connection_id
:
string
;
appid
:
string
})
{
export
function
asyncOfficialAccountUsers
(
params
:
{
connection_id
:
string
;
appid
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/wechat-platform/async-official-account-users'
,
{
params
})
return
httpRequest
.
get
(
'/api/lab/v1/experiment/wechat-platform/async-official-account-users'
,
{
params
})
}
}
// 获取生成用户数据
export
function
getScheduleMember
(
params
:
{
experiment_id
:
string
;
connect_id
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/connection/schedule-member'
,
{
params
})
}
// 生成用户数据保存
interface
ScheduleMember
{
experiment_id
:
string
connect_id
:
string
size
:
number
cover_type
:
number
name
:
number
name_value
?:
string
status
:
number
gender
:
number
mobile
:
number
create_data
:
string
}
export
function
submitScheduleMember
(
data
:
ScheduleMember
)
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/connection/schedule-member'
,
data
)
}
// 获取生成事件数据
export
function
getScheduleEvent
(
params
:
{
experiment_id
:
string
;
connect_id
:
string
;
event_id
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/connection/schedule-event'
,
{
params
})
}
// 生成用户数据保存
interface
ScheduleEvent
{
experiment_id
:
string
connect_id
:
string
event_id
:
number
member_rate
:
number
size
:
number
cover_type
:
number
create_data
:
string
other_fields
:
string
}
export
function
submitScheduleEvent
(
data
:
ScheduleEvent
)
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/connection/schedule-event'
,
data
)
}
// 获取数据进度
export
function
getScheduleList
(
params
:
{
experiment_id
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/connection/schedule-list'
,
{
params
})
}
// 删除进度
export
function
scheduleDelete
(
data
:
{
id
:
string
})
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/connection/schedule-delete'
,
data
)
}
// 用户触达信息
export
function
getStudentFollow
(
params
:
{
experiment_id
:
string
experiment_connection_id
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/experiment/connection/student-follow'
,
{
params
})
}
// 用户触达提交表单信息
export
function
studentSubmitForm
(
data
:
{
experiment_connection_id
:
any
;
gender
:
string
;
name
:
string
;
mobile
:
string
})
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/connection/student-submit-form'
,
data
)
}
// 用户触达提交聊天信息
export
function
studentSubmitLog
(
data
:
{
experiment_connection_id
:
any
;
logs
:
string
})
{
return
httpRequest
.
post
(
'/api/lab/v1/experiment/connection/student-submit-log'
,
data
)
}
src/modules/connect/components/DataProgressDialog.vue
0 → 100644
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
import
{
getScheduleList
,
scheduleDelete
}
from
'../api'
import
{
ElMessage
}
from
'element-plus'
const
appList
=
$ref
<
InstanceType
<
typeof
AppList
>
|
null
>
(
null
)
const
listOptions
=
computed
(()
=>
{
return
{
remote
:
{
httpRequest
:
getScheduleList
},
columns
:
[
{
label
:
'数据属性'
,
prop
:
'type_name'
},
{
label
:
'来源链接'
,
prop
:
'connect_name'
},
{
label
:
'数据量'
,
prop
:
'size'
},
{
label
:
'状态'
,
prop
:
'status_name'
,
computed
:
(
row
:
any
)
=>
{
let
sName
=
row
.
row
.
status_name
if
(
row
.
row
.
message
!==
''
)
{
sName
=
`<span>
${
row
.
row
.
status_name
}
</span><br/><span style="margin-top:10px">
${
row
.
row
.
message
}
</span>`
}
return
sName
}
},
{
label
:
'生成时间'
,
prop
:
'created_time'
},
{
label
:
'操作'
,
slots
:
'table-x'
}
]
}
})
const
handleDelete
=
function
(
item
:
any
)
{
scheduleDelete
({
id
:
item
.
id
}).
then
(
res
=>
{
if
(
res
.
data
)
{
appList
?.
refetch
()
ElMessage
({
message
:
'删除成功'
,
type
:
'success'
})
}
})
}
</
script
>
<
template
>
<el-dialog
style=
"width: 800px"
class=
"data-form"
title=
"数据生成进度"
:close-on-click-modal=
"false"
@
update:modelValue=
"$emit('update:modelValue')"
>
<AppList
v-bind=
"listOptions"
ref=
"appList"
>
<template
#
table-x=
"
{ row }">
<el-button
type=
"primary"
plain
@
click=
"handleDelete(row)"
v-permission=
"'v1-experiment-member-delete'"
>
删除
</el-button
>
</
template
></AppList
>
</el-dialog>
</template>
<
style
lang=
"scss"
>
.connect-form
{
.el-dialog__body
{
padding-top
:
10px
;
}
}
.button-flex
{
// margin-top: 40px;
display
:
flex
;
justify-content
:
center
;
}
</
style
>
src/modules/connect/components/EventDataDialog.vue
0 → 100644
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
import
{
ElMessage
}
from
'element-plus'
import
{
submitScheduleEvent
,
getScheduleEvent
}
from
'../api'
import
type
{
ScheduleEvent
}
from
'../types'
import
type
{
FormInstance
}
from
'element-plus'
import
{
useUserStore
}
from
'@/stores/user'
const
userStore
=
useUserStore
()
const
OtherFields
=
defineAsyncComponent
(()
=>
import
(
'../components/OtherFields.vue'
))
const
props
=
defineProps
<
{
data
?:
ScheduleEvent
}
>
()
const
emit
=
defineEmits
<
{
(
e
:
'update'
):
void
(
e
:
'update:modelValue'
,
visible
:
boolean
):
void
}
>
()
// const formSize = ref('default')
const
ruleFormRef
=
ref
<
FormInstance
>
()
let
ruleForm
=
$ref
<
any
>
({
experiment_id
:
props
.
data
?.
experiment_id
,
connect_id
:
props
.
data
?.
connect_id
,
event_id
:
''
,
member_rate
:
''
,
size
:
''
,
cover_type
:
1
})
let
fieldsValue
=
$ref
<
any
[]
>
([])
const
submitForm
=
async
(
formEl
:
FormInstance
|
undefined
,
bl
:
string
)
=>
{
if
(
!
formEl
)
return
await
formEl
.
validate
((
valid
,
fields
)
=>
{
if
(
valid
)
{
ruleForm
.
create_data
=
bl
ruleForm
.
other_fields
=
JSON
.
stringify
(
fieldsValue
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
[
b
.
id
]
=
{
type
:
b
.
rule
?.
type
,
fixed_value
:
b
.
rule
?.
fixed_value
,
rand_value
:
b
.
rule
?.
rand_value
}
return
a
},
{})
)
submitScheduleEvent
(
ruleForm
).
then
((
res
:
any
)
=>
{
if
(
res
.
code
===
0
)
{
ElMessage
({
message
:
'保存成功'
,
type
:
'success'
})
emit
(
'update:modelValue'
,
false
)
}
else
{
ElMessage
({
message
:
res
.
message
})
}
})
}
else
{
console
.
log
(
'error submit!'
,
fields
)
}
})
}
const
rules
=
[{
required
:
true
}]
onMounted
(()
=>
{
if
(
props
.
data
?.
last_event
!==
''
)
{
eventChange
(
props
.
data
?.
last_event
)
}
})
// 用户事件改变的时候
const
eventChange
=
function
(
eId
?:
string
)
{
getScheduleEvent
({
experiment_id
:
ruleForm
.
experiment_id
,
connect_id
:
ruleForm
.
connect_id
,
event_id
:
eId
||
ruleForm
.
event_id
}).
then
(
res
=>
{
if
(
res
.
data
)
{
mergeJson
(
ruleForm
,
res
.
data
)
fieldsValue
=
res
.
data
.
other_fields
.
map
((
item
:
any
)
=>
{
if
(
item
?.
rule
)
{
item
.
rule
.
type
=
parseInt
(
item
.
rule
.
type
)
}
return
item
})
}
})
}
const
mergeJson
=
function
(
target
:
any
,
source
:
any
)
{
if
(
source
)
{
Object
.
keys
(
target
).
forEach
(
function
(
key
)
{
if
(
source
.
hasOwnProperty
(
key
))
{
target
[
key
]
=
source
[
key
]
}
})
}
}
</
script
>
<
template
>
<el-dialog
style=
"width: 800px"
class=
"data-form"
title=
"自动生成用户事件数据"
:close-on-click-modal=
"false"
@
update:modelValue=
"$emit('update:modelValue')"
>
<div
class=
"button-flex"
>
<el-form
:disabled=
"userStore.role?.id === 1"
label-suffix=
":"
ref=
"ruleFormRef"
:model=
"ruleForm"
label-width=
"auto"
class=
"demo-ruleForm"
status-icon
>
<el-form
:model=
"ruleForm"
label-suffix=
":"
>
<el-form-item
label=
"用户事件"
:rules=
"rules"
label-width=
"205"
>
<el-select
@
change=
"eventChange"
v-model=
"ruleForm.event_id"
placeholder=
"请选择"
>
<el-option
v-for=
"item in props.data?.events"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</el-form-item>
</el-form>
<el-form-item
label=
"请输入随机调取用户比例"
:rules=
"rules"
>
<el-input
v-model=
"ruleForm.member_rate"
placeholder=
"请输入"
>
<template
#
append
>
%
</
template
>
</el-input>
</el-form-item>
<el-form-item
label=
"请输入每个用户生成事件数"
:rules=
"rules"
>
<el-input
v-model=
"ruleForm.size"
placeholder=
"请输入"
>
</el-input>
</el-form-item>
<el-form-item
label=
"请选择数据覆盖形式"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.cover_type"
>
<el-radio
:label=
"1"
>
全新覆盖
</el-radio>
<el-radio
:label=
"2"
>
追加
</el-radio>
</el-radio-group>
</el-form-item>
<el-divider
/>
<OtherFields
:data=
"fieldsValue || []"
></OtherFields>
<el-form
:model=
"ruleForm"
>
<el-form-item
style=
"justify-content: center"
>
<div
style=
"justify-content: center; display: flex; width: 100%"
>
<el-button
@
click=
"$emit('update:modelValue', false)"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm(ruleFormRef, 'false')"
>
保存
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm(ruleFormRef, 'true')"
>
提交生成数据
</el-button>
</div>
</el-form-item>
</el-form>
</el-form>
</div>
</el-dialog>
</template>
<
style
lang=
"scss"
>
.connect-form
{
.el-dialog__body
{
padding-top
:
10px
;
}
}
.button-flex
{
// margin-top: 40px;
display
:
flex
;
justify-content
:
center
;
}
</
style
>
src/modules/connect/components/ListItem.vue
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
Delete
,
Edit
}
from
'@element-plus/icons-vue'
import
{
Delete
,
Edit
,
MoreFilled
,
EditPen
,
User
,
Avatar
,
PieChart
,
UserFilled
}
from
'@element-plus/icons-vue'
import
{
ElMessageBox
,
ElMessage
}
from
'element-plus'
import
{
ElMessageBox
,
ElMessage
}
from
'element-plus'
import
Icon
from
'@/components/ConnectionIcon.vue'
import
Icon
from
'@/components/ConnectionIcon.vue'
import
{
deleteConnection
}
from
'../api'
import
{
deleteConnection
}
from
'../api'
import
{
useUserStore
}
from
'@/stores/user'
const
props
=
defineProps
<
{
data
:
{
id
:
string
;
type_name
:
string
;
type
:
string
;
config_attributes
:
any
}
}
>
()
const
userStore
=
useUserStore
()
const
props
=
defineProps
<
{
data
:
{
experiment_id
:
string
id
:
string
type_name
:
string
type
:
string
config_attributes
:
any
events
:
any
last_event
:
string
}
}
>
()
const
router
=
useRouter
()
const
router
=
useRouter
()
const
emits
=
defineEmits
([
'update'
,
'edit'
])
const
emits
=
defineEmits
([
'update'
,
'edit'
,
'generateUserData'
,
'generateEventData'
,
'viewDataProgress'
,
'handleStudentFollow'
])
// 删除
// 删除
function
handleRemove
()
{
function
handleRemove
()
{
...
@@ -31,38 +51,102 @@ const iconMap: Record<string, string> = {
...
@@ -31,38 +51,102 @@ const iconMap: Record<string, string> = {
'13'
:
'99'
,
'13'
:
'99'
,
'14'
:
'100'
'14'
:
'100'
}
}
const
generateUserData
=
function
()
{
emits
(
'generateUserData'
,
props
.
data
.
experiment_id
,
props
.
data
.
id
,
props
.
data
.
type_name
)
}
const
generateEventData
=
function
()
{
emits
(
'generateEventData'
,
props
.
data
.
experiment_id
,
props
.
data
.
id
,
props
.
data
.
events
,
props
.
data
.
last_event
)
}
const
viewDataProgress
=
function
()
{
emits
(
'viewDataProgress'
,
props
.
data
.
experiment_id
)
}
const
handleStudentFollow
=
function
()
{
emits
(
'handleStudentFollow'
,
props
.
data
.
experiment_id
,
props
.
data
.
id
,
props
.
data
.
type
)
}
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"connect-item"
@
click=
"routerView"
>
<div
class=
"connect-item"
@
click=
"routerView"
>
<div
class=
"connect-item__edit"
>
<div
class=
"connect-item_top"
>
<!--
<img
@
click=
"edit"
src=
"https://webapp-pub.ezijing.com/pages/assa/dml_edit.png"
/>
-->
<!--
<div
class=
"connect-item__edit"
>
<el-icon
size=
"20"
color=
"#333"
@
click
.
stop=
"edit"
><Edit
/></el-icon>
<img
@
click=
"edit"
src=
"https://webapp-pub.ezijing.com/pages/assa/dml_edit.png"
/>
</div>
<el-icon
size=
"20"
color=
"#333"
@
click
.
stop=
"edit"
><Edit
/></el-icon>
<div
class=
"connect-item__remove"
@
click
.
stop=
"handleRemove"
>
</div>
<!--
<img
src=
"https://webapp-pub.ezijing.com/pages/assa/dml_delete.png"
/>
-->
<div
class=
"connect-item__remove"
@
click
.
stop=
"handleRemove"
>
<el-icon
size=
"20"
color=
"#333"
><Delete
/></el-icon>
<img
src=
"https://webapp-pub.ezijing.com/pages/assa/dml_delete.png"
/>
<el-icon
size=
"20"
color=
"#333"
><Delete
/></el-icon>
</div>
-->
<div
class=
"connect-item__icon"
>
<Icon
w=
"40"
h=
"40"
:multiColor=
"true"
class=
"svg"
:name=
"iconMap[data.type] || data.type"
></Icon>
</div>
</div>
</div>
<div
class=
"connect-item__icon"
>
<div
class=
"connect-item_bottom"
>
<Icon
w=
"40"
h=
"40"
:multiColor=
"true"
class=
"svg"
:name=
"iconMap[data.type] || data.type"
></Icon>
<p>
{{
data
.
type
===
'12'
?
data
.
config_attributes
[
0
].
value
:
data
.
type_name
}}
</p>
<el-popover
popper-class=
"popper"
>
<template
#
reference
>
<el-icon
size=
"14"
color=
"#333"
><MoreFilled
/></el-icon>
</
template
>
<
template
#
default
>
<ul
class=
"connect-item_tool"
>
<li
@
click
.
stop=
"handleStudentFollow"
v-if=
"userStore.role?.id === 1"
>
<el-icon
size=
"16"
color=
"#000"
><UserFilled
/></el-icon>
<span>
用户触达
</span>
</li>
<li
@
click
.
stop=
"edit"
v-if=
"userStore.role?.id !== 1"
>
<el-icon
size=
"16"
color=
"#000"
><EditPen
/></el-icon>
<span>
编辑
</span>
</li>
<li
@
click
.
stop=
"generateUserData"
>
<el-icon
size=
"16"
color=
"#000"
><User
/></el-icon>
<span>
自动生成用户数据
</span>
</li>
<li
@
click
.
stop=
"generateEventData"
>
<el-icon
size=
"16"
color=
"#000"
@
click
.
stop=
"edit"
><Avatar
/></el-icon>
<span>
自动生成事件数据
</span>
</li>
<li
@
click
.
top=
"viewDataProgress"
>
<el-icon
size=
"16"
color=
"#000"
@
click
.
stop=
"edit"
><PieChart
/></el-icon>
<span>
数据生成进度
</span>
</li>
<li
@
click
.
stop=
"handleRemove"
v-if=
"userStore.role?.id !== 1"
>
<el-icon
size=
"16"
color=
"#000"
@
click
.
stop=
"edit"
><Delete
/></el-icon>
<span>
删除
</span>
</li>
</ul>
</
template
>
</el-popover>
</div>
</div>
<p>
{{
data
.
type
===
'12'
?
data
.
config_attributes
[
0
].
value
:
data
.
type_name
}}
</p>
</div>
</div>
</template>
</template>
<
style
lang=
"scss"
>
<
style
lang=
"scss"
>
.connect-item
{
.connect-item
{
position
:
relative
;
position
:
relative
;
height
:
124px
;
background-color
:
#f3f3f3
;
display
:
flex
;
border
:
1px
solid
#f3f3f3
;
flex-direction
:
column
;
border-radius
:
5px
;
align-items
:
center
;
overflow
:
hidden
;
justify-content
:
center
;
background-color
:
#fff
;
border
:
1px
dashed
#bbb
;
cursor
:
pointer
;
cursor
:
pointer
;
.connect-item_bottom
{
height
:
40px
;
background-color
:
#fff
;
display
:
flex
;
// flex-direction: column;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
0
20px
;
}
.connect-item_top
{
height
:
124px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
}
&
:hover
{
&
:hover
{
box-shadow
:
rgb
(
0
0
0
/
40%
)
0px
2px
6px
0px
;
box-shadow
:
rgb
(
0
0
0
/
40%
)
0px
2px
6px
0px
;
.connect-item__remove
{
.connect-item__remove
{
...
@@ -73,8 +157,8 @@ const iconMap: Record<string, string> = {
...
@@ -73,8 +157,8 @@ const iconMap: Record<string, string> = {
}
}
}
}
p
{
p
{
font-size
:
1
8
px
;
font-size
:
1
2
px
;
margin-top
:
8px
;
//
margin-top: 8px;
}
}
}
}
.connect-item__remove
{
.connect-item__remove
{
...
@@ -91,4 +175,32 @@ const iconMap: Record<string, string> = {
...
@@ -91,4 +175,32 @@ const iconMap: Record<string, string> = {
top
:
8px
;
top
:
8px
;
z-index
:
999
;
z-index
:
999
;
}
}
.connect-item_tool
{
li
{
display
:
flex
;
}
}
.popper
{
padding
:
20px
0
!
important
;
width
:
fit-content
!
important
;
min-width
:
fit-content
!
important
;
border-radius
:
10px
!
important
;
ul
{
li
{
display
:
flex
;
height
:
35px
;
align-items
:
center
;
padding
:
0
15px
;
&
:hover
{
background-color
:
#f1f1f1
;
}
cursor
:
pointer
;
span
{
margin-left
:
8px
;
font-size
:
12px
;
line-height
:
100%
;
}
}
}
}
</
style
>
</
style
>
src/modules/connect/components/OtherFields.vue
0 → 100644
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
type
{
OtherFields
}
from
'../types'
import
{
RemoveFilled
,
CirclePlusFilled
}
from
'@element-plus/icons-vue'
import
{
useUserStore
}
from
'@/stores/user'
const
userStore
=
useUserStore
()
const
props
=
defineProps
<
{
data
?:
OtherFields
[]
}
>
()
const
fieldsType
:
any
=
{
1
:
'字符串'
,
2
:
'整数'
,
3
:
'数字'
,
4
:
'日期'
,
5
:
'时间'
}
const
ruleTips
:
any
=
{
1
:
'最大字符数'
,
2
:
'最大位数'
,
3
:
'小数点后位数'
,
4
:
'格式'
,
5
:
'格式'
}
const
add
=
function
(
item
:
any
)
{
item
.
push
({
min
:
''
,
max
:
''
,
value
:
''
,
rate
:
''
})
}
const
remove
=
function
(
item
:
any
,
index
:
number
)
{
item
.
splice
(
index
,
1
)
}
</
script
>
<
template
>
<el-form-item
v-for=
"item in props.data"
:label=
"item.name"
>
<div>
<el-radio-group
v-model=
"item.rule.type"
>
<el-radio
:label=
"2"
>
指定随机
</el-radio>
<el-radio
:label=
"1"
>
固定
</el-radio>
</el-radio-group>
<p
class=
"specify-tips"
>
字段类型:
{{
fieldsType
[
item
.
type
]
}}
{{
ruleTips
[
item
.
type
]
}}
:
{{
item
.
format
}}
</p>
<div
class=
"specify"
v-if=
"item.rule.type === 2"
>
<div
class=
"specify-item"
v-for=
"(specify, index) in item.rule.rand_value"
>
<!-- 字符串 -->
<template
v-if=
"item.type === 1"
>
<el-input
v-model=
"specify.value"
placeholder=
"请输入"
></el-input>
<el-input
v-model=
"specify.rate"
placeholder=
"请输入随机几率"
style=
"margin-left: 15px"
></el-input>
%
</
template
>
<!-- 整数 -->
<
template
v-if=
"item.type === 2"
>
<el-input
v-model=
"specify.min"
placeholder=
"请输入随机整数最小值"
></el-input>
<el-input
v-model=
"specify.max"
placeholder=
"请输入随机整数最大值"
style=
"margin-left: 15px"
></el-input>
</
template
>
<!-- 数字 -->
<
template
v-if=
"item.type === 3"
>
<el-input
v-model=
"specify.min"
placeholder=
"请输入随机数字最小值"
></el-input>
<el-input
v-model=
"specify.max"
placeholder=
"请输入随机数字最大值"
style=
"margin-left: 15px"
></el-input>
</
template
>
<!-- 日期 -->
<
template
v-if=
"item.type === 4"
>
<el-date-picker
placeholder=
"请选择随机日期最小值"
style=
"width: 200px"
value-format=
"YYYY-MM-DD"
v-model=
"specify.min"
/>
<el-date-picker
placeholder=
"请选择随机日期最大值"
style=
"width: 200px; margin-left: 15px"
value-format=
"YYYY-MM-DD"
v-model=
"specify.max"
/>
</
template
>
<
template
v-if=
"item.type === 5"
>
<el-date-picker
placeholder=
"请选择随机时间最小值"
v-model=
"specify.min"
style=
"width: 200px"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
/>
<el-date-picker
placeholder=
"请选择随机时间最大值"
v-model=
"specify.max"
style=
"width: 200px; margin-left: 15px"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
/>
</
template
>
<
template
v-if=
"userStore.role?.id !== 1"
>
<el-icon
v-if=
"item.rule.rand_value.length - 1 === index || item.rule.rand_value.length === 0"
@
click=
"add(item.rule.rand_value)"
size=
"20"
style=
"margin-left: 10px; cursor: pointer"
><CirclePlusFilled
/></el-icon>
<el-icon
@
click=
"remove(item.rule.rand_value, index)"
size=
"20"
style=
"margin-left: 10px; cursor: pointer"
v-if=
"item.rule.rand_value.length > 1"
><RemoveFilled
/></el-icon>
</
template
>
</div>
<!-- <el-icon
v-if="item.rule.rand_value.length === 0"
@click="add(item.rule.rand_value)"
size="20"
style="margin-left: 10px; cursor: pointer"
><CirclePlusFilled
/></el-icon> -->
</div>
<div
v-else
>
<
template
v-if=
"item.type === 4"
>
<el-date-picker
placeholder=
"请选择固定属性值"
style=
"width: 200px"
value-format=
"YYYY-MM-DD"
v-model=
"item.rule.fixed_value"
/>
</
template
>
<
template
v-else-if=
"item.type === 5"
>
<el-date-picker
placeholder=
"请选择固定属性值"
v-model=
"item.rule.fixed_value"
style=
"width: 200px"
type=
"datetime"
value-format=
"YYYY-MM-DD HH:mm:ss"
/>
</
template
>
<
template
v-else
>
<el-input
v-model=
"item.rule.fixed_value"
placeholder=
"请输入固定属性值"
></el-input>
</
template
>
</div>
</div>
</el-form-item>
</template>
<
style
lang=
"scss"
>
.connect-form
{
.el-dialog__body
{
padding-top
:
10px
;
}
}
.button-flex
{
// margin-top: 40px;
display
:
flex
;
justify-content
:
center
;
}
.specify-item
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
10px
;
.el-input
{
width
:
200px
;
}
}
.specify-tips
{
line-height
:
100%
;
font-size
:
12px
;
color
:
#ccc
;
margin-bottom
:
10px
;
}
</
style
>
src/modules/connect/components/StudentFollowDialog.vue
0 → 100644
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
import
{
studentSubmitForm
,
studentSubmitLog
}
from
'../api'
import
{
ElMessage
}
from
'element-plus'
import
type
{
StudentFollow
}
from
'../types'
import
type
{
FormInstance
,
FormRules
}
from
'element-plus'
import
Icon
from
'@/components/ConnectionIcon.vue'
import
{
Check
}
from
'@element-plus/icons-vue'
const
props
=
defineProps
<
{
data
?:
StudentFollow
}
>
()
let
step
=
$ref
(
1
)
let
logs
=
$ref
<
any
>
([])
watchEffect
(()
=>
{
if
(
props
.
data
?.
follow_flag
)
{
step
=
3
}
if
(
props
.
data
?.
data
)
{
if
(
props
.
data
?.
data
?.
logs
&&
props
.
data
?.
data
?.
logs
!==
''
)
{
console
.
log
(
props
.
data
?.
data
?.
logs
,
'11'
)
logs
=
JSON
.
parse
(
props
.
data
?.
data
?.
logs
)
}
}
})
interface
RuleForm
{
name
:
string
mobile
:
string
gender
:
string
experiment_connection_id
:
any
}
const
ruleFormRef
=
ref
<
FormInstance
>
()
const
ruleForm
=
reactive
<
RuleForm
>
({
name
:
''
,
mobile
:
''
,
gender
:
'1'
,
experiment_connection_id
:
''
})
const
rules
=
reactive
<
FormRules
<
RuleForm
>>
({
name
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}],
mobile
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}],
gender
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}]
})
const
submitForm
=
async
(
formEl
:
FormInstance
|
undefined
)
=>
{
if
(
!
formEl
)
return
await
formEl
.
validate
((
valid
,
fields
)
=>
{
if
(
valid
)
{
ruleForm
.
experiment_connection_id
=
props
.
data
?.
connect_id
studentSubmitForm
(
ruleForm
).
then
((
res
:
any
)
=>
{
if
(
res
.
code
===
0
)
{
step
=
3
}
})
}
else
{
console
.
log
(
'error submit!'
,
fields
)
}
})
}
let
textarea
=
$ref
(
''
)
const
handleKeyUp
=
function
(
e
:
any
)
{
if
(
e
.
key
===
'Enter'
&&
textarea
!==
''
)
{
const
currentDate
=
new
Date
()
const
currentDateTime
=
currentDate
.
toLocaleString
()
console
.
log
(
currentDateTime
)
logs
.
push
({
time
:
currentDateTime
,
text
:
textarea
})
studentSubmitLog
({
experiment_connection_id
:
props
.
data
?.
connect_id
,
logs
:
JSON
.
stringify
([{
time
:
currentDateTime
,
text
:
textarea
}])
}).
then
()
textarea
=
''
setTimeout
(()
=>
{
scrollToBottom
()
},
10
)
}
}
const
scrollToBottom
=
function
()
{
const
scrollContainer
:
any
=
document
.
getElementById
(
'scrollContainer'
)
if
(
scrollContainer
)
{
scrollContainer
.
scrollTop
=
10000
}
}
const
sendChat
=
function
()
{
step
=
4
setTimeout
(()
=>
{
scrollToBottom
()
},
10
)
}
</
script
>
<
template
>
<el-dialog
class=
"data-form"
title=
"关注"
:close-on-click-modal=
"false"
:style=
"`width: fit-content; $
{step === 4 ? 'background-color: #f1f1f1;' : 'background-color: #fff;'}`"
@update:modelValue="$emit('update:modelValue')"
>
<div
class=
"step1"
v-if=
"step === 1"
>
<div
class=
"connect-item__icon"
>
<Icon
w=
"60"
h=
"60"
:multiColor=
"true"
class=
"svg"
:name=
"props.data?.type || '1'"
></Icon>
</div>
<p>
紫荆教育EDU
</p>
<div
class=
"step1-btn"
>
<el-button
type=
"success"
style=
"width: 100px"
color=
"rgb(25, 170, 32)"
@
click=
"step = 2"
>
关注
</el-button>
</div>
</div>
<div
class=
"step2"
v-if=
"step === 2"
>
<el-form
ref=
"ruleFormRef"
style=
"width: 400px"
:model=
"ruleForm"
:rules=
"rules"
label-width=
"auto"
class=
"demo-ruleForm"
status-icon
center
>
<p
style=
"color: #ccc; font-size: 12px; text-align: center; margin-bottom: 20px"
>
注:填写信息保存之后,关注成功
</p>
<el-form-item
label=
"性别"
prop=
"gender"
>
<el-radio-group
v-model=
"ruleForm.gender"
>
<el-radio
label=
"1"
>
男
</el-radio>
<el-radio
label=
"2"
>
女
</el-radio>
<el-radio
label=
"0"
>
未知
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"手机号"
prop=
"mobile"
>
<el-input
v-model=
"ruleForm.mobile"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"姓名"
prop=
"name"
>
<el-input
v-model=
"ruleForm.name"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item>
<div
style=
"display: flex; justify-content: center; width: 100%"
>
<el-button
type=
"primary"
@
click=
"submitForm(ruleFormRef)"
>
保存
</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div
class=
"step3"
v-if=
"step === 3"
>
<div
class=
"step3-t"
>
<Icon
w=
"20"
h=
"20"
:multiColor=
"true"
class=
"svg"
:name=
"props.data?.type || '1'"
></Icon>
<div
class=
"e-name"
>
紫荆教育EDU
</div>
</div>
<div
class=
"step3-btn"
>
<el-button
disabled
:icon=
"Check"
type=
"primary"
>
已关注
</el-button>
<el-button
type=
"primary"
@
click=
"sendChat"
>
发消息
</el-button>
</div>
</div>
<div
class=
"step4"
v-if=
"step === 4"
>
<div
class=
"chat-box"
>
<div
class=
"chat-view"
id=
"scrollContainer"
>
<div
class=
"chat-view_left"
>
<!--
<div
class=
"view__time"
>
16:29
</div>
-->
<div
class=
"view-left_content"
>
<div
class=
"view-left__avatar"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/highway/h5/banner-0420.png"
/>
</div>
<div
class=
"view-left__text"
>
欢迎关注“清控紫荆EDU”
<br
/><br
/>
紫荆教育以教育为本、坚持国际化品质标准,通过科技创新、专业创新,为企业、院校及个人学习者,提供国际在线学位教育、国际留学、数字经济产业学院整体解决方案等产品服务,为各行各业培养和输送高质量的国际化和产业化人才。
</div>
</div>
</div>
<div
class=
"chat-view_right"
v-for=
"item in logs"
>
<div
class=
"view__time"
>
{{
item
.
time
}}
</div>
<div
class=
"view-right_content"
>
<div
class=
"view-right__text"
>
{{
item
.
text
}}
</div>
<div
class=
"view-right__avatar"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/highway/h5/banner-0420.png"
/>
</div>
</div>
</div>
</div>
<div
class=
"chat-input"
>
<el-input
@
keyup=
"handleKeyUp"
v-model=
"textarea"
style=
"height: 100%"
placeholder=
"请输入"
type=
"textarea"
/>
</div>
</div>
</div>
</el-dialog>
</
template
>
<
style
lang=
"scss"
>
.data-form
{
// background-color: #f1f1f1;
.el-dialog__body
{
padding
:
0
;
}
}
.button-flex
{
// margin-top: 40px;
display
:
flex
;
justify-content
:
center
;
}
.step1
{
padding
:
20px
;
width
:
300px
;
.connect-item__icon
{
display
:
flex
;
justify-content
:
center
;
padding-top
:
20px
;
}
p
{
text-align
:
center
;
font-size
:
18px
;
color
:
#333
;
}
.step1-btn
{
padding
:
100px
0
50px
;
text-align
:
center
;
}
}
.step2
{
padding
:
20px
;
}
.step3
{
padding
:
20px
;
width
:
300px
;
.step3-t
{
display
:
flex
;
justify-content
:
center
;
.e-name
{
margin-left
:
5px
;
}
}
.step3-btn
{
padding-top
:
20px
;
text-align
:
center
;
}
}
.step4
{
width
:
550px
;
.chat-view
{
height
:
300px
;
// background-color: #000;
border-bottom
:
1px
solid
#ccc
;
overflow-y
:
scroll
;
padding-bottom
:
30px
;
box-sizing
:
border-box
;
.view__time
{
color
:
#ccc
;
text-align
:
center
;
font-size
:
12px
;
padding-top
:
20px
;
}
.chat-view_left
{
padding-left
:
20px
;
.view-left_content
{
margin-top
:
10px
;
display
:
flex
;
img
{
width
:
30px
;
height
:
30px
;
border-radius
:
3px
;
}
.view-left__text
{
background-color
:
#fff
;
margin-left
:
10px
;
padding
:
10px
;
border-radius
:
3px
;
max-width
:
300px
;
}
}
}
.chat-view_right
{
padding-right
:
20px
;
.view-right_content
{
margin-top
:
10px
;
display
:
flex
;
justify-content
:
right
;
img
{
width
:
30px
;
height
:
30px
;
border-radius
:
3px
;
}
.view-right__text
{
background-color
:
#b7e687
;
margin-right
:
10px
;
padding
:
10px
;
border-radius
:
3px
;
max-width
:
300px
;
}
}
}
}
.chat-input
{
height
:
150px
;
padding-top
:
5px
;
box-sizing
:
border-box
;
.el-textarea__inner
{
height
:
100%
;
background-color
:
rgba
(
0
,
0
,
0
,
0
);
border
:
none
;
resize
:
none
;
box-shadow
:
none
;
&
:focus
{
box-shadow
:
none
;
}
}
}
}
</
style
>
src/modules/connect/components/UserDataDialog.vue
0 → 100644
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
submitScheduleMember
}
from
'../api'
import
type
{
ScheduleMember
}
from
'../types'
import
type
{
FormInstance
}
from
'element-plus'
import
{
useUserStore
}
from
'@/stores/user'
const
userStore
=
useUserStore
()
const
OtherFields
=
defineAsyncComponent
(()
=>
import
(
'../components/OtherFields.vue'
))
const
props
=
defineProps
<
{
data
?:
ScheduleMember
}
>
()
const
emit
=
defineEmits
<
{
(
e
:
'update'
):
void
(
e
:
'update:modelValue'
,
visible
:
boolean
):
void
}
>
()
// const formSize = ref('default')
const
ruleFormRef
=
ref
<
FormInstance
>
()
let
ruleForm
=
$ref
<
any
>
({
experiment_id
:
''
,
connect_id
:
''
,
size
:
1000
,
cover_type
:
1
,
name
:
1
,
name_value
:
''
,
status
:
1
,
gender
:
1
,
mobile
:
1
,
create_data
:
''
,
type_name
:
''
})
watchEffect
(()
=>
{
const
mergeJson
=
function
(
target
:
any
,
source
:
any
)
{
if
(
source
)
{
Object
.
keys
(
target
).
forEach
(
function
(
key
)
{
if
(
source
.
hasOwnProperty
(
key
))
{
if
(
key
!==
'other_fields'
)
{
target
[
key
]
=
source
[
key
]
}
}
})
}
}
mergeJson
(
ruleForm
,
props
.
data
)
})
const
submitForm
=
async
(
formEl
:
FormInstance
|
undefined
,
bl
:
string
)
=>
{
if
(
!
formEl
)
return
await
formEl
.
validate
((
valid
,
fields
)
=>
{
if
(
valid
)
{
ruleForm
.
create_data
=
bl
if
(
props
.
data
?.
other_fields
)
{
ruleForm
.
other_fields
=
JSON
.
stringify
(
props
.
data
?.
other_fields
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
[
b
.
id
]
=
{
type
:
b
.
rule
.
type
,
fixed_value
:
b
.
rule
.
fixed_value
,
rand_value
:
b
.
rule
.
rand_value
}
return
a
},
{})
)
}
submitScheduleMember
(
ruleForm
).
then
(
res
=>
{
if
(
res
.
data
)
{
ElMessage
({
message
:
'保存成功'
,
type
:
'success'
})
emit
(
'update:modelValue'
,
false
)
}
})
}
else
{
console
.
log
(
'error submit!'
,
fields
)
}
})
}
const
resetForm
=
(
formEl
:
FormInstance
|
undefined
)
=>
{
if
(
!
formEl
)
return
formEl
.
resetFields
()
}
const
options
=
Array
.
from
({
length
:
10000
}).
map
((
_
,
idx
)
=>
({
value
:
`
${
idx
+
1
}
`
,
label
:
`
${
idx
+
1
}
`
}))
const
rules
=
[{
required
:
true
}]
</
script
>
<
template
>
<el-dialog
style=
"width: 800px"
class=
"data-form"
title=
"自动生成用户数据"
:close-on-click-modal=
"false"
@
update:modelValue=
"$emit('update:modelValue')"
>
<div
class=
"button-flex"
>
<el-form
:disabled=
"userStore.role?.id === 1"
label-suffix=
":"
ref=
"ruleFormRef"
:model=
"ruleForm"
label-width=
"auto"
class=
"demo-ruleForm"
status-icon
>
<el-form-item
label=
"请输入需要生成的数据量"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.size"
>
<el-radio
:label=
"1000"
>
1000
</el-radio>
<el-radio
:label=
"3000"
>
3000
</el-radio>
<el-radio
:label=
"5000"
>
5000
</el-radio>
<el-radio
:label=
"10000"
>
10000
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"请选择数据覆盖形式:"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.cover_type"
>
<el-radio
:label=
"1"
>
全新覆盖
</el-radio>
<el-radio
:label=
"2"
>
去重追加
</el-radio>
</el-radio-group>
</el-form-item>
<el-divider
/>
<el-form-item
label=
"来源链接"
:rules=
"rules"
>
{{
ruleForm
.
type_name
}}
</el-form-item>
<el-form-item
label=
"姓名"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.name"
>
<el-radio
:label=
"1"
>
随机
</el-radio>
<el-radio
:label=
"2"
>
固定
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if=
"ruleForm.name === 2"
label=
"固定姓名"
:rules=
"rules"
>
<el-input
v-model=
"ruleForm.name_value"
placeholder=
"请输入固定姓名"
></el-input>
</el-form-item>
<el-form-item
label=
"状态"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.status"
>
<el-radio
:label=
"1"
>
生效
</el-radio>
<el-radio
:label=
"2"
>
失效
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"性别"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.gender"
>
<el-radio
:label=
"1"
>
随机
</el-radio>
<el-radio
:label=
"2"
>
男
</el-radio>
<el-radio
:label=
"3"
>
女
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"手机号吗"
:rules=
"rules"
>
<el-radio-group
v-model=
"ruleForm.mobile"
>
<el-radio
:label=
"1"
>
随机
</el-radio>
</el-radio-group>
</el-form-item>
<OtherFields
:data=
"props.data?.other_fields || []"
></OtherFields>
<el-form
:model=
"ruleForm"
>
<el-form-item
style=
"justify-content: center"
>
<div
style=
"justify-content: center; display: flex; width: 100%"
>
<el-button
@
click=
"$emit('update:modelValue', false)"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm(ruleFormRef, 'false')"
>
保存
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm(ruleFormRef, 'true')"
>
提交生成数据
</el-button>
</div>
</el-form-item>
</el-form>
</el-form>
</div>
</el-dialog>
</
template
>
<
style
lang=
"scss"
>
.connect-form
{
.el-dialog__body
{
padding-top
:
10px
;
}
}
.button-flex
{
// margin-top: 40px;
display
:
flex
;
justify-content
:
center
;
}
</
style
>
src/modules/connect/types.ts
浏览文件 @
db1e84fd
...
@@ -24,3 +24,46 @@ export interface PlatformItem {
...
@@ -24,3 +24,46 @@ export interface PlatformItem {
config_attributes
?:
ConfigAttribute
[]
config_attributes
?:
ConfigAttribute
[]
onBeforeNext
?:
(
index
:
number
,
data
:
PlatformItem
)
=>
Promise
<
boolean
>
|
boolean
onBeforeNext
?:
(
index
:
number
,
data
:
PlatformItem
)
=>
Promise
<
boolean
>
|
boolean
}
}
export
interface
ScheduleMember
{
experiment_id
?:
string
connect_id
?:
string
type_name
:
string
size
:
number
cover_type
:
number
name
:
number
name_value
:
string
status
:
number
gender
:
number
mobile
:
number
connect_name
:
string
other_fields
?:
OtherFields
[]
}
export
interface
ScheduleEvent
{
experiment_id
?:
string
connect_id
?:
string
member_rate
:
string
size
:
number
cover_type
:
number
event_name
:
string
other_fields
?:
OtherFields
[]
events
?:
any
[]
last_event
:
string
}
export
interface
OtherFields
{
id
:
string
name
:
string
type
:
number
format
:
string
rule
:
any
}
export
interface
StudentFollow
{
follow_flag
:
string
logs
:
any
[]
|
undefined
connect_id
:
string
type
:
string
data
:
any
}
\ No newline at end of file
src/modules/connect/views/Index.vue
浏览文件 @
db1e84fd
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
type
{
DetailsProp
}
from
'../types'
import
type
{
DetailsProp
,
ScheduleMember
,
ScheduleEvent
,
StudentFollow
}
from
'../types'
import
{
Plus
}
from
'@element-plus/icons-vue'
import
{
Plus
}
from
'@element-plus/icons-vue'
import
AppList
from
'@/components/base/AppList.vue'
import
AppList
from
'@/components/base/AppList.vue'
import
ListItem
from
'../components/ListItem.vue'
import
ListItem
from
'../components/ListItem.vue'
import
{
getConnectionList
,
getConnectionDetails
}
from
'../api'
import
{
getConnectionList
,
getConnectionDetails
,
getScheduleMember
,
getStudentFollow
}
from
'../api'
import
{
useMapStore
}
from
'@/stores/map'
import
{
useMapStore
}
from
'@/stores/map'
const
store
=
useMapStore
()
const
store
=
useMapStore
()
const
FormDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/FormDialog.vue'
))
const
FormDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/FormDialog.vue'
))
const
UserDataDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/UserDataDialog.vue'
))
const
EventDataDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/EventDataDialog.vue'
))
const
DataProgressDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/DataProgressDialog.vue'
))
const
StudentFollowDialog
=
defineAsyncComponent
(()
=>
import
(
'../components/StudentFollowDialog.vue'
))
const
appList
=
$ref
<
InstanceType
<
typeof
AppList
>
|
null
>
(
null
)
const
appList
=
$ref
<
InstanceType
<
typeof
AppList
>
|
null
>
(
null
)
// 列表配置
// 列表配置
...
@@ -36,14 +40,14 @@ function handleRefresh() {
...
@@ -36,14 +40,14 @@ function handleRefresh() {
appList
?.
refetch
()
appList
?.
refetch
()
}
}
// 新建
// 新建
链接
let
formVisible
=
$ref
(
false
)
let
formVisible
=
$ref
(
false
)
const
createConnect
=
function
()
{
const
createConnect
=
function
()
{
formData
=
undefined
formData
=
undefined
formVisible
=
true
formVisible
=
true
}
}
// 编辑
// 编辑
链接
let
formData
=
$ref
<
DetailsProp
|
undefined
>
()
let
formData
=
$ref
<
DetailsProp
|
undefined
>
()
const
editConnect
=
function
(
id
:
string
)
{
const
editConnect
=
function
(
id
:
string
)
{
getConnectionDetails
({
id
:
id
}).
then
(
res
=>
{
getConnectionDetails
({
id
:
id
}).
then
(
res
=>
{
...
@@ -51,6 +55,64 @@ const editConnect = function (id: string) {
...
@@ -51,6 +55,64 @@ const editConnect = function (id: string) {
formVisible
=
true
formVisible
=
true
})
})
}
}
// 生成用户数据相关
let
userDataVisible
=
$ref
(
false
)
let
userData
=
$ref
<
ScheduleMember
>
()
const
handleGenerateUserData
=
function
(
experimentId
:
string
,
id
:
string
,
name
:
string
)
{
getScheduleMember
({
experiment_id
:
experimentId
,
connect_id
:
id
}).
then
((
res
:
{
data
:
ScheduleMember
})
=>
{
userData
=
res
.
data
if
(
res
.
data
?.
other_fields
)
{
userData
.
other_fields
=
res
.
data
.
other_fields
.
map
((
item
:
any
)
=>
{
if
(
item
?.
rule
)
{
item
.
rule
.
type
=
parseInt
(
item
.
rule
.
type
)
}
return
item
})
}
userData
.
type_name
=
name
userData
.
experiment_id
=
experimentId
userData
.
connect_id
=
id
})
userDataVisible
=
true
}
// 生成事件数据
let
eventDataVisible
=
$ref
(
false
)
let
eventData
=
$ref
<
any
>
({})
const
handleGenerateEventData
=
function
(
experimentId
:
string
,
id
:
string
,
events
:
any
,
last_event
:
string
)
{
eventData
=
{
experiment_id
:
experimentId
,
connect_id
:
id
,
events
:
events
,
last_event
:
last_event
}
eventDataVisible
=
true
}
// 数据生成进度
let
dataProgressVisible
=
$ref
(
false
)
const
viewDataProgress
=
function
()
{
dataProgressVisible
=
true
}
// 用户触达
let
studentFollowVisible
=
$ref
(
false
)
let
studentFollowData
=
$ref
<
StudentFollow
>
()
const
handleStudentFollow
=
function
(
experimentId
:
string
,
id
:
string
,
type
:
string
)
{
studentFollowVisible
=
true
getStudentFollow
({
experiment_id
:
experimentId
,
experiment_connection_id
:
id
}).
then
((
res
:
any
)
=>
{
if
(
res
.
code
===
0
)
{
const
data
=
res
.
data
data
.
connect_id
=
id
data
.
type
=
type
studentFollowData
=
data
}
})
}
</
script
>
</
script
>
<
template
>
<
template
>
...
@@ -64,6 +126,10 @@ const editConnect = function (id: string) {
...
@@ -64,6 +126,10 @@ const editConnect = function (id: string) {
v-for=
"item in data"
v-for=
"item in data"
:key=
"item.id"
:key=
"item.id"
@
update=
"handleRefresh"
@
update=
"handleRefresh"
@
generateUserData=
"handleGenerateUserData"
@
generateEventData=
"handleGenerateEventData"
@
viewDataProgress=
"viewDataProgress"
@
handleStudentFollow=
"handleStudentFollow"
></ListItem>
></ListItem>
<div
class=
"connect-item"
@
click=
"createConnect"
>
<div
class=
"connect-item"
@
click=
"createConnect"
>
<div
class=
"connect-add-button"
>
<div
class=
"connect-add-button"
>
...
@@ -77,6 +143,18 @@ const editConnect = function (id: string) {
...
@@ -77,6 +143,18 @@ const editConnect = function (id: string) {
</AppCard>
</AppCard>
<!-- 新建链接 -->
<!-- 新建链接 -->
<FormDialog
:data=
"formData"
v-model=
"formVisible"
v-if=
"formVisible"
@
update=
"handleRefresh"
/>
<FormDialog
:data=
"formData"
v-model=
"formVisible"
v-if=
"formVisible"
@
update=
"handleRefresh"
/>
<!-- 生成用户数据 -->
<UserDataDialog
:data=
"userData"
v-model=
"userDataVisible"
v-if=
"userDataVisible"
></UserDataDialog>
<!-- 生成事件数据 -->
<EventDataDialog
:data=
"eventData"
v-model=
"eventDataVisible"
v-if=
"eventDataVisible"
></EventDataDialog>
<!-- 数据进度 -->
<DataProgressDialog
v-model=
"dataProgressVisible"
v-if=
"dataProgressVisible"
></DataProgressDialog>
<!-- 用户触达 -->
<StudentFollowDialog
:data=
"studentFollowData"
v-model=
"studentFollowVisible"
v-if=
"studentFollowVisible"
></StudentFollowDialog>
</template>
</template>
<
style
lang=
"scss"
>
<
style
lang=
"scss"
>
...
@@ -85,13 +163,14 @@ const editConnect = function (id: string) {
...
@@ -85,13 +163,14 @@ const editConnect = function (id: string) {
grid-template-columns
:
repeat
(
5
,
1fr
);
grid-template-columns
:
repeat
(
5
,
1fr
);
gap
:
20px
;
gap
:
20px
;
padding
:
20px
;
padding
:
20px
;
background-color
:
#efefef
;
//
background-color: #efefef;
border-radius
:
5px
;
border-radius
:
5px
;
}
}
.connect-add-button
{
.connect-add-button
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
height
:
100%
;
.el-icon
{
.el-icon
{
font-size
:
20px
;
font-size
:
20px
;
margin-right
:
10px
;
margin-right
:
10px
;
...
...
vite.config.ts
浏览文件 @
db1e84fd
...
@@ -26,11 +26,11 @@ export default defineConfig(({ mode }) => ({
...
@@ -26,11 +26,11 @@ export default defineConfig(({ mode }) => ({
cert
:
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'./https/ezijing.com.pem'
))
cert
:
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'./https/ezijing.com.pem'
))
},
},
proxy
:
{
proxy
:
{
// '/api/resource
': {
'/api/lab
'
:
{
// target: 'http://com-resource-admin-tes
t.ezijing.com/',
target
:
'http://localhost-resource-experimen
t.ezijing.com/'
,
//
changeOrigin: true,
changeOrigin
:
true
,
// rewrite: path => path.replace(/^\/api\/resource
/, '')
rewrite
:
path
=>
path
.
replace
(
/^
\/
api
\/
lab
/
,
''
)
//
},
},
'/api'
:
'https://saas-dml.ezijing.com'
'/api'
:
'https://saas-dml.ezijing.com'
}
}
},
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论