Merge pull request #9135 from dataease/pr@dev-v2_st

fix(仪表板): 支持批量拖拽字段
This commit is contained in:
dataeaseShu 2024-04-16 17:10:52 +08:00 committed by GitHub
commit 47b685d551
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 277 additions and 56 deletions

View File

@ -11,6 +11,13 @@ export interface DatasetOrFolder {
allFields?: Array<{}>
}
export interface EnumValue {
queryId: string
displayId?: string
sortId?: string
sort?: string
}
interface Fields {
fields: Array<{}>
data: Array<{}>
@ -76,6 +83,12 @@ export const renameDatasetTree = async (data: DatasetOrFolder): Promise<IRespons
})
}
export const enumValueObj = async (data: EnumValue): Promise<Record<string, string>[]> => {
return request.post({ url: '/datasetData/enumValueObj', data }).then(res => {
return res?.data
})
}
export const moveDatasetTree = async (data: DatasetOrFolder): Promise<IResponse> => {
return request.post({ url: '/datasetTree/move', data }).then(res => {
return res?.data

View File

@ -198,23 +198,24 @@ const dragover = () => {
}
const drop = e => {
const componentInfo: ComponentInfo = JSON.parse(e.dataTransfer.getData('dimension') || '{}')
if (!componentInfo.id) return
const checkedFields = []
const checkedFieldsMap = {}
datasetFieldList.value.forEach(ele => {
if (ele.tableId === componentInfo.datasetId) {
checkedFields.push(ele.id)
checkedFieldsMap[ele.id] = componentInfo.id
}
})
list.value.push({
...infoFormat(componentInfo),
auto: true,
optionValueSource: 1,
checkedFields,
checkedFieldsMap,
displayType: `${componentInfo.deType}`
const componentInfoArr: ComponentInfo[] = JSON.parse(e.dataTransfer.getData('dimension') || '{}')
componentInfoArr.forEach(componentInfo => {
const checkedFields = []
const checkedFieldsMap = {}
datasetFieldList.value.forEach(ele => {
if (ele.tableId === componentInfo.datasetId) {
checkedFields.push(ele.id)
checkedFieldsMap[ele.id] = componentInfo.id
}
})
list.value.push({
...infoFormat(componentInfo),
auto: true,
optionValueSource: 1,
checkedFields,
checkedFieldsMap,
displayType: `${componentInfo.deType}`
})
})
element.value.propValue = [...list.value]
snapshotStore.recordSnapshotCache()

View File

@ -240,6 +240,8 @@ const computedTree = computed(() => {
const handleDatasetChange = () => {
curComponent.value.field.id = ''
curComponent.value.displayId = ''
curComponent.value.sortId = ''
getOptions(curComponent.value.dataset.id, curComponent.value)
}
@ -672,6 +674,7 @@ const parameterCompletion = () => {
const attributes = {
timeType: 'fixed',
required: false,
defaultMapValue: [],
parametersStart: null,
conditionType: 0,
conditionValueOperatorF: 'eq',
@ -691,6 +694,9 @@ const parameterCompletion = () => {
timeNumRange: 0,
relativeToCurrentTypeRange: 'year',
aroundRange: 'f',
displayId: '',
sortId: '',
sort: 'asc',
arbitraryTimeRange: new Date(),
setTimeRange: false,
showEmpty: false,
@ -743,10 +749,6 @@ const handleCondition = item => {
valueSource.value.push('')
valueSource.value.push('')
}
curComponent.value.sortField = curComponent.value.sortField ?? {
id: '',
sortType: 'asc'
}
parameterCompletion()
nextTick(() => {
curComponent.value.showError = showError.value
@ -1330,7 +1332,7 @@ defineExpose({
<div class="value">
<el-select
@change="handleFieldChange"
placeholder="请选择展示字段"
placeholder="查询字段"
v-model="curComponent.field.id"
>
<template v-if="curComponent.field.id" #prefix>
@ -1374,24 +1376,18 @@ defineExpose({
</el-select>
</div>
<div class="value">
<el-select
clearable
placeholder="请选择排序字段"
v-model="curComponent.sortField.id"
class="sort-field"
@change="handleFieldChange"
>
<template v-if="curComponent.sortField.id" #prefix>
<el-select placeholder="显示字段" v-model="curComponent.displayId">
<template v-if="curComponent.displayId" #prefix>
<el-icon>
<Icon
:name="`field_${
fieldType[
getDetype(curComponent.sortField.id, curComponent.dataset.fields)
getDetype(curComponent.displayId, curComponent.dataset.fields)
]
}`"
:className="`field-icon-${
fieldType[
getDetype(curComponent.sortField.id, curComponent.dataset.fields)
getDetype(curComponent.displayId, curComponent.dataset.fields)
]
}`"
></Icon>
@ -1424,9 +1420,57 @@ defineExpose({
</div>
</el-option>
</el-select>
</div>
<div class="value">
<el-select
clearable
placeholder="请选择排序字段"
v-model="curComponent.sortId"
class="sort-field"
@change="handleFieldChange"
>
<template v-if="curComponent.sortId" #prefix>
<el-icon>
<Icon
:name="`field_${
fieldType[getDetype(curComponent.sortId, curComponent.dataset.fields)]
}`"
:className="`field-icon-${
fieldType[getDetype(curComponent.sortId, curComponent.dataset.fields)]
}`"
></Icon>
</el-icon>
</template>
<el-option
v-for="ele in curComponent.dataset.fields.filter(
ele =>
ele.deType === +curComponent.displayType ||
([3, 4].includes(ele.deType) && +curComponent.displayType === 2)
)"
:key="ele.id"
:label="ele.name"
:value="ele.id"
:disabled="ele.desensitized"
>
<div
class="flex-align-center icon"
:title="ele.desensitized ? '脱敏字段,不能被设置为查询条件' : ''"
>
<el-icon>
<Icon
:name="`field_${fieldType[ele.deType]}`"
:className="`field-icon-${fieldType[ele.deType]}`"
></Icon>
</el-icon>
<span>
{{ ele.name }}
</span>
</div>
</el-option>
</el-select>
<el-select
class="sort-type"
v-model="curComponent.sortField.sortType"
v-model="curComponent.sort"
@change="handleFieldChange"
>
<el-option label="升序" value="asc" />

View File

@ -1,15 +1,19 @@
<script lang="ts" setup>
import { ref, toRefs, PropType, onBeforeMount, shallowRef, watch, nextTick, computed } from 'vue'
import { getEnumValue } from '@/api/dataset'
import { enumValueObj, type EnumValue, getEnumValue } from '@/api/dataset'
import { cloneDeep, debounce } from 'lodash-es'
interface SelectConfig {
selectValue: any
defaultMapValue: any
defaultValue: any
checkedFieldsMap: object
displayType: string
showEmpty: boolean
id: string
displayId: string
sort: string
sortId: string
checkedFields: string[]
field: {
id: string
@ -44,22 +48,49 @@ const props = defineProps({
}
})
const { config } = toRefs(props)
let enumValueArr = []
const selectValue = ref()
const loading = ref(false)
const multiple = ref(false)
const options = shallowRef([])
const setDefaultMapValue = arr => {
const { displayId, field } = config.value
if (!displayId || displayId === field?.id) {
return []
}
let defaultMapValue = {}
let defaultValue = []
arr.forEach(ele => {
defaultMapValue[ele] = []
})
enumValueArr.forEach(ele => {
if (defaultMapValue[ele[displayId]]) {
defaultMapValue[ele[displayId]].push(ele[field?.id])
}
})
Object.values(defaultMapValue).forEach(ele => {
defaultValue = [...defaultValue, ...(ele as unknown as string[])]
})
return defaultValue
}
const handleValueChange = () => {
const value = Array.isArray(selectValue.value) ? [...selectValue.value] : selectValue.value
if (!props.isConfig) {
config.value.selectValue = Array.isArray(selectValue.value)
? [...selectValue.value]
: selectValue.value
config.value.defaultMapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
return
}
config.value.defaultValue = value
config.value.defaultMapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
}
const displayTypeChange = () => {
@ -68,7 +99,7 @@ const displayTypeChange = () => {
selectValue.value = config.value.multiple ? [] : undefined
}
const handleFieldIdChange = (val: string[]) => {
const handleFieldIdDefaultChange = (val: string[]) => {
loading.value = true
getEnumValue(val)
.then(res => {
@ -96,6 +127,40 @@ const handleFieldIdChange = (val: string[]) => {
})
}
const handleFieldIdChange = (val: EnumValue) => {
enumValueArr = []
loading.value = true
enumValueObj(val)
.then(res => {
enumValueArr = res || []
options.value = [
...new Set(
(res || []).map(ele => {
return ele[val.displayId || val.queryId]
})
)
].map(ele => {
return {
label: ele,
value: ele
}
})
})
.finally(() => {
loading.value = false
if (config.value.defaultValueCheck) {
selectValue.value = Array.isArray(config.value.defaultValue)
? [...config.value.defaultValue]
: config.value.defaultValue
} else {
selectValue.value = Array.isArray(selectValue.value)
? [...selectValue.value]
: selectValue.value
}
setEmptyData()
})
}
const visible = ref(false)
const visibleChange = (val: boolean) => {
setTimeout(() => {
@ -181,7 +246,12 @@ watch(
)
watch(
() => config.value.field.id,
[
() => config.value.field.id,
() => config.value.displayId,
() => config.value.sort,
() => config.value.sortId
],
val => {
if (!val) return
debounceOptions(1)
@ -214,19 +284,30 @@ watch(
const setOptions = (num: number) => {
if (num !== config.value.optionValueSource) return
const { optionValueSource, checkedFieldsMap, checkedFields, field, valueSource } = config.value
const {
optionValueSource,
checkedFieldsMap,
checkedFields,
field,
valueSource,
displayId,
sort,
sortId
} = config.value
switch (optionValueSource) {
case 0:
const arr = Object.values(checkedFieldsMap).filter(ele => !!ele) as string[]
if (!!checkedFields.length && !!arr.length) {
handleFieldIdChange(checkedFields.map(ele => checkedFieldsMap[ele]).filter(ele => !!ele))
handleFieldIdDefaultChange(
checkedFields.map(ele => checkedFieldsMap[ele]).filter(ele => !!ele)
)
} else {
options.value = []
}
break
case 1:
if (field.id) {
handleFieldIdChange([field.id])
handleFieldIdChange({ queryId: field.id, displayId: displayId || field.id, sort, sortId })
} else {
options.value = []
}

View File

@ -15,10 +15,19 @@ const infoFormat = (obj: ComponentInfo) => {
name,
deType
},
sortField: {
id: '',
sortType: 'asc'
},
displayId: '',
sortId: '',
sort: 'asc',
defaultMapValue: [],
conditionType: 0,
conditionValueOperatorF: 'eq',
conditionValueF: '',
conditionValueOperatorS: 'like',
conditionValueS: '',
defaultConditionValueOperatorF: 'eq',
defaultConditionValueF: '',
defaultConditionValueOperatorS: 'like',
defaultConditionValueS: '',
timeType: 'fixed',
relativeToCurrent: 'custom',
required: false,

View File

@ -81,8 +81,17 @@ const getValueByDefaultValueCheckOrFirstLoad = (
defaultValue: any,
selectValue: any,
firstLoad: boolean,
multiple: boolean
multiple: boolean,
defaultMapValue: any,
optionValueSource: number
) => {
if (optionValueSource === 1) {
if (firstLoad && !selectValue?.length) {
return defaultValueCheck ? defaultMapValue : multiple ? [] : ''
}
return (selectValue?.length ? defaultMapValue : selectValue) || ''
}
if (firstLoad && !selectValue?.length) {
return defaultValueCheck ? defaultValue : multiple ? [] : ''
}
@ -134,7 +143,8 @@ const getOperator = (
conditionValueF,
conditionValueOperatorS,
conditionValueS,
firstLoad
firstLoad,
optionValueSource
) => {
const valueF = firstLoad ? defaultConditionValueF : conditionValueF
const valueS = firstLoad ? defaultConditionValueS : conditionValueS
@ -153,7 +163,11 @@ const getOperator = (
return valueF === '' ? operatorS : operatorF
}
return [1, 7].includes(+displayType) ? 'between' : multiple ? 'in' : 'eq'
return [1, 7].includes(+displayType)
? 'between'
: multiple || optionValueSource === 1
? 'in'
: 'eq'
}
export const searchQuery = (queryComponentList, filter, curComponentId, firstLoad) => {
@ -184,6 +198,8 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
defaultValueCheck,
timeType = 'fixed',
defaultValue,
optionValueSource,
defaultMapValue,
parameters = [],
parametersCheck = false,
isTree = false,
@ -247,7 +263,9 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
defaultValue,
value,
firstLoad,
multiple
multiple,
defaultMapValue,
optionValueSource
)
}
if (
@ -273,7 +291,8 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
conditionValueF,
conditionValueOperatorS,
conditionValueS,
firstLoad
firstLoad,
optionValueSource
)
filter.push({
componentId: ele.id,

View File

@ -9,7 +9,8 @@ import {
nextTick,
onBeforeMount,
provide,
h
h,
unref
} from 'vue'
import Icon from '@/components/icon-custom/src/Icon.vue'
import type { FormInstance, FormRules } from 'element-plus-secondary'
@ -289,7 +290,14 @@ const realQuota = computed(() => {
provide('quotaData', realQuota)
const startToMove = (e, item) => {
e.dataTransfer.setData('dimension', JSON.stringify({ ...item, datasetId: view.value.tableId }))
e.dataTransfer.setData(
'dimension',
JSON.stringify(
item
.filter(ele => ele.id)
.map(ele => ({ ...cloneDeep(unref(ele)), datasetId: view.value.tableId }))
)
)
}
const dimensionItemChange = () => {
@ -785,7 +793,6 @@ const onChangeYAxisForm = val => {
}
const onChangeYAxisExtForm = val => {
console.log('onChangeYAxisExtForm', val)
view.value.customStyle.yAxisExt = val
renderChart(view.value)
}
@ -1297,8 +1304,54 @@ const setActiveCtrl = (ele, type = 'dimension') => {
activeChild.value.push(ele)
}
const setActiveShift = (ele, type = 'dimension') => {
const activeChild = type === 'dimension' ? activeDimension : activeQuota
const deactivateChild = type === 'quota' ? activeDimension : activeQuota
const dataArr = type === 'dimension' ? dimensionData : quotaData
deactivateChild.value = []
const dimensionDataId = dataArr.value.map(ele => ele.id)
const dimensionDataActiveChild = activeChild.value.filter(ele => dimensionDataId.includes(ele.id))
if (!dimensionDataActiveChild.length) {
const index = activeChild.value.findIndex(item => item.id === ele.id)
if (index !== -1) {
activeChild.value.splice(index, 1)
return
}
activeChild.value.push(ele)
} else {
const startItx = dataArr.value.findIndex(
item => item.id === dimensionDataActiveChild[dimensionDataActiveChild.length - 1].id
)
const endItx = dataArr.value.findIndex(item => item.id === ele.id)
if (startItx === endItx) return
if (startItx > endItx) {
activeChild.value = [...activeChild.value, ...dataArr.value.slice(endItx, startItx)]
}
if (startItx < endItx) {
activeChild.value = [...activeChild.value, ...dataArr.value.slice(startItx + 1, endItx + 1)]
}
}
}
const isDrag = ref(false)
const dragStartD = (e: DragEvent) => {
isDrag.value = true
setTimeout(() => {
isDraggingItem.value = true
}, 0)
}
const singleDragStartD = (e: DragEvent, ele, type) => {
const activeChild = type === 'dimension' ? activeDimension : activeQuota
const deactivateChild = type === 'quota' ? activeDimension : activeQuota
deactivateChild.value = []
if (!activeChild.value.length) {
activeChild.value = [unref(ele)]
}
startToMove(e, unref(activeDimension.value))
}
const dragStart = (e: DragEvent) => {
isDrag.value = true
setTimeout(() => {
@ -1313,7 +1366,6 @@ const singleDragStart = (e: DragEvent, ele, type) => {
if (!activeChild.value.length) {
activeChild.value = [ele]
}
dragStart(e)
}
const dragEnd = () => {
@ -2187,11 +2239,12 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
@click.ctrl="setActiveCtrl(element)"
@click.meta="setActiveCtrl(element)"
@click.exact="setActive(element)"
@dragstart="$event => singleDragStart($event, element, 'dimension')"
@click.shift="setActiveShift(element)"
@dragstart="$event => singleDragStartD($event, element, 'dimension')"
:draggable="true"
@dragend="singleDragEnd"
class="item"
v-for="element in state.dimensionData"
v-for="element in dimensionData"
:key="element.id"
>
<div
@ -2241,7 +2294,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
</el-dropdown>
</div>
<div
@dragstart="dragStart"
@dragstart="dragStartD"
:draggable="true"
@dragend="dragEnd"
v-if="activeDimension.map(itx => itx.id).includes(element.id)"
@ -2314,6 +2367,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
@click.ctrl="setActiveCtrl(element, 'quota')"
@click.meta="setActiveCtrl(element, 'quota')"
@click.exact="setActive(element, 'quota')"
@click.shift="setActiveShift(element, 'quota')"
class="item"
@dragstart="$event => singleDragStart($event, element, 'quota')"
:draggable="true"