vue3+element-plus 封装列表页,分页,排序,导出

本文介绍了如何使用Vue3和Element-Plus构建一个包含搜索、排序、分页和导出功能的系统日志列表页,通过组件化开发实现代码复用和高效维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

背景描述:

开发流程:

详细开发:

总结:


背景描述:

web很多时候,要开发一个列表页,展示大量数据,并且提供一些交互功能,例如排序和分页,导出功能,有时候为了避免麻烦也会封装组件。下面使用 Vue 3 和 Element-Plus 来开发一个列表页。

这里,以一个系统日志的列表页为例。


开发流程:

创建一个名为 recordList.vue 的组件,该组件将用于展示系统日志。

一般这种页面都由三部分组成:

1.页面名称

2.搜索框

3.表格和分页

前两部分,往往和其他页面都是固定的样式,复用样式就行,表格和分页的样式也差不多一样,所以整个页面,大部分都是简单的,封装也比较好封装。

如图:


 

 

详细开发:

1.子组件——template(搜索组件是我自己封装的一套)

<template>
  <div class="dashboard-container">
    <el-card class="card-style">
      <div class="mt-1">
        <h2 class="fwb-mb-1">{{ listName }}</h2>
        <el-row>
         <!---搜索组件--->
          <SearchBox
            v-if="dataReady"
            :search-options="searchOptions"
            :showButton="true"
            :btnLoading="btnLoading"
            button-name="导出"
            @search="searchData"
          />
        </el-row>
      </div>
      <el-table
        v-loading="loading"
        :data="tableData"
        class="table-small-custom"
        height="calc(100vh - 240px)"
        stripe
        @sort-change="changeTableSort"
      >
        <el-table-column type="index" width="70" label="序号">
          <template #default="scope">
            <span v-text="getIndex(scope.$index)"></span>
          </template>
        </el-table-column>
        <el-table-column
          v-for="(col, index) in tableColumn"
          :key="index"
          :prop="col.prop"
          :label="col.label"
          :min-width="col.minWidth"
          :sortable="col.sortable"
          :show-overflow-tooltip="col.showOverflowTooltip"
        ></el-table-column>
        <el-table-column v-if="pageName === 'operationRecord'" label="操作" min-width="80" fixed="right">
          <template #default="scope">
            <el-button type="primary" size="small" @click="seeDetail(scope.row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        v-model:current-page="params.page"
        v-model:page-size="params.pageSize"
        class="pg-block"
        layout="total, sizes, prev, pager, next, jumper"
        :total="pageTotal"
        background
        small
        @current-change="handleCurrentChange"
        @size-change="handleSizeChange"
      />
    </el-card>
  </div>
</template>

上面涉及的一些样式也都是常见的,控制margin和padding的,没有什么特殊的,根据项目来。

2.子组件——script

<script setup>
import { ref, reactive, defineProps, computed, defineEmits, onMounted} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import SearchOptionsBox from '@/components/searchOptionsBox.vue' //搜索组件

const emit = defineEmits(['openDialog']) //这是查看详情的弹窗
const props = defineProps({
  listName: {  //列表的名称
    default: '',
    type: String
  },
  tableColumn: { // 表格里的列
    default: [],
    type: Array
  },
  url: {  //请求数据的url
    default: '',
    type: String
  },
  pageName: { //用在多个页面时,某些页面会有些特殊要求,用于区分的
    default: '',
    type: String
  }
})

let pageTotal = ref(0)
let params = reactive({
  page: 1,
  pageSize: 20
})
//搜索组件的选项,个人定义的
let searchOptions = ref([]) 
let dataReady = ref(false)
let btnLoading = ref(false) 


let tableData = ref([])
let loading = ref(true)

onMounted(() => {
 
  getOperationRecordData()
  
  //...
})
//download 是控制导出的,自己定义的
const getOperationRecordData = async (download) => {
  let tempParams = { ...params, ...searchParams }
  if (!download) {
    download = 0
    loading.value = true
  } else {
    btnLoading.value = true
  }

  let res = (
    await axios.get(props.url, {
      params: {
        ...tempParams,
        download
      }
    })
  ).data
  if (res.code == 200) {
    if (download) {
      ElMessage.success(res.message || '成功!')
      btnLoading.value = false
      return
    }
    tableData.value = res.data
    params.page = res.page
    params.pageSize = res.pageSize
    pageTotal.value = res.total
    loading.value = false
  } else {
    ElMessage.error(res.message || '获取失败!')
    loading.value = false
    btnLoading.value = false
  }
}


//处理搜索组件的数据

let searchParams = reactive({})
const searchData = (searchForm, download) => {
  params.page = 1
  searchParams = JSON.parse(JSON.stringify(searchForm))
 
  if (!download) {
    download = 0
  }
  getOperationRecordData(download)
}
//下面是分页和排序
const handleSizeChange = (val) => {
  params.page = 1
  params.pageSize = val
  getOperationRecordData()
}
const handleCurrentChange = (val) => {
  params.page = val
  getOperationRecordData()
}

//序号
const getIndex = (index) => {
  return (params.page - 1) * params.pageSize + index + 1
}

// 设置默认的排序字段和正序倒序
const changeTableSort = (column) => {
  params.sort_key = 'create_at'
  params.sort_val = 'asc'
  if (column.order == 'ascending') {
    params.sort_val = 'asc'
  } else if (column.order == 'descending') {
    params.sort_val = 'desc'
  } else {
    delete params.sort_key
    delete params.sort_val
  }
  getOperationRecordData()
}

//查看详情的弹窗打开
const seeDetail = (row) => {
  emit('openDialog', row)
}

3.父组件——tableColumn是表格的列,具体格式可以根据需求定义

<template>
  <RecordList
    list-name="系统日志"
    :table-column="tableColumn"
    url="/log-list"
    page-name="operationRecord"
    @openDialog="openDialog"
  />

  <!-- 操作详情弹窗!-->
  <el-dialog v-model="seeDialogVisible" title="操作详情" width="40%" :close-on-click-modal="false" top="10vh">
    <el-descriptions v-if="ready" size="large" column="1" style="margin-left: 20px">
      <el-descriptions-item v-for="(item, index) in tableColumn" :key="index" :label="`${item.label} :`">
        {{ recordInfo[item.prop] }}
      </el-descriptions-item>
    </el-descriptions>
    <template #footer>
      <el-button @click="seeDialogVisible = false">取消</el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import RecordList from '@/components/recordList.vue'

let tableColumn = ref([
  {
    prop: 'user_user',
    label: '操作人',
    minWidth: '140',
    sortable: false,
    showOverflowTooltip: false
  },
  {
    prop: 'create_at',
    label: '操作时间',
    minWidth: '160',
    sortable: true,
    showOverflowTooltip: false
  },
  {
    prop: 'content_type',
    label: '操作类型',
    minWidth: '140',
    sortable: false,
    showOverflowTooltip: false
  },
  {
    prop: 'content',
    label: '操作内容',
    minWidth: '330',
    sortable: false,
    showOverflowTooltip: true
  },
  {
    prop: 'user_role',
    label: '角色',
    minWidth: '150',
    sortable: false,
    showOverflowTooltip: false
  },
  {
    prop: 'operation_ip',
    label: 'IP地址',
    minWidth: '120',
    sortable: false,
    showOverflowTooltip: false
  }
])

const seeDialogVisible = ref(false)
const ready = ref(false)

const recordInfo = ref({})
const openDialog = (row) => {
  recordInfo.value = row
  ready.value = true
  seeDialogVisible.value = true
}
</script>

<style lang="scss" scoped>
:deep(.el-dialog__body) {
  padding-bottom: 0px !important;
  height: 450px;
  overflow-y: auto;
}
.el-descriptions--large {
  height: 420px;
  overflow-y: auto;
  :deep(.el-descriptions__cell) {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
  }
  :deep(.el-descriptions__label) {
    font-weight: bold !important;
    width: auto;
  }
  :deep(.el-descriptions__content) {
    width: 70%;
    display: flex;
    flex-direction: row;
  }
}
</style>

4.子组件里的导出功能,有很多种方式,如果后端返回一段base64,那么就可以直接处理,如下面这样: 

 let res = (await axios.post(props.apiUrl, tempParams)).data
  if (res.code == 200) {
    if (download) {
      let b64 = res.data.res
      let a = document.createElement('a')
      a.href = 'data:application/vnd.ms-excel;base64,' + b64
      a.download = `文件名.xlsx`
      a.click()
      ElMessage.success(res.message || '成功!')
      changeButtonLoading(false) //更新父组件里页面的一些数据或者状态
      updateParams() //更新父组件里页面的一些数据或者状态
      return
    }
    tableData.value = res.data.res
    columns.value = res.data.field_list
    params.page = res.page
    params.pageSize = res.pageSize
    pageTotal.value = res.total

    loading.value = false
    updateParams()
  } else {
    ElMessage.error(res.message || '获取表格数据失败!')
    loading.value = false
    changeButtonLoading(false)  //更新父组件里页面的一些数据或者状态
    updateParams() //更新父组件里页面的一些数据或者状态
  }

总结:

虽然是简单的业务逻辑,但是封装起来使用更方便,这里是记录这种常见类型的列表页的业务需求实现方式。

还有一篇实现,可以筛选的表格列 的实现方式。

### 回答1: 是的,可以在 Vue3 和 TypeScript 下使用 element 的 table 组件封装一个具有单选、多选、分页导出、筛选、排序功能的表格组件。 首先,安装 element-ui 和 @types/element-ui: ``` npm install element-ui -S npm install @types/element-ui -D ``` 然后,在 Vue3 组件中引入 element 的 table 组件: ``` <template> <el-table ref="multipleTable" :data="tableData" :row-class-name="tableRowClassName" :cell-class-name="tableCellClassName" :header-cell-class-name="tableHeaderCellClassName" :height="tableHeight" :max-height="tableMaxHeight" :stripe="stripe" :border="border" :fit="fit" :show-header="showHeader" :highlight-current-row="highlightCurrentRow" :current-row-key="currentRowKey" :row-style="rowStyle" :row-hover="rowHover" :cell-style="cellStyle" :header-row-style="headerRowStyle" :header-cell-style="headerCellStyle" :row-key="rowKey" :empty-text="emptyText" :default-expand-all="defaultExpandAll" :expand-row-keys="expandRowKeys" :default-sort="defaultSort" :tooltip-effect="tooltipEffect" :show-summary="showSummary" :sum-text="sumText" :summary-method="summaryMethod" :span-method="spanMethod" :select-on-indeterminate="selectOnIndeterminate" :indent="indent" :tree-props="treeProps" :lazy="lazy" :load="load" :tree-node-key="treeNodeKey" :tree-data="treeData" :tree-node-children="treeNodeChildren" :tree-indent="treeIndent" :tree-default-expand-all="treeDefaultExpandAll" :tree-expand-keys="treeExpandKeys" :tree-default-expanded-keys="treeDefaultExpandedKeys" :tree-lazy="treeLazy" :tree-load="treeLoad" :tree-accordion="treeAccordion" :tree-indent="treeIndent" :tree-new-line="treeNewLine" :tree-highlight-current="treeHighlightCurrent" :tree-current-node-key="treeCurrentNodeKey" :tree-default-expand-level="treeDefault ### 回答2: 在Vue3和Typescript下,可以使用Element UI的Table组件进行封装,实现具备单选、多选、分页导出、筛选、排序等功能的TABLE组件。 首先,安装Element UI和相关依赖包: ``` npm install element-plus axios ``` 创建一个Table组件,导入相关模块: ```javascript import { defineComponent, ref } from &#39;vue&#39;; import { ElTable, ElTableColumn, ElButton } from &#39;element-plus&#39;; import axios from &#39;axios&#39;; // 定义Table组件 export default defineComponent({ name: &#39;MyTable&#39;, components: { ElTable, ElTableColumn, ElButton, }, setup() { // 定义数据列表和选中项 const list = ref([]); const selected = ref([]); // 获取数据 const fetchData = () => { axios.get(&#39;api/data&#39;).then((res) => { list.value = res.data; }); }; // 表格配置项 const tableProps = { data: list.value, selection: { selected: selected.value, onChange: (selection) => { selected.value = selection; }, }, // 其他配置项可根据需求自行添加 }; return { list, selected, fetchData, tableProps, }; }, }); ``` 在模板中使用Table组件,并添加相应的按钮: ```html <template> <div> <el-button type="primary" @click="fetchData">刷新数据</el-button> <el-table v-bind="tableProps"> <!-- 表格列配置 --> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> <!-- 其他列根据需求自行添加 --> </el-table> </div> </template> ``` 以上只是一个简单的示例,根据具体需求可以添加更多功能,如分页导出、筛选、排序等,Element UI的Table组件提供了丰富的配置项和API供使用。可以参考Element UI官方文档进行配置和开发。 需要注意的是,需要根据具体情况进行数据获取和处理,并根据接口返回的数据结构对表格进行配置。此外,可以根据项目需要自定义样式或添加其他交互功能。 ### 回答3: 在Vue3和TypeScript环境下,是可以封装一个具有单选、多选、分页导出、筛选、排序等功能的ElementUI的Table组件的。 首先,我们可以使用Vue3的Composition API来编写可复用的Table组件。通过引入ElementUI的Table组件以及其相关的Column、Pagination、Checkbox等组件,可以很方便地实现单选、多选、分页等功能。同时,使用ElementUI的导出Excel组件,也可以轻松实现导出功能。 对于筛选和排序功能,可以使用ElementUI的Table组件提供的filter-method和sort-method属性来自定义筛选和排序的逻辑。以筛选为例,你可以在Table组件中监听筛选条件的变化,然后根据条件筛选出符合条件的数据,并重新渲染Table组件。对于排序功能,你可以监听表头点击事件,然后根据点击的表头以及排序类型,对表格数据进行排序操作。 至于在TypeScript环境下的使用,完全可以根据ElementUI的官方文档提供的类型定义来操作和使用Table组件。可以通过TypeScript的接口来定义Table组件的props和data的类型,确保数据的类型安全。 总而言之,通过合理运用Vue3的Composition API以及ElementUI的Table组件的相关功能,结合TypeScript进行类型安全检查,你可以很好地封装一个具有单选、多选、分页导出、筛选、排序等功能的Table组件。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值