diff --git a/core/core-frontend/src/api/dataset.ts b/core/core-frontend/src/api/dataset.ts index d0e07760c7..df843b335b 100644 --- a/core/core-frontend/src/api/dataset.ts +++ b/core/core-frontend/src/api/dataset.ts @@ -293,3 +293,9 @@ export const listByDsIds = async (data): Promise => { return res?.data }) } + +export const getFieldTree = async (data): Promise => { + return request.post({ url: 'datasetData/getFieldTree', data }).then(res => { + return res?.data + }) +} diff --git a/core/core-frontend/src/custom-component/v-query/ConditionDefaultConfiguration.vue b/core/core-frontend/src/custom-component/v-query/ConditionDefaultConfiguration.vue index 00dba34bfc..a1e4da2c58 100644 --- a/core/core-frontend/src/custom-component/v-query/ConditionDefaultConfiguration.vue +++ b/core/core-frontend/src/custom-component/v-query/ConditionDefaultConfiguration.vue @@ -8,6 +8,7 @@ import DynamicTime from '@/custom-component/v-query/DynamicTime.vue' import DynamicTimeRange from '@/custom-component/v-query/DynamicTimeRange.vue' import Time from '@/custom-component/v-query/Time.vue' import Select from '@/custom-component/v-query/Select.vue' +import Tree from '@/custom-component/v-query/Tree.vue' const { t } = useI18n() const visiblePopover = ref(false) @@ -29,6 +30,8 @@ const filterTypeCom = computed(() => { ? DynamicTime : DynamicTimeRange : Time + : '9' === displayType + ? Tree : Select }) @@ -191,10 +194,12 @@ const multipleChange = (val: boolean, isMultipleChange = false) => { : [] : defaultValue } + if (curComponent.value.field.deType === 1) { curComponent.value.multiple = val return } + curComponent.value.multiple = val } diff --git a/core/core-frontend/src/custom-component/v-query/QueryConditionConfiguration.vue b/core/core-frontend/src/custom-component/v-query/QueryConditionConfiguration.vue index 35e92e3b0a..6c61f8942b 100644 --- a/core/core-frontend/src/custom-component/v-query/QueryConditionConfiguration.vue +++ b/core/core-frontend/src/custom-component/v-query/QueryConditionConfiguration.vue @@ -23,6 +23,7 @@ import { ElMessage, ElSelect } from 'element-plus-secondary' import type { DatasetDetail } from '@/api/dataset' import { getDsDetailsWithPerm, getSqlParams, listFieldsWithPermissions } from '@/api/dataset' import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue' +import TreeFieldDialog from '@/custom-component/v-query/TreeFieldDialog.vue' import { cloneDeep } from 'lodash-es' import { getDatasetTree } from '@/api/dataset' import { Tree } from '@/views/visualized/data/dataset/form/CreatDsGroup.vue' @@ -176,6 +177,23 @@ const showTypeError = computed(() => { return displayTypeField !== field?.deType }) }) + +const showDatasetError = computed(() => { + if (!curComponent.value) return false + if (!curComponent.value.checkedFields?.length) return false + if (!fields.value?.length) return false + let displayField = null + return curComponent.value.checkedFields.some(id => { + const arr = fields.value.find(itx => itx.componentId === id) + const field = arr.id + if (!field) return false + if (displayField === null) { + displayField = field + return false + } + return displayField !== field + }) +}) const typeList = [ { label: '重命名', @@ -234,10 +252,12 @@ const setType = () => { if (field?.deType !== undefined) { let displayType = curComponent.value.displayType + if (curComponent.value.displayType === '9') { + return + } if (!(field?.deType === 1 && curComponent.value.displayType === '7')) { curComponent.value.displayType = `${[3, 4].includes(field?.deType) ? 2 : field?.deType}` } - if ( displayType !== curComponent.value.displayType && !([3, 4].includes(+displayType) && +curComponent.value.displayType === 2) @@ -299,10 +319,10 @@ const typeTimeMap = { const timeParameterList = computed(() => { if (!isTimeParameter.value) return timeList - const [_, y] = curComponent.value.parameters?.filter( + const [year, y] = curComponent.value.parameters?.filter( ele => ele.deType === 1 && !!ele.variableName )[0].type - return timeList.filter(ele => ele.value === typeTimeMap[y]) + return timeList.filter(ele => ele.value === (typeTimeMap[y] || typeTimeMap[year])) }) const cancelClick = () => { @@ -344,7 +364,26 @@ const handleValueSourceChange = () => { } const multipleChange = (val: boolean, isMultipleChange = false) => { - defaultConfigurationRef.value?.multipleChange(val, isMultipleChange) + if (isMultipleChange) { + curComponent.value.defaultValue = val ? [] : undefined + } + const { defaultValue } = curComponent.value + if (Array.isArray(defaultValue)) { + curComponent.value.selectValue = val ? defaultValue : undefined + } else { + curComponent.value.selectValue = val + ? defaultValue !== undefined + ? [defaultValue] + : [] + : defaultValue + } + + if (curComponent.value.field.deType === 1) { + curComponent.value.multiple = val + return + } + + curComponent.value.multiple = val } const isInRange = (ele, startWindowTime, timeStamp) => { @@ -461,6 +500,8 @@ const openCascadeDialog = () => { cascadeDialog.value.init(cascadeMap) } +const indexCascade = ' 一二三四五' + const validateConditionType = ({ defaultConditionValueF, defaultConditionValueS, @@ -612,12 +653,16 @@ const validate = () => { return false } - if (ele.optionValueSource === 2 && !ele.valueSource?.filter(ele => !!ele).length) { + if ( + ele.displayType !== '9' && + ele.optionValueSource === 2 && + !ele.valueSource?.filter(ele => !!ele).length + ) { ElMessage.error('手工输入-选项值不能为空') return true } - if (ele.optionValueSource === 1 && !ele.field.id) { + if (ele.displayType !== '9' && ele.optionValueSource === 1 && !ele.field.id) { ElMessage.error('请选择数据集的选项值字段') return true } @@ -829,7 +874,8 @@ const parameterCompletion = () => { timeNumRange: 0, relativeToCurrentTypeRange: 'year', aroundRange: 'f' - } + }, + treeFieldList: [] } Object.entries(attributes).forEach(([key, val]) => { !curComponent.value[key] && (curComponent.value[key] = val) @@ -888,6 +934,18 @@ const getOptions = (id, component) => { }) } +const treeDialog = ref() +const startTreeDesign = () => { + treeDialog.value.init( + curComponent.value.dataset?.fields.filter( + ele => ele.deType === +curComponent.value.field.deType + ), + curComponent.value.treeFieldList + ) +} +const saveTree = arr => { + curComponent.value.treeFieldList = arr +} const showError = computed(() => { if (!curComponent.value) return false const { optionValueSource, checkedFieldsMap, checkedFields, field, valueSource, displayType } = @@ -899,6 +957,20 @@ const showError = computed(() => { if ([1, 7, 8].includes(+displayType)) { return false } + + if (displayType === '9') { + let displayField = null + return checkedFields.some(id => { + const arr = fields.value.find(itx => itx.componentId === id) + const field = arr.id + if (!field) return false + if (displayField === null) { + displayField = field + return false + } + return displayField !== field + }) + } return (optionValueSource === 1 && !field.id) || (optionValueSource === 2 && !valueSource.length) }) const handleDialogClick = () => { @@ -1313,7 +1385,10 @@ defineExpose({ label="必填项" /> -
+
展示类型
@@ -1323,15 +1398,20 @@ defineExpose({ v-model="curComponent.displayType" > +
+
+
+ 下拉树结构设计 + + + +
+
+ + + + 点击进行树结构设计 + +
+ +
时间粒度
@@ -1391,7 +1507,7 @@ defineExpose({
选项值来源
@@ -1680,6 +1796,9 @@ defineExpose({
+
+ +
@@ -1874,6 +1993,7 @@ defineExpose({ display: flex; align-items: center; margin-bottom: 8px; + .ed-checkbox__label { display: inline-flex; align-items: center; @@ -1970,6 +2090,41 @@ defineExpose({ justify-content: space-between; margin-bottom: 10.5px; flex-wrap: wrap; + .search-tree { + width: 100%; + height: 200px; + margin-top: 8px; + position: relative; + padding: 16px; + box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12); + + .ed-button { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } + + .tree-field { + display: flex; + align-items: center; + margin-bottom: 16px; + .level-index { + margin-right: 40px; + } + + .field-type { + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + } + + .field-tree_name { + margin-left: 8px; + } + } + } .setting-content { width: 100%; diff --git a/core/core-frontend/src/custom-component/v-query/StyleInject.vue b/core/core-frontend/src/custom-component/v-query/StyleInject.vue index b149f50cf8..e879532c6a 100644 --- a/core/core-frontend/src/custom-component/v-query/StyleInject.vue +++ b/core/core-frontend/src/custom-component/v-query/StyleInject.vue @@ -3,6 +3,7 @@ import { provide, PropType } from 'vue' import Select from './Select.vue' import Time from './Time.vue' import TextSearch from './TextSearch.vue' +import Tree from './Tree.vue' interface SelectConfig { selectValue: any @@ -55,7 +56,7 @@ const filterTypeCom = (displayType: string) => { if (displayType === '8') { return TextSearch } - return ['1', '7'].includes(displayType) ? Time : Select + return ['1', '7'].includes(displayType) ? Time : displayType === '9' ? Tree : Select } provide('$custom-style-filter', props.customStyle) diff --git a/core/core-frontend/src/custom-component/v-query/Tree.vue b/core/core-frontend/src/custom-component/v-query/Tree.vue new file mode 100644 index 0000000000..e8aaa653ad --- /dev/null +++ b/core/core-frontend/src/custom-component/v-query/Tree.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/core/core-frontend/src/custom-component/v-query/TreeFieldDialog.vue b/core/core-frontend/src/custom-component/v-query/TreeFieldDialog.vue new file mode 100644 index 0000000000..0cd7fdb4f7 --- /dev/null +++ b/core/core-frontend/src/custom-component/v-query/TreeFieldDialog.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/core/core-frontend/src/hooks/web/useFilter.ts b/core/core-frontend/src/hooks/web/useFilter.ts index c1273a2c82..9a49209380 100644 --- a/core/core-frontend/src/hooks/web/useFilter.ts +++ b/core/core-frontend/src/hooks/web/useFilter.ts @@ -76,6 +76,15 @@ const getDayEnd = timestamp => { return [+new Date(timestamp), +new Date(timestamp) + 60 * 1000 * 60 * 24 - 1000] } +const getFieldId = (arr, result) => { + const [obj] = result + const idArr = obj.split(',') + return arr + .map(ele => ele.id) + .slice(0, idArr.length) + .join(',') +} + const getValueByDefaultValueCheckOrFirstLoad = ( defaultValueCheck: boolean, defaultValue: any, @@ -88,6 +97,20 @@ const getValueByDefaultValueCheckOrFirstLoad = ( displayType: string, displayId: string ) => { + if (+displayType === 9) { + if (firstLoad) { + return defaultValueCheck + ? multiple + ? defaultValue.map(ele => ele.split('-de-').join(',')) + : (defaultValue || '').split('-de-').join(',') + : [] + } + return selectValue?.length + ? multiple + ? selectValue.map(ele => ele.split('-de-').join(',')) + : (selectValue || '').split('-de-').join(',') + : [] + } if ( optionValueSource === 1 && (defaultMapValue?.length || displayId) && @@ -152,6 +175,9 @@ const getOperator = ( conditionValueS, firstLoad ) => { + if (+displayType === 9) { + return multiple ? 'in' : 'eq' + } const valueF = firstLoad ? defaultConditionValueF : conditionValueF const valueS = firstLoad ? defaultConditionValueS : conditionValueS const operatorF = firstLoad ? defaultConditionValueOperatorF : conditionValueOperatorF @@ -186,9 +212,8 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa const { selectValue: value, timeGranularityMultiple, - parametersStart, - parametersEnd, conditionType = 0, + treeFieldList = [], defaultConditionValueOperatorF = 'eq', defaultConditionValueF = '', defaultConditionValueOperatorS = 'like', @@ -204,13 +229,14 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa defaultMapValue, mapValue, parameters = [], - isTree = false, timeGranularity = 'date', displayType, displayId, multiple } = item + const isTree = +displayType === 9 + if (timeType === 'dynamic' && [1, 7].includes(+displayType) && firstLoad) { if (+displayType === 1) { selectValue = getDynamicRange(item) @@ -303,7 +329,9 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa if (result?.length) { filter.push({ componentId: ele.id, - fieldId: item.checkedFieldsMap[curComponentId], + fieldId: isTree + ? getFieldId(treeFieldList, result) + : item.checkedFieldsMap[curComponentId], operator, value: result, parameters,