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<{}> allFields?: Array<{}>
} }
export interface EnumValue {
queryId: string
displayId?: string
sortId?: string
sort?: string
}
interface Fields { interface Fields {
fields: Array<{}> fields: Array<{}>
data: 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> => { export const moveDatasetTree = async (data: DatasetOrFolder): Promise<IResponse> => {
return request.post({ url: '/datasetTree/move', data }).then(res => { return request.post({ url: '/datasetTree/move', data }).then(res => {
return res?.data return res?.data

View File

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

View File

@ -240,6 +240,8 @@ const computedTree = computed(() => {
const handleDatasetChange = () => { const handleDatasetChange = () => {
curComponent.value.field.id = '' curComponent.value.field.id = ''
curComponent.value.displayId = ''
curComponent.value.sortId = ''
getOptions(curComponent.value.dataset.id, curComponent.value) getOptions(curComponent.value.dataset.id, curComponent.value)
} }
@ -672,6 +674,7 @@ const parameterCompletion = () => {
const attributes = { const attributes = {
timeType: 'fixed', timeType: 'fixed',
required: false, required: false,
defaultMapValue: [],
parametersStart: null, parametersStart: null,
conditionType: 0, conditionType: 0,
conditionValueOperatorF: 'eq', conditionValueOperatorF: 'eq',
@ -691,6 +694,9 @@ const parameterCompletion = () => {
timeNumRange: 0, timeNumRange: 0,
relativeToCurrentTypeRange: 'year', relativeToCurrentTypeRange: 'year',
aroundRange: 'f', aroundRange: 'f',
displayId: '',
sortId: '',
sort: 'asc',
arbitraryTimeRange: new Date(), arbitraryTimeRange: new Date(),
setTimeRange: false, setTimeRange: false,
showEmpty: false, showEmpty: false,
@ -743,10 +749,6 @@ const handleCondition = item => {
valueSource.value.push('') valueSource.value.push('')
valueSource.value.push('') valueSource.value.push('')
} }
curComponent.value.sortField = curComponent.value.sortField ?? {
id: '',
sortType: 'asc'
}
parameterCompletion() parameterCompletion()
nextTick(() => { nextTick(() => {
curComponent.value.showError = showError.value curComponent.value.showError = showError.value
@ -1330,7 +1332,7 @@ defineExpose({
<div class="value"> <div class="value">
<el-select <el-select
@change="handleFieldChange" @change="handleFieldChange"
placeholder="请选择展示字段" placeholder="查询字段"
v-model="curComponent.field.id" v-model="curComponent.field.id"
> >
<template v-if="curComponent.field.id" #prefix> <template v-if="curComponent.field.id" #prefix>
@ -1374,24 +1376,18 @@ defineExpose({
</el-select> </el-select>
</div> </div>
<div class="value"> <div class="value">
<el-select <el-select placeholder="显示字段" v-model="curComponent.displayId">
clearable <template v-if="curComponent.displayId" #prefix>
placeholder="请选择排序字段"
v-model="curComponent.sortField.id"
class="sort-field"
@change="handleFieldChange"
>
<template v-if="curComponent.sortField.id" #prefix>
<el-icon> <el-icon>
<Icon <Icon
:name="`field_${ :name="`field_${
fieldType[ fieldType[
getDetype(curComponent.sortField.id, curComponent.dataset.fields) getDetype(curComponent.displayId, curComponent.dataset.fields)
] ]
}`" }`"
:className="`field-icon-${ :className="`field-icon-${
fieldType[ fieldType[
getDetype(curComponent.sortField.id, curComponent.dataset.fields) getDetype(curComponent.displayId, curComponent.dataset.fields)
] ]
}`" }`"
></Icon> ></Icon>
@ -1424,9 +1420,57 @@ defineExpose({
</div> </div>
</el-option> </el-option>
</el-select> </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 <el-select
class="sort-type" class="sort-type"
v-model="curComponent.sortField.sortType" v-model="curComponent.sort"
@change="handleFieldChange" @change="handleFieldChange"
> >
<el-option label="升序" value="asc" /> <el-option label="升序" value="asc" />

View File

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

View File

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

View File

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

View File

@ -9,7 +9,8 @@ import {
nextTick, nextTick,
onBeforeMount, onBeforeMount,
provide, provide,
h h,
unref
} from 'vue' } from 'vue'
import Icon from '@/components/icon-custom/src/Icon.vue' import Icon from '@/components/icon-custom/src/Icon.vue'
import type { FormInstance, FormRules } from 'element-plus-secondary' import type { FormInstance, FormRules } from 'element-plus-secondary'
@ -289,7 +290,14 @@ const realQuota = computed(() => {
provide('quotaData', realQuota) provide('quotaData', realQuota)
const startToMove = (e, item) => { 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 = () => { const dimensionItemChange = () => {
@ -785,7 +793,6 @@ const onChangeYAxisForm = val => {
} }
const onChangeYAxisExtForm = val => { const onChangeYAxisExtForm = val => {
console.log('onChangeYAxisExtForm', val)
view.value.customStyle.yAxisExt = val view.value.customStyle.yAxisExt = val
renderChart(view.value) renderChart(view.value)
} }
@ -1297,8 +1304,54 @@ const setActiveCtrl = (ele, type = 'dimension') => {
activeChild.value.push(ele) 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 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) => { const dragStart = (e: DragEvent) => {
isDrag.value = true isDrag.value = true
setTimeout(() => { setTimeout(() => {
@ -1313,7 +1366,6 @@ const singleDragStart = (e: DragEvent, ele, type) => {
if (!activeChild.value.length) { if (!activeChild.value.length) {
activeChild.value = [ele] activeChild.value = [ele]
} }
dragStart(e)
} }
const dragEnd = () => { const dragEnd = () => {
@ -2187,11 +2239,12 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
@click.ctrl="setActiveCtrl(element)" @click.ctrl="setActiveCtrl(element)"
@click.meta="setActiveCtrl(element)" @click.meta="setActiveCtrl(element)"
@click.exact="setActive(element)" @click.exact="setActive(element)"
@dragstart="$event => singleDragStart($event, element, 'dimension')" @click.shift="setActiveShift(element)"
@dragstart="$event => singleDragStartD($event, element, 'dimension')"
:draggable="true" :draggable="true"
@dragend="singleDragEnd" @dragend="singleDragEnd"
class="item" class="item"
v-for="element in state.dimensionData" v-for="element in dimensionData"
:key="element.id" :key="element.id"
> >
<div <div
@ -2241,7 +2294,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
</el-dropdown> </el-dropdown>
</div> </div>
<div <div
@dragstart="dragStart" @dragstart="dragStartD"
:draggable="true" :draggable="true"
@dragend="dragEnd" @dragend="dragEnd"
v-if="activeDimension.map(itx => itx.id).includes(element.id)" 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.ctrl="setActiveCtrl(element, 'quota')"
@click.meta="setActiveCtrl(element, 'quota')" @click.meta="setActiveCtrl(element, 'quota')"
@click.exact="setActive(element, 'quota')" @click.exact="setActive(element, 'quota')"
@click.shift="setActiveShift(element, 'quota')"
class="item" class="item"
@dragstart="$event => singleDragStart($event, element, 'quota')" @dragstart="$event => singleDragStart($event, element, 'quota')"
:draggable="true" :draggable="true"