提交 22f299a5 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 群组规则配置

上级 fa868381
......@@ -113,6 +113,7 @@
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useArrayUnique": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
......@@ -196,6 +197,7 @@
"useParallax": true,
"usePermission": true,
"usePointer": true,
"usePointerLock": true,
"usePointerSwipe": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
......
......@@ -114,6 +114,7 @@ declare global {
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
......@@ -197,6 +198,7 @@ declare global {
const useParallax: typeof import('@vueuse/core')['useParallax']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
......
......@@ -54,6 +54,6 @@ export function getMetaEventList() {
}
// 获取实验下的所有标签
export function getTagList(params?: { check_role?: boolean }) {
export function getTagList(params?: { check_role?: 0 | 1 }) {
return httpRequest.get('/api/lab/v1/experiment/tag/all', { params })
}
.rule {
display: flex;
.el-form-item {
display: inline-flex;
margin-right: 10px;
margin-bottom: 0;
}
.el-form-item__content {
margin-left: 0 !important;
}
.el-select,
.el-input {
width: 120px;
......
......@@ -38,35 +38,44 @@ function toggleOperate(rule: EventRule) {
}
// 添加条件
function handleAdd() {
eventAttrRule.value.items.push({
function handleAdd(items: EventRuleItem[]) {
items.push({
happen_info: { is_happened: true, event_id: '', event_name: '', attr_list: [] },
trigger_info: { operate: '', operate_name: '', value: '' }
})
}
// 删除条件
function handleRemove(index: number) {
eventAttrRule.value.items.splice(index, 1)
}
// 事件改变
function handleEventChange(value: string, rule: EventRuleItem) {
const currentEvent = metaEventList.value.find(item => item.id === value)
rule.happen_info.event_name = currentEvent?.name || ''
function handleRemove(items: EventRuleItem[], index: number) {
items.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: EventRuleItem) {
item.happen_info.attr_list = []
item.trigger_info.value = ''
}
// 事件改变
function handleEventChange(value: string, item: EventRuleItem) {
const currentEvent = metaEventList.value.find(item => item.id === value)
item.happen_info.event_name = currentEvent?.name || ''
item.happen_info.attr_list = []
}
// 属性改变
function handleAttrChange(value: string, attr: RuleAttr, rule: EventRuleItem) {
const found = getEventAttrList(rule.happen_info.event_id).find(item => item.id === value)
function handleAttrChange(value: string, attr: RuleAttr, item: EventRuleItem) {
const found = getEventAttrList(item.happen_info.event_id).find(item => item.id === value)
attr.attr = found?.english_name || ''
attr.attr_name = found?.name || ''
attr.attr_type = found?.type || ''
......@@ -75,6 +84,7 @@ function handleAttrChange(value: string, attr: RuleAttr, rule: EventRuleItem) {
attr.operate_name = ''
attr.value = ''
}
// 条件改变
function handleOperateChange(value: string, item: RuleAttr) {
const found = getOperatorList(item.attr_type).find(item => item.value === value)
......@@ -82,10 +92,11 @@ function handleOperateChange(value: string, item: RuleAttr) {
item.value = ''
}
// 条件改变
function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
// 触发条件改变
function handleTriggerOperateChange(value: string, item: EventRuleItem) {
const found = numberOperatorList.find(item => item.value === value)
rule.trigger_info.operate_name = found?.label || ''
item.trigger_info.operate_name = found?.label || ''
item.trigger_info.value = ''
}
</script>
......@@ -104,11 +115,13 @@ function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
<el-row>
<!-- 发生 -->
<el-form-item>
<el-select v-model="rule.happen_info.is_happened">
<el-select
v-model="rule.happen_info.is_happened"
@change="value => handleHappenOperateChange(value, rule)">
<el-option v-for="option in happenInfoList" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-form-item v-if="rule.happen_info.is_happened">
<el-select v-model="rule.happen_info.event_id" @change="value => handleEventChange(value, rule)">
<el-option
v-for="option in metaEventList"
......@@ -117,58 +130,71 @@ function handleTriggerOperateChange(value: string, rule: EventRuleItem) {
:value="option.id"></el-option>
</el-select>
</el-form-item>
<el-button text :icon="Plus" @click="handleAttrAdd(rule.happen_info.attr_list)">添加条件</el-button>
<el-button text :icon="CloseBold" @click="handleRemove(index)"></el-button>
<el-button
text
:icon="Plus"
@click="handleAttrAdd(rule.happen_info.attr_list)"
v-if="rule.happen_info.is_happened"
>添加条件</el-button
>
<el-button text :icon="CloseBold" @click="handleRemove(eventAttrRule.items, index)"></el-button>
</el-row>
<!-- 属性条件 -->
<el-row class="rule-item" justify="space-between" v-for="(attr, index) in rule.happen_info.attr_list">
<div>
<template v-if="rule.happen_info.is_happened">
<!-- 属性条件 -->
<el-row class="rule-item" justify="space-between" v-for="(attr, index) in rule.happen_info.attr_list">
<div>
<el-form-item>
<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-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)"
:label="option.alias || option.label"
:value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="attr.value" />
</el-form-item>
</div>
<el-button
text
:icon="CloseBold"
@click="handleAttrRemove(rule.happen_info.attr_list, index)"></el-button>
</el-row>
<!-- 触发 -->
<el-row style="margin-top: 10px">
<el-form-item>
<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-select model-value="触发次数">
<el-option v-for="option in triggerInfoList" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="attr.operate" @change="value => handleOperateChange(value, attr)">
<el-select
v-model="rule.trigger_info.operate"
@change="value => handleTriggerOperateChange(value, rule)">
<el-option
v-for="option in getOperatorList(attr.attr_type)"
v-for="option in numberOperatorList"
:label="option.alias || option.label"
:value="option.value"></el-option>
:value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="attr.value" />
<el-input v-model="rule.trigger_info.value" />
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleAttrRemove(rule.happen_info.attr_list, index)"></el-button>
</el-row>
<!-- 触发 -->
<el-row style="margin-top: 10px">
<el-form-item>
<el-select model-value="触发次数">
<el-option v-for="option in triggerInfoList" v-bind="option"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="rule.trigger_info.operate" @change="value => handleTriggerOperateChange(value, rule)">
<el-option
v-for="option in numberOperatorList"
:label="option.alias || option.label"
:value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="rule.trigger_info.value" />
</el-form-item>
</el-row>
</el-row>
</template>
</section>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd">添加条件</el-button>
<el-button text :icon="Plus" @click="handleAdd(eventAttrRule.items)">添加条件</el-button>
</el-card>
</template>
......
<script setup lang="ts"></script>
<script setup lang="ts">
import type { TagRule } from '@/types'
import { PriceTag, Plus, CloseBold } from '@element-plus/icons-vue'
import { useTag } from '@/composables/useAllData'
<template></template>
const tagRule = ref(inject('tagRule') as TagRule)
const { tagList } = useTag()
// 获取逻辑运算符名称
function getLogicalName(value: 'and' | 'or') {
return value === 'or' ? '或' : '且'
}
// 切换逻辑运算符
function toggleOperate(rule: TagRule) {
rule.current_logic_operate = rule.current_logic_operate === 'or' ? 'and' : 'or'
}
// 添加条件
function handleAdd(items: string[]) {
items.push('')
}
// 删除
function handleRemove(items: string[], index: number) {
items.splice(index, 1)
}
</script>
<template>
<el-card shadow="never">
<template #header>
<el-button circle color="#567722" :icon="PriceTag"></el-button>
标签满足以下条件
</template>
<div class="rule" v-if="tagRule.items.length">
<div class="rule-operator">
<span @click="toggleOperate(tagRule)">{{ getLogicalName(tagRule.current_logic_operate) }}</span>
</div>
<div class="rule-list">
<el-row class="rule-item" v-for="(item, index) in tagRule.items" justify="space-between">
<div>
标签 等于
<el-form-item>
<el-select v-model="tagRule.items[index]">
<el-option
v-for="option in tagList"
:key="option.id"
:label="option.name"
:value="option.id"></el-option>
</el-select>
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleRemove(tagRule.items, index)"></el-button>
</el-row>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd(tagRule.items)">添加条件</el-button>
</el-card>
</template>
<style src="@/assets/styles/rule.scss"></style>
......@@ -27,21 +27,13 @@ function toggleOperate(rule: UserAttrRule) {
}
// 添加条件
function handleAdd() {
userAttrRule.value.items.push({
attr_id: '',
attr: '',
attr_name: '',
attr_type: '',
operate: '',
operate_name: '',
value: ''
})
function handleAdd(items: RuleAttr[]) {
items.push({ attr_id: '', attr: '', attr_name: '', attr_type: '', operate: '', operate_name: '', value: '' })
}
// 删除
function handleRemove(rule: UserAttrRule, index: number) {
rule.items.splice(index, 1)
function handleRemove(items: RuleAttr[], index: number) {
items.splice(index, 1)
}
// 属性改变
......@@ -98,11 +90,11 @@ function handleOperateChange(value: string, item: RuleAttr) {
<el-input v-model="item.value" />
</el-form-item>
</div>
<el-button text :icon="CloseBold" @click="handleRemove(userAttrRule, index)"></el-button>
<el-button text :icon="CloseBold" @click="handleRemove(userAttrRule.items, index)"></el-button>
</el-row>
</div>
</div>
<el-button text :icon="Plus" @click="handleAdd">添加条件</el-button>
<el-button text :icon="Plus" @click="handleAdd(userAttrRule.items)">添加条件</el-button>
</el-card>
</template>
......
......@@ -41,10 +41,14 @@ export function useMetaEvent() {
return { fetchMetaEventList, metaEventList }
}
const tagList = ref([])
interface TagType {
id: string
name: string
}
const tagList = ref<TagType[]>([])
export function useTag() {
function fetchTagList() {
getTagList({ check_role: true }).then((res: any) => {
getTagList({ check_role: 1 }).then((res: any) => {
tagList.value = res.data.items
})
}
......
......@@ -12,6 +12,11 @@ export function getGroupList(params?: GroupListRequest) {
return httpRequest.get('/api/lab/v1/experiment/group/list', { params })
}
// 获取群组详情
export function getGroupInfo(params: { id: string }) {
return httpRequest.get('/api/lab/v1/experiment/group/detail', { params })
}
// 创建静态群组
export function createStaticGroup(data: StaticGroupCreateRequest) {
return httpRequest.post('/api/lab/v1/experiment/group/create-static-group', data)
......
......@@ -3,7 +3,10 @@ import type { Group } from '../types'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { updateStatusRuleList, dateUnitList, weekList } from '@/utils/dictionary'
import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamicGroup } from '../api'
import { createStaticGroup, updateStaticGroup, createDynamicGroup, updateDynamicGroup, getGroupInfo } from '../api'
import UserRule from '@/components/rule/UserRule.vue'
import EventRule from '@/components/rule/EventRule.vue'
import LabelRule from '@/components/rule/LabelRule.vue'
import { pick } from 'lodash-es'
interface Props {
......@@ -33,10 +36,15 @@ const form = reactive({
status: '1',
update_status: '1',
update_rule: { type: 1, info: 1 },
user_attr_rule: [],
event_attr_rule: [],
tag_rule: []
user_attr_rule: { current_logic_operate: 'and', items: [] },
event_attr_rule: { current_logic_operate: 'and', items: [] },
tag_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(() => {
if (props.data?.id) {
let updateRule = { type: 1, info: 1 }
......@@ -49,6 +57,19 @@ watchEffect(() => {
}
})
function fetchInfo() {
if (!props.data.id) return
getGroupInfo({ id: props.data.id }).then(res => {
const { detail } = res.data
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 [tag_rule = { current_logic_operate: 'and', items: [] }] = detail.tag_rule
Object.assign(form, { user_attr_rule, event_attr_rule, tag_rule })
})
}
watchEffect(() => fetchInfo())
const rules = ref<FormRules>({
name: [{ required: true, message: '请输入群组名称' }],
url: [{ required: true, message: '请选择标签类型图标' }]
......@@ -70,9 +91,9 @@ async function handleCreate() {
{
...form,
update_rule: JSON.stringify(form.update_rule),
user_attr_rule: JSON.stringify(form.user_attr_rule),
event_attr_rule: JSON.stringify(form.event_attr_rule),
tag_rule: JSON.stringify(form.tag_rule)
user_attr_rule: JSON.stringify([form.user_attr_rule]),
event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([form.tag_rule])
},
['name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status']
)
......@@ -94,9 +115,9 @@ async function handleUpdate() {
{
...form,
update_rule: JSON.stringify(form.update_rule),
user_attr_rule: JSON.stringify(form.user_attr_rule),
event_attr_rule: JSON.stringify(form.event_attr_rule),
tag_rule: JSON.stringify(form.tag_rule)
user_attr_rule: JSON.stringify([form.user_attr_rule]),
event_attr_rule: JSON.stringify([form.event_attr_rule]),
tag_rule: JSON.stringify([form.tag_rule])
},
['id', 'name', 'update_status', 'update_rule', 'user_attr_rule', 'event_attr_rule', 'tag_rule', 'status']
)
......@@ -109,7 +130,7 @@ async function handleUpdate() {
</script>
<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="800px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":" label-width="100px">
<el-form-item label="群组名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
......@@ -149,6 +170,14 @@ async function handleUpdate() {
<el-form-item label="状态" prop="status">
<el-switch v-model="form.status" active-text="生效" active-value="1" inactive-text="失效" inactive-value="0" />
</el-form-item>
<template v-if="data.type === '2'">
<!-- 用户属性规则 -->
<UserRule></UserRule>
<!-- 事件属性规则 -->
<EventRule style="margin-top: 20px"></EventRule>
<!-- 标签 -->
<LabelRule style="margin-top: 20px"></LabelRule>
</template>
</el-form>
<template #footer>
<el-row justify="center">
......
......@@ -104,3 +104,9 @@ export interface EventRuleItem {
value: string
}
}
// 标签规则
export interface TagRule {
current_logic_operate: 'and' | 'or'
items: string[]
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论