提交 1b3ea913 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 配置数字营销实验室

上级 02817997
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LAB_URL=https://bi.ezijing.com/bi/?proc=0&action=index
VITE_SASS_DML=https://saas-dml.ezijing.com
\ No newline at end of file
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
# VITE_LAB_URL=https://digitalmarketinglab.ezijing.com
VITE_LAB_URL=https://bi.ezijing.com/bi/?proc=0&action=index
VITE_LAB_URL=https://digitalmarketinglab.ezijing.com
VITE_LOGIN_URL=https://login2.ezijing.com/auth/login/index
VITE_LAB_URL=https://bi.ezijing.com/bi/
VITE_LAB_URL=https://bi.ezijing.com/bi/?proc=0&action=index
......@@ -93,3 +93,42 @@ export function updateExperimentReportRule(data: {
}) {
return httpRequest.post('/api/resource/v1/backend/experiment-report/save', data)
}
// 获取旅程配置
export function getTripConfig(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/itinerary-config/detail', { params })
}
// 更新旅程配置
export function updateTripConfig(data: { experiment_id: string }) {
return httpRequest.post('/api/lab/v1/experiment/itinerary-config/save', data)
}
// 获取实验下的所有用户属性
export function getMetaUserAttrList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/meta-member/all', { params })
}
// 获取实验下的所有事件
export function getMetaEventList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/meta-event/all', { params })
}
// 获取实验下的所有标签
export function getTagList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/all', { params })
}
// 获取实验下的所有群组
export function getGroupList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/all', { params })
}
// 获取实验下的所有连接
export function getConnectionList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/connection/all', { params })
}
// 获取实验下的所有资料
export function getMaterialList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/marketing-material/all', { params })
}
<script setup lang="ts">
import type { FormInstance } from 'element-plus'
import type { ExperimentItem } from '../types'
import { ElMessage } from 'element-plus'
import { getTripConfig, updateTripConfig } from '../api'
import { useConnection, useUserAttr, useMetaEvent, useTag, useGroup, useMaterial } from '../composables/useAllData'
const props = defineProps<{
data: ExperimentItem
}>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const { connectionList } = useConnection(props.data.id)
const { userAttrList } = useUserAttr(props.data.id)
const { metaEventList } = useMetaEvent(props.data.id)
const { tagList } = useTag(props.data.id)
const { groupList } = useGroup(props.data.id)
const { materialList } = useMaterial(props.data.id)
const formRef = $ref<FormInstance>()
const form = reactive({
experiment_id: props.data.id,
itinerary_id: '',
connect_ids: [],
user_attr_config: { is_all: true, items: [] },
event_config: { is_all: true, items: [] },
is_use_common: 0,
tag_ids: [],
group_ids: [],
material_ids: []
})
// 模板列表
let templateList = $ref<{ id: string; name: string }[]>([])
function fetchInfo() {
getTripConfig({ experiment_id: props.data.id }).then(res => {
const data = res.data
templateList = [data.itinerary]
const connect_ids = data.connections.map((item: any) => item.id)
const user_attr_config = {
is_all: data.user_attr_config.is_all,
items: data.user_attr_config.items.map((item: any) => item.id)
}
const event_config = {
is_all: data.event_config.is_all,
items: data.event_config.items.map((item: any) => item.id)
}
const tag_ids = data.tags.map((item: any) => item.id)
const group_ids = data.groups.map((item: any) => item.id)
const material_ids = data.marketing_materials.map((item: any) => item.id)
Object.assign(form, {
itinerary_id: data.itinerary.id,
connect_ids,
user_attr_config,
event_config,
is_use_common: data.is_use_common,
tag_ids,
group_ids,
material_ids
})
})
}
watchEffect(() => fetchInfo())
const step = ref(0)
// 上一步
function handlePrev() {
step.value--
}
// 下一步
function handleNext() {
step.value++
}
// 提交
function handleSubmit() {
formRef?.validate().then(() => {
const params = {
...form,
connect_ids: JSON.stringify(form.connect_ids),
user_attr_config: JSON.stringify(form.user_attr_config),
event_config: JSON.stringify(form.event_config),
tag_ids: JSON.stringify(form.tag_ids),
group_ids: JSON.stringify(form.group_ids),
material_ids: JSON.stringify(form.material_ids)
}
updateTripConfig(params).then(() => {
ElMessage({ message: '保存成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
})
}
</script>
<template>
<el-dialog
title="配置数字营销实验"
:close-on-click-modal="false"
width="600px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" label-suffix=":">
<el-row justify="space-between">
<el-form-item label="实验名称">{{ data.name }}</el-form-item>
<el-form-item label="实验类型">{{ data.type_name }} </el-form-item>
<el-form-item label="实验总成绩">{{ data.score }}</el-form-item>
</el-row>
<el-tabs v-model="step">
<el-tab-pane label="模板与连接" :name="0">
<el-form-item label="旅程模板" label-width="82" prop="itinerary_id">
<el-select v-model="form.itinerary_id" style="width: 100%">
<el-option v-for="item in templateList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="连接" label-width="82" prop="connect_ids">
<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-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="元数据" :name="1">
<el-form-item label="用户属性" label-width="82">
<el-radio-group v-model="form.user_attr_config.is_all">
<el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio>
</el-radio-group>
<el-select
v-model="form.user_attr_config.items"
multiple
style="margin-left: 40px"
v-if="!form.user_attr_config.is_all">
<el-option v-for="item in userAttrList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="事件" label-width="82">
<el-radio-group v-model="form.event_config.is_all">
<el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio>
</el-radio-group>
<el-select
v-model="form.event_config.items"
multiple
style="margin-left: 40px"
v-if="!form.event_config.is_all">
<el-option v-for="item in metaEventList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="旅程资源" :name="2">
<el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common">
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
</el-radio-group>
</el-form-item>
<template v-if="form.is_use_common === 0">
<el-divider />
<el-form-item label="用户/事件数据" label-width="118">
<el-select style="width: 100%"></el-select>
</el-form-item>
<el-form-item label="标签数据" label-width="118" prop="tag_ids">
<el-select v-model="form.tag_ids" multiple style="width: 100%">
<el-option v-for="item in tagList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="用户群组" label-width="118" prop="group_ids">
<el-select v-model="form.group_ids" multiple style="width: 100%">
<el-option v-for="item in groupList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="营销资料" label-width="118" prop="material_ids">
<el-select v-model="form.material_ids" multiple style="width: 100%">
<el-option v-for="item in materialList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
</template>
</el-tab-pane>
</el-tabs>
</el-form>
<template #footer>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)" v-if="step === 0">关闭</el-button>
<el-button round auto-insert-space @click="handlePrev" v-else>上一步</el-button>
<el-button type="primary" round auto-insert-space @click="handleNext" v-if="step < 2">下一步</el-button>
<el-button type="primary" round auto-insert-space @click="handleSubmit" v-else>保存</el-button>
</el-row>
</template>
</el-dialog>
</template>
import {
getMetaUserAttrList,
getMetaEventList,
getTagList,
getGroupList,
getConnectionList,
getMaterialList
} from '../api'
// 用户属性类型
export interface AttrType {
id: string
name: string
type: string
format: string
english_name: string
pinyin: string
}
// 事件类型
interface MetaEventType {
id: string
name: string
english_name: string
pinyin: string
event_attrs: AttrType[]
}
// 标签类型
export interface TagType {
id: string
name: string
}
// 群组类型
export interface GroupType {
id: string
name: string
}
// 连接类型
export interface ConnectionType {
id: string
name: string
type: string
status: '0' | '1'
config_attributes: any
}
export interface MaterialType {
id: string
name: string
}
// 所有用户属性
const userAttrList = ref<AttrType[]>([])
export function useUserAttr(experiment_id: string) {
function fetchUserAttrList() {
getMetaUserAttrList({ experiment_id }).then((res: any) => {
userAttrList.value = res.data.items
})
}
onMounted(() => {
if (!userAttrList.value?.length) fetchUserAttrList()
})
return { fetchUserAttrList, userAttrList }
}
// 所有事件
const metaEventList = ref<MetaEventType[]>([])
export function useMetaEvent(experiment_id: string) {
function fetchMetaEventList() {
getMetaEventList({ experiment_id }).then((res: any) => {
metaEventList.value = res.data.items
})
}
onMounted(() => {
if (!metaEventList.value?.length) fetchMetaEventList()
})
return { fetchMetaEventList, metaEventList }
}
// 所有标签
const tagList = ref<TagType[]>([])
export function useTag(experiment_id: string) {
function fetchTagList() {
getTagList({ experiment_id }).then((res: any) => {
tagList.value = res.data.items
})
}
onMounted(() => {
if (!tagList.value?.length) fetchTagList()
})
return { fetchTagList, tagList }
}
// 所有群组
const groupList = ref<GroupType[]>([])
export function useGroup(experiment_id: string) {
function fetchGroupList() {
getGroupList({ experiment_id }).then((res: any) => {
groupList.value = res.data.items
})
}
onMounted(() => {
if (!groupList.value?.length) fetchGroupList()
})
return { fetchGroupList, groupList }
}
// 所有连接
const connectionList = ref<ConnectionType[]>([])
export function useConnection(experiment_id: string) {
function fetchConnectionList() {
getConnectionList({ experiment_id }).then((res: any) => {
connectionList.value = res.data.items.map((item: any) => {
const attrs = JSON.parse(item.config_attributes)
const name = Array.isArray(attrs) ? attrs.find((item: any) => item.prop === 'name')?.value : attrs.name
return { ...item, config_attributes: attrs, name }
})
})
}
onMounted(() => {
if (!connectionList.value?.length) fetchConnectionList()
})
return { fetchConnectionList, connectionList }
}
// 所有资料
const materialList = ref<MaterialType[]>([])
export function useMaterial(experiment_id: string) {
function fetchMaterialList() {
getMaterialList({ experiment_id }).then((res: any) => {
materialList.value = res.data.items
})
}
onMounted(() => {
if (!materialList.value?.length) fetchMaterialList()
})
return { fetchMaterialList, materialList }
}
......@@ -9,6 +9,7 @@ import { useMapStore } from '@/stores/map'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const GradeRulesDialog = defineAsyncComponent(() => import('../components/GradeRulesDialog.vue'))
const DMLFormDialog = defineAsyncComponent(() => import('../components/DMLFormDialog.vue'))
// 数据状态
const status = useMapStore().getMapValuesByKey('system_status')
......@@ -120,6 +121,13 @@ function handleUpdateGradeRules(row: ExperimentItem) {
rowData.value = row
gradeRulesDialogVisible = true
}
// 配置数字营销实验
let dmlDialogVisible = $ref(false)
function handleUpdateDML(row: ExperimentItem) {
rowData.value = row
dmlDialogVisible = true
}
</script>
<template>
......@@ -132,6 +140,11 @@ function handleUpdateGradeRules(row: ExperimentItem) {
</template>
<template #table-x="{ row }: { row: ExperimentItem }">
<template v-if="row.type === '4'">
<el-button type="primary" round style="width: 132px; margin-bottom: 12px" @click="handleUpdateDML(row)"
>配置数字营销实验</el-button
><br />
</template>
<template v-if="!row.stu_commit_count">
<el-button type="info" round style="width: 132px; margin-bottom: 12px" @click="handleUpdateGradeRules(row)"
>编辑成绩规则</el-button
......@@ -160,4 +173,6 @@ function handleUpdateGradeRules(row: ExperimentItem) {
:data="rowData"
@update="onUpdateSuccess"
v-if="gradeRulesDialogVisible && rowData"></GradeRulesDialog>
<!-- 配置数字营销实验 -->
<DMLFormDialog v-model="dmlDialogVisible" :data="rowData" v-if="dmlDialogVisible && rowData"></DMLFormDialog>
</template>
......@@ -79,12 +79,18 @@ function handleRemoveClass(row: ClassItem) {
}
const gradeRulesVisible = $ref(false)
const reportRulesVisible = $ref(false)
const dmlURL = computed(() => {
return `${import.meta.env.VITE_SASS_DML}?experiment_id=${props.id}`
})
</script>
<template>
<AppCard title="实验管理">
<el-descriptions title="基本信息" v-if="detail">
<template #extra>
<el-button type="primary" v-if="detail.type === '4'">
<a :href="dmlURL" target="_blank">进入实验平台</a>
</el-button>
<el-button type="primary" @click="gradeRulesVisible = true">查看成绩规则</el-button>
<el-button type="primary" @click="reportRulesVisible = true">查看报告规则</el-button>
</template>
......
......@@ -26,11 +26,11 @@ export default defineConfig(({ mode }) => ({
cert: fs.readFileSync(path.join(__dirname, './https/ezijing.com.pem'))
},
proxy: {
// '/api/lab': {
// target: 'http://test-resource-api.ezijing.com:8001',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api\/lab/, '')
// },
'/api/lab': {
target: 'http://com-resource-api-test.ezijing.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/lab/, '')
},
'/api': 'https://saas-lab.ezijing.com'
}
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论