表格组件
架构
bash
├── src
│ ├── components
│ │ ├── chooseTable
│ │ │ ├── lib (引入的第三方库)
│ │ │ │ └── xxx.json
│ │ │ ├── src (源码)
│ │ │ │ ├── index.vue
│ │ │ ├── index.ts
index.ts
ts
import { type App } from 'vue'
import chooseTable from './src/index.vue'
// 让这个组件可以通过use的形式使用
export default {
install(app: App) {
app.component('YJ-choose-table', chooseTable)
}
}
interface/chooseTable/type.ts
ts
export interface TableOptions {}
components/chooseTable/src/index.vue
- 自己用的时候一定要修改 getlist 这个方法,根据自己接口返回的数据格式来修改
vue
<template>
<el-card class="my-table-card">
<div v-if="title !== ''" slot="header" class="my-table-header">
<span v-if="totalFlag" class="title">共计{{ total }}条</span>
<span class="title">{{ title }}</span>
<span class="btn-box">
<slot name="btn"></slot>
</span>
</div>
<el-table
v-loading="tableLoading"
:data="tableList"
:highlight-current-row="true"
tooltip-effect="dark"
border
style="width: 100%"
@selection-change="selectionChange"
@sort-change="sortChange"
>
<el-table-column v-if="isSelection" type="selection" align="center" width="50"></el-table-column>
<slot></slot>
</el-table>
<el-pagination
v-if="hasPagination"
background
class="my-table-pagination"
:current-page="currPage"
:page-sizes="pageSizes"
:page-size="limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</el-card>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const emit = defineEmits(['data'])
const props = defineProps({
title: {
type: String,
default: ''
},
api: {
type: Function,
required: true
},
search: {
type: Object,
default: () => {
return {}
}
},
isSelection: {
type: Boolean,
default: false
},
pageRecordName: {
type: String,
default: ''
},
totalFlag: {
type: Boolean,
default: false
},
hasPagination: {
type: Boolean,
default: true
},
fixedTableHeader: {
type: Boolean,
default: true
},
pageSizes: {
type: Array,
default: () => {
return [10, 20, 50, 100, 200]
}
}
})
const scroll = ref(0)
const mySearch = ref(props.search)
const multipleSelection = ref([])
const tableList = ref([])
const tableLoading = ref(false)
const total = ref(0)
const currPage = ref(1)
const totalPage = ref(1)
const limit = ref(20)
const myPageRecordName = ref(props.pageRecordName ? props.pageRecordName : route.fullPath)
const order_field = ref('')
const order_sort = ref('')
const handleScroll = () => {
scroll.value = document.documentElement.scrollTop || document.body.scrollTop // 滚动条距顶部距离
const containerEleTable = document.querySelector(`.el-table`) // el-table元素
const containerEleHeader = document.querySelector(`.el-table .el-table__header-wrapper`) // el-table元素的子元素el-table__header
const containerEleHeaderLeftFixed = document.querySelector(
`.el-table .el-table__fixed .el-table__fixed-header-wrapper`
) // el-table有左边fixed的table元素的子元素el-table__header
const containerEleHeaderRightFixed = document.querySelector(
`.el-table .el-table__fixed-right .el-table__fixed-header-wrapper`
) // el-table有右边fixed的table元素的子元素el-table__header
const containerEleBody = document.querySelector(`.el-table .el-table__body-wrapper`) // el-table元素的子元素el-table__body
const containerEleHeaderTh = document.querySelectorAll(`.el-table thead tr th `)
const offsetTop = containerEleTable.offsetTop - 50 // 头部固定条高度
const table_w = window.getComputedStyle(containerEleTable).width
const height = window.getComputedStyle(containerEleHeader).height // el-table元素的子元素el-table__header高度
if (scroll.value > offsetTop) {
// el-table元素的子元素el-table__header距顶部50个单位时
containerEleHeader.classList.add('table-header-fixed') // el-table元素的子元素el-table__header添加样式class类名 table-header-fixed
containerEleBody.style.marginTop = height // el-table元素的子元素el-table__body添加距marginTop,否则会有抖动
containerEleHeader.style.width = table_w // 控制el-table__header-wrapper宽度,解决el-table__header-wrapper超出table
if (containerEleHeaderLeftFixed) {
containerEleHeaderLeftFixed.classList.add('table-header-fixed')
containerEleHeaderLeftFixed.style.width = table_w
}
if (containerEleHeaderRightFixed) {
containerEleHeaderRightFixed.classList.add('table-header-right-fixed')
}
} else {
containerEleHeader.classList.remove('table-header-fixed')
if (containerEleHeaderLeftFixed) {
containerEleHeaderLeftFixed.classList.remove('table-header-fixed')
}
if (containerEleHeaderRightFixed) {
containerEleHeaderRightFixed.classList.remove('table-header-right-fixed')
}
containerEleBody.style.marginTop = 0
}
containerEleHeaderTh.forEach(ele => {
// 找到el-table__header-wrapper下边的th有is-hidden类名的删除is-hidden类名
if (ele.classList.contains('is-hidden')) {
ele.classList.add('is-hidden-z')
}
})
}
// 初始化
const init = (reset = true) => {
if (reset) {
currPage.value = 1
}
getList()
}
// 获取数据
const getList = () => {
tableLoading.value = true
const data = mySearch.value
data.page = currPage.value
data.size = limit.value
if (order_field.value != '') {
data.order_field = order_field.value
}
if (order_sort.value != '') {
data.order_sort = order_sort.value
}
props
.api(data)
.then(res => {
if (res.code === 200) {
if (props.hasPagination) {
// 有分页
if (typeof res.total !== 'undefined') {
totalPage.value = Math.ceil(res.total / limit.value)
total.value = res.total
tableList.value = []
tableList.value = tableList.value.concat(res.list)
} else {
if (typeof res.total !== 'undefined') {
totalPage.value = Math.ceil(res.total / limit.value)
total.value = res.total
tableList.value = []
let result = res.list || res.data.data
tableList.value = tableList.value.concat(result)
} else {
currPage.value = res.curentpage
totalPage.value = res.page
total.value = res.total
tableList.value = res.list
}
}
} else {
if (res.list) {
tableList.value = res.list
} else {
tableList.value = res.data
}
}
emit('data', res.data)
}
if (res.status == 200) {
if (res.list) {
tableList.value = res.list
} else {
tableList.value = res.data
}
emit('data', res.data)
}
tableLoading.value = false
})
.catch(() => {
ElMessage.warning('请求失败了')
tableLoading.value = false
})
}
const savePage = () => {
if (!myPageRecordName.value) {
return false
}
window.sessionStorage.setItem(
myPageRecordName.value,
JSON.stringify({ page: currPage.value, size: limit.value })
)
}
const getPageRecord = () => {
let data = window.sessionStorage.getItem(myPageRecordName.value)
if (!data || typeof data === 'undefined' || data === 'null') {
return false
}
data = JSON.parse(data)
currPage.value = data.page
limit.value = data.size
}
const handleCurrentChange = page => {
currPage.value = page
getList()
savePage()
}
const handleSizeChange = size => {
currPage.value = 1
limit.value = size
getList()
savePage()
}
const selectionChange = val => {
multipleSelection.value = val
}
const getSelection = () => {
return multipleSelection.value
}
const getTableDate = () => {
const tableDate = Object.assign({
tableList: tableList.value,
limit: limit.value,
currPage: currPage.value
})
return tableDate
}
const sortChange = column => {
order_field.value = column.prop
order_sort.value = column.order.replace('ending', '')
getList()
}
watch(
() => {
return props.search
},
val => {
mySearch.value = val
}
)
onMounted(() => {
// 设置分页
getPageRecord()
init(false) // 初始化
if (props.fixedTableHeader) {
window.addEventListener('scroll', handleScroll)
}
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
defineExpose({
init,
getSelection,
getTableDate
})
</script>
<style lang="scss" scoped>
.my-table-card {
:deep(.el-card__header) {
padding: 10px 20px;
}
.my-table-header {
display: flex;
justify-content: space-between;
.title {
font-size: 12px;
font-weight: bold;
line-height: 32px;
}
}
:deep(.el-table__header thead tr th) {
background: #f9f9f9;
}
:deep(.el-table--small .el-table__body td) {
padding: 5px 0;
}
.my-table-pagination {
margin-top: 10px;
}
}
.my-table-card {
:deep(.el-table) {
.table-header-fixed {
position: fixed;
top: 50px;
left: auto;
z-index: 100;
border-top: 1px solid #dfe6ec;
}
.table-header-right-fixed {
position: fixed;
top: 50px;
right: 46px;
z-index: 200;
border-top: 1px solid #dfe6ec;
}
.el-table__header {
thead {
tr {
background: rgba(0, 0, 0, 0);
}
}
}
}
}
:deep(.is-hidden-z) {
opacity: 0;
// top: -50px;
}
:deep(.el-scrollbar__bar) {
&.is-horizontal {
height: 16px !important;
background: #dfe6ec !important;
}
}
</style>
父组件调用
vue
<template>
<div class="useManagePage">
<el-button type="primary" @click="getselectdata" style="padding-left: 20px; padding-right: 20px"
>获取选中数据</el-button
>
<YJ-choose-table :api="getUserManageList" :search="searchdata" ref="userManageRef" :isSelection="true">
<el-table-column prop="id" label="文章id" width="180" align="center" />
<el-table-column prop="userId" label="用户ID" width="180" align="center" />
<el-table-column prop="title" label="文章名称" align="center" />
<el-table-column prop="body" label="内容" align="center" />
</YJ-choose-table>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { ref } from 'vue'
import { getUserManageList } from '@/components/chooseTable/lib/api'
const searchdata = ref({})
const userManageRef: any = ref(null)
const getselectdata = () => {
console.log(userManageRef.value.getSelection())
let result: any = []
userManageRef.value.getSelection().forEach((item: any) => {
result.push(item.id)
})
let str = result.join(',')
ElMessage({
message: `选中了id是${str}数据`,
type: 'success'
})
}
</script>
<style lang="scss" scoped>
.useManagePage {
padding-top: 30px;
padding-left: 15px;
padding-right: 15px;
}
</style>