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

目录

背景描述:

开发流程:

详细开发:

总结:


背景描述:

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() //更新父组件里页面的一些数据或者状态
  }

总结:

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

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

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用Vue 3.0+Element-Plus开发后台管理系统的步骤: 1.首先,确保你已经安装了Node.js和npm包管理器。 2.使用以下命令安装Vue CLI 4: ```shell npm install -g @vue/cli ``` 3.使用Vue CLI创建一个新项目: ```shell vue create my-project ``` 4.在创建项目时,选择使用Vue 3.0版本,并启用class-style component语法: ```shell ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Vuex, CSS Pre-processors, Linter, Unit ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use class-style component syntax? Yes ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass) ? Pick a linter / formatter config: ESLint with error prevention only ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ``` 5.安装Element-Plus和Echarts 5.0: ```shell npm install element-plus [email protected] ``` 6.在main.js中引入Element-Plus和Echarts: ```javascript import { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' import echarts from 'echarts' const app = createApp(App) app.use(store) app.use(router) app.use(ElementPlus) app.config.globalProperties.$echarts = echarts app.mount('#app') ``` 7.现在你可以开始使用Element-Plus和Echarts来开发你的后台管理系统了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值