Vue v-data-table Package 器组件具有相同列值名称的问题

lztngnrs  于 2023-08-07  发布在  Vue.js
关注(0)|答案(1)|浏览(169)

我的v-data-table Package 器组件:

<template>
  <v-data-table
    v-model="selected"
    :headers="headers"
    :items="remappedItems"
    :page="state.currentPage"
    :items-per-page="state.pageSize"
    :height="state.tableHeight"
    :server-items-length="isServerSide ? totalItems : -1"
    v-bind="{ ...$props, ...$attrs }"
    :sort-by="sortOptions.sortBy"
    :sort-desc="sortOptions.sortDesc"
    must-sort
    hide-default-footer
    @click:row="$emit('click:row', $event)"
    @update:options="handleTableOptions"
  >
    <template v-for="header in headers" #[`item.${header.value}`]="{ item }">
      <div :key="header.value" :style="{ width: header.width }">
        <!-- RENDER custom slot -->
        <template v-if="header.type === 'slot'">
          <slot :name="header.value" :row="item" />
        </template>
        <!-- Normal rendering -->
        <template v-else>
          <div v-if="header.type === 'link'">
            <nuxt-link
              v-if="header.customProps"
              v-bind="header.customProps(item)"
            >
              {{ item[header.value] }}
            </nuxt-link>
          </div>

          <div v-else-if="header.type === 'date'">
            {{
              formatDateTime(item[header.value], {
                format: header.dateFormat,
              }) || item[header.value]
            }}
          </div>

          <div v-else-if="header.type === 'checkbox'">
            <v-checkbox
              :input-value="getCheckboxValue(item[header.value])"
              readonly
              v-bind="header.customProps"
            />
          </div>

          <div v-else-if="header.type === 'button'">
            <ConsBtn
              v-bind="header.customProps"
              @click="header.clickEvent ? header.clickEvent(item) : null"
            >
              {{ item[header.value] }}
            </ConsBtn>
          </div>

          <!-- eslint-disable-next-line vue/no-v-html -->
          <div v-else-if="header.isHtml" v-html="item[header.value]" />

          <div v-else>
            {{ item[header.key || header.value] }}
          </div>
        </template>
      </div>
    </template>

    <template v-if="!props.hideFooter" #footer>
      <v-row ref="footer" align="center" class="grey--text text--darken-2">
        <v-col v-if="totalItems > state.pageSize" cols="auto">
          <v-pagination
            v-model="state.currentPage"
            :length="pageCount"
            :total-visible="totalVisiblePages"
          />
        </v-col>
        <v-col class="d-flex align-center">
          <div v-if="totalItems > state.pageSize">
            Записи {{ range }} из {{ totalItems }}
          </div>
          <div v-else>Записей {{ totalItems }}</div>

          <div class="ml-auto d-flex align-center sizer">
            <span class="mr-3">Показывать по</span>
            <v-select
              v-model="state.pageSize"
              :items="[5, 10, 20, 50, 100, 300]"
              solo
              dense
              hide-details
            />
          </div>
        </v-col>
      </v-row>
    </template>
  </v-data-table>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, reactive } from '@nuxtjs/composition-api'
import { useDate } from '@web-aml-v2/shared/composables'
import { get } from 'lodash'

type ColumnType = 'link' | 'date' | 'checkbox' | 'button' | 'slot'

interface Props {
  headers?: Array<{
    key: string
    text: string
    value: string
    type: ColumnType
    dateFormat: string
    customProps?: (item: Record<string, unknown>) => Record<string, unknown>
    clickEvent?: (item: Record<string, unknown>) => void
    formatter?: (value: unknown) => unknown
    isHtml?: boolean
    width?: string
  }>
  items?: Array<Record<string, unknown>>
  totalItems?: number
  totalVisiblePages?: number
  defaultPageSize?: number
  isServerSide?: boolean
  hideFooter?: boolean
  sort?: string | null
  value?: any[]
}

const props = withDefaults(defineProps<Props>(), {
  headers: () => [],
  items: () => [],
  totalItems: 0,
  totalVisiblePages: 10,
  defaultPageSize: 10,
  isServerSide: true,
  hideFooter: false,
  sort: null,
  value: () => [],
})

const { formatDateTime } = useDate()

const emit = defineEmits(['update:options', 'input'])

interface State {
  currentPage: number
  pageSize: number
  previousPageSize: number
  currentSort: string | null
  tableHeight: string
}

const defaultState = {
  currentPage: 1,
  pageSize: props.defaultPageSize,
  previousPageSize: props.defaultPageSize,
  currentSort: null,
  tableHeight: 'auto',
}

const state = reactive<State>({ ...defaultState })
const isMounted = ref(false)

const remappedItems = computed(() => {
  function getItemValue(
    value: unknown,
    formatter?: (value: unknown) => unknown
  ) {
    if (formatter) {
      return formatter(value)
    }
    return value === null || value === undefined || value === '' ? '—' : value
  }

  return props.items?.map((item) => {
    const newItem: Record<string, unknown> = { ...item }

    // remove useless links array
    if ('links' in newItem) {
      delete newItem.links
    }

    props.headers?.forEach((header) => {
      const key = header.key || header.value
      newItem[key] = getItemValue(get(item, header.value), header.formatter)
      header.value = key
    })
    return newItem
  })
})

function getCheckboxValue(value: unknown) {
  return Boolean(value) && value !== '—'
}

const range = computed(() => {
  const start = (state.currentPage - 1) * state.pageSize
  let end = start + state.pageSize
  if (end > props.totalItems) {
    end = props.totalItems
  }
  return `${start + 1} - ${end}`
})

const pageCount = computed(() => {
  return Math.ceil(props.totalItems / state.pageSize)
})

const selected = computed({
  get() {
    return props.value || []
  },
  set(val) {
    emit('input', val)
  },
})

/**
 * Sorting logic
 */
const sortOptions = computed(() => {
  const [sortBy = '', sortDesc = 'asc'] = props.sort
    ? props.sort.split(',')
    : []
  return {
    sortBy,
    sortDesc: sortDesc === 'desc',
  }
})

function handleTableOptions(options: {
  page: number
  itemsPerPage: number
  sortBy: string[]
  sortDesc: boolean[]
}) {
  if (!isMounted.value) {
    return
  }

  const { sortBy, sortDesc } = options

  // Handle page reset when itemsPerPage changes
  if (state.pageSize !== state.previousPageSize) {
    state.currentPage = 1
    state.previousPageSize = state.pageSize
  }

  // Handle sort changes
  if (sortBy.length) {
    const sortOrder = sortDesc[0] ? 'desc' : 'asc'
    const sortString = `${sortBy[0]},${sortOrder}`
    if (state.currentSort !== sortString) {
      state.currentSort = sortString
    }
  }

  emit('update:options', {
    page: state.currentPage - 1,
    size: state.pageSize,
    sort: state.currentSort,
  })
}

onMounted(() => {
  isMounted.value = true
})

function resetState() {
  Object.assign(state, defaultState)
}

defineExpose({
  resetState,
})
</script>
</style>

字符串
使用此表格组件:

const headers = computed(() => {
  return [
          {
        text: 'Refuse1',
        value: 'refuseCodeReason',
      },
      {
        text: 'Refuse2',
        value: 'refuseCodeReason',
        key: 'refuseCodeReasonFormatted',
        formatter: (value: string) => {
          return (
            dictStore.formattedDictionaries[271]?.find(
              (item) => item.code === value
            )?.name || '—'
          )
        },
      }
  ]
})
<CustomTable :headers="headers" />


dictStore.formattedDictionaries[271]只是一个计算方法,其值存储在字典中,而不是从API中获取,它是静态数据。正如你所看到的,我在表中有2列,但是最新的一列应用了formatter函数,因此它用它的格式化值覆盖了前一列。有办法解决吗?

z9ju0rcb

z9ju0rcb1#

由于v-data-table中的两列具有相同的value属性值,这意味着它们绑定到数据源中的相同变量。
因此,当您在最新的列配置上应用formatter时。它将更新这两个列,因为它们链接到相同的底层数据,因为Vuetify中的格式化程序对绑定属性的值进行操作,并且由于您的两个列绑定到相同的属性变量,因此一个值的任何更改都将反映在两个列的值中。
要摆脱这个问题,你必须确保两列value属性绑定到数据源中的单独变量.
示例**:**

const headers = computed(() => {
  return [
          {
        text: 'Refuse1',
        value: 'refuseCodeReason',
      },
      {
        text: 'Refuse2',
        value: 'refuseCodeReason1', // You have to assign a separate variable here
        key: 'refuseCodeReasonFormatted',
        formatter: (value: string) => {
          return (
            dictStore.formattedDictionaries[271]?.find(
              (item) => item.code === value
            )?.name || '—'
          )
        },
      }
  ]
})

字符串

相关问题