123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- <template>
- <div>
- <div v-if="isTools" class="text-end mb-3">
- <v-btn class="ml-2" color="primary" @click="emit('add')">
- <v-icon left>mdi-plus</v-icon>
- {{ t('common.add') }}
- <!-- 新增 -->
- </v-btn>
- <slot name="addToTools"></slot>
- </div>
- <v-data-table
- ref="table"
- v-model="selected"
- :class="`elevation-${elevation} ${showFixedLastItem ? 'fixed-last-item' : ''}`"
- :headers="headers"
- :items="items"
- :item-key="itemKey"
- :show-select="showSelect"
- :loading="loading"
- :select-strategy="selectStrategy"
- :return-object="returnObject"
- color="primary"
- hover
- :height="height"
- hide-default-footer
- loading-text="Loading... Please wait"
- fixed-header
- :disable-sort="disableSort"
- :items-per-page="itemsPerPage"
- :no-data-text="noDataText || t('common.noData')"
- :hide-default-header="hideDefaultHeader"
- @update:modelValue="handleSelect"
- >
- <template v-for="name in itemSlot" v-slot:[`item.${name}`]="slotProps">
- <slot :name="name" v-bind="slotProps"></slot>
- </template>
- <template v-for="name in headerSlot" v-slot:[`${name}`]="slotProps">
- <slot :name="name" v-bind="slotProps"></slot>
- </template>
- <template v-if="!Object.keys(slot).includes('actions')" v-slot:[`item.actions`]="{ item }">
- <td>
- <v-btn variant="text" color="primary" @click="edit(item)">{{ t('common.edit') }}</v-btn>
- <v-btn variant="text" color="error" @click="del(item)">{{ t('common.delete') }}</v-btn>
- </td>
- </template>
- <template #bottom>
- <div v-if="showPage && total > 0">
- <v-divider></v-divider>
- <CtPagination :total="total" :page="pageInfo.pageNo" :limit="pageInfo.pageSize" @handleChange="handleChangePage"></CtPagination>
- </div>
- </template>
- </v-data-table>
- </div>
- </template>
- <script setup>
- defineOptions({ name: 'CtTable'})
- import { ref, computed, useSlots, watch, onMounted } from 'vue'
- import { useI18n } from '@/hooks/web/useI18n'
- const { t } = useI18n()
- const selected = ref([])
- const emit = defineEmits(['pageHandleChange', 'del', 'edit', 'add', 'selected'])
- const props = defineProps({
- modelValue: Array,
- elevation: {
- type: [Number, String],
- default: 0
- },
- itemKey: {
- type: String,
- default: 'id'
- },
- disableSort: {
- type: Boolean,
- default: true
- },
- // 展示列表所有数据
- itemsPerPage: {
- type: Number,
- default: -1
- },
- hideDefaultHeader: {
- type: Boolean,
- default: false
- },
- showPage: {
- type: Boolean,
- default: true
- },
- loading: {
- type: Boolean,
- default: true
- },
- headers: {
- type: Array,
- default: () => []
- },
- items: {
- type: Array,
- default: () => []
- },
- total: {
- type: [String, Number],
- default: 0
- },
- pageInfo: {
- type: Object,
- default: () => ({
- size: 10
- })
- },
- isTools: {
- type: Boolean,
- default: true
- },
- height: {
- type: [String, Number],
- default: ''
- },
- noDataText: {
- type: String,
- default: ''
- },
- showSelect: {
- type: Boolean,
- default: false
- },
- returnObject: {
- type: Boolean,
- default: false
- },
- selectStrategy: {
- type: String,
- default: 'single'
- },
- // 是否将最后一项固定在表格右侧
- showFixedLastItem: {
- type: Boolean,
- default: true
- }
- })
- watch(() => props.modelValue, (val) => {
- selected.value = val
- }, { deep: true, immediate: true })
- const table = ref()
- const slot = useSlots()
- const itemSlot = computed(() => {
- return Object.keys(slot).filter(key => {
- const data = key.split('.')
- return data.length === 1
- })
- })
- const headerSlot = computed(() => {
- return Object.keys(slot).filter(key => {
- const data = key.split('.')
- return data.length === 2 && data[0] === 'header'
- })
- })
- onMounted(() => {
- const wrapper = table.value.$el.querySelector('.v-table__wrapper');
-
- const observer = new ResizeObserver(() => {
- if (wrapper.scrollWidth > wrapper.clientWidth) {
- wrapper.classList.add('hasScroll');
- } else {
- wrapper.classList.remove('hasScroll');
- }
- });
- observer.observe(wrapper);
- });
- const edit = (item) => {
- emit('edit', item)
- }
- const del = (item) => {
- emit('del', item)
- }
- const handleChangePage = (e) => {
- emit('pageHandleChange', e)
- }
- const handleSelect = (e) => {
- emit('selected', e)
- }
- </script>
- <style scoped lang="scss">
- :deep(.v-table.v-table--fixed-header > .v-table__wrapper > table > thead > tr > th) {
- text-wrap: nowrap !important;
- background-color: #f7f8fa !important;
- }
- :deep(.v-selection-control__input) {
- color: var(--v-primary-base) !important;
- }
- :deep(.v-table.v-table--hover > .v-table__wrapper > table > tbody > tr > td) {
- white-space: nowrap !important;
- }
- :deep(table > thead > tr > th:last-child) {
- border-bottom: 1px solid #e0e0e0 !important;
- }
- .fixed-last-item {
- :deep(.v-table__wrapper) { position: relative; }
- :deep(.v-table__wrapper::-webkit-scrollbar:horizontal) { height: 8px; }
- :deep(.v-table__wrapper:not(:hover)::-webkit-scrollbar:horizontal) { display: none; }
- :deep(.v-table__wrapper:not(:hover), .v-table__wrapper::-webkit-scrollbar-thumb:horizontal) { background: transparent; }
- :deep(table > tbody > tr > td:last-child) {
- position: sticky !important;
- position: -webkit-sticky !important;
- right: 0;
- z-index: 1;
- background: white !important;
- box-shadow: none;
- }
- :deep(table > thead > tr > th:last-child) {
- position: sticky !important;
- position: -webkit-sticky !important;
- right: 0;
- z-index: 1;
- background: white !important;
- box-shadow: none;
- }
- :deep(.v-table__wrapper.hasScroll table > tbody > tr > td:last-child) {
- border-left: 1px solid #e0e0e0 !important;
- }
- :deep(.v-table__wrapper.hasScroll table > thead > tr > th:last-child) {
- border-left: 1px solid #e0e0e0 !important;
- }
- // :deep {
- // .v-table__wrapper {
- // position: relative;
- // &::-webkit-scrollbar:horizontal {
- // height: 8px;
- // }
- // &:not(:hover)::-webkit-scrollbar:horizontal {
- // display: none;
- // }
- // }
- // .v-table__wrapper:not(:hover),
- // .v-table__wrapper::-webkit-scrollbar-thumb:horizontal {
- // background: transparent;
- // }
- // table > tbody > tr > td:last-child,
- // table > thead > tr > th:last-child {
- // position: sticky !important;
- // position: -webkit-sticky !important;
- // right: 0;
- // z-index: 1;
- // background: white !important;
- // box-shadow: none;
- // }
- // .v-table__wrapper.hasScroll {
- // table > tbody > tr > td:last-child,
- // table > thead > tr > th:last-child {
- // border-left: 1px solid #e0e0e0 !important;
- // // box-shadow: inset 10px 0 10px -10px rgba(0, 0, 0, .35) !important;
- // }
- // // table > thead > tr > th:last-child {
- // // z-index: 10 !important;
- // // // box-shadow: inset 10px 0 10px -10px rgba(0, 0, 0, .35) !important;
- // // border-bottom: 1px solid #e0e0e0 !important;
- // // }
- // }
- // }
- }
- </style>
|