提交 0dfd43a6 authored 作者: 王鹏飞's avatar 王鹏飞

chore: 修改标签管理和群组管理

上级 202fa072
...@@ -56,8 +56,27 @@ ...@@ -56,8 +56,27 @@
.rule-item { .rule-item {
margin: 10px 0; margin: 10px 0;
} }
.event-rule { .event-rule,
.user-action-rule {
section + section { section + section {
margin-top: 30px; margin-top: 30px;
} }
} }
.rule-tips {
color: #999;
margin-bottom: 10px;
display: flex;
align-items: center;
.el-icon {
margin-right: 5px;
color: var(--main-color);
}
}
.rule-tag {
margin-right: 10px;
}
.rule-input {
width: 100px;
}
<script setup lang="ts">
import { Warning } from '@element-plus/icons-vue'
import EventRule from './EventRule.vue'
import { useMetaEvent } from '@/composables/useAllData'
const { metaEventList } = useMetaEvent()
const eventAttrRule = defineModel<any>({
default: () => ({
event_attr_rule: {
current_logic_operate: 'and',
happen_info: { is_happened: true, event_id: '-1', event_name: '所有事件', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: '' }
},
tag_rule: { event_id: '', event_name: '', attr_id: '', attr_name: '', type: 1, value: undefined }
})
})
const form: any = reactive({
event_attr_rule: {
current_logic_operate: 'and',
items: [
{
happen_info: { is_happened: true, event_id: '-1', event_name: '所有事件', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: undefined }
}
]
},
tag_rule: { event_id: '', event_name: '', attr_id: '', attr_name: '', type: 1, value: '' }
})
const { current_logic_operate, ...rest } = eventAttrRule.value.event_attr_rule
Object.assign(form, { tag_rule: eventAttrRule.value.tag_rule, event_attr_rule: { current_logic_operate, items: [rest] } })
const first = computed(() => form.event_attr_rule.items[0])
watch(
form,
() => {
eventAttrRule.value = {
event_attr_rule: { current_logic_operate: form.event_attr_rule.current_logic_operate, ...first.value },
tag_rule: form.tag_rule
}
},
{ deep: true }
)
watch(
() => first.value.happen_info.event_id,
() => {
form.tag_rule.event_id = first.value.happen_info.event_id
form.tag_rule.event_name = first.value.happen_info.event_name
form.tag_rule.attr_id = ''
form.tag_rule.attr_name = ''
}
)
// 获取事件属性列表
function getEventAttrList(eventId: string) {
return metaEventList.value.find(item => item.id === eventId)?.event_attrs || []
}
function handleAttrChange(value: string) {
const found = getEventAttrList(form.tag_rule.event_id).find(item => item.id === value)
form.tag_rule.attr_name = found?.name
}
</script>
<template>
<p class="rule-tips">
<el-icon><Warning /></el-icon>
事件偏好标签:将事件按照某个规则分组排序,使用排名前几个的事件属性作为用户标签值。
</p>
<!-- 事件属性规则 -->
<EventRule :limit="1" v-model="form.event_attr_rule" style="margin-top: 20px">
<template #footer>
<el-row align="middle" style="margin-top: 10px" v-if="first.happen_info.event_id">
<p></p>
<el-button type="info" style="margin: 0 10px">{{ form.tag_rule.event_name }}</el-button>
<el-select v-model="form.tag_rule.attr_id" style="width: 110px" @change="handleAttrChange">
<el-option v-for="option in getEventAttrList(form.tag_rule.event_id)" :key="option.id" :label="option.name" :value="option.id"></el-option>
</el-select>
<el-button type="info" style="margin: 0 10px">出现次数最多</el-button>
<p></p>
<el-input-number v-model="form.tag_rule.value" style="margin: 0 10px; width: 60px" :controls="false"></el-input-number>
<p></p>
<el-button type="info" style="margin: 0 10px">{{ form.tag_rule.attr_name }}</el-button>
<p>作为用户标签</p>
</el-row>
</template>
</EventRule>
</template>
<style src="@/assets/styles/rule.scss"></style>
...@@ -5,7 +5,10 @@ import { useMetaEvent } from '@/composables/useAllData' ...@@ -5,7 +5,10 @@ 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 eventAttrRule = ref(inject('eventAttrRule') as EventRule) const { limit = Infinity } = defineProps<{ limit?: number }>()
// const eventAttrRule = ref(inject('eventAttrRule') as EventRule)
const eventAttrRule = defineModel<EventRule>({ default: { current_logic_operate: 'and', items: [] } })
const { metaEventList } = useMetaEvent() const { metaEventList } = useMetaEvent()
...@@ -72,7 +75,7 @@ function handleHappenOperateChange(value: string, item: EventRuleItem) { ...@@ -72,7 +75,7 @@ function handleHappenOperateChange(value: string, item: EventRuleItem) {
// 事件改变 // 事件改变
function handleEventChange(value: string, item: EventRuleItem) { function handleEventChange(value: string, item: EventRuleItem) {
const currentEvent = metaEventList.value.find(item => item.id === value) const currentEvent = currentMetaEventList.value.find(item => item.id === value)
item.happen_info.event_name = currentEvent?.name || '' item.happen_info.event_name = currentEvent?.name || ''
item.happen_info.attr_list = [] item.happen_info.attr_list = []
} }
...@@ -173,7 +176,7 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) { ...@@ -173,7 +176,7 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</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)"></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">
...@@ -185,7 +188,11 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) { ...@@ -185,7 +188,11 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)"> <el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)">
<el-option v-for="option in getOperatorList(attr.attr_type)" :key="option.value" :label="option.alias || option.label" :value="option.value"></el-option> <el-option
v-for="option in getOperatorList(attr.attr_type)"
:key="option.value"
:label="option.alias || option.label"
: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)">
...@@ -248,7 +255,8 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) { ...@@ -248,7 +255,8 @@ function remoteMethod(rule: EventRuleItem, attr: RuleAttr, search: string) {
</section> </section>
</div> </div>
</div> </div>
<el-button text :icon="Plus" @click="handleAdd(eventAttrRule.items)">添加条件</el-button> <el-button text :icon="Plus" @click="handleAdd(eventAttrRule.items)" v-if="eventAttrRule.items.length < limit">添加条件</el-button>
<slot name="footer"></slot>
</el-card> </el-card>
</template> </template>
......
<script setup lang="ts">
import { Warning } from '@element-plus/icons-vue'
import EventRule from './EventRule.vue'
import { wayList } from '@/utils/dictionary'
const eventAttrRule = defineModel<any>({
default: () => ({
event_attr_rule: {
current_logic_operate: 'and',
happen_info: { is_happened: true, event_id: '-1', event_name: '所有事件', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: '' }
},
tag_rule: { way: '' }
})
})
const form: any = reactive({
event_attr_rule: {
current_logic_operate: 'and',
items: [
{
happen_info: { is_happened: true, event_id: '-1', event_name: '所有事件', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: '' }
}
]
},
tag_rule: { way: '' }
})
const { current_logic_operate, ...rest } = eventAttrRule.value.event_attr_rule
Object.assign(form, { tag_rule: eventAttrRule.value.tag_rule, event_attr_rule: { current_logic_operate, items: [rest] } })
const first = computed(() => form.event_attr_rule.items[0])
watch(
form,
() => {
eventAttrRule.value = {
event_attr_rule: { current_logic_operate: form.event_attr_rule.current_logic_operate, ...first.value },
tag_rule: form.tag_rule
}
},
{ deep: true }
)
</script>
<template>
<p class="rule-tips">
<el-icon><Warning /></el-icon>
事件指标标签:将事件指标作为用户的标签。
</p>
<!-- 事件属性规则 -->
<EventRule :limit="1" v-model="form.event_attr_rule" style="margin-top: 20px">
<template #footer>
<el-row align="middle" style="margin-top: 10px" v-if="first.happen_info.event_id">
<p></p>
<el-button type="info" style="margin: 0 10px">{{ first.happen_info.event_name }}</el-button>
<el-select v-model="form.tag_rule.way" style="margin-right: 10px; width: 130px">
<el-option v-for="item in wayList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<p>作为用户标签</p>
</el-row>
</template>
</EventRule>
</template>
<style src="@/assets/styles/rule.scss"></style>
...@@ -3,7 +3,8 @@ import type { TagRule } from '@/types' ...@@ -3,7 +3,8 @@ import type { TagRule } from '@/types'
import { PriceTag, Plus, CloseBold } from '@element-plus/icons-vue' import { PriceTag, Plus, CloseBold } from '@element-plus/icons-vue'
import { useTag } from '@/composables/useAllData' import { useTag } from '@/composables/useAllData'
const tagRule = ref(inject('tagRule') as TagRule) // const tagRule = ref(inject('tagRule') as TagRule)
const tagRule = defineModel<TagRule>({ default: { current_logic_operate: 'and', items: [] } })
const { tagList } = useTag() const { tagList } = useTag()
...@@ -44,11 +45,7 @@ function handleRemove(items: string[], index: number) { ...@@ -44,11 +45,7 @@ function handleRemove(items: string[], index: number) {
标签 等于 标签 等于
<el-form-item> <el-form-item>
<el-select v-model="tagRule.items[index]"> <el-select v-model="tagRule.items[index]">
<el-option <el-option v-for="option in tagList" :key="option.id" :label="option.name" :value="option.id"></el-option>
v-for="option in tagList"
:key="option.id"
:label="option.name"
:value="option.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
......
<script setup lang="ts">
import { Warning } from '@element-plus/icons-vue'
import { ElInput } from 'element-plus'
import UserRule from '@/components/rule/UserRule.vue'
import EventRule from '@/components/rule/EventRule.vue'
import UserActionRule from '@/components/rule/UserActionRule.vue'
const rules = defineModel<Array<any>>({ default: [] })
const inputValue = ref('')
const inputVisible = ref(false)
const InputRef = ref<InstanceType<typeof ElInput>>()
const activeIndex = ref(0)
const handleClose = (index: number) => {
rules.value.splice(index, 1)
activeIndex.value = 0
}
const showInput = () => {
inputVisible.value = true
nextTick(() => {
InputRef.value!.input!.focus()
})
}
const handleInputConfirm = () => {
if (inputValue.value) {
rules.value.push({
level: rules.value.length,
level_name: inputValue.value,
user_attr_rule: { current_logic_operate: 'and', items: [] },
event_attr_rule: { current_logic_operate: 'and', items: [] },
user_action_rule: { current_logic_operate: 'and', items: [] }
})
activeIndex.value = rules.value.length - 1
}
inputVisible.value = false
inputValue.value = ''
}
</script>
<template>
<p class="rule-tips">
<el-icon><Warning /></el-icon>
自定义分层标签:将满足不同分层规则的用户打上分层标签,同一用户会被优先匹配在顺序靠前的分层
</p>
<div>
<el-tag
class="rule-tag"
v-for="(rule, index) in rules"
size="large"
effect="light"
:type="index !== activeIndex ? 'info' : ''"
:key="rule.level"
closable
:disable-transitions="false"
@close="handleClose(index)"
@click="activeIndex = index">
{{ rule.level_name }}
</el-tag>
<el-input v-model="inputValue" class="rule-input" ref="InputRef" @keyup.enter="handleInputConfirm" @blur="handleInputConfirm" v-if="inputVisible" />
<el-button v-else style="width: 100px" @click="showInput"> + 添加分层 </el-button>
</div>
<template v-if="rules.length">
<!-- 用户属性规则 -->
<UserRule v-model="rules[activeIndex].user_attr_rule" style="margin-top: 20px"></UserRule>
<!-- 事件属性规则 -->
<EventRule v-model="rules[activeIndex].event_attr_rule" style="margin-top: 20px"></EventRule>
<!-- 用户行为序列规则 -->
<UserActionRule v-model="rules[activeIndex].user_action_rule" style="margin-top: 20px"></UserActionRule>
</template>
</template>
<style src="@/assets/styles/rule.scss"></style>
<script setup lang="ts">
import type { UserActionRule, UserActionRuleItem, RuleEvent, RuleAttr } from '@/types'
import { Paperclip, Plus, CloseBold } from '@element-plus/icons-vue'
import { useMetaEvent } from '@/composables/useAllData'
import { stringOperatorList, numberOperatorList, dateOperatorList } from '@/utils/dictionary'
import { searchEventAttrs } from '@/api/base'
// const userActionRule = ref(inject('userActionRule') as UserActionRule)
const userActionRule = defineModel<UserActionRule>({ default: { current_logic_operate: 'and', items: [] } })
const { metaEventList } = useMetaEvent()
const currentMetaEventList = computed(() => {
return [{ id: '-1', name: '所有事件', event_attrs: [] }, ...metaEventList.value]
})
// 获取逻辑运算符名称
function getLogicalName(value: 'and' | 'or') {
return value === 'or' ? '或' : '且'
}
// 获取运算符列表
function getOperatorList(type: string) {
if (type === '1') return stringOperatorList
if (type === '2' || type === '3') return numberOperatorList
if (type === '4' || type === '5') return dateOperatorList
return stringOperatorList
}
// 获取事件属性列表
function getEventAttrList(eventId: string) {
return currentMetaEventList.value.find(item => item.id === eventId)?.event_attrs || []
}
// 切换逻辑运算符
function toggleOperate(rule: UserActionRule) {
rule.current_logic_operate = rule.current_logic_operate === 'or' ? 'and' : 'or'
}
// 添加条件
function handleAdd(items: UserActionRuleItem[]) {
items.push({ operate: '', operate_name: '', value: '', event_list: [] })
}
// 删除条件
function handleRemove(items: UserActionRuleItem[], index: number) {
items.splice(index, 1)
}
// 添加事件
function handleEventAdd(events: RuleEvent[]) {
events.push({ event_id: '', event_name: '', attr_list: [] })
}
// 删除事件条件
function handleEventRemove(events: RuleEvent[], index: number) {
events.splice(index, 1)
}
// 添加属性条件
function handleAttrAdd(attrs: RuleAttr[]) {
attrs.push({ attr_id: '', attr: '', attr_name: '', attr_type: '', operate: '', operate_name: '', value: '' })
}
// 删除属性条件
function handleAttrRemove(attrs: RuleAttr[], index: number) {
attrs.splice(index, 1)
}
// 发生条件改变
function handleHappenOperateChange(value: string, item: UserActionRuleItem) {
const found = dateOperatorList.find(item => item.value === value)
item.operate_name = found?.label || ''
item.value = ''
}
// 事件改变
function handleEventChange(value: string, event: RuleEvent) {
const currentEvent = metaEventList.value.find(item => item.id === value)
event.event_name = currentEvent?.name || ''
event.attr_list = []
}
// 属性改变
function handleAttrChange(value: string, attr: RuleAttr, event: RuleEvent) {
const found = getEventAttrList(event.event_id).find(item => item.id === value)
attr.attr = found?.english_name || ''
attr.attr_name = found?.name || ''
attr.attr_type = found?.type || ''
// 清空条件数据
attr.operate = ''
attr.operate_name = ''
attr.value = ''
}
// 条件改变
function handleOperateChange(value: string, item: RuleAttr) {
const found = getOperatorList(item.attr_type).find(item => item.value === value)
item.operate_name = found?.label || ''
item.value = ''
// 区间
if (value === 'range') {
item.value = { start: undefined, end: undefined }
}
}
function querySearch(event: RuleEvent, attr: RuleAttr, search: string, cb: (arg: any) => void) {
const found = getEventAttrList(event.event_id).find(item => item.id === attr.attr_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 => {
const list: string[] = []
res.data.list.forEach((item: any) => {
try {
const fields = JSON.parse(item.fields)
const value = fields[attr.attr_id]
if (!list.includes(value)) list.push(value)
} catch (error) {
console.log(error)
}
})
cb(list.map((item: string) => ({ value: item })))
})
}
const options = ref<{ label: string; value: string }[]>([])
const loading = ref(false)
function remoteMethod(event: RuleEvent, attr: RuleAttr, search: string) {
const found = getEventAttrList(event.event_id).find(item => item.id === attr.attr_id)
const experiment_meta_event_id = found?.experiment_meta_event_id || ''
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 => {
const list: string[] = []
res.data.list.forEach((item: any) => {
try {
const fields = JSON.parse(item.fields)
const value = fields[attr.attr_id]
if (!list.includes(value)) list.push(value)
} catch (error) {
console.log(error)
}
})
options.value = list.map((item: string) => ({ value: item, label: item }))
loading.value = false
})
}
</script>
<template>
<el-card shadow="never" class="user-action-rule">
<template #header>
<el-button circle color="#67c23a" :icon="Paperclip"></el-button>
用户行为序列满足以下条件
</template>
<div class="rule" v-if="userActionRule.items.length">
<div class="rule-operator">
<span @click="toggleOperate(userActionRule)">{{ getLogicalName(userActionRule.current_logic_operate) }}</span>
</div>
<div class="rule-list">
<section class="rule-item" v-for="(rule, index) in userActionRule.items" :key="index">
<el-row align="middle">
<!-- 发生 -->
<el-form-item>
<el-select v-model="rule.operate" @change="value => handleHappenOperateChange(value, rule)">
<el-option v-for="option in dateOperatorList" :key="option.label" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<template v-if="rule.operate === 'after' || rule.operate === 'before'">
<el-date-picker v-model="rule.value" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</template>
<el-input v-model="rule.value" v-else />
</el-form-item>
<p style="margin-right: 10px">依次发生过</p>
<el-button text :icon="Plus" @click="handleEventAdd(rule.event_list)">添加条件</el-button>
<el-button text :icon="CloseBold" @click="handleRemove(userActionRule.items, index)"></el-button>
</el-row>
<div class="rule-item" v-for="(event, index) in rule.event_list" :key="index" style="margin: 15px 0">
<el-form-item>
<el-select v-model="event.event_id" @change="value => handleEventChange(value, event)">
<el-option v-for="option in currentMetaEventList" :key="option.id" :label="option.name" :value="option.id"></el-option>
</el-select>
</el-form-item>
<el-button text :icon="Plus" @click="handleAttrAdd(event.attr_list)">添加条件</el-button>
<el-button text :icon="CloseBold" @click="handleEventRemove(rule.event_list, index)"></el-button>
<!-- 属性条件 -->
<el-row justify="space-between" class="rule-item" v-for="(attr, index) in event.attr_list" :key="index">
<div>
<el-form-item>
<el-select v-model="attr.attr_id" @change="value => handleAttrChange(value, attr, event)">
<el-option v-for="option in getEventAttrList(event.event_id)" :key="option.id" :label="option.name" :value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)">
<el-option
v-for="option in getOperatorList(attr.attr_type)"
:key="option.value"
:label="option.alias || option.label"
:value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="!['null', 'not null'].includes(attr.operate)">
<!-- 数字区间 -->
<template v-if="['2', '3'].includes(attr.attr_type) && attr.operate === 'range'">
<el-input-number step-strictly :controls="false" :min="0" v-model="attr.value.start" />
<el-input-number step-strictly :controls="false" :min="0" v-model="attr.value.end" />
</template>
<!-- 日期区间 -->
<template v-else-if="attr.attr_type === '4' && attr.operate === 'range'">
<el-date-picker v-model="attr.value.start" type="date" value-format="YYYY-MM-DD" />
<el-date-picker v-model="attr.value.end" type="date" value-format="YYYY-MM-DD" />
</template>
<!-- 时间区间 -->
<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 v-model="attr.value.end" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</template>
<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" />
</template>
<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" />
</template>
<template v-else>
<el-select
v-model="attr.value"
filterable
remote
allow-create
multiple
:remote-method="(query:string) => remoteMethod(event, attr, query)"
:loading="loading"
style="width: 320px"
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-select>
<el-autocomplete v-model="attr.value" :fetch-suggestions="(query, cb) => querySearch(event, attr, query, cb)" style="width: 320px" v-else />
</template>
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleAttrRemove(event.attr_list, index)"></el-button>
</el-row>
</div>
</section>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd(userActionRule.items)">添加条件</el-button>
</el-card>
</template>
<style src="@/assets/styles/rule.scss"></style>
...@@ -5,7 +5,8 @@ import { useUserAttr } from '@/composables/useAllData' ...@@ -5,7 +5,8 @@ import { useUserAttr } from '@/composables/useAllData'
import { stringOperatorList, numberOperatorList, dateOperatorList } from '@/utils/dictionary' import { stringOperatorList, numberOperatorList, dateOperatorList } from '@/utils/dictionary'
import { searchMetaMemberAttrs } from '@/api/base' import { searchMetaMemberAttrs } from '@/api/base'
const userAttrRule = ref(inject('userAttrRule') as UserAttrRule) // const userAttrRule = ref(inject('userAttrRule') as UserAttrRule)
const userAttrRule = defineModel<UserAttrRule>({ default: { current_logic_operate: 'and', items: [] } })
const { userAttrList } = useUserAttr() const { userAttrList } = useUserAttr()
...@@ -107,17 +108,16 @@ function remoteMethod(item: RuleAttr, search: string = '') { ...@@ -107,17 +108,16 @@ function remoteMethod(item: RuleAttr, search: string = '') {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-select v-model="item.operate" @change="value => handleOperateChange(value, item)"> <el-select v-model="item.operate" @change="value => handleOperateChange(value, item)">
<el-option v-for="option in getOperatorList(item.attr_type)" :key="option.value" :label="option.alias || option.label" :value="option.value"></el-option> <el-option
v-for="option in getOperatorList(item.attr_type)"
:key="option.value"
:label="option.alias || option.label"
:value="option.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="!['null', 'not null'].includes(item.operate)"> <el-form-item v-if="!['null', 'not null'].includes(item.operate)">
<!-- 数字区间 -->
<template v-if="['2', '3'].includes(item.attr_type) && item.operate === 'range'">
<el-input-number step-strictly :controls="false" :min="0" v-model="item.value.start" />
<el-input-number step-strictly :controls="false" :min="0" v-model="item.value.end" />
</template>
<!-- 日期区间 --> <!-- 日期区间 -->
<template v-else-if="item.attr_type === '4' && item.operate === 'range'"> <template v-if="item.attr_type === '4' && item.operate === 'range'">
<el-date-picker v-model="item.value.start" type="date" value-format="YYYY-MM-DD" /> <el-date-picker v-model="item.value.start" type="date" value-format="YYYY-MM-DD" />
<el-date-picker v-model="item.value.end" type="date" value-format="YYYY-MM-DD" /> <el-date-picker v-model="item.value.end" type="date" value-format="YYYY-MM-DD" />
</template> </template>
...@@ -145,7 +145,12 @@ function remoteMethod(item: RuleAttr, search: string = '') { ...@@ -145,7 +145,12 @@ function remoteMethod(item: RuleAttr, search: string = '') {
v-if="['in', 'not in'].includes(item.operate)"> v-if="['in', 'not in'].includes(item.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="item.value" value-key="attr_value" :fetch-suggestions="(query, cb) => querySearch(item, query, cb)" style="width: 320px" v-else /> <el-autocomplete
v-model="item.value"
value-key="attr_value"
:fetch-suggestions="(query, cb) => querySearch(item, query, cb)"
style="width: 320px"
v-else />
</template> </template>
</el-form-item> </el-form-item>
</div> </div>
......
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
import type { import type { GroupListRequest, StaticGroupCreateRequest, StaticGroupUpdateRequest, DynamicGroupCreateRequest, DynamicGroupUpdateRequest } from './types'
GroupListRequest,
StaticGroupCreateRequest,
StaticGroupUpdateRequest,
DynamicGroupCreateRequest,
DynamicGroupUpdateRequest
} from './types'
// 获取群组列表 // 获取群组列表
export function getGroupList(params?: GroupListRequest) { export function getGroupList(params?: GroupListRequest) {
return httpRequest.get('/api/lab/v1/experiment/group/list', { params }) return httpRequest.get('/api/lab/v1/experiment/group/bda-list', { params })
} }
// 获取群组详情 // 获取群组详情
export function getGroupInfo(params: { id: string }) { export function getGroupInfo(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/detail', { params }) return httpRequest.get('/api/lab/v1/experiment/group/bda-detail', { params })
} }
// 创建静态群组 // 创建静态群组
export function createStaticGroup(data: StaticGroupCreateRequest) { export function createStaticGroup(data: StaticGroupCreateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/create-static-group', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-create-static-group', data)
} }
// 创建动态群组 // 创建动态群组
export function createDynamicGroup(data: DynamicGroupCreateRequest) { export function createDynamicGroup(data: DynamicGroupCreateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/create-dynamic-group', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-create-dynamic-group', data)
} }
// 更新静态群组 // 更新静态群组
export function updateStaticGroup(data: StaticGroupUpdateRequest) { export function updateStaticGroup(data: StaticGroupUpdateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/update-static-group', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-update-static-group', data)
} }
// 更新动态群组 // 更新动态群组
export function updateDynamicGroup(data: DynamicGroupUpdateRequest) { export function updateDynamicGroup(data: DynamicGroupUpdateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/update-dynamic-group', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-update-dynamic-group', data)
} }
// 删除群组 // 删除群组
export function deleteGroup(data: { ids: string[] }) { export function deleteGroup(data: { ids: string[] }) {
return httpRequest.post('/api/lab/v1/experiment/group/delete', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-delete', data)
} }
// 获取群组数据信息 // 获取群组数据信息
export function getGroupStatistics(params: { group_id: string }) { export function getGroupStatistics(params: { group_id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/statistics-detail', { params }) return httpRequest.get('/api/lab/v1/experiment/group/bda-statistics-detail', { params })
} }
// 更新群组数据信息 // 更新群组数据信息
export function updateGroupStatistics(data: { id: string }) { export function updateGroupStatistics(data: { id: string }) {
return httpRequest.post('/api/lab/v1/experiment/group/statistics-user-group', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-statistics-user-group', data)
} }
// 获取群组成员 // 获取群组成员
export function getGroupMembers(params: { group_id: string; name?: string; id?: string }) { export function getGroupMembers(params: { group_id: string; name?: string; id?: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/members', { params }) return httpRequest.get('/api/lab/v1/experiment/group/bda-members', { params })
} }
// 清空静态群组成员 // 清空静态群组成员
export function clearGroupMembers(data: { group_id: string }) { export function clearGroupMembers(data: { group_id: string }) {
return httpRequest.post('/api/lab/v1/experiment/group/clear-members', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-clear-members', data)
} }
// 搜索群组成员 // 搜索群组成员
export function searchGroupMembers(params: { group_id: string; name?: string; id?: string }) { export function searchGroupMembers(params: { group_id: string; name?: string; id?: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/search-members', { params }) return httpRequest.get('/api/lab/v1/experiment/group/bda-search-members', { params })
} }
// 手动添加静态群组 // 手动添加静态群组
export function addGroupMembers(data: { group_id: string; user_ids: string[] }) { export function addGroupMembers(data: { group_id: string; user_ids: string[] }) {
return httpRequest.post('/api/lab/v1/experiment/group/add-members', data) return httpRequest.post('/api/lab/v1/experiment/group/bda-add-members', data)
} }
...@@ -7,6 +7,7 @@ import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamic ...@@ -7,6 +7,7 @@ import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamic
import UserRule from '@/components/rule/UserRule.vue' import UserRule from '@/components/rule/UserRule.vue'
import EventRule from '@/components/rule/EventRule.vue' import EventRule from '@/components/rule/EventRule.vue'
import LabelRule from '@/components/rule/LabelRule.vue' import LabelRule from '@/components/rule/LabelRule.vue'
import UserActionRule from '@/components/rule/UserActionRule.vue'
import { pick } from 'lodash-es' import { pick } from 'lodash-es'
interface Props { interface Props {
...@@ -29,7 +30,7 @@ const title = $computed(() => { ...@@ -29,7 +30,7 @@ const title = $computed(() => {
}) })
const formRef = $ref<FormInstance>() const formRef = $ref<FormInstance>()
const form = reactive({ const form: any = reactive({
id: '', id: '',
name: '', name: '',
type: '', type: '',
...@@ -38,13 +39,10 @@ const form = reactive({ ...@@ -38,13 +39,10 @@ const form = reactive({
update_rule: { type: 1, info: 1 }, update_rule: { type: 1, info: 1 },
user_attr_rule: { current_logic_operate: 'and', items: [] }, user_attr_rule: { current_logic_operate: 'and', items: [] },
event_attr_rule: { current_logic_operate: 'and', items: [] }, event_attr_rule: { current_logic_operate: 'and', items: [] },
tag_rule: { current_logic_operate: 'and', items: [] } tag_rule: { current_logic_operate: 'and', items: [] },
user_action_rule: { current_logic_operate: 'and', items: [] }
}) })
provide('userAttrRule', toRef(form, 'user_attr_rule'))
provide('eventAttrRule', toRef(form, 'event_attr_rule'))
provide('tagRule', toRef(form, 'tag_rule'))
watchEffect(() => { watchEffect(() => {
if (props.data?.id) { if (props.data?.id) {
let updateRule = { type: 1, info: 1 } let updateRule = { type: 1, info: 1 }
...@@ -75,7 +73,12 @@ function fetchInfo() { ...@@ -75,7 +73,12 @@ function fetchInfo() {
return item return item
}) })
Object.assign(form, { user_attr_rule: { ...user_attr_rule, attrRuleItems }, event_attr_rule: { ...event_attr_rule, eventRuleItems }, tag_rule }) Object.assign(form, {
user_attr_rule: { ...user_attr_rule, attrRuleItems },
event_attr_rule: { ...event_attr_rule, eventRuleItems },
tag_rule,
user_action_rule: detail.user_action_rule
})
}) })
} }
...@@ -106,7 +109,7 @@ async function handleCreate() { ...@@ -106,7 +109,7 @@ async function handleCreate() {
event_attr_rule: JSON.stringify([form.event_attr_rule]), event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([form.tag_rule]) tag_rule: JSON.stringify([form.tag_rule])
}, },
['name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status'] ['name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'user_action_rule', 'status']
) )
await createDynamicGroup(params) await createDynamicGroup(params)
} }
...@@ -136,9 +139,10 @@ async function handleUpdate() { ...@@ -136,9 +139,10 @@ async function handleUpdate() {
update_rule: JSON.stringify(form.update_rule), update_rule: JSON.stringify(form.update_rule),
user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]), user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]),
event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }]), event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }]),
tag_rule: JSON.stringify([form.tag_rule]) tag_rule: JSON.stringify([form.tag_rule]),
user_action_rule: JSON.stringify(form.user_action_rule)
}, },
['id', 'name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status'] ['id', 'name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'user_action_rule', 'status']
) )
await updateDynamicGroup(params) await updateDynamicGroup(params)
} }
...@@ -191,11 +195,12 @@ async function handleUpdate() { ...@@ -191,11 +195,12 @@ async function handleUpdate() {
</el-form-item> </el-form-item>
<template v-if="data.type === '2'"> <template v-if="data.type === '2'">
<!-- 用户属性规则 --> <!-- 用户属性规则 -->
<UserRule></UserRule> <UserRule v-model="form.user_attr_rule"></UserRule>
<!-- 事件属性规则 --> <!-- 事件属性规则 -->
<EventRule style="margin-top: 20px"></EventRule> <EventRule v-model="form.event_attr_rule" style="margin-top: 20px"></EventRule>
<!-- 标签 --> <!-- 标签 -->
<LabelRule style="margin-top: 20px"></LabelRule> <LabelRule v-model="form.tag_rule" style="margin-top: 20px"></LabelRule>
<UserActionRule v-model="form.user_action_rule" style="margin-top: 20px"></UserActionRule>
</template> </template>
</el-form> </el-form>
<template #footer> <template #footer>
......
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
import type { import type { LabelTypeListRequest, LabelTypeCreateRequest, LabelTypeUpdateRequest, LabelListRequest, LabelCreateRequest, LabelUpdateRequest } from './types'
LabelTypeListRequest,
LabelTypeCreateRequest,
LabelTypeUpdateRequest,
LabelListRequest,
LabelCreateRequest,
LabelUpdateRequest
} from './types'
// 获取标签类型列表 // 获取标签类型列表
export function getLabelTypeList(params?: LabelTypeListRequest) { export function getLabelTypeList(params?: LabelTypeListRequest) {
...@@ -30,40 +23,40 @@ export function deleteLabelType(data: { id: string }) { ...@@ -30,40 +23,40 @@ export function deleteLabelType(data: { id: string }) {
// 获取标签列表 // 获取标签列表
export function getLabelList(params?: LabelListRequest) { export function getLabelList(params?: LabelListRequest) {
return httpRequest.get('/api/lab/v1/experiment/tag/list', { params }) return httpRequest.get('/api/lab/v1/experiment/tag/bda-list', { params })
} }
// 创建标签 // 创建标签
export function createLabel(data: LabelCreateRequest) { export function createLabel(data: LabelCreateRequest) {
return httpRequest.post('/api/lab/v1/experiment/tag/create', data) return httpRequest.post('/api/lab/v1/experiment/tag/bda-create', data)
} }
// 更新标签 // 更新标签
export function updateLabel(data: LabelUpdateRequest) { export function updateLabel(data: LabelUpdateRequest) {
return httpRequest.post('/api/lab/v1/experiment/tag/update', data) return httpRequest.post('/api/lab/v1/experiment/tag/bda-update', data)
} }
// 更新标签 // 删除标签
export function deleteLabel(data: { id: string }) { export function deleteLabel(data: { id: string }) {
return httpRequest.post('/api/lab/v1/experiment/tag/delete', data) return httpRequest.post('/api/lab/v1/experiment/tag/bda-delete', data)
} }
// 获取标签数据信息 // 获取标签数据信息
export function getLabelStatistics(params: { id: string }) { export function getLabelStatistics(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/statistics', { params }) return httpRequest.get('/api/lab/v1/experiment/tag/bda-statistics', { params })
} }
// 更新标签数据信息 // 更新标签数据信息
export function updateLabelStatistics(data: { id: string }) { export function updateLabelStatistics(data: { id: string }) {
return httpRequest.post('/api/lab/v1/experiment/tag/statistics-user-tag', data) return httpRequest.post('/api/lab/v1/experiment/tag/bda-statistics-user-tag', data)
} }
// 获取标签规则 // 获取标签规则
export function getLabelRule(params: { id: string }) { export function getLabelRule(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/tag/rule', { params }) return httpRequest.get('/api/lab/v1/experiment/tag/bda-rules', { params })
} }
// 更新标签规则 // 更新标签规则
export function updateLabelRule(data: { id: string; user_attr_rule: string; event_attr_rule: string }) { export function updateLabelRule(data: { id: string; rules: string }) {
return httpRequest.post('/api/lab/v1/experiment/tag/save-rule', data) return httpRequest.post('/api/lab/v1/experiment/tag/bda-save-rules', data)
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import type { Label } from '../types' import type { Label } from '../types'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { updateStatusRuleList, dateUnitList, weekList } from '@/utils/dictionary' import { updateStatusRuleList, dateUnitList, weekList, labelList } from '@/utils/dictionary'
import { createLabel, updateLabel } from '../api' import { createLabel, updateLabel } from '../api'
import { useLabelType } from '../composables/useLabelType' import { useLabelType } from '../composables/useLabelType'
import { pick } from 'lodash-es' import { pick } from 'lodash-es'
...@@ -28,7 +28,8 @@ const form = reactive({ ...@@ -28,7 +28,8 @@ const form = reactive({
type_id: '', type_id: '',
update_status: '2', update_status: '2',
update_rule: { type: 1, info: 1 }, update_rule: { type: 1, info: 1 },
status: '1' status: '1',
label: ''
}) })
watchEffect(() => { watchEffect(() => {
if (props.data) { if (props.data) {
...@@ -44,7 +45,8 @@ watchEffect(() => { ...@@ -44,7 +45,8 @@ watchEffect(() => {
const rules = ref<FormRules>({ const rules = ref<FormRules>({
name: [{ required: true, message: '请输入标签名称' }], name: [{ required: true, message: '请输入标签名称' }],
type_id: [{ required: true, message: '请选择标签类型' }], label: [{ required: true, message: '请选择标签类型' }],
type_id: [{ required: true, message: '请选择标签目录' }],
update_status: [{ required: true, message: '请选择更新评率' }] update_status: [{ required: true, message: '请选择更新评率' }]
}) })
...@@ -54,13 +56,7 @@ function handleSubmit() { ...@@ -54,13 +56,7 @@ function handleSubmit() {
} }
// 新建 // 新建
function handleCreate() { function handleCreate() {
const params = pick({ ...form, update_rule: JSON.stringify(form.update_rule) }, [ const params = pick({ ...form, update_rule: JSON.stringify(form.update_rule) }, ['name', 'type_id', 'update_status', 'update_rule', 'status', 'label'])
'name',
'type_id',
'update_status',
'update_rule',
'status'
])
createLabel(params).then(() => { createLabel(params).then(() => {
ElMessage({ message: '创建成功', type: 'success' }) ElMessage({ message: '创建成功', type: 'success' })
emit('update') emit('update')
...@@ -69,14 +65,7 @@ function handleCreate() { ...@@ -69,14 +65,7 @@ function handleCreate() {
} }
// 修改 // 修改
function handleUpdate() { function handleUpdate() {
const params = pick({ ...form, update_rule: JSON.stringify(form.update_rule) }, [ const params = pick({ ...form, update_rule: JSON.stringify(form.update_rule) }, ['id', 'name', 'type_id', 'update_status', 'update_rule', 'status'])
'id',
'name',
'type_id',
'update_status',
'update_rule',
'status'
])
updateLabel(params).then(() => { updateLabel(params).then(() => {
ElMessage({ message: '修改成功', type: 'success' }) ElMessage({ message: '修改成功', type: 'success' })
emit('update') emit('update')
...@@ -91,29 +80,26 @@ function handleUpdate() { ...@@ -91,29 +80,26 @@ function handleUpdate() {
<el-form-item label="标签名称" prop="name"> <el-form-item label="标签名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" /> <el-input v-model="form.name" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="标签类型" prop="type_id"> <el-form-item label="标签类型" prop="label">
<el-select v-model="form.label" style="width: 100%" :disabled="isUpdate">
<el-option v-for="item in labelList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="标签目录" prop="type_id">
<el-select v-model="form.type_id" style="width: 100%"> <el-select v-model="form.type_id" style="width: 100%">
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id"></el-option> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="更新频率" prop="update_status"> <el-form-item label="更新频率" prop="update_status">
<el-radio-group v-model="form.update_status"> <el-radio-group v-model="form.update_status">
<el-radio <el-radio v-for="item in updateStatusRuleList" :key="item.value" :label="item.value" :disabled="item.value === '1'">
v-for="item in updateStatusRuleList"
:key="item.value"
:label="item.value"
:disabled="item.value === '1'">
{{ item.label }} {{ item.label }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
<div class="update-rule-wrap" v-if="form.update_status === '1'"> <div class="update-rule-wrap" v-if="form.update_status === '1'">
<span></span> <span></span>
<el-select v-model="form.update_rule.type" placeholder=" " style="width: 60px"> <el-select v-model="form.update_rule.type" placeholder=" " style="width: 60px">
<el-option <el-option v-for="item in dateUnitList" :key="item.value" :label="item.label" :value="item.value"></el-option>
v-for="item in dateUnitList"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select> </el-select>
<template v-if="form.update_rule.type === 1"> <template v-if="form.update_rule.type === 1">
<span>的凌晨更新</span> <span>的凌晨更新</span>
......
<script setup lang="ts"> <script setup lang="ts">
import type { Label } from '../types' import type { Label } from '../types'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { getNameByValue, updateStatusRuleList } from '@/utils/dictionary' import { getNameByValue, updateStatusRuleList, labelList } from '@/utils/dictionary'
import { getLabelRule, updateLabelRule } from '../api' import { getLabelRule, updateLabelRule } from '../api'
import UserRule from '@/components/rule/UserRule.vue' import LevelRule from '@/components/rule/LevelRule.vue'
import EventRule from '@/components/rule/EventRule.vue' import EventPreferenceRule from '@/components/rule/EventPreferenceRule.vue'
import EventTargetRule from '@/components/rule/EventTargetRule.vue'
const props = defineProps<{ const props = defineProps<{
data: Label data: Label
...@@ -27,37 +28,30 @@ const statusList = useMapStore().getMapValuesByKey('system_status') ...@@ -27,37 +28,30 @@ const statusList = useMapStore().getMapValuesByKey('system_status')
const formRef = $ref<FormInstance>() const formRef = $ref<FormInstance>()
const form = reactive({ const form = reactive({
id: props.data.id, id: '',
user_attr_rule: { current_logic_operate: 'and', items: [] }, rules: undefined
event_attr_rule: { current_logic_operate: 'and', items: [] }
}) })
provide('userAttrRule', toRef(form, 'user_attr_rule'))
provide('eventAttrRule', toRef(form, 'event_attr_rule'))
function fetchInfo() { function fetchInfo() {
getLabelRule({ id: props.data.id }).then(res => { getLabelRule({ id: props.data.id }).then(res => {
const { detail } = res.data const { detail } = res.data
const [user_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.user_attr_rule // const [user_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.user_attr_rule
const [event_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.event_attr_rule // const [event_attr_rule = { current_logic_operate: 'and', items: [] }] = detail.event_attr_rule
const attrRuleItems = user_attr_rule.items.map((item: any) => { // const attrRuleItems = user_attr_rule.items.map((item: any) => {
item.value = ['in', 'not in'].includes(item.operate) ? item.value.split(',') : item.value // item.value = ['in', 'not in'].includes(item.operate) ? item.value.split(',') : item.value
return item // return item
}) // })
const eventRuleItems = event_attr_rule.items.map((item: any) => { // const eventRuleItems = event_attr_rule.items.map((item: any) => {
item.value = ['in', 'not in'].includes(item.operate) ? item.value.split(',') : item.value // item.value = ['in', 'not in'].includes(item.operate) ? item.value.split(',') : item.value
return item // return item
}) // })
Object.assign(form, detail, { user_attr_rule: { ...user_attr_rule, attrRuleItems }, event_attr_rule: { ...event_attr_rule, eventRuleItems } }) const rules = detail.rules.length === 0 ? undefined : detail.rules
Object.assign(form, { id: props.data.id, rules })
}) })
} }
watchEffect(() => fetchInfo()) watchEffect(() => fetchInfo())
const rules = ref<FormRules>({
name: [{ required: true, message: '请输入标签名称' }],
type: [{ required: true, message: '请选择标签类型' }]
})
// 提交 // 提交
function handleSubmit() { function handleSubmit() {
formRef?.validate().then(handleUpdate) formRef?.validate().then(handleUpdate)
...@@ -65,18 +59,19 @@ function handleSubmit() { ...@@ -65,18 +59,19 @@ function handleSubmit() {
// 保存 // 保存
function handleUpdate() { function handleUpdate() {
const attrRuleItems = form.user_attr_rule.items.map((item: any) => { // const attrRuleItems = form.user_attr_rule.items.map((item: any) => {
item.value = Array.isArray(item.value) ? item.value.join(',') : item.value // item.value = Array.isArray(item.value) ? item.value.join(',') : item.value
return item // return item
}) // })
const eventRuleItems = form.event_attr_rule.items.map((item: any) => { // const eventRuleItems = form.event_attr_rule.items.map((item: any) => {
item.value = Array.isArray(item.value) ? item.value.join(',') : item.value // item.value = Array.isArray(item.value) ? item.value.join(',') : item.value
return item // return item
}) // })
const params = { const params = {
id: form.id, id: form.id,
user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]), rules: JSON.stringify(form.rules)
event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }]) // user_attr_rule: JSON.stringify([{ ...form.user_attr_rule, items: attrRuleItems }]),
// event_attr_rule: JSON.stringify([{ ...form.event_attr_rule, items: eventRuleItems }])
} }
updateLabelRule(params).then(() => { updateLabelRule(params).then(() => {
ElMessage({ message: '保存成功', type: 'success' }) ElMessage({ message: '保存成功', type: 'success' })
...@@ -94,7 +89,7 @@ function handleUpdate() { ...@@ -94,7 +89,7 @@ function handleUpdate() {
<el-form-item label="标签名称">{{ data.name }}</el-form-item> <el-form-item label="标签名称">{{ data.name }}</el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="标签类型">{{ data.tag_type.name }}</el-form-item> <el-form-item label="标签类型">{{ getNameByValue(data.label, labelList) }}</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
...@@ -110,11 +105,17 @@ function handleUpdate() { ...@@ -110,11 +105,17 @@ function handleUpdate() {
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
<el-form :model="form" :rules="rules" inline ref="formRef" :disabled="disabled"> <el-form :model="form" inline ref="formRef" :disabled="disabled" @submit.prevent v-if="form.id">
<!-- 自定义分层 -->
<LevelRule v-model="form.rules" v-if="data.label == 1"></LevelRule>
<!-- 事件偏好 -->
<EventPreferenceRule v-model="form.rules" v-if="data.label == 2"></EventPreferenceRule>
<!-- 事件指标 -->
<EventTargetRule v-model="form.rules" v-if="data.label == 3"></EventTargetRule>
<!-- 用户属性规则 --> <!-- 用户属性规则 -->
<UserRule></UserRule> <!-- <UserRule></UserRule> -->
<!-- 事件属性规则 --> <!-- 事件属性规则 -->
<EventRule style="margin-top: 20px"></EventRule> <!-- <EventRule style="margin-top: 20px"></EventRule> -->
</el-form> </el-form>
<template #footer> <template #footer>
<el-row justify="center"> <el-row justify="center">
......
...@@ -30,7 +30,7 @@ function handleUpdate(row: LabelType) { ...@@ -30,7 +30,7 @@ function handleUpdate(row: LabelType) {
} }
// 删除 // 删除
function handleRemove(row: LabelType) { function handleRemove(row: LabelType) {
ElMessageBox.confirm('确定要删除该类型吗?', '提示').then(() => { ElMessageBox.confirm('确定要删除该目录吗?', '提示').then(() => {
deleteLabelType({ id: row.id }).then(() => { deleteLabelType({ id: row.id }).then(() => {
ElMessage({ message: '删除成功', type: 'success' }) ElMessage({ message: '删除成功', type: 'success' })
fetchTypeList() fetchTypeList()
...@@ -40,7 +40,7 @@ function handleRemove(row: LabelType) { ...@@ -40,7 +40,7 @@ function handleRemove(row: LabelType) {
</script> </script>
<template> <template>
<el-button type="primary" style="width: 100%" @click="handleAdd" v-permission="'experiment_tag_type_create'">添加标签类型</el-button> <el-button type="primary" style="width: 100%" @click="handleAdd" v-permission="'experiment_tag_type_create'">添加标签目录</el-button>
<div class="label-type-total" @click="$emit('select', '')"> <div class="label-type-total" @click="$emit('select', '')">
<h4>全部标签</h4> <h4>全部标签</h4>
<p>{{ labelCount }}</p> <p>{{ labelCount }}</p>
......
...@@ -14,7 +14,7 @@ const emit = defineEmits<{ ...@@ -14,7 +14,7 @@ const emit = defineEmits<{
}>() }>()
const isUpdate = $computed(() => !!props.data?.id) const isUpdate = $computed(() => !!props.data?.id)
const title = $computed(() => (isUpdate ? '修改标签类型' : '新建标签类型')) const title = $computed(() => (isUpdate ? '修改标签目录' : '新建标签目录'))
const formRef = $ref<FormInstance>() const formRef = $ref<FormInstance>()
const form = reactive({ id: '', name: '', url: '' }) const form = reactive({ id: '', name: '', url: '' })
...@@ -24,8 +24,8 @@ watchEffect(() => { ...@@ -24,8 +24,8 @@ watchEffect(() => {
}) })
const rules = ref<FormRules>({ const rules = ref<FormRules>({
name: [{ required: true, message: '请输入标签类型名称' }], name: [{ required: true, message: '请输入标签目录名称' }],
url: [{ required: true, message: '请选择标签类型图标' }] url: [{ required: true, message: '请选择标签目录图标' }]
}) })
const iconColorList = $ref(['#AA593B', '#CD9A23', '#20AE6C', '#196D42', '#614BE8', '#6D4E8B', '#A01578', '#BD2547']) const iconColorList = $ref(['#AA593B', '#CD9A23', '#20AE6C', '#196D42', '#614BE8', '#6D4E8B', '#A01578', '#BD2547'])
...@@ -58,10 +58,10 @@ function handleUpdate() { ...@@ -58,10 +58,10 @@ function handleUpdate() {
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px"> <el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px">
<el-form-item label="类型名称" prop="name"> <el-form-item label="目录名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" /> <el-input v-model="form.name" placeholder="请输入" />
</el-form-item> </el-form-item>
<el-form-item label="类型图标" prop="url"> <el-form-item label="目录图标" prop="url">
<div class="label-type-icon-list"> <div class="label-type-icon-list">
<div <div
class="label-type-icon-item" class="label-type-icon-item"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import type { Label } from '../types' import type { Label } from '../types'
import { UserFilled } from '@element-plus/icons-vue' import { UserFilled } from '@element-plus/icons-vue'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { getNameByValue, updateStatusRuleList, updateStatusList } from '@/utils/dictionary' import { getNameByValue, updateStatusRuleList, updateStatusList, labelList } from '@/utils/dictionary'
import { getLabelStatistics, updateLabelStatistics } from '../api' import { getLabelStatistics, updateLabelStatistics } from '../api'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
...@@ -36,7 +36,7 @@ function handleUpdate() { ...@@ -36,7 +36,7 @@ function handleUpdate() {
<el-form-item label="标签名称">{{ data.name }}</el-form-item> <el-form-item label="标签名称">{{ data.name }}</el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="标签类型">{{ data.tag_type.name }}</el-form-item> <el-form-item label="标签类型">{{ getNameByValue(data.label, labelList) }}</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
...@@ -69,14 +69,7 @@ function handleUpdate() { ...@@ -69,14 +69,7 @@ function handleUpdate() {
<dt>更新状态</dt> <dt>更新状态</dt>
<dd> <dd>
<span style="margin-right: 10px">{{ getNameByValue(detail.status, updateStatusList) }}</span> <span style="margin-right: 10px">{{ getNameByValue(detail.status, updateStatusList) }}</span>
<el-button <el-button type="primary" plain @click="handleUpdate" size="small" :disabled="['2', '4'].includes(detail.status)">立即更新</el-button>
type="primary"
plain
@click="handleUpdate"
size="small"
:disabled="['2', '4'].includes(detail.status)"
>立即更新</el-button
>
</dd> </dd>
</dl> </dl>
</div> </div>
......
...@@ -26,6 +26,7 @@ export interface Label { ...@@ -26,6 +26,7 @@ export interface Label {
created_operator: Operator created_operator: Operator
updated_time: string updated_time: string
updated_operator: Operator updated_operator: Operator
label: number
} }
// 标签更新规则 // 标签更新规则
export interface LabelUpdateRule { export interface LabelUpdateRule {
...@@ -35,7 +36,7 @@ export interface LabelUpdateRule { ...@@ -35,7 +36,7 @@ export interface LabelUpdateRule {
export type LabelListRequest = Pick<Label, 'id' | 'name'> & { experiment_id?: string } export type LabelListRequest = Pick<Label, 'id' | 'name'> & { experiment_id?: string }
export type LabelUpdateRequest = Pick<Label, 'id' | 'name' | 'type_id' | 'update_status' | 'update_rule' | 'status'> & { export type LabelUpdateRequest = Pick<Label, 'id' | 'name' | 'type_id' | 'update_status' | 'update_rule' | 'status' | 'label'> & {
experiment_id?: string experiment_id?: string
} }
......
...@@ -6,7 +6,7 @@ import LabelType from '../components/LabelType.vue' ...@@ -6,7 +6,7 @@ import LabelType from '../components/LabelType.vue'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { getLabelList, deleteLabel } from '../api' import { getLabelList, deleteLabel } from '../api'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { getNameByValue, updateStatusRuleList } from '@/utils/dictionary' import { getNameByValue, updateStatusRuleList, labelList } from '@/utils/dictionary'
import { useLabelType } from '../composables/useLabelType' import { useLabelType } from '../composables/useLabelType'
import SelectUser from '@/components/SelectUser.vue' import SelectUser from '@/components/SelectUser.vue'
...@@ -31,7 +31,6 @@ const listOptions = computed(() => { ...@@ -31,7 +31,6 @@ const listOptions = computed(() => {
listParams.type_id = '' listParams.type_id = ''
} else { } else {
params.updated_operator = listParams.updated_operator params.updated_operator = listParams.updated_operator
params.type_id = listParams.type_id
} }
return params return params
} }
...@@ -41,11 +40,17 @@ const listOptions = computed(() => { ...@@ -41,11 +40,17 @@ const listOptions = computed(() => {
{ {
type: 'select', type: 'select',
prop: 'type_id', prop: 'type_id',
placeholder: '请选择标签类型', placeholder: '请选择标签目录',
options: typeList.value, options: typeList.value,
labelKey: 'name', labelKey: 'name',
valueKey: 'id' valueKey: 'id'
}, },
{
type: 'select',
prop: 'label',
placeholder: '请选择标签类型',
options: labelList
},
{ type: 'select', prop: 'status', placeholder: '请选择标签状态', options: statusList }, { type: 'select', prop: 'status', placeholder: '请选择标签状态', options: statusList },
{ type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' } { type: 'input', prop: 'updated_operator', placeholder: '更新人', slots: 'filter-user' }
], ],
...@@ -53,7 +58,14 @@ const listOptions = computed(() => { ...@@ -53,7 +58,14 @@ const listOptions = computed(() => {
{ label: '序号', type: 'index', width: 60 }, { label: '序号', type: 'index', width: 60 },
{ label: '标签ID', prop: 'id' }, { label: '标签ID', prop: 'id' },
{ label: '标签名称', prop: 'name' }, { label: '标签名称', prop: 'name' },
{ label: '标签类型', prop: 'tag_type.name' }, {
label: '标签类型',
prop: 'label',
computed({ row }: { row: Label }) {
return getNameByValue(row.label, labelList) || row.label
}
},
{ label: '标签目录', prop: 'tag_type.name' },
{ {
label: '更新频率', label: '更新频率',
prop: 'update_status', prop: 'update_status',
...@@ -117,7 +129,9 @@ function handleRule(row: Label) { ...@@ -117,7 +129,9 @@ function handleRule(row: Label) {
function handleSelect(id: string) { function handleSelect(id: string) {
listParams.type_id = id listParams.type_id = id
appList?.refetch() nextTick(() => {
appList?.refetch()
})
} }
</script> </script>
......
...@@ -111,6 +111,25 @@ export interface TagRule { ...@@ -111,6 +111,25 @@ export interface TagRule {
items: string[] items: string[]
} }
// 用户行为规则
export interface UserActionRule {
current_logic_operate: 'and' | 'or'
items: UserActionRuleItem[]
}
export interface UserActionRuleItem {
operate: string
operate_name: string
value: string
event_list: RuleEvent[]
}
export interface RuleEvent {
event_id: string
event_name: string
attr_list: RuleAttr[]
}
// 资料管理 // 资料管理
export interface MaterialProp { export interface MaterialProp {
id: string id: string
......
...@@ -18,15 +18,15 @@ httpRequest.interceptors.request.use( ...@@ -18,15 +18,15 @@ httpRequest.interceptors.request.use(
if (config.method === 'get') if (config.method === 'get')
config.params = Object.assign({}, config.params, { config.params = Object.assign({}, config.params, {
experiment_id: params.get('experiment_id'), experiment_id: params.get('experiment_id'),
student_id: params.get('student_id'), student_id: params.get('student_id') || undefined,
force_tgc: params.get('force_tgc') force_tgc: params.get('force_tgc') || undefined
}) })
if (config.method === 'post') if (config.method === 'post')
config.data = Object.assign({}, config.data, { config.data = Object.assign({}, config.data, {
experiment_id: params.get('experiment_id'), experiment_id: params.get('experiment_id'),
student_id: params.get('student_id'), student_id: params.get('student_id') || undefined,
force_tgc: params.get('force_tgc') force_tgc: params.get('force_tgc') || undefined
}) })
return config return config
...@@ -45,7 +45,7 @@ httpRequest.interceptors.response.use( ...@@ -45,7 +45,7 @@ httpRequest.interceptors.response.use(
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}` location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
return Promise.reject(data) return Promise.reject(data)
} }
if (data.code === 1) { if (data.code === 1 || data.code === -1) {
ElMessage.error(data.message || data.msg) ElMessage.error(data.message || data.msg)
return Promise.reject(data) return Promise.reject(data)
} }
......
...@@ -94,3 +94,15 @@ export const happenInfoList = [ ...@@ -94,3 +94,15 @@ export const happenInfoList = [
] ]
export const triggerInfoList = [{ label: '触发次数', value: '触发次数' }] export const triggerInfoList = [{ label: '触发次数', value: '触发次数' }]
export const labelList = [
{ label: '自定义分层标签 ', value: '1' },
{ label: '事件偏好标签 ', value: '2' },
{ label: '事件指标标签 ', value: '3' }
]
export const wayList = [
{ label: '总金额 ', value: '1' },
{ label: '总次数 ', value: '2' },
{ label: '平均金额 ', value: '3' }
]
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论