提交 74c281ce authored 作者: 王鹏飞's avatar 王鹏飞

feat: 数字营销实验室

上级 e31d7992
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LAB_URL=https://bi.ezijing.com/bi/?proc=0&action=index VITE_LAB_URL=https://bi.ezijing.com/bi/?proc=0&action=index
VITE_SASS_DML=https://saas-dml.ezijing.com VITE_DML_URL=https://saas-dml.ezijing.com
\ No newline at end of file \ No newline at end of file
...@@ -101,7 +101,9 @@ export function getTripConfig(params: { experiment_id: string }) { ...@@ -101,7 +101,9 @@ export function getTripConfig(params: { experiment_id: string }) {
// 更新旅程配置 // 更新旅程配置
export function updateTripConfig(data: { experiment_id: string }) { export function updateTripConfig(data: { experiment_id: string }) {
return httpRequest.post('/api/lab/v1/experiment/itinerary-config/save', data) return httpRequest.post('/api/lab/v1/experiment/itinerary-config/save', data, {
headers: { 'Content-Type': 'application/json' }
})
} }
// 获取实验下的所有用户属性 // 获取实验下的所有用户属性
......
...@@ -4,6 +4,7 @@ import type { ExperimentItem } from '../types' ...@@ -4,6 +4,7 @@ import type { ExperimentItem } from '../types'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getTripConfig, updateTripConfig } from '../api' import { getTripConfig, updateTripConfig } from '../api'
import { useConnection, useUserAttr, useMetaEvent, useTag, useGroup, useMaterial } from '../composables/useAllData' import { useConnection, useUserAttr, useMetaEvent, useTag, useGroup, useMaterial } from '../composables/useAllData'
import { useDocumentVisibility } from '@vueuse/core'
const props = defineProps<{ const props = defineProps<{
data: ExperimentItem data: ExperimentItem
...@@ -21,6 +22,10 @@ const { tagList } = useTag(props.data.id) ...@@ -21,6 +22,10 @@ const { tagList } = useTag(props.data.id)
const { groupList } = useGroup(props.data.id) const { groupList } = useGroup(props.data.id)
const { materialList } = useMaterial(props.data.id) const { materialList } = useMaterial(props.data.id)
const dmlURL = computed(() => {
return `${import.meta.env.VITE_DML_URL}/trip/template?experiment_id=${props.data.id}`
})
const formRef = $ref<FormInstance>() const formRef = $ref<FormInstance>()
const form = reactive({ const form = reactive({
experiment_id: props.data.id, experiment_id: props.data.id,
...@@ -29,6 +34,7 @@ const form = reactive({ ...@@ -29,6 +34,7 @@ const form = reactive({
user_attr_config: { is_all: true, items: [] }, user_attr_config: { is_all: true, items: [] },
event_config: { is_all: true, items: [] }, event_config: { is_all: true, items: [] },
is_use_common: 0, is_use_common: 0,
ids: [],
tag_ids: [], tag_ids: [],
group_ids: [], group_ids: [],
material_ids: [] material_ids: []
...@@ -40,8 +46,13 @@ let templateList = $ref<{ id: string; name: string }[]>([]) ...@@ -40,8 +46,13 @@ let templateList = $ref<{ id: string; name: string }[]>([])
function fetchInfo() { function fetchInfo() {
getTripConfig({ experiment_id: props.data.id }).then(res => { getTripConfig({ experiment_id: props.data.id }).then(res => {
const data = res.data const data = res.data
if (!data.itinerary?.id) return
templateList = [data.itinerary] templateList = [data.itinerary]
const connect_ids = data.connections.map((item: any) => item.id) const connect_ids = data.connections.map((item: any) => item.id)
if (!data.is_config_created) {
Object.assign(form, { itinerary_id: data.itinerary.id, connect_ids })
return
}
const user_attr_config = { const user_attr_config = {
is_all: data.user_attr_config.is_all, is_all: data.user_attr_config.is_all,
items: data.user_attr_config.items.map((item: any) => item.id) items: data.user_attr_config.items.map((item: any) => item.id)
...@@ -67,6 +78,11 @@ function fetchInfo() { ...@@ -67,6 +78,11 @@ function fetchInfo() {
} }
watchEffect(() => fetchInfo()) watchEffect(() => fetchInfo())
const visibility = useDocumentVisibility()
watch(visibility, (current, previous) => {
if (current === 'visible' && previous === 'hidden') fetchInfo()
})
const step = ref(0) const step = ref(0)
// 上一步 // 上一步
function handlePrev() { function handlePrev() {
...@@ -113,13 +129,25 @@ function handleSubmit() { ...@@ -113,13 +129,25 @@ function handleSubmit() {
<el-tabs v-model="step"> <el-tabs v-model="step">
<el-tab-pane label="模板与连接" :name="0"> <el-tab-pane label="模板与连接" :name="0">
<el-form-item label="旅程模板" label-width="82" prop="itinerary_id"> <el-form-item label="旅程模板" label-width="82" prop="itinerary_id">
<el-select v-model="form.itinerary_id" style="width: 100%"> <template v-if="templateList.length">
<el-option v-for="item in templateList" :label="item.name" :value="item.id" :key="item.id"></el-option> <el-select v-model="form.itinerary_id" style="width: 100%">
</el-select> <el-option v-for="item in templateList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</template>
<template v-else>
<a :href="dmlURL" target="_blank">
<el-button type="primary">新建旅程模板</el-button>
</a>
</template>
</el-form-item> </el-form-item>
<el-form-item label="连接" label-width="82" prop="connect_ids"> <el-form-item label="连接" label-width="82" prop="connect_ids">
<el-select v-model="form.connect_ids" multiple style="width: 100%"> <el-select v-model="form.connect_ids" multiple style="width: 100%">
<el-option v-for="item in connectionList" :label="item.name" :value="item.id" :key="item.id"></el-option> <el-option
v-for="item in connectionList"
:label="item.name"
:value="item.id"
:key="item.id"
disabled></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
...@@ -161,7 +189,9 @@ function handleSubmit() { ...@@ -161,7 +189,9 @@ function handleSubmit() {
<template v-if="form.is_use_common === 0"> <template v-if="form.is_use_common === 0">
<el-divider /> <el-divider />
<el-form-item label="用户/事件数据" label-width="118"> <el-form-item label="用户/事件数据" label-width="118">
<el-select style="width: 100%"></el-select> <el-select v-model="form.ids" multiple style="width: 100%">
<el-option value="教师维护的用户和事件数据"></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="标签数据" label-width="118" prop="tag_ids"> <el-form-item label="标签数据" label-width="118" prop="tag_ids">
<el-select v-model="form.tag_ids" multiple style="width: 100%"> <el-select v-model="form.tag_ids" multiple style="width: 100%">
......
...@@ -93,9 +93,15 @@ function handleSubmit() { ...@@ -93,9 +93,15 @@ function handleSubmit() {
// 当前评分方法 // 当前评分方法
function currentRuleNames(value: number) { function currentRuleNames(value: number) {
const typeList = form.rule_list.map((item: any) => item.type) const typeList = form.rule_list.map((item: any) => item.type)
return gradeRuleList.filter(item => { const tempList = gradeRuleList.filter(item => {
return item.value === value || item.value === 5 || !typeList.includes(item.value) return item.value === value || item.value === 5 || !typeList.includes(item.value)
}) })
if (props.data.type === '4') {
// 数字营销实验
return tempList.filter(item => [1, 5, 6, 7, 8, 9].includes(item.value as number))
} else {
return tempList.filter(item => item.value <= 5)
}
} }
function handleTypeChange(row: any) { function handleTypeChange(row: any) {
......
...@@ -113,7 +113,7 @@ export function useConnection(experiment_id: string) { ...@@ -113,7 +113,7 @@ export function useConnection(experiment_id: string) {
function fetchConnectionList() { function fetchConnectionList() {
getConnectionList({ experiment_id }).then((res: any) => { getConnectionList({ experiment_id }).then((res: any) => {
connectionList.value = res.data.items.map((item: any) => { connectionList.value = res.data.items.map((item: any) => {
const attrs = JSON.parse(item.config_attributes) const attrs = item.config_attributes
const name = Array.isArray(attrs) ? attrs.find((item: any) => item.prop === 'name')?.value : attrs.name const name = Array.isArray(attrs) ? attrs.find((item: any) => item.prop === 'name')?.value : attrs.name
return { ...item, config_attributes: attrs, name } return { ...item, config_attributes: attrs, name }
}) })
......
...@@ -80,7 +80,7 @@ function handleRemoveClass(row: ClassItem) { ...@@ -80,7 +80,7 @@ function handleRemoveClass(row: ClassItem) {
const gradeRulesVisible = $ref(false) const gradeRulesVisible = $ref(false)
const reportRulesVisible = $ref(false) const reportRulesVisible = $ref(false)
const dmlURL = computed(() => { const dmlURL = computed(() => {
return `${import.meta.env.VITE_SASS_DML}?experiment_id=${props.id}` return `${import.meta.env.VITE_DML_URL}?experiment_id=${props.id}`
}) })
</script> </script>
......
...@@ -130,6 +130,33 @@ function scoreValue(value: any) { ...@@ -130,6 +130,33 @@ function scoreValue(value: any) {
const pictureVisible = $ref(false) const pictureVisible = $ref(false)
const prepareVisible = $ref(false) const prepareVisible = $ref(false)
const resultVisible = $ref(false) const resultVisible = $ref(false)
function getOperationUrl(type: number) {
if (type === 1) {
// 批改实验报告
return `/admin/lab/score/report?experiment_id=${props.data.experiment_id}&student_id=${props.data.student_id}`
} else if (type === 6) {
// 用户标签
return `${import.meta.env.VITE_DML_URL}/label?experiment_id=${props.data.experiment_id}&student_id=${
props.data.student_id
}`
} else if (type === 7) {
// 用户群组
return `${import.meta.env.VITE_DML_URL}/group?experiment_id=${props.data.experiment_id}&student_id=${
props.data.student_id
}`
} else if (type === 8) {
// 用户旅程
return `${import.meta.env.VITE_DML_URL}/trip?experiment_id=${props.data.experiment_id}&student_id=${
props.data.student_id
}`
} else if (type === 9) {
// 营销资料
return `${import.meta.env.VITE_DML_URL}/material?experiment_id=${props.data.experiment_id}&student_id=${
props.data.student_id
}`
}
}
</script> </script>
<template> <template>
...@@ -186,7 +213,21 @@ const resultVisible = $ref(false) ...@@ -186,7 +213,21 @@ const resultVisible = $ref(false)
<template #default="{ row }"> {{ row.percent }}% </template> <template #default="{ row }"> {{ row.percent }}% </template>
</el-table-column> </el-table-column>
<el-table-column label="满分" prop="score" align="center"></el-table-column> <el-table-column label="满分" prop="score" align="center"></el-table-column>
<el-table-column label="得分" prop="commit_score" align="center"></el-table-column> <el-table-column label="得分" prop="commit_score" align="center">
<template #default="{ row }">
<el-input-number
v-model="row.commit_score"
:min="0"
:max="row.score"
:controls="false"
:step="0.01"
step-strictly
placeholder="请输入成绩"
style="width: 100px"
v-if="!(row.type === 1 && experiment.report_upload_way === 2)"></el-input-number>
<span v-else>{{ row.commit_score }}</span>
</template>
</el-table-column>
<el-table-column label="操作" prop="commit_score" align="center"> <el-table-column label="操作" prop="commit_score" align="center">
<template #default="{ row }"> <template #default="{ row }">
<template v-if="row.type === 1"> <template v-if="row.type === 1">
...@@ -195,38 +236,19 @@ const resultVisible = $ref(false) ...@@ -195,38 +236,19 @@ const resultVisible = $ref(false)
type="primary" type="primary"
@click="$emit('update:modelValue')" @click="$emit('update:modelValue')"
v-if="experiment.report_upload_way === 2"> v-if="experiment.report_upload_way === 2">
<router-link <a :href="getOperationUrl(row.type)" target="_blank">批改</a>
:to="`/admin/lab/score/report?experiment_id=${data.experiment_id}&student_id=${data.student_id}`"
target="_blank"
>批改</router-link
>
</el-button> </el-button>
<template v-if="experiment.report_upload_way === 1"> <template v-if="experiment.report_upload_way === 1">
<el-input-number
v-model="row.commit_score"
:min="0"
:max="row.score"
:controls="false"
:step="0.01"
step-strictly
placeholder="请输入成绩"
style="width: 100px"></el-input-number>
<el-button text type="primary" v-if="file?.url"> <el-button text type="primary" v-if="file?.url">
<a :href="file.url" target="_blank">查阅报告</a> <a :href="file.url" target="_blank">查阅报告</a>
</el-button> </el-button>
<p style="color: red" v-else>未上传</p> <p style="color: red" v-else>未上传</p>
</template> </template>
</template> </template>
<template v-else> <template v-else-if="[6, 7, 8, 9].includes(row.type)">
<el-input-number <el-button text type="primary">
v-model="row.commit_score" <a :href="getOperationUrl(row.type)" target="_blank">查看结果</a>
:min="0" </el-button>
:max="row.score"
:controls="false"
:step="0.01"
step-strictly
placeholder="请输入成绩"
style="width: 100px"></el-input-number>
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
......
...@@ -33,6 +33,16 @@ function fetchInfo() { ...@@ -33,6 +33,16 @@ function fetchInfo() {
onMounted(() => { onMounted(() => {
fetchInfo() fetchInfo()
}) })
function getOperationUrl(type: number) {
if (type === 1) {
// 实验报告
return `/student/lab/report/view/${experiment.id}`
} else if (type === 8) {
// 用户旅程
return `${import.meta.env.VITE_DML_URL}/trip?experiment_id=${experiment.id}&student_id=${experiment.student.id}`
}
}
</script> </script>
<template> <template>
...@@ -85,9 +95,10 @@ onMounted(() => { ...@@ -85,9 +95,10 @@ onMounted(() => {
<el-table-column label="得分" width="100" align="center"> <el-table-column label="得分" width="100" align="center">
<template #default="{ row }"> <template #default="{ row }">
{{ row.commit_score }} {{ row.commit_score }}
<template v-if="row.type === 1 && experiment.report_upload_way === 2"> <!-- 实验报告 | 用户旅程 -->
<template v-if="(row.type === 1 && experiment.report_upload_way === 2) || row.type === 8">
<el-button text type="primary"> <el-button text type="primary">
<a :href="`/student/lab/report/view/${experiment.id}`" target="_blank">查看明细</a> <a :href="getOperationUrl(row.type)" target="_blank">查看明细</a>
</el-button> </el-button>
</template> </template>
</template> </template>
......
...@@ -90,7 +90,11 @@ watchEffect(() => { ...@@ -90,7 +90,11 @@ watchEffect(() => {
}) })
// 右侧 // 右侧
const LAB_URL = import.meta.env.VITE_LAB_URL const LAB_URL = computed(() => {
return experimentInfo?.type === 4
? `${import.meta.env.VITE_DML_URL}/trip?experiment_id=${form.experiment_id}`
: import.meta.env.VITE_LAB_URL
})
let iframeKey = $ref(Date.now()) let iframeKey = $ref(Date.now())
// 返回首页 // 返回首页
function handleBackHome() { function handleBackHome() {
......
...@@ -26,7 +26,11 @@ export const gradeRule: Record<number, any> = { ...@@ -26,7 +26,11 @@ export const gradeRule: Record<number, any> = {
2: '实验准备', 2: '实验准备',
3: '实验结果', 3: '实验结果',
4: '课堂活跃度', 4: '课堂活跃度',
5: '自定义' 5: '自定义',
6: '用户标签',
7: '用户群组',
8: '用户旅程',
9: '营销资料'
} }
// 参赛模式列表 // 参赛模式列表
export const gradeRuleList = json2Array(gradeRule) export const gradeRuleList = json2Array(gradeRule)
......
...@@ -31,6 +31,11 @@ export default defineConfig(({ mode }) => ({ ...@@ -31,6 +31,11 @@ export default defineConfig(({ mode }) => ({
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/api\/lab/, '') rewrite: path => path.replace(/^\/api\/lab/, '')
}, },
'/api/resource': {
target: 'http://com-resource-admin-test.ezijing.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/resource/, '')
},
'/api': 'https://saas-lab.ezijing.com' '/api': 'https://saas-lab.ezijing.com'
} }
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论