vue封装select组件,封装成带有表格分页的组件

142 阅读3分钟

众所周知,vue中自带的select组件是单列并且没有分页的组件,但是在实际项目中,经常用到需要分页的select组件,甚至是select下拉选项是表格分页的组件。

以下是我简单的封装select的组件,使其变成了带有表格分页的组件,最终如下图所示:

image.png

废话不多说,直接上代码吧,先说明上面图片的颜色是项目的样式统一封装了,这里不贴出样式代码了。

第一:先封装一个分页组件

<template>
  <div :class="{ hidden: hidden }" class="pagination-container">
    <el-pagination
      :small="small"
      style="float: right"
      :background="background"
      :current-page="currentPage"
      :page-sizes="[10, 30, 50]"
      :page-size="currentLimit"
      :layout="layout"
      :total="total || 0"
      :disabled="loading"
      @current-change="handleCurrentChange"
      @size-change="handleSizeChange"
    />
    <el-button
      v-show="refreshBtn"
      :loading="loading"
      plain
      type="primary"
      :small="small"
      style="margin-left: 20px"
      :class="{ marginA: !small, marginB: small }"
      @click="handleRefresh"
    >
      <i-ic:outline-loop />
    </el-button>
  </div>
</template>
<script lang="ts" setup name="Pagination">
// name: 'Pagination',
const props = defineProps({
  disabled: {
    type: Boolean,
    default: false,
  },
  small: {
    type: Boolean,
    default: true,
  },
  refreshBtn: {
    type: Boolean,
    default: false,
  },
  loading: {
    type: Boolean,
    default: false,
  },
  total: {
    required: true,
    type: Number,
  },
  page: {
    type: Number,
    default: 1,
  },
  limit: {
    type: Number,
    default: 50,
  },
  layout: {
    type: String,
    default: 'total, sizes, prev, pager, next,jumper',
  },
  background: {
    type: Boolean,
    default: true,
  },
  hidden: {
    type: Boolean,
    default: false,
  },
  pagesizes: {
    type: Array,
    default: function () {
      return [10, 30, 50]
    },
  },
  pagerCount: {
    type: Number,
    default: function () {
      return 11
    },
  },
})
// 'page', 'limit',
const emit = defineEmits(['update:page', 'update:limit', 'pagination'])
// computed
const currentPage = computed(() => {
  return props.page
})
const currentLimit = computed(() => {
  return props.limit
})
// function
function handleRefresh() {
  emit('pagination', {
    refresh: true,
    toPage: true,
    page: props.page,
    limit: props.limit,
    isPrev: false,
    isNext: false,
  })
}
function handleCurrentChange(val) {
  emit('update:page', val)
  emit('pagination', {
    toPage: true,
    page: val,
    limit: props.limit,
    isPrev: false,
    isNext: false,
  })
}
function handleSizeChange(val) {
  emit('update:limit', val)
  emit('pagination', {
    toPage: true,
    page: 1,
    limit: val,
    isPrev: false,
    isNext: false,
  })
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.marginA {
  margin-top: 6px;
}
.marginB {
  margin-top: 0px;
}
.pagination-container {
  /* padding: 32px 16px; */
  margin: 0px;
  padding: 0px;
  padding-top: 10px;
  // padding-left: 10px;
  // height: 47px;
  /* border: 1px solid #ebeef5; */
}
.pagination-container.hidden {
  display: none;
}
</style>

第二封装select组件

<template>
  <el-container>
    <el-select
      v-model="modelValueName"
      placeholder="请选择"
      style="width: 180px"
      @change="selectChange"
    >
      <el-table
        ref="plSelectTableRef"
        row-key="id"
        :data="dataList"
        show-overflow-tooltip
        @row-click="handleRowClick"
      >
        <template v-for="(item, index) in elTableOptions.columns" :key="index">
          <el-table-column
            :label="item.label"
            :prop="item.props"
            :width="item.width"
            show-overflow-tooltip
          >
            <template #default="scope">
              <el-option :key="item.props" :value="scope.row">
                {{ scope.row[item.props] }}
              </el-option>
            </template>
          </el-table-column>
        </template>
      </el-table>
      <template #footer>
        <div style="margin-bottom: 8px">
          <Pagination
            v-model:page="pageInfo.page"
            v-model:limit="pageInfo.limit"
            :total="pageInfo.total"
            @pagination="pageChange"
          />
        </div>
      </template>
    </el-select>
  </el-container>
</template>

<script setup lang="ts">
import { getUUID } from '@/utils/util'
import { onMounted, ref } from 'vue'

export type Colcumn = {
  props: string
  label: string
  width: number
}

export type Pagination = {
  page: number
  limit: number
  total: number
}

export type TableOptions = {
  colcumns: Colcumn[]
  pagination: Pagination
}

export type PLSelectProps = {
  tableOptions: TableOptions
}

const emit = defineEmits(['onPageChange', 'onSelectChange'])
const props = defineProps({
  // 表格配置参数
  tableOptions: {
    type: Object,
    default() {
      return {}
    },
  },
  dataSource: {
    type: Array,
    default() {
      return []
    },
  },
  // 绑定值对应显示的名称
  modelValueName: {
    type: String,
    default: '',
  },
})
const dataList = ref([])
const pageInfo = reactive<Pagination>({
  page: 1,
  limit: 10,
  total: 0,
})
const modelValueName = ref()
const elTableOptions = ref<any>({})
const plSelectTableRef = ref()

const pageChange = (pageValue: Pagination) => {
  // console.log('pageChange', pageValue)
  pageInfo.page = pageValue.page
  emit('onPageChange', pageValue)
}

const handleRowClick = (row) => {
  // console.log('handleRowClick', row)
}

const selectChange = (data) => {
  // console.log('selectChange', data)
  emit('onSelectChange', data)
}

onMounted(() => {})

watch(
  () => props.tableOptions,
  (newVal, oldVal) => {
    const options = {
      ...props.tableOptions,
    }
    const columns = []
    if (props.tableOptions?.columns?.length) {
      for (let index = 0; index < props.tableOptions?.columns.length; index++) {
        const element = props.tableOptions?.columns[index]
        columns.push({ ...element })
      }
    }
    options.columns = columns
    // console.log('tableOptions change', options)
    elTableOptions.value = options
  },
  {
    deep: true,
    immediate: true,
  }
)

watch(
  () => props.dataSource,
  (newVal, oldVal) => {
    // console.log('dataSource change', props.dataSource)
    dataList.value = props.dataSource
  },
  {
    deep: true,
    immediate: true,
  }
)

watch(
  () => props.tableOptions.pagination,
  (newVal, oldVal) => {
    // console.log('tableOptions.pageInfo change', props.tableOptions.pagination)
    if (props.tableOptions?.pagination) {
      pageInfo.limit = props.tableOptions.pagination.limit
      pageInfo.page = props.tableOptions.pagination.page
      pageInfo.total = props.tableOptions.pagination.total
    }
  },
  {
    deep: true,
    immediate: true,
  }
)

watch(
  () => props.modelValueName,
  (val, oldVal) => {
    modelValueName.value = val
  },
  {
    deep: true,
    immediate: true,
  }
)
</script>

<style lang="scss" scoped>

</style>

最后就是使用

<template>
  <el-container class="test_data_container">
    <el-row style="margin-top: 20px">
      <el-col :span="8"></el-col>
      <el-col :span="8">
        <pl-select
          :data-source="plSelectDataSource"
          :table-options="tableOptions"
          :model-value-name="plSelectValueName"
          @on-select-change="plSelectChange"
          @on-page-change="plSelectPageChange"
        />
      </el-col>
    </el-row>
  </el-container>

</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import dayjs from 'dayjs'
import { get32UUID, getUUID } from '@/utils/util'

const plSelectDataSource = ref([])
const plSelectItem = ref()
const plSelectValueName = ref()

// 定义表格结构
const tableOptions = ref({
  pagination: {
    page: 1,
    limit: 10,
    total: 0,
  },
  columns: [
    {
      props: 'label1',
      label: '列1',
      width: 150,
    },
    {
      props: 'label2',
      label: '列2',
      width: 150,
    },
    {
      props: 'label3',
      label: '列3',
      width: 150,
    },
    {
      props: 'label4',
      label: '列4',
      width: 150,
    },
    {
      props: 'label5',
      label: '列5',
      width: 150,
    },
  ],
})

const searchData = (pageIndex = 1) => {
  const list = []
  // 请求数据后,这里为模拟数据
  const dataTotalCount = 35
  for (let index = 0; index < tableOptions.value.pagination.limit; index++) {
    const row = {
      id: getUUID(),
      label1: '页' + pageIndex + '行' + (index + 1) + '值' + '-1',
      label2: '页' + pageIndex + '行' + (index + 1) + '值' + '-2',
      label3: '页' + pageIndex + '行' + (index + 1) + '值' + '-3',
      label4: '页' + pageIndex + '行' + (index + 1) + '值' + '-4',
      label5: '页' + pageIndex + '行' + (index + 1) + '值' + '-5',
      value: index + '' + pageIndex + '' + 1,
    }
    list.push(row)
  }
  plSelectDataSource.value = list
  tableOptions.value.pagination.total = dataTotalCount
}

const plSelectPageChange = (pageValue) => {
  searchData(pageValue.page)
}

const plSelectChange = (data) => {
  plSelectItem.value = data
  plSelectValueName.value = data.label1
}

onMounted(() => {
  searchData()
})

</script>

<style lang="scss" scoped>
.test_data_container {
  display: flex;
  flex-direction: column;
}
</style>

上面代码的pl-select就是第二步中封装的select组件,可以根据自己项目使用相对引用路径也可以的。

最后结果就上面图片的样子了。