我的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
函数,因此它用它的格式化值覆盖了前一列。有办法解决吗?
1条答案
按热度按时间z9ju0rcb1#
由于
v-data-table
中的两列具有相同的value
属性值,这意味着它们绑定到数据源中的相同变量。因此,当您在最新的列配置上应用
formatter
时。它将更新这两个列,因为它们链接到相同的底层数据,因为Vuetify中的格式化程序对绑定属性的值进行操作,并且由于您的两个列绑定到相同的属性变量,因此一个值的任何更改都将反映在两个列的值中。要摆脱这个问题,你必须确保两列
value
属性绑定到数据源中的单独变量.示例**:**
字符串