forked from github/dataease
feat(查询组件): 支持组件级联
This commit is contained in:
parent
bfb8032398
commit
31758cf333
283
core/core-frontend/src/custom-component/v-query/QueryCascade.vue
Normal file
283
core/core-frontend/src/custom-component/v-query/QueryCascade.vue
Normal file
@ -0,0 +1,283 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { guid } from '@/views/visualized/data/dataset/form/util.js'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const handleBeforeClose = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const cascadeList = ref([])
|
||||
const optionsMap = shallowRef({})
|
||||
const datasetMap = shallowRef([])
|
||||
const cancelClick = () => {
|
||||
handleBeforeClose()
|
||||
}
|
||||
|
||||
const confirmClick = () => {
|
||||
handleBeforeClose()
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const disabledDatasetId = shallowRef([])
|
||||
|
||||
const visibleChange = (val, index, idx) => {
|
||||
let topId = ''
|
||||
let topIdArr = []
|
||||
let bottomId = ''
|
||||
let bottomIdArr = []
|
||||
for (let i in cascadeList.value[index]) {
|
||||
if (i > idx) {
|
||||
if (cascadeList.value[index][i].datasetId && !bottomId) {
|
||||
bottomId = cascadeList.value[index][i].datasetId
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (cascadeList.value[index][i].datasetId) {
|
||||
topId = cascadeList.value[index][i].datasetId
|
||||
}
|
||||
if (i === idx) {
|
||||
topId = (cascadeList.value[index][idx - 1] || {}).datasetId
|
||||
}
|
||||
}
|
||||
|
||||
cascadeList.value.forEach(ele => {
|
||||
let tentativeTopArr = []
|
||||
let tentativeBottomArr = []
|
||||
for (let i in ele) {
|
||||
if (topIdArr[topIdArr.length - 1] === tentativeTopArr || bottomId === ele[i].datasetId) {
|
||||
if (bottomId === ele[i].datasetId) {
|
||||
bottomIdArr.push(tentativeBottomArr)
|
||||
}
|
||||
|
||||
if (bottomIdArr[bottomIdArr.length - 1] === tentativeBottomArr) {
|
||||
tentativeBottomArr.push(ele[i].datasetId)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (ele[i].datasetId) {
|
||||
tentativeTopArr.push(ele[i].datasetId)
|
||||
}
|
||||
if (topId === ele[i].datasetId) {
|
||||
topIdArr.push(tentativeTopArr)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (val) {
|
||||
disabledDatasetId.value = [...new Set([...topIdArr.flat(), ...bottomIdArr.flat()])]
|
||||
}
|
||||
}
|
||||
|
||||
const addCascadeItem = item => {
|
||||
item.push({
|
||||
datasetId: '',
|
||||
topLevelIsSameDataset: false,
|
||||
fieldId: '',
|
||||
placeholder: item.length ? '' : '第一级无需配置被级联字段',
|
||||
id: guid()
|
||||
})
|
||||
setPlaceholder(item.length - 1, item)
|
||||
}
|
||||
|
||||
const setPlaceholder = (idx, item) => {
|
||||
if (
|
||||
item[idx] &&
|
||||
item[idx - 1] &&
|
||||
item[idx].datasetId &&
|
||||
item[idx].datasetId === item[idx - 1].datasetId
|
||||
) {
|
||||
item[idx].placeholder = '与上一级使用同一个数据集,无需配置被级联字段'
|
||||
item[idx].fieldId = ''
|
||||
}
|
||||
}
|
||||
|
||||
const deleteCascade = (idx, item) => {
|
||||
item.splice(idx, 1)
|
||||
item[0].fieldId = ''
|
||||
item[0].placeholder = '第一级无需配置被级联字段'
|
||||
setPlaceholder(idx, item)
|
||||
}
|
||||
|
||||
const addCascadeBlock = () => {
|
||||
const arr = []
|
||||
addCascadeItem(arr)
|
||||
cascadeList.value.push(arr)
|
||||
}
|
||||
|
||||
const indexCascade = ' 一二三四五'
|
||||
|
||||
defineExpose({
|
||||
init
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
class="query-condition-cascade"
|
||||
v-model="dialogVisible"
|
||||
width="900px"
|
||||
@click.stop
|
||||
:before-close="handleBeforeClose"
|
||||
@mousedown.stop
|
||||
@mousedup.stop
|
||||
>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
查询条件级联配置<span class="tip">(仅上级能级联下级,不可反向级联)</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="content">
|
||||
<el-icon style="font-size: 16px">
|
||||
<Icon name="icon_info_colorful"></Icon>
|
||||
</el-icon>
|
||||
基于当前查询组件的查询条件,如果需要进行及联配置,需要满足以下条件:<br />
|
||||
1、展示类型为:文本下拉组件和数字下拉组件;2、选项值来源为:选择数据集<br />
|
||||
</div>
|
||||
<el-button text @click="addCascadeBlock">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined"></Icon>
|
||||
</template>
|
||||
添加级联配置
|
||||
</el-button>
|
||||
<div class="cascade-content" v-for="(item, index) in cascadeList" :key="index">
|
||||
<el-button text @click="addCascadeItem(item)">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined"></Icon>
|
||||
</template>
|
||||
添加级联条件
|
||||
</el-button>
|
||||
<div class="cascade-item">
|
||||
<div class="label">查询条件层级</div>
|
||||
<div class="item-name">请选择查询条件</div>
|
||||
<div class="cascade-icon"></div>
|
||||
<div class="item-field">请选择被级联字段</div>
|
||||
</div>
|
||||
<div class="cascade-item" v-for="(ele, idx) in item" :key="ele.id">
|
||||
<div class="label">第{{ indexCascade[idx + 1] }}级</div>
|
||||
<div class="item-name">
|
||||
<el-select v-model="ele.datasetId" style="width: 300px">
|
||||
<el-option
|
||||
v-for="item in datasetMap"
|
||||
:key="item.value"
|
||||
@visible-change="val => visibleChange(val, index, idx)"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:disabled="disabledDatasetId.includes(item.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="cascade-icon">
|
||||
<el-icon>
|
||||
<Icon name="join-join"></Icon>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="item-field">
|
||||
<el-select
|
||||
:placeholder="ele.placeholder"
|
||||
:disabled="!!ele.placeholder"
|
||||
v-model="ele.fieldId"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in optionsMap[ele.datasetId]"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button @click="deleteCascade(idx, item)" class="cascade-delete" text>
|
||||
<template #icon>
|
||||
<Icon name="icon_delete-trash_outlined"></Icon>
|
||||
</template>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelClick">{{ t('chart.cancel') }} </el-button>
|
||||
<el-button @click="confirmClick" type="primary">{{ t('chart.confirm') }} </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.query-condition-cascade {
|
||||
.title {
|
||||
.tip {
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 62px;
|
||||
width: 852px;
|
||||
border-radius: 4px;
|
||||
background: #e1eaff;
|
||||
position: relative;
|
||||
padding: 9px 0 9px 40px;
|
||||
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
.ed-icon {
|
||||
position: absolute;
|
||||
top: 10.6px;
|
||||
left: 16px;
|
||||
font-size: 14px;
|
||||
color: var(--ed-color-primary, #3370ff);
|
||||
}
|
||||
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.cascade-content {
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
||||
border: 1px solid #e4e7ed;
|
||||
padding: 24px;
|
||||
padding-top: 8px;
|
||||
margin-top: 8px;
|
||||
|
||||
.cascade-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
.label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.cascade-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.item-field {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.cascade-delete {
|
||||
width: 40px;
|
||||
font-size: 20px;
|
||||
color: #646a73;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,6 +1,15 @@
|
||||
getLastStart
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, nextTick, computed, shallowRef, toRefs, watch } from 'vue'
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
nextTick,
|
||||
computed,
|
||||
shallowRef,
|
||||
toRefs,
|
||||
watch,
|
||||
defineAsyncComponent
|
||||
} from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { addQueryCriteriaConfig } from './options'
|
||||
import { getCustomTime } from './time-format'
|
||||
@ -436,6 +445,12 @@ const isInRange = (ele, startWindowTime, timeStamp) => {
|
||||
}
|
||||
}
|
||||
|
||||
const CascadeDialog = defineAsyncComponent(() => import('./QueryCascade.vue'))
|
||||
const cascadeDialog = ref()
|
||||
const openCascadeDialog = () => {
|
||||
cascadeDialog.value.init()
|
||||
}
|
||||
|
||||
const validateConditionType = ({
|
||||
defaultConditionValueF,
|
||||
defaultConditionValueS,
|
||||
@ -1989,11 +2004,13 @@ defineExpose({
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button class="query-cascade" @click="openCascadeDialog">查询组件级联配置</el-button>
|
||||
<el-button @click="cancelClick">{{ t('chart.cancel') }} </el-button>
|
||||
<el-button @click="confirmClick" type="primary">{{ t('chart.confirm') }} </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<CascadeDialog ref="cascadeDialog"></CascadeDialog>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
@ -2040,6 +2057,12 @@ defineExpose({
|
||||
.query-condition-configuration {
|
||||
--ed-font-weight-primary: 400;
|
||||
|
||||
.query-cascade {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
bottom: 24px;
|
||||
}
|
||||
|
||||
.ed-dialog__headerbtn {
|
||||
top: 21px;
|
||||
display: flex;
|
||||
|
Loading…
Reference in New Issue
Block a user