ParamsTable.vue 6.39 KB
<script setup lang="ts">
import { getUUID } from '@/utils'
import {
  ButtonProps,
  DataTableColumns,
  InputProps,
  NButton,
  NDataTable,
  NIcon,
  NInput,
  NSpace,
  NTooltip,
  NSelect,
  SelectProps,
  NDatePicker,
  DatePickerProps
} from 'naive-ui'
import { computed, h, ref, unref, watch } from 'vue'
import { Subtract, Add } from '@vicons/carbon'
import { isArray, isObject } from '@/utils/external/is'
import { DataTypeEnum, DataTypeNameEnum } from '@/enums/external/dataTypeEnum'
import { Recordable } from 'vite-plugin-mock'

interface DataSource {
  id: string
  value?: string
  keyName?: string
  indexName?: number
  typeName?: string
  dateRange?: number[] | null
  result?: boolean
}

const columns: DataTableColumns<DataSource> = [
  {
    title: '序号',
    key: 'index',
    render: (_, index) => index + 1,
    width: 50
  },
  {
    title: 'Type',
    key: 'typeName',
    render: (row, index) => {
      return h(NSelect, {
        value: row.typeName,
        options: [
          {
            label: DataTypeNameEnum.DATA_RANGE,
            value: DataTypeEnum.DATA_RANGE
          },
          {
            label: DataTypeNameEnum.DATA_INPUT,
            value: DataTypeEnum.DATA_INPUT
          }
        ],
        onUpdateValue: (value: string) => {
          unref(dataSource)[index].typeName = value
          unref(dataSource)[index].indexName = index
        },
        size: 'small',
        disabled: props.disabled
      } as SelectProps)
    },
    width: 120
  },
  {
    title: 'Key',
    key: 'keyName',
    render: (row, index) => {
      return h(NInput, {
        value: row.keyName,
        onBlur: () => handleInputBlur(),
        onUpdateValue: (value: string) => (unref(dataSource)[index].keyName = value),
        size: 'small',
        disabled: props.disabled
      } as InputProps)
    },
    width: 120
  },
  {
    title: 'Value',
    key: 'value',
    render: (row, index) => {
      const findIndexName = unref(dataSource).find(item => item.indexName === index)
      return findIndexName?.indexName === index && findIndexName.typeName === DataTypeEnum.DATA_INPUT
        ? h(NInput, {
            value: row.value,
            onBlur: () => handleInputBlur(),
            onUpdateValue: (value: string) => (unref(dataSource)[index].value = value),
            size: 'small',
            disabled: props.disabled
          } as InputProps)
        : ''
    },
    width: 120
  },
  {
    title: 'Date',
    key: 'dateRange',
    render: (row, index) => {
      const findIndexName = unref(dataSource).find(item => item.indexName === index)
      return findIndexName?.indexName === index && findIndexName.typeName === DataTypeEnum.DATA_RANGE
        ? h(NDatePicker, {
            value: row.value,
            size: 'small',
            type: 'datetimerange',
            onBlur: () => handleInputBlur(),
            onUpdateValue: (value: number[]) => {
              unref(dataSource)[index].dateRange = value
            },
            disabled: props.disabled
          } as DatePickerProps)
        : ''
    },
    width: 200
  },
  {
    title: '操作',
    key: 'actions',
    render: row => {
      return h(NSpace, () => [
        h(NTooltip, null, {
          trigger: () =>
            h(
              NButton,
              {
                type: 'success',
                size: 'small',
                ghost: true,
                onClick: () => handleAddRow(row),
                disabled: props.disabled || !unref(canAddRow)
              } as ButtonProps,
              {
                default: () => h(NIcon, () => h(Add))
              }
            ),
          default: () => '插入行'
        }),
        h(NTooltip, null, {
          trigger: () =>
            h(
              NButton,
              {
                type: 'warning',
                size: 'small',
                ghost: true,
                onClick: () => handleSubtractRow(row)
              } as ButtonProps,
              {
                default: () => h(NIcon, () => h(Subtract))
              }
            ),
          default: () => '删除行'
        })
      ])
    },
    width: 120
  }
]

const props = withDefaults(
  defineProps<{
    value?: Recordable
    disabled?: boolean
    maxRow?: number
  }>(),
  {
    disabled: false,
    maxRow: 50
  }
)

const emit = defineEmits(['update:value'])

const createNewRow = (index?: number) => {
  return { id: getUUID(), result: true, indexName: index, typeName: DataTypeEnum.DATA_INPUT } as DataSource
}

const dataSource = ref<DataSource[]>([createNewRow(0)])

const blurFlag = ref(false)

watch(
  () => props.value,
  (target: Recordable) => {
    if (target && isObject(target) && Object.keys(target).length) {
      if (blurFlag.value) return
      dataSource.value = Object.keys(props.value || {}).map((keyName, keyIndex) => ({
        ...createNewRow(keyIndex),
        keyName,
        indexName: keyIndex,
        typeName: isArray(props.value![keyName]) ? DataTypeEnum.DATA_RANGE : DataTypeEnum.DATA_INPUT,
        value: Reflect.get(props.value || {}, keyName)
      }))
    }
  },
  {
    immediate: true,
    deep: true
  }
)

const canAddRow = computed(() => {
  return unref(dataSource).length < props.maxRow
})

const handleAddRow = (record: DataSource) => {
  const index = unref(dataSource).findIndex(item => item.id === record.id)
  unref(dataSource).splice(index + 1, 0, createNewRow(index + 1))
}

const handleSubtractRow = (record: DataSource) => {
  const index = unref(dataSource).findIndex(item => item.id === record.id)
  if (unref(dataSource).length === 1) {
    unref(dataSource)[0].keyName = ''
    unref(dataSource)[0].value = ''
    unref(dataSource)[0].dateRange = []
    emit('update:value', {})
  } else {
    unref(dataSource).splice(index, 1)
    emit('update:value', getHeaderConfiguration())
  }
}

const handleInputBlur = () => {
  blurFlag.value = true
  emit('update:value', getHeaderConfiguration())
}

const getHeaderConfiguration = () => {
  return unref(dataSource).reduce((prev, next) => {
    const { value, keyName, dateRange } = next
    const header = keyName ? { [keyName as unknown as string]: dateRange && isArray(dateRange) ? dateRange: value } : {}
    for (let item in header) if (!Reflect.get(header, item)) Reflect.deleteProperty(header, item)
    return { ...prev, ...header }
  }, {} as Recordable)
}
</script>

<template>
  <NDataTable size="small" :columns="columns" :row-key="rowData => rowData.id" :data="dataSource" max-height="300" />
</template>