add bi
This commit is contained in:
parent
4ed261d9b4
commit
5f165f82ae
5
ui/src/assets/styles/editorToolbarBtn.scss
Normal file
5
ui/src/assets/styles/editorToolbarBtn.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.editorTopRightToolBarBtn {
|
||||
margin: 0;
|
||||
padding: 0 8px 0 8px;
|
||||
border: 0
|
||||
}
|
@ -24,7 +24,6 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
*/
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
@ -0,0 +1,277 @@
|
||||
<script setup>
|
||||
import {defineAsyncComponent} from 'vue';
|
||||
|
||||
const propertyFilterShow = defineAsyncComponent(() => import("./propertyConditionType.vue"))
|
||||
const propertyFilterSelectShow = defineAsyncComponent(() => import("./propertyConditionFilter.vue"))
|
||||
|
||||
|
||||
/*
|
||||
{
|
||||
nodeType: "", // "" "and" "or"分别代表属性节点、and节点、or节点
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
cond: "eq", // 数字:"eq", "ne", "st", "se", "gt", "ge", "valued", "valueless","range"
|
||||
// 字符串:"eq","ne", "contain", "exclusive", "regmatch", "regnotmatch"
|
||||
value1: "",
|
||||
value2: ""
|
||||
}
|
||||
}
|
||||
{
|
||||
nodeType: "and", // "" "and" "or"分别代表属性节点、and节点、or节点
|
||||
children: [
|
||||
{...}
|
||||
]
|
||||
}
|
||||
{
|
||||
nodeType: "or", // "" "and" "or"分别代表属性节点、and节点、or节点
|
||||
children: [
|
||||
{...}
|
||||
]
|
||||
}
|
||||
*/
|
||||
const rawSelectedTree = reactive({
|
||||
nodeType: "and",
|
||||
children: [
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
alias: "账户id",
|
||||
cond: "eq",
|
||||
value1: "account_xxx1"
|
||||
}
|
||||
},
|
||||
{
|
||||
nodeType: "and",
|
||||
children: [
|
||||
{
|
||||
nodeType: "or",
|
||||
children: [
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "role_id",
|
||||
alias: "角色id",
|
||||
cond: "contain",
|
||||
value1: "role123"
|
||||
}
|
||||
},
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
alias: "账户id",
|
||||
cond: "eq",
|
||||
value1: "account_xxx1"
|
||||
}
|
||||
},
|
||||
{
|
||||
nodeType: "and",
|
||||
children: [
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "role_id",
|
||||
alias: "角色id",
|
||||
cond: "contain",
|
||||
value1: "role123"
|
||||
}
|
||||
},
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
alias: "账户id",
|
||||
cond: "eq",
|
||||
value1: "account_xxx1"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
alias: "账户id",
|
||||
cond: "eq",
|
||||
value1: "account_xxx1"
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{ // 无效的节点
|
||||
nodeType: "or",
|
||||
desc: "invalid node",
|
||||
children: []
|
||||
},
|
||||
{ // 待整理的节点
|
||||
nodeType: "and",
|
||||
desc: "wait tidy node",
|
||||
children: [
|
||||
{
|
||||
nodeType: "or",
|
||||
children: [
|
||||
{
|
||||
nodeType: "",
|
||||
propertyInfo: {
|
||||
name: "user_id",
|
||||
alias: "账户id",
|
||||
cond: "eq",
|
||||
value1: "account_xxx1"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// 整理树信息,去掉里面无效的节点、精简只有一个孩子的节点路径
|
||||
const tidySelectedTreeInvalidChild = (node) => {
|
||||
if (node.nodeType !== "") {
|
||||
if (node.children.length === 0) {
|
||||
return null
|
||||
}
|
||||
let i = 0
|
||||
while (true) {
|
||||
if (i >= node.children.length) {
|
||||
break
|
||||
}
|
||||
let child = node.children[i]
|
||||
let newChild = tidySelectedTreeInvalidChild(child)
|
||||
if (newChild !== null) {
|
||||
node.children[i] = newChild
|
||||
i++
|
||||
} else {
|
||||
node.children.splice(i, 1)
|
||||
}
|
||||
}
|
||||
if (node.children.length === 1) {
|
||||
node = node.children[0]
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
const treeMaxNodeId = ref(0)
|
||||
const getTreeMaxNodeId = () => {
|
||||
treeMaxNodeId.value += 1
|
||||
return treeMaxNodeId.value
|
||||
}
|
||||
const numberTreeId = (node) => {
|
||||
node.id = getTreeMaxNodeId()
|
||||
if (node.nodeType !== "") {
|
||||
node.children.forEach((child, index) => {
|
||||
node.children[index] = numberTreeId(child)
|
||||
})
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
tidySelectedTreeInvalidChild(rawSelectedTree)
|
||||
numberTreeId(rawSelectedTree)
|
||||
const newTree = rawSelectedTree
|
||||
|
||||
const findNodeAndApply = (curNodeChildIndex, curNode, parent, findNodeId, applyFunc) => {
|
||||
if (curNode.id === findNodeId) {
|
||||
applyFunc(curNodeChildIndex, curNode, parent)
|
||||
return true
|
||||
}
|
||||
|
||||
if (curNode.nodeType === "") {
|
||||
return false
|
||||
}
|
||||
|
||||
for (let i = 0; i < curNode.children.length; i++) {
|
||||
const child = curNode.children[i]
|
||||
const find = findNodeAndApply(i, child, curNode, findNodeId, applyFunc)
|
||||
if (find) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const onNodeSplit = (nodeId) => {
|
||||
// 分裂一个叶子节点,叶子节点必然是属性选择组件
|
||||
// 需要找到节点的父节点,父节点连接的叶子节点变为and|or节点
|
||||
findNodeAndApply(0, newTree, {
|
||||
nodeType: 'and',
|
||||
children: [newTree]
|
||||
}, nodeId, (findNodeChildIndex, findNode, findParent) => {
|
||||
findParent.children[findNodeChildIndex] = {
|
||||
nodeType: 'and',
|
||||
id: getTreeMaxNodeId(),
|
||||
children: [findNode, {
|
||||
nodeType: '',
|
||||
id: getTreeMaxNodeId(),
|
||||
propertyInfo: {
|
||||
name: "",
|
||||
alias: "",
|
||||
cond: "",
|
||||
value1: "",
|
||||
value2: "",
|
||||
},
|
||||
children: []
|
||||
}]
|
||||
}
|
||||
})
|
||||
tidySelectedTreeInvalidChild(newTree)
|
||||
numberTreeId(newTree)
|
||||
}
|
||||
|
||||
const onAddBrother = (nodeId) => {
|
||||
findNodeAndApply(0, newTree, {
|
||||
nodeType: 'and',
|
||||
children: [newTree]
|
||||
}, nodeId, (findNodeChildIndex, findNode, findParent) => {
|
||||
findParent.children.push({
|
||||
nodeType: '',
|
||||
id: getTreeMaxNodeId(),
|
||||
propertyInfo: {
|
||||
name: "",
|
||||
alias: "",
|
||||
cond: "",
|
||||
value1: "",
|
||||
value2: "",
|
||||
},
|
||||
children: []
|
||||
})
|
||||
})
|
||||
tidySelectedTreeInvalidChild(newTree)
|
||||
numberTreeId(newTree)
|
||||
}
|
||||
|
||||
const onDeleteNode = (nodeId) => {
|
||||
findNodeAndApply(0, newTree, {
|
||||
nodeType: 'and',
|
||||
children: [newTree]
|
||||
}, nodeId, (findNodeChildIndex, findNode, findParent) => {
|
||||
findParent.children.splice(findNodeChildIndex, 1)
|
||||
})
|
||||
tidySelectedTreeInvalidChild(newTree)
|
||||
numberTreeId(newTree)
|
||||
}
|
||||
|
||||
const propertyShowHandlerInfo = {
|
||||
showComp: propertyFilterSelectShow,
|
||||
onSplitNode: onNodeSplit,
|
||||
onAddBrother: onAddBrother,
|
||||
onDeleteNode: onDeleteNode,
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<propertyFilterShow :node="newTree" :propertyShowHandlerInfo="propertyShowHandlerInfo"/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,106 @@
|
||||
<script setup>
|
||||
|
||||
import {ForkOutlined} from '@ant-design/icons-vue';
|
||||
|
||||
const props = defineProps({
|
||||
propertyHandlerInfo: {
|
||||
propertyInfo: {},
|
||||
onSplitNode: null,
|
||||
onAddBrother: null,
|
||||
onDeleteNode: null,
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
// console.log("进入字段渲染:", props.propertyInfo)
|
||||
|
||||
const allProperties = ref([
|
||||
{
|
||||
label: "用户属性",
|
||||
options: [
|
||||
{
|
||||
value: "account_id",
|
||||
label: "游戏账号"
|
||||
},
|
||||
{
|
||||
value: "role_id",
|
||||
label: "角色id"
|
||||
},
|
||||
{
|
||||
value: "role_name",
|
||||
label: "角色名"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "事件属性",
|
||||
options: [
|
||||
{
|
||||
value: "item_id",
|
||||
label: "道具id"
|
||||
},
|
||||
{
|
||||
value: "item_name",
|
||||
label: "道具名"
|
||||
},
|
||||
{
|
||||
value: "cur_num",
|
||||
label: "cur_num"
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const canDelete = ref(false)
|
||||
|
||||
const filterOption = (input, option) => {
|
||||
return option.value.filter((item) => {
|
||||
if (item.options && item.options.length > 0) {
|
||||
return item.options.filter(opt => {
|
||||
return opt.includes(input)
|
||||
})
|
||||
}
|
||||
return item.label.includes(input)
|
||||
})
|
||||
};
|
||||
|
||||
const selectedPropertyValue = ref('')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-space :size="2">
|
||||
<a-select showArrow show-search v-model:value="selectedPropertyValue"
|
||||
:options="allProperties" :filter-option="filterOption" @change=""
|
||||
style="width: 100px;margin: 0 5px 0 0"/>
|
||||
|
||||
<el-button class="filterToolBtn" @click="propertyHandlerInfo.onSplitNode(propertyHandlerInfo.propertyInfo.id)">
|
||||
<ForkOutlined style="transform: rotate(90deg)"/>
|
||||
</el-button>
|
||||
<el-button class="filterToolBtn" @click="propertyHandlerInfo.onAddBrother(propertyHandlerInfo.propertyInfo.id)">
|
||||
<el-icon :size="16">
|
||||
<CirclePlus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button class="filterToolBtn" @click="propertyHandlerInfo.onDeleteNode(propertyHandlerInfo.propertyInfo.id)">
|
||||
<el-icon :size="16">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</a-space>
|
||||
<!-- {{ propertyInfo.propertyInfo.alias }}-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.filterToolBtn {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
</style>
|
@ -0,0 +1,64 @@
|
||||
<script setup>
|
||||
const name = "PropertyConditionType";
|
||||
const props = defineProps({
|
||||
node: {},
|
||||
propertyShowHandlerInfo: {},
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <el-row>-->
|
||||
<!-- <div class="conditionShowAnd">-->
|
||||
<!-- <p style="color: white">and</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="display: inline-flex;flex-direction: column">-->
|
||||
<!-- <component :is="propertyComp" :propertyInfo="{propertyInfo: {alias: '角色名'}}"></component>-->
|
||||
<!-- <component :is="propertyComp" :propertyInfo="{propertyInfo: {alias: '账号id'}}"></component>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
|
||||
<el-row v-if="node.nodeType !== ''">
|
||||
|
||||
<div :class="{
|
||||
'conditionShowAnd': node.nodeType === 'and',
|
||||
'conditionShowOr': node.nodeType === 'or'
|
||||
}">
|
||||
<p style="color: black">{{ node.nodeType }}</p>
|
||||
</div>
|
||||
<div style="display: inline-flex;flex-direction: column">
|
||||
<template v-for="child in node.children">
|
||||
<PropertyConditionType :node="child" :propertyShowHandlerInfo="propertyShowHandlerInfo"/>
|
||||
</template>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row v-else>
|
||||
<component :is="propertyShowHandlerInfo.showComp" :propertyHandlerInfo="{
|
||||
propertyInfo: node,
|
||||
onSplitNode: props.propertyShowHandlerInfo.onSplitNode,
|
||||
onAddBrother: props.propertyShowHandlerInfo.onAddBrother,
|
||||
onDeleteNode: props.propertyShowHandlerInfo.onDeleteNode,
|
||||
}"></component>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.conditionShowAnd {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-right: 2px solid #202241;
|
||||
//background-color: #202241;
|
||||
}
|
||||
|
||||
.conditionShowOr {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-right: 2px solid #42b983;
|
||||
//background-color: #42b983;
|
||||
}
|
||||
|
||||
</style>
|
@ -2,8 +2,8 @@
|
||||
import {ref} from 'vue';
|
||||
import {defineAsyncComponent} from 'vue';
|
||||
|
||||
const editLayout = defineAsyncComponent(() => import("./analyseMetricEditorLayout.vue"))
|
||||
const editor = defineAsyncComponent(() => import("./editor/event.vue"))
|
||||
const editLayout = defineAsyncComponent(() => import("./editor/layout/analyseMetricEditorLayout.vue"))
|
||||
const editor = defineAsyncComponent(() => import("./editor/event/index.vue"))
|
||||
|
||||
const editorAreaInfo = {
|
||||
editorPane: editor,
|
||||
|
@ -1,7 +1,11 @@
|
||||
<script setup>
|
||||
import {FunctionOutlined} from '@ant-design/icons-vue';
|
||||
import {ref, computed} from "vue";
|
||||
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
index: 0,
|
||||
onDeleteMetricSelect: null,
|
||||
})
|
||||
|
||||
const eventAllData = {
|
||||
userProperties: [
|
||||
@ -92,6 +96,8 @@ const eventAllData = {
|
||||
const selectEventValue = ref('')
|
||||
const selectEventOptions = ref([])
|
||||
|
||||
const selectedEventInfo = ref({})
|
||||
|
||||
eventAllData.eventDescList.forEach((eventInfo) => {
|
||||
let addOptionInfo = {
|
||||
value: eventInfo.name,
|
||||
@ -104,7 +110,6 @@ eventAllData.eventDescList.forEach((eventInfo) => {
|
||||
})
|
||||
|
||||
selectEventValue.value = selectEventOptions.value[0].label
|
||||
const selectedEventInfo = ref({})
|
||||
|
||||
const defaultSelectEventRelationMetrics = {
|
||||
label: "预置指标",
|
||||
@ -147,7 +152,7 @@ for (let i = 0; i < defaultMetricTypeNumberOpList1.length; i++) {
|
||||
})
|
||||
}
|
||||
|
||||
console.log("oplist:", defaultMetricTypeNumberOpList)
|
||||
// console.log("oplist:", defaultMetricTypeNumberOpList)
|
||||
|
||||
const selectedEventRelationMetricValue = ref('')
|
||||
const disableMetricSelectOp = ref(true)
|
||||
@ -156,7 +161,7 @@ const selectEventRelationMetrics = computed(() => {
|
||||
if (!eventInfo) {
|
||||
return [defaultSelectEventRelationMetrics]
|
||||
}
|
||||
console.log("选择事件:", eventInfo)
|
||||
// console.log("选择事件:", eventInfo)
|
||||
selectedEventInfo.value = eventInfo
|
||||
|
||||
let userMetrics = {
|
||||
@ -198,8 +203,8 @@ const selectEventRelationMetrics = computed(() => {
|
||||
|
||||
const selectedMetricRelationOpValue = ref('')
|
||||
const selectMetricRelationOpList = computed(() => {
|
||||
console.log("selectEventRelationMetrics:", selectedEventRelationMetricValue.value)
|
||||
console.log("selectEventRelationMetrics:", selectEventRelationMetrics.value)
|
||||
// console.log("selectEventRelationMetrics:", selectedEventRelationMetricValue.value)
|
||||
// console.log("selectEventRelationMetrics:", selectEventRelationMetrics.value)
|
||||
let opList = []
|
||||
selectEventRelationMetrics.value.find((v, idx) => {
|
||||
const selectedMetric = v.options.find((v1, idx1) => {
|
||||
@ -211,7 +216,7 @@ const selectMetricRelationOpList = computed(() => {
|
||||
}
|
||||
return false
|
||||
})
|
||||
console.log("oplist", opList)
|
||||
// console.log("oplist", opList)
|
||||
selectedMetricRelationOpValue.value = ''
|
||||
disableMetricSelectOp.value = opList.length === 0;
|
||||
return opList
|
||||
@ -221,119 +226,77 @@ const onSelectEvent = (value) => {
|
||||
|
||||
}
|
||||
|
||||
const filterOption = (input, option) => {
|
||||
return option.value.filter((item) => {
|
||||
if (item.options && item.options.length > 0) {
|
||||
return item.options.filter(opt => {
|
||||
return opt.includes(input)
|
||||
})
|
||||
}
|
||||
return item.label.includes(input)
|
||||
})
|
||||
};
|
||||
|
||||
onSelectEvent(selectEventValue.value)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editorAreaOuter">
|
||||
<div class="editorAreaInner">
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
分析指标
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<FunctionOutlined style="font-size: 20px"/>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="editorOperationArea">
|
||||
<!-- <div-->
|
||||
<!-- style="background-color: #202241;margin-right: 5px;border-radius: 5px;;width: 3px;height: 70px;display: inline-flex;justify-content: center;align-items: center;">-->
|
||||
<!-- <!– <p style="font-weight: bold;color: white;margin: 0">1</p>–>-->
|
||||
<!-- </div>-->
|
||||
<el-row justify="end">
|
||||
<a-space>
|
||||
<el-button class="editorTopRightToolBarBtn" @click="onDeleteMetricSelect(index)">
|
||||
<el-icon :size="20">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</a-space>
|
||||
</el-row>
|
||||
|
||||
<el-row style="width: 100%;height: 50px;">
|
||||
<a-space :size="5">
|
||||
<div
|
||||
style="background-color: #202241;border-radius: 5px;;width: 3px;height: 35px;display: inline-flex;justify-content: center;align-items: center;">
|
||||
<!-- <p style="font-weight: bold;color: white;margin: 0">1</p>-->
|
||||
</div>
|
||||
<a-select ref="select" v-model:value="selectEventValue" :options="selectEventOptions" @change="onSelectEvent"
|
||||
style="width: 100px;margin: 0"/>
|
||||
<div
|
||||
style="background-color: #202241;border-radius: 5px;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">
|
||||
<p style="color: white;margin: 0">的</p>
|
||||
</div>
|
||||
<a-select ref="metricSelect" v-model:value="selectedEventRelationMetricValue"
|
||||
:options="selectEventRelationMetrics" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
<!-- <div-->
|
||||
<!-- style="background-color: #202241;border-radius: 5px;;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">-->
|
||||
<!-- <p style="color: white;margin: 0">.</p>-->
|
||||
<!-- </div>-->
|
||||
<div
|
||||
style="height: 100%;display: inline-flex;flex-direction: column;justify-content: end">
|
||||
<p style="color: black;font-weight: bold;font-size: 20px;margin: 0">·</p>
|
||||
</div>
|
||||
<a-select ref="metricSelect" :disabled="disableMetricSelectOp" v-model:value="selectedMetricRelationOpValue"
|
||||
:options="selectMetricRelationOpList" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
</a-space>
|
||||
</el-row>
|
||||
<el-row style="height: 30px"></el-row>
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
全局筛选
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="height: 30px"></el-row>
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
分组项
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row justify="end">
|
||||
<a-space :size="5">
|
||||
<div
|
||||
style="background-color: #202241;border-radius: 5px;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">
|
||||
<p style="color: white;margin: 0">的</p>
|
||||
</div>
|
||||
<a-select showArrow show-search v-model:value="selectedEventRelationMetricValue"
|
||||
:options="selectEventRelationMetrics" :filter-option="filterOption" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
<!-- <div-->
|
||||
<!-- style="background-color: #202241;border-radius: 5px;;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">-->
|
||||
<!-- <p style="color: white;margin: 0">.</p>-->
|
||||
<!-- </div>-->
|
||||
<div
|
||||
style="height: 100%;display: inline-flex;flex-direction: column;justify-content: end">
|
||||
<p style="color: black;font-weight: bold;font-size: 20px;margin: 0">·</p>
|
||||
</div>
|
||||
<a-select showArrow :disabled="disableMetricSelectOp"
|
||||
v-model:value="selectedMetricRelationOpValue"
|
||||
:filter-option="filterOption"
|
||||
:options="selectMetricRelationOpList" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
</a-space>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
|
||||
.editorAreaOuter {
|
||||
width: 100%;
|
||||
@use '@/assets/styles/editorToolbarBtn.scss';
|
||||
|
||||
.editorOperationArea {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
margin-bottom: 5px;
|
||||
border-left: 3px solid #202241;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.editorAreaInner {
|
||||
width: calc(100% - 30px);
|
||||
height: calc(100% - 10px);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.editorTopRightToolBarBtn {
|
||||
margin: 0;
|
||||
padding: 0 8px 0 8px;
|
||||
border: 0
|
||||
}
|
||||
|
||||
</style>
|
@ -0,0 +1,224 @@
|
||||
<script setup>
|
||||
import {FunctionOutlined} from '@ant-design/icons-vue';
|
||||
import {ref, computed, defineAsyncComponent} from "vue";
|
||||
|
||||
const metricSelect = defineAsyncComponent(() => import("./metricSelect.vue"))
|
||||
const globalFilterSelect = defineAsyncComponent(() => import("./globalFilterSelect.vue"))
|
||||
const filterPropertiesSelect = defineAsyncComponent(() => import("@/components/bi/propertyCondition/propertiesConditionFilter.vue"))
|
||||
|
||||
const eventAllData = {
|
||||
userProperties: [
|
||||
{
|
||||
name: "account_id",
|
||||
alias: "账户id",
|
||||
propertyType: 2,
|
||||
},
|
||||
{
|
||||
name: "account_name",
|
||||
alias: "账户名",
|
||||
propertyType: 2,
|
||||
},
|
||||
{
|
||||
name: "role_id",
|
||||
alias: "角色id",
|
||||
propertyType: 2,
|
||||
},
|
||||
{
|
||||
name: "server_id",
|
||||
alias: "服务器id",
|
||||
propertyType: 2,
|
||||
},
|
||||
{
|
||||
name: "currency_coin",
|
||||
alias: "金币数",
|
||||
propertyType: 1,
|
||||
},
|
||||
],
|
||||
eventDescList: [
|
||||
{
|
||||
name: "role_login",
|
||||
alias: "角色登录",
|
||||
fields: [
|
||||
{
|
||||
name: "sign_day",
|
||||
alias: "签到天数",
|
||||
propertyType: 1,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "item_change",
|
||||
alias: "item_change",
|
||||
fields: [
|
||||
{
|
||||
name: "item_id",
|
||||
alias: "item_id",
|
||||
propertyType: 1,
|
||||
},
|
||||
{
|
||||
name: "item_name",
|
||||
alias: "item_name",
|
||||
propertyType: 2,
|
||||
},
|
||||
{
|
||||
name: "num_before",
|
||||
alias: "num_before",
|
||||
propertyType: 1,
|
||||
},
|
||||
{
|
||||
name: "num_after",
|
||||
alias: "num_after",
|
||||
propertyType: 1,
|
||||
},
|
||||
{
|
||||
name: "delta",
|
||||
alias: "delta",
|
||||
propertyType: 1,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "role_logout",
|
||||
alias: "角色登出",
|
||||
fields: [
|
||||
{
|
||||
name: "online_duration",
|
||||
alias: "在线时长",
|
||||
propertyType: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const metricSelectMaxNo = ref(0)
|
||||
const metricSelectList = ref([])
|
||||
|
||||
const onAddMetricSelect = () => {
|
||||
const curNo = metricSelectMaxNo.value + 1
|
||||
metricSelectMaxNo.value += curNo
|
||||
metricSelectList.value.push(curNo)
|
||||
}
|
||||
|
||||
const onDeleteMetricSelect = (no) => {
|
||||
metricSelectList.value.splice(metricSelectList.value.indexOf(no), 1)
|
||||
}
|
||||
|
||||
const globalFilterSelectMaxNo = ref(0)
|
||||
const globalFilterSelectList = ref([])
|
||||
|
||||
const onAddGlobalFilterSelect = () => {
|
||||
const curNo = globalFilterSelectMaxNo.value + 1
|
||||
globalFilterSelectMaxNo.value += curNo
|
||||
globalFilterSelectList.value.push(curNo)
|
||||
}
|
||||
|
||||
const onDeleteGlobalFilterSelect = (no) => {
|
||||
globalFilterSelectList.value.splice(globalFilterSelectList.value.indexOf(no), 1)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onAddMetricSelect()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editorAreaOuter">
|
||||
<div class="editorAreaInner">
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
分析指标
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn" @click="onAddMetricSelect">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<FunctionOutlined style="font-size: 20px"/>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row style="width: 100%;min-height: 70px;">
|
||||
<template v-for="(no,index) in metricSelectList" :key="no">
|
||||
<metricSelect :index="no" :canDelete="(metricSelectList.length > 1)"
|
||||
:onDeleteMetricSelect="onDeleteMetricSelect" :eventAllData="eventAllData"/>
|
||||
</template>
|
||||
</el-row>
|
||||
|
||||
<el-row style="height: 30px"></el-row>
|
||||
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
全局筛选
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn" @click="onAddGlobalFilterSelect">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="width: 100%;min-height: 0px;">
|
||||
|
||||
<filterPropertiesSelect/>
|
||||
|
||||
<template v-for="(no,index) in globalFilterSelectList" :key="no">
|
||||
<globalFilterSelect :index="no" :onDeleteMetricSelect="onDeleteGlobalFilterSelect"/>
|
||||
</template>
|
||||
</el-row>
|
||||
|
||||
<el-row style="height: 1px"></el-row>
|
||||
|
||||
<el-row style="height: 40px" align="middle">
|
||||
<el-col :span="6">
|
||||
<span style="font-weight: bold;">
|
||||
分组项
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="10" :offset="8">
|
||||
<el-row justify="end">
|
||||
<el-button class="editorTopRightToolBarBtn">
|
||||
<el-icon :size="20">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@use '@/assets/styles/editorToolbarBtn.scss';
|
||||
|
||||
.editorAreaOuter {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.editorAreaInner {
|
||||
width: calc(100% - 30px);
|
||||
height: calc(100% - 10px);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
@ -0,0 +1,224 @@
|
||||
<script setup>
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
index: 0,
|
||||
canDelete: true,
|
||||
onDeleteMetricSelect: null,
|
||||
eventAllData: {},
|
||||
})
|
||||
|
||||
const eventAllData = props.eventAllData
|
||||
|
||||
const selectEventValue = ref('')
|
||||
const selectEventOptions = ref([])
|
||||
|
||||
eventAllData.eventDescList.forEach((eventInfo) => {
|
||||
let addOptionInfo = {
|
||||
value: eventInfo.name,
|
||||
label: eventInfo.name,
|
||||
}
|
||||
if (eventInfo.alias !== undefined && eventInfo.alias !== "") {
|
||||
addOptionInfo.label = eventInfo.alias
|
||||
}
|
||||
selectEventOptions.value.push(addOptionInfo)
|
||||
})
|
||||
|
||||
selectEventValue.value = selectEventOptions.value[0].label
|
||||
const selectedEventInfo = ref({})
|
||||
|
||||
const defaultSelectEventRelationMetrics = {
|
||||
label: "预置指标",
|
||||
options: [
|
||||
{
|
||||
value: "totalCount",
|
||||
label: "总次数",
|
||||
opList: []
|
||||
},
|
||||
{
|
||||
value: "triggerUserCount",
|
||||
label: "触发用户数",
|
||||
opList: []
|
||||
},
|
||||
{
|
||||
value: "userAvgCount",
|
||||
label: "人均次数",
|
||||
opList: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
const defaultMetricTypeNumberOpList1 = [
|
||||
"总和", "均值", "人均值", "中位数", "最大值", "最小值", "去重数", "方差", "标准差",
|
||||
"99分位数", "95分位数", "90分位数", "85分位数", "80分位数", "75分位数", "70分位数",
|
||||
"60分位数", "50分位数", "40分位数", "30分位数", "20分位数", "10分位数", "5分位数"
|
||||
]
|
||||
const defaultMetricTypeNumberOpList2 = [
|
||||
"totalCount", "avg", "avgPerUser", "median", "max", "min", "distinct", "variance", "standardDeviation",
|
||||
"99qualtile", "95qualtile", "90qualtile", "85qualtile", "80qualtile", "75qualtile", "70qualtile",
|
||||
"60qualtile", "50qualtile", "40qualtile", "30qualtile", "20qualtile", "10qualtile", "5qualtile"
|
||||
]
|
||||
const defaultMetricTypeNumberOpList = []
|
||||
const defaultMetricTypeStringOpList = [{value: "distinct", label: "去重数"}]
|
||||
for (let i = 0; i < defaultMetricTypeNumberOpList1.length; i++) {
|
||||
defaultMetricTypeNumberOpList.push({
|
||||
value: defaultMetricTypeNumberOpList2[i],
|
||||
label: defaultMetricTypeNumberOpList1[i]
|
||||
})
|
||||
}
|
||||
|
||||
// console.log("oplist:", defaultMetricTypeNumberOpList)
|
||||
|
||||
const selectedEventRelationMetricValue = ref('')
|
||||
const disableMetricSelectOp = ref(true)
|
||||
const selectEventRelationMetrics = computed(() => {
|
||||
const eventInfo = eventAllData.eventDescList.find((v, i) => v.alias === selectEventValue.value)
|
||||
if (!eventInfo) {
|
||||
return [defaultSelectEventRelationMetrics]
|
||||
}
|
||||
// console.log("选择事件:", eventInfo)
|
||||
selectedEventInfo.value = eventInfo
|
||||
|
||||
let userMetrics = {
|
||||
label: "用户属性",
|
||||
options: []
|
||||
}
|
||||
let eventMetrics = {
|
||||
label: "事件属性",
|
||||
options: []
|
||||
}
|
||||
eventInfo.fields.forEach(field => {
|
||||
let opList = []
|
||||
if (field.propertyType === 1) {
|
||||
opList = defaultMetricTypeNumberOpList
|
||||
} else if (field.propertyType === 2) {
|
||||
opList = defaultMetricTypeStringOpList
|
||||
}
|
||||
eventMetrics.options.push({
|
||||
value: field.name,
|
||||
label: field.alias,
|
||||
opList: opList
|
||||
})
|
||||
})
|
||||
eventAllData.userProperties.forEach(field => {
|
||||
let opList = []
|
||||
if (field.propertyType === 1) {
|
||||
opList = defaultMetricTypeNumberOpList
|
||||
} else if (field.propertyType === 2) {
|
||||
opList = defaultMetricTypeStringOpList
|
||||
}
|
||||
userMetrics.options.push({
|
||||
value: field.name,
|
||||
label: field.alias,
|
||||
opList: opList
|
||||
})
|
||||
})
|
||||
return [defaultSelectEventRelationMetrics, eventMetrics, userMetrics,]
|
||||
})
|
||||
|
||||
const selectedMetricRelationOpValue = ref('')
|
||||
const selectMetricRelationOpList = computed(() => {
|
||||
// console.log("selectEventRelationMetrics:", selectedEventRelationMetricValue.value)
|
||||
// console.log("selectEventRelationMetrics:", selectEventRelationMetrics.value)
|
||||
let opList = []
|
||||
selectEventRelationMetrics.value.find((v, idx) => {
|
||||
const selectedMetric = v.options.find((v1, idx1) => {
|
||||
return v1.value === selectedEventRelationMetricValue.value
|
||||
})
|
||||
if (selectedMetric) {
|
||||
opList = selectedMetric.opList
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
// console.log("oplist", opList)
|
||||
selectedMetricRelationOpValue.value = ''
|
||||
disableMetricSelectOp.value = opList.length === 0;
|
||||
return opList
|
||||
})
|
||||
|
||||
const onSelectEvent = (value) => {
|
||||
|
||||
}
|
||||
|
||||
const filterOption = (input, option) => {
|
||||
return option.value.filter((item) => {
|
||||
if (item.options && item.options.length > 0) {
|
||||
return item.options.filter(opt => {
|
||||
return opt.includes(input)
|
||||
})
|
||||
}
|
||||
return item.label.includes(input)
|
||||
})
|
||||
};
|
||||
|
||||
onSelectEvent(selectEventValue.value)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editorOperationArea">
|
||||
<!-- <div-->
|
||||
<!-- style="background-color: #202241;margin-right: 5px;border-radius: 5px;;width: 3px;height: 70px;display: inline-flex;justify-content: center;align-items: center;">-->
|
||||
<!-- <!– <p style="font-weight: bold;color: white;margin: 0">1</p>–>-->
|
||||
<!-- </div>-->
|
||||
<el-row>
|
||||
<el-row>
|
||||
<el-row justify="end" style="width: 100%">
|
||||
<a-space align="end" style="float: right">
|
||||
<el-button class="editorTopRightToolBarBtn" :disabled="(!canDelete)" @click="onDeleteMetricSelect(index)">
|
||||
<el-icon :size="20">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</a-space>
|
||||
</el-row>
|
||||
|
||||
<a-space :size="5">
|
||||
<a-select showArrow :show-search="true" v-model:value="selectEventValue" :options="selectEventOptions"
|
||||
@change="onSelectEvent" :filter-option="filterOption"
|
||||
style="width: 100px;margin: 0"/>
|
||||
<div
|
||||
style="background-color: #202241;border-radius: 5px;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">
|
||||
<p style="color: white;margin: 0">的</p>
|
||||
</div>
|
||||
<a-select showArrow show-search v-model:value="selectedEventRelationMetricValue"
|
||||
:options="selectEventRelationMetrics" :filter-option="filterOption" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
<!-- <div-->
|
||||
<!-- style="background-color: #202241;border-radius: 5px;;width: 25px;height: 25px;display: flex;justify-content: center;align-items: center;">-->
|
||||
<!-- <p style="color: white;margin: 0">.</p>-->
|
||||
<!-- </div>-->
|
||||
<div
|
||||
style="height: 100%;display: inline-flex;flex-direction: column;justify-content: end">
|
||||
<p style="color: black;font-weight: bold;font-size: 20px;margin: 0">·</p>
|
||||
</div>
|
||||
<a-select showArrow :disabled="disableMetricSelectOp"
|
||||
v-model:value="selectedMetricRelationOpValue"
|
||||
:filter-option="filterOption"
|
||||
:options="selectMetricRelationOpList" @change=""
|
||||
style="width: 100px;margin: 0"/>
|
||||
</a-space>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@use '@/assets/styles/editorToolbarBtn.scss';
|
||||
|
||||
.editorOperationArea {
|
||||
height: 100%;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
border-left: 3px solid #202241;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
</style>
|
@ -11,7 +11,9 @@ defineProps({
|
||||
<template>
|
||||
<div class="editorContainer .clearfix">
|
||||
<div class="editorOperationArea">
|
||||
<component :is="editorAreaInfo.editorPane"/>
|
||||
<el-scrollbar>
|
||||
<component :is="editorAreaInfo.editorPane"/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="editorFinishArea">
|
||||
<div class="editorFinishBtns">
|
Loading…
x
Reference in New Issue
Block a user