DdataeaseShufix: 样式优化
6b3f1a84创建于 4月17日历史提交
<script lang="ts" setup>
import icon_info_colorful from '@/assets/svg/icon_info_colorful.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
import joinJoin from '@/assets/svg/join-join.svg'
import { ref, shallowRef } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { ElMessage } from 'element-plus-secondary'
import { guid } from '@/views/visualized/data/dataset/form/util.js'
import { listByDsIds } from '@/api/dataset'
import { cloneDeep } from 'lodash-es'
interface Cascade {
  datasetId: string
  name: string
  queryId: string
  deType: string
  fieldId: string
  isTree: boolean
}

type cascadeMap = Record<string, Cascade>
const { t } = useI18n()
let deTypeMap = shallowRef({})
const emits = defineEmits(['saveCascade'])

const dialogVisible = ref(false)
const handleBeforeClose = () => {
  dialogVisible.value = false
}
const cascadeList = ref([])
const optionsMap = shallowRef({})
const datasetMap = shallowRef([])
const cancelClick = () => {
  handleBeforeClose()
}

const confirmClick = () => {
  const { isError, arr } = setCascadeArrBack()
  if (isError) {
    ElMessage.error(t('v_query.cannot_be_empty'))
    return
  }
  emits('saveCascade', arr)
  handleBeforeClose()
}

const setCascadeArrBack = () => {
  let isError = false
  const arr = cloneDeep(cascadeList.value)
  arr.forEach(ele => {
    ele.forEach(item => {
      if (!item.placeholder && !item.fieldId) {
        isError = true
      }
      if (!item.datasetId) {
        isError = true
      }
      item.selectValue = []
    })
  })

  return {
    arr,
    isError
  }
}

const init = (cascadeMap: cascadeMap, arr) => {
  cascadeList.value = cloneDeep(arr)
  datasetMap.value = Object.values(cascadeMap).map(ele => ({
    label: ele.name,
    isTree: ele.isTree,
    deType: ele.deType,
    value: `${ele.datasetId}--${ele.queryId}--${ele.fieldId}`
  }))
  let obj = {}
  Object.values(cascadeMap).forEach(ele => {
    obj[`${ele.datasetId}--${ele.queryId}--${ele.fieldId}`] = ele.deType
  })
  deTypeMap.value = obj
  listByDsIds(datasetMap.value.map(ele => ele.value.split('--')[0]))
    .then(res => {
      optionsMap.value = res
    })
    .finally(() => {
      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()])].filter(
      ele => !!ele
    )
  }
}

const addCascadeItem = item => {
  item.push({
    datasetId: '',
    fieldId: '',
    placeholder: item.length ? '' : t('v_query.the_first_level'),
    id: guid()
  })
}

const setPlaceholder = () => {
  cascadeList.value.forEach(ele => {
    ele.forEach((item, idx) => {
      if (idx) {
        item.placeholder = ''
      }
      if (
        item &&
        ele[idx - 1] &&
        item.datasetId &&
        item.datasetId.split('--')[0] === ele[idx - 1].datasetId.split('--')[0]
      ) {
        item.placeholder = t('v_query.configure_cascaded_fields')
        item.fieldId = ''
      }
    })
  })
}

const deleteCascade = (idx, item) => {
  item.splice(idx, 1)
  item[0].fieldId = ''
  item[0].placeholder = t('v_query.the_first_level')
  setPlaceholder()
}

const deleteCascadeBlock = idx => {
  cascadeList.value.splice(idx, 1)
}

const addCascadeBlock = () => {
  const arr = []
  addCascadeItem(arr)
  cascadeList.value.push(arr)
}

const indexNumCascade = [
  t('visualization.number1'),
  t('visualization.number2'),
  t('visualization.number3'),
  t('visualization.number4'),
  t('visualization.number5')
]

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">
        {{ t('v_query.condition_cascade_configuration')
        }}<span style="margin-left: 8px" class="tip">{{ t('v_query.not_reverse_cascade') }}</span>
      </div>
    </template>
    <div class="content">
      <el-icon style="font-size: 16px">
        <Icon name="icon_info_colorful"><icon_info_colorful class="svg-icon" /></Icon>
      </el-icon>
      {{ t('v_query.must_be_met') }}<br />
      {{ t('v_query.select_data_set') }}<br />
    </div>
    <el-button text @click="addCascadeBlock">
      <template #icon>
        <Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon>
      </template>
      {{ t('v_query.add_cascade_configuration') }}
    </el-button>
    <div class="cascade-content" v-for="(item, index) in cascadeList" :key="index">
      <div style="display: flex; align-items: center; justify-content: space-between">
        <el-button :disabled="item.length === 2" text @click="addCascadeItem(item)">
          <template #icon>
            <Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon>
          </template>
          {{ t('v_query.add_cascade_condition') }}
        </el-button>
        <el-button @click="deleteCascadeBlock(index)" class="cascade-delete-block" text>
          <template #icon>
            <Icon name="icon_delete-trash_outlined"
              ><icon_deleteTrash_outlined class="svg-icon"
            /></Icon>
          </template>
        </el-button>
      </div>
      <div class="cascade-item">
        <div class="label">{{ t('v_query.query_condition_level') }}</div>
        <div class="item-name">{{ t('v_query.select_query_condition') }}</div>
        <div class="cascade-icon"></div>
        <div class="item-field">{{ t('v_query.select_cascaded_field') }}</div>
      </div>
      <div class="cascade-item" v-for="(ele, idx) in item" :key="ele.id">
        <div class="label">{{ t('v_query.level_1', { msg: indexNumCascade[idx] }) }}</div>
        <div class="item-name">
          <el-select
            @visible-change="val => visibleChange(val, index, idx)"
            v-model="ele.datasetId"
            @change="setPlaceholder"
            style="width: 300px"
          >
            <el-option
              v-for="itx in datasetMap.filter(ele => (idx === 0 && !ele.isTree) || idx === 1)"
              :key="itx.value"
              :label="itx.label"
              :value="itx.value"
              :disabled="
                (disabledDatasetId.includes(itx.value) &&
                  item.map(ele => ele.datasetId).includes(itx.value)) ||
                (!!ele.datasetId && deTypeMap[ele.datasetId] !== itx.deType)
              "
            />
          </el-select>
        </div>
        <div class="cascade-icon">
          <el-icon>
            <Icon name="join-join"><joinJoin class="svg-icon" /></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.split('--')[0]]"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </div>
        <el-button v-show="idx !== 0" @click="deleteCascade(idx, item)" class="cascade-delete" text>
          <template #icon>
            <Icon name="icon_delete-trash_outlined"
              ><icon_deleteTrash_outlined class="svg-icon"
            /></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: 6px;
    background: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1));
    position: relative;
    padding: 9px 0 9px 40px;
    font-family: var(--de-custom_font, 'PingFang');
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;

    .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-block,
    .cascade-delete {
      width: 40px;
      font-size: 20px;
      color: #646a73;
      margin-left: 20px;
    }
  }
}
</style>