基于vue3和elementplus实现的自定义table组件,集成查询工具栏分页,可通过配置直接实现基础的列表页基于vue3和elementplus实现的自定义table组件,集成查询工具栏分页,可通过配置直接实现基础的列表页
目录结构如下:
类型声明:
declare type DictType = {
value: string | boolean | number
label: string
type?: string
}
/**
* table传入column的配置项
* 注:selection,多选配置下接口和elementPlus接口一致
* @param label:名称
* @param key:key值,key值不可以使用type属性规定几个关键字
* @param type: element Table-column type属性 selection / index / expand / operation / link / tag ( 注意:暂只支持使用index,selection,operation(暂时无内置按钮,需自己slot传入))
* @param format:表格回显格式化函数
* @param onlyTable: 是否只在表格中显示
* @param onlySearch: 是否只在查询中显示
* @param searchFormatDate: 查询中含有日期选择器时的格式化
* @param searchType:查询条件以哪种方式展示,暂时支持input,select,tree(tree多选)以及date下的type( 'year','month','date','datetime','week','datetimerange','daterange')等
* @param searchKey: 查询参数表单key值,不传默认使用key
* @param dict:字典,searchType为select需要填写
* @param unit: 单位,可设置单位添加在单元格数据后面
* @param btnConfig: type=operation时的操作按钮配置
* @param sortable: 开启列排序,默认不开启列排序,遵循element-plus table sortable规则
* @param children: 多级表头
*/
declare type ColumnProps = {
label?: string
key: string
type?: 'selection' | 'index' | 'expand' | 'operation' | 'link' | 'tag'
linkClick?: any
format?: any
onlyTable?: boolean
onlySearch?: boolean
searchFormatDate?: string
searchType?:
| 'input'
| 'select'
| 'select-multiple'
| 'tree'
| 'tree-strictly'
| IDatePickerType
searchKey?: string | string[]
searchDefaultValue?: any
dict?: Array<DictType>
unit?: string
btnConfig?: Array<Operation>
sortable?: boolean | 'custom'
width?: string | number
fixed?: true | 'left' | 'right'
children?: Array<ColumnProps>
}
/**
* 自定义表格查询组件
* @param rowKey: 行key值,开启多选必填
* @param labelWidth: 查询表单label宽度
* @param searchAble: 是否需要查询
* @param api: 请求数据方法名(需在api目录中什么请求接口)
* @param tableConfig: table配置
* @param optBtnCfg: 操作按钮配置
* @param hasOptOrToolBtnCfg?: 是否需要操作或者工具栏
* @param hasPagination?: 是否需要分页
* @param toolBtnCfg: 工具按钮配置
*/
declare type CustomTable = {
rowKey?: string
expandRow: any
labelWidth?: string
searchAble?: boolean
api: string
tableConfig: ColumnProps[]
hasOptOrToolBtnCfg?: boolean
hasPagination?: boolean
optBtnCfg?: any
toolBtnCfg?: any
queryParam?: object
}
declare type PageRefType = {
HTMLElement
pageSize: number
currentPage: number
reset: any
returnPage: any
}
/**
* @param type: 内置类型"detail" | "delete" | "edit",暂时只有detail
* @param flowKey: 流程关键字,type=detail时必填
*/
declare type Operation = {
type: 'detail' | 'delete'
flowKey?: string
}
declare type OptType =
| 'add'
| 'edit'
| 'delete-all'
| 'delete-select'
| 'export'
declare type ToolType = 'refresh' | 'printer' | 'operation' | 'search'
declare type OptBtnCfg = {
type: OptType
api?: string
}
declare type ToolBtnCfg = {
type: ToolType
api?: string
}
index.vue
<template>
<div ref="tableList" class="table-list" style="width: 100%">
<SearchForm
v-if="searchAble"
:label-width="labelWidth"
:query-param="queryParam"
:search-from-config="searchFromConfig"
@condition-change="conditionChange"
/>
<div class="table-center" v-if="hasOptOrToolBtnCfg">
<slot name="opt-btn">
<OptionsBtn
v-if="optBtnCfg.length > 0"
:config="optBtnCfg"
:params="condForm"
:total="total"
/>
</slot>
<slot name="tool-btn">
<ToolBtn
v-if="toolBtnCfg.length > 0"
:config="toolBtnCfg"
:params="condForm"
:total="total"
/>
</slot>
</div>
<el-table
ref="cusElTable"
:data="tableData"
:expand-row-keys="expandRow"
:row-key="rowKey"
style="width: 100%"
@expand-change="expandChange"
@row-click="rowClick"
@select="select"
@select-all="selectAll"
@selection-change="selectionChange"
@sort-change="sortChange"
>
<template v-for="column in tableConfig" :key="column.key">
<el-table-column
v-if="column.type === 'expand' || column.key === 'expand'"
:fixed="column.fixed"
type="expand"
:width="column.width || 44"
:reserve-selection="true"
>
<template #default="scope">
<!-- 提供默认插槽 -->
<slot name="expand" :scope="scope.row">
<el-empty
description="暂无数据"
:image-size="0"
style="padding: 10px"
/>
</slot>
</template>
</el-table-column>
<el-table-column
v-else-if="column.type === 'selection' || column.key === 'selection'"
:fixed="column.fixed"
:label="column.label"
type="selection"
:width="column.width || 44"
/>
<el-table-column
v-else-if="column.type === 'index' || column.key === 'index'"
:fixed="column.fixed"
:label="column.label"
type="index"
:width="column.width || 55"
/>
<el-table-column
v-else-if="column.type === 'operation' || column.key === 'operation'"
fixed="right"
label="操作"
:width="column.width || 120"
>
<template #default="scope">
<!-- 提供默认插槽 -->
<slot name="operation" :scope="scope.row">
<template v-for="operation in column.btnConfig">
<el-button
v-if="operation.type === 'detail'"
:key="operation.type"
link
size="small"
type="primary"
@click="toDetail(scope.row, operation.flowKey)"
>
详情
</el-button>
</template>
</slot>
</template>
</el-table-column>
<TableColumn
v-else-if="!column.onlySearch"
:column="column"
:params="condForm"
/>
</template>
</el-table>
<!--分页查询工具条-->
<TablePagination
v-if="hasPagination"
ref="cusPage"
:total="total"
@page-change="handlePageChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, PropType, watch } from 'vue'
import SearchForm from './SearchForm.vue'
import TablePagination from './TablePagination.vue'
import TableColumn from './TableColumn.vue'
import ToolBtn from './ToolBtn.vue'
import OptionsBtn from './OptionsBtn.vue'
import Api from '@/api/index.ts'
import { useRouter } from 'vue-router'
import { id } from 'element-plus/es/locale'
const router = useRouter()
const props: CustomTable = defineProps({
/**
*@description rowKey: 行数据key值
*/
rowKey: {
type: String,
default: 'id',
},
// 默认展开行
expandRow: { type: Array, default: () => [] },
/**
* 是否需要查询,默认true
*/
searchAble: {
type: Boolean,
default: true,
},
/**
* 是否需要中间操作按钮,默认true
*/
hasOptOrToolBtnCfg: {
type: Boolean,
default: true,
},
/**
* 是否需要分页,默认true
*/
hasPagination: {
type: Boolean,
default: true,
},
labelWidth: {
type: String,
default: '100px',
},
api: {
type: String,
default: '',
required: true,
},
tableConfig: {
type: Array as PropType<ColumnProps[]>,
default: () => [],
required: true,
},
optBtnCfg: {
type: Array as PropType<OptBtnCfg[]>,
default: () => [],
},
toolBtnCfg: {
type: Array as PropType<ToolBtnCfg[]>,
default: () => [],
},
// 默认查询参数
queryParam: {
type: Object,
required: false,
default: () => {},
},
})
const cusElTable = ref<any>()
const tableList = ref<HTMLElement>()
const typeEum: any = ['selection', 'index', 'expand', 'operation']
const searchFromConfig = computed(() => {
return props.tableConfig.filter(
(el) => !el.onlyTable && !typeEum.includes([el?.type])
)
})
// 构建条件查询form,没有申明searchKey,使用key作为表单属性
const condForm = ref({})
onMounted(async () => {
await nextTick()
getRecordList()
})
const tableData = ref([])
const conditionChange = (form: any) => {
if (props.queryParam) {
const info: any = props.queryParam
Object.keys(info).forEach((el) => {
if (!form[el]) {
form[el] = info[el]
}
})
}
condForm.value = form
cusPage?.value?.reset()
getRecordList()
}
const toDetail = (scope: any, key: string | undefined) => {
// 流程id
// console.log('scope>>>>>', scope)
if (key) {
router.push(`/bpm/bpm/instanceDetail?instId=${key ? scope[key] : ''}`)
} else {
throw '当type为detail时,flowKey是必填项'
}
}
// 分页相关
const total = ref(0)
const cusPage: any = ref<PageRefType>()
const selectedDataOld: any = ref([])
const handlePageChange = (e: any) => {
// TODO 查询
if (selectedData.value[e.currentPage - 1]) {
selectedDataOld.value = JSON.parse(
JSON.stringify(selectedData.value[e.currentPage - 1])
)
}
getRecordList()
}
const getRecordList = async (prop?: string, order?: 'DESC' | 'ASC') => {
const params = JSON.parse(JSON.stringify(condForm.value))
Object.keys(condForm.value).forEach((element) => {
if (element.split(',').length > 1) {
element.split(',').forEach((el, index) => {
params[el] = condForm.value[element]
? condForm.value[element][index]
: ''
})
delete params[element]
}
})
// 删除为空的条件
Object.keys(params).forEach((el) => {
if (!params[el] && typeof params[el] !== 'boolean' && params[el] !== 0) {
delete params[el]
}
})
const info = {
offset: cusPage?.value
? cusPage?.value?.pageSize * (cusPage?.value?.currentPage - 1)
: 0,
limit: cusPage?.value ? cusPage?.value?.pageSize : 10,
sortColumn: prop ? prop : '',
sortOrder: order ? order : '',
enablePage: !props.hasPagination,
searchCount: true,
queryParam: {
...params,
},
}
const apiParam = props.api.split('.')
let Fn: any = Api
apiParam.forEach((el) => {
Fn = Fn[el]
})
const _data = await Fn(info)
tableData.value = _data.data.rows
total.value = _data.data.total
await nextTick()
if (selectedDataOld.value.length > 0) {
tableData.value.forEach((element: any) => {
let ids = selectedDataOld.value.findIndex(
(el: any) => el.id === element.id
)
if (ids !== -1) {
toggleRowSelection(element, true)
}
})
}
}
// 排序
const sortChange = ({ column, prop, order }: any) => {
//prop:name, order: 'ascending' 'descending'
const _order =
order === 'ascending'
? 'ASC'
: order === 'descending'
? 'DESC'
: undefined
getRecordList(prop, _order)
}
const emit = defineEmits([
'select',
'select-all',
'selection-change',
'row-click',
'expand-click',
])
// 多选相关方法
// 已经选择的选项
const selectedData: any = ref([])
const select = (selection: any, row: any) => {
// console.log('select', selection, row)
emit('select', selection, row)
}
const selectAll = (selection: any) => {
// console.log('selectAll', selection)
emit('select-all', selection)
}
const selectionChange = (selection: any) => {
// 页面选中的map
selectedData.value[cusPage?.value?.currentPage - 1] = selection
emit('selection-change', selection, selectedData.value)
}
const rowClick = (row: any, column: any, event: any) => {
emit('row-click', row, column, event)
}
const expandChange = (row: any, expand: any) => {
emit('expand-click', row, expand)
}
const clearSelection = () => {
cusElTable.value.clearSelection()
}
const toggleRowSelection = (row: any, selected: any) => {
cusElTable.value.toggleRowSelection(row, selected)
}
const toggleAllSelection = () => {
cusElTable.value.toggleAllSelection()
}
const toggleRowExpansion = (row: any, expanded: any) => {
cusElTable.value.toggleRowExpansion(row, expanded)
}
const setCurrentRow = (row: any) => {
cusElTable.value.setCurrentRow(row)
}
const clearSort = () => {
cusElTable.value.clearSort()
}
const refresh = () => {
selectedData.value = []
selectedDataOld.value = []
cusPage?.value?.reset()
getRecordList()
}
onUnmounted(() => {
console.log(23)
cusPage?.value?.reset()
selectedData.value = []
selectedDataOld.value = []
})
defineExpose({
clearSelection,
toggleRowSelection,
toggleAllSelection,
toggleRowExpansion,
setCurrentRow,
clearSort,
refresh,
})
</script>
<style lang="scss">
.btn-row {
width: 100%;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.table-center {
display: flex;
justify-content: space-between;
}
.table-list {
.el-empty__image {
display: none;
}
.el-empty__description {
margin-top: 0px;
}
}
</style>
OptionsBtn.vue
<template>
<div style="display: flex; margin-bottom: 8px">
<template v-for="(item, index) in config" :key="index">
<el-button
v-if="item.type === 'add'"
type="success"
@click="handleClick(item)"
>
新增
</el-button>
<el-button
v-if="item.type === 'edit'"
type="warning"
@click="handleClick(item)"
>
修改
</el-button>
<el-button
v-if="item.type === 'delete-all'"
type="danger"
@click="handleClick(item)"
>
全部删除
</el-button>
<el-button
v-if="item.type === 'delete-select'"
type="danger"
@click="handleClick(item)"
>
批量删除
</el-button>
<el-button
v-if="item.type === 'export'"
type="primary"
@click="handleExportClick(item)"
>
批量导出
</el-button>
</template>
</div>
</template>
<script lang="ts" setup>
import { ref, nextTick, PropType, watch } from 'vue'
import AttendanceApi from '@/api/attendance'
import { downLoadFile } from '~/src/utils'
import { split } from 'lodash'
const props = defineProps({
config: {
type: Array as PropType<OptBtnCfg[]>,
default: () => [],
},
params: {
type: Object,
default: () => {},
},
total: {
type: Number,
default: 0,
},
})
const queryParams = ref({})
// 查询条件form
watch(
() => props.params,
(newVal) => {
if (props.params) {
const info = JSON.parse(JSON.stringify(props.params))
Object.keys(props.params).forEach((element) => {
if (element.split(',').length > 1) {
element.split(',').forEach((el, index) => {
info[el] = props.params[element]
? props.params[element][index]
: ''
})
delete info[element]
}
})
// 删除为空的条件
Object.keys(info).forEach((el) => {
if (!info[el]) {
delete info[el]
}
})
queryParams.value = info
}
},
{
immediate: true,
deep: true,
}
)
const handleClick = (e: any) => {
// console.log(e)
ElMessage('功能开发中...')
}
const handleExportClick = async (e: any) => {
const _info = {
offset: 0,
limit: props.total,
// sortColumn: '',
// sortOrder: '',
queryParam: {
...queryParams.value,
},
}
const _data = await AttendanceApi[e.api](_info)
const fileSetting = decodeURIComponent(_data.headers['content-disposition'])
// console.log('export---->', _data)
downLoadFile(
fileSetting.split('filename=')[1],
_data.data,
_data.headers['content-type']
)
}
</script>
<style lang="scss" scoped></style>
SearchBtn.vue
<template>
<el-form-item style="margin-left: 0px; margin-right: 0">
<div style="display: flex; height: 32px; align-items: center; width: 300px">
<el-button :icon="Search" type="primary" @click="search">查询</el-button>
<el-button :icon="Refresh" type="primary" @click="reset">重置</el-button>
<el-button
v-if="showArrow"
link
style="color: #409efc"
@click="closeSearch"
>
{{ word }}
<el-icon class="no-inherit" color="#409EFC">
<ArrowUp v-if="!showAll" />
<ArrowDown v-else />
</el-icon>
</el-button>
</div>
</el-form-item>
</template>
<script setup lang="ts">
import { Refresh, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import { toRefs, computed } from 'vue'
const props = defineProps({
showAll: {
type: Boolean,
default: true,
},
showArrow: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['close-pop', 'btn-click'])
const { showAll } = toRefs(props)
const word = computed(() => {
if (showAll.value == false) {
//对文字进行处理
return '收起'
} else {
return '展开'
}
})
const closeSearch = () => {
// console.log(`子组件的状态:${showAll.value}`)
emit('close-pop')
}
const search = () => {
emit('btn-click', 'search')
}
const reset = () => {
emit('btn-click', 'reset')
}
</script>
<style lang="scss"></style>
SearchForm.vue
<template>
<el-form
ref="form"
:inline="true"
:label-width="labelWidth"
:model="condForm"
>
<div ref="formItemRef" class="btn-row">
<template v-for="(item, index) in searchFromConfig" :key="item.key">
<el-form-item
v-show="isShow(index, item)"
class="formList"
:label="item.label"
:prop="item.key"
style="margin-right: 16px"
>
<el-input
v-if="item.searchType === 'input'"
v-model="
condForm[
Array.isArray(item.searchKey)
? item.searchKey.join(',')
: item.searchKey || item.key
]
"
clearable
:placeholder="`请输入${item.label}`"
:style="{ width: valueWidth }"
@change="conditionChange"
/>
<el-select
v-if="
item.searchType === 'select' ||
item.searchType === 'select-multiple'
"
v-model="
condForm[
Array.isArray(item.searchKey)
? item.searchKey.join(',')
: item.searchKey || item.key
]
"
clearable
:multiple="item.searchType === 'select-multiple'"
:placeholder="`请选择${item.label}`"
:style="{ width: valueWidth }"
@change="conditionChange"
>
<el-option
v-for="opt in item.dict"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
<el-tree-select
v-if="
item.searchType === 'tree' || item.searchType === 'tree-strictly'
"
v-model="
condForm[
Array.isArray(item.searchKey)
? item.searchKey.join(',')
: item.searchKey || item.key
]
"
check-on-click-node
:check-strictly="item.searchType === 'tree-strictly'"
collapse-tags
collapse-tags-tooltip
:data="item.dict"
:max-collapse-tags="2"
multiple
:render-after-expand="false"
show-checkbox
:style="{ width: valueWidth }"
@change="conditionChange"
/>
<el-date-picker
v-if="dateType.includes(item.searchType || '')"
v-model="
condForm[
Array.isArray(item.searchKey)
? item.searchKey.join(',')
: item.searchKey || item.key
]
"
clearable
end-placeholder="结束时间"
:placeholder="`请选择${item.label}`"
start-placeholder="开始时间"
:style="{ width: valueWidth }"
:type="item.searchType"
:value-format="item.searchFormatDate"
@change="conditionChange"
/>
</el-form-item>
</template>
<SearchBtn
:show-all="showAll"
:show-arrow="showArrow"
@btn-click="optionClick"
@close-pop="closePop"
/>
</div>
</el-form>
</template>
<script lang="ts" setup>
import SearchBtn from './SearchBtn.vue'
import { useRoute } from 'vue-router' //1.先在需要跳转的页面引入useRouter
const router = useRoute()
const props = defineProps({
labelWidth: {
type: String,
default: '100px',
},
valueWidth: {
type: String,
default: '250px',
},
searchFromConfig: {
type: Array<any>,
default: () => [],
},
// 默认查询参数
queryParam: {
type: Object,
default: () => {},
},
})
// 构建条件查询form,没有申明searchKey,使用key作为表单属性
const condForm = ref({})
const defaultTime: [Date, Date] = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
] // '12:00:00', '08:00:00'
const dateType = [
'year',
'month',
'date',
'datetime',
'week',
'datetimerange',
'daterange',
'dates',
'monthrange',
]
const extendIndex = ref<number>(20)
const form = ref<HTMLElement>()
const formItemRef = ref<HTMLElement>()
const showAll = ref(true)
const conditionNum = ref(0)
const emit = defineEmits(['condition-change'])
const typeEum: any = ['selection', 'index', 'expand', 'operation']
// 查询条件form
watch(
() => props.searchFromConfig,
async (newVal) => {
const obj = {}
conditionNum.value = props.searchFromConfig.length
newVal.forEach((el) => {
if (typeof el.searchKey === 'string') {
obj[el.searchKey] = el.searchDefaultValue || ''
} else if (Array.isArray(el.searchKey)) {
obj[el.searchKey.join(',')] = el.searchDefaultValue || ''
} else {
obj[el.key] = el.searchDefaultValue || ''
}
})
condForm.value = obj
await nextTick()
emit('condition-change', condForm.value)
const parent = formItemRef.value?.clientWidth || 0
const child =
Number(props.valueWidth.split('px')[0]) +
Number(props.labelWidth.split('px')[0]) || 350
const num = Math.floor(parent / Number(child))
if (num > conditionNum.value) {
showArrow.value = false
extendIndex.value = 20
} else {
extendIndex.value = num - 1
}
},
{
immediate: true,
deep: true,
}
)
const showArrow = ref(true)
onMounted(async () => {
const obj = JSON.parse(JSON.stringify(router.query))
Object.keys(router.query).forEach((el) => {
const item: any = router?.query[el]
if (el.includes(',')) {
obj[el] = item.split(',')
} else {
obj[el] = item
}
})
condForm.value = obj
emit('condition-change', condForm.value)
await nextTick()
const parent = formItemRef.value?.clientWidth || 0
const child =
Number(props.valueWidth.split('px')[0]) +
Number(props.labelWidth.split('px')[0]) || 350
const num = Math.floor(parent / Number(child))
if (num > conditionNum.value) {
showArrow.value = false
extendIndex.value = 20
} else {
extendIndex.value = num - 1
}
})
const closePop = () => {
showAll.value = !showAll.value
extendIndex.value = 0 - extendIndex.value
}
const conditionChange = () => {
emit('condition-change', condForm.value)
}
const optionClick = (e: string) => {
const obj = {}
switch (e) {
case 'search':
// 查询
emit('condition-change', condForm.value)
break
case 'reset':
// 重置
props.searchFromConfig.forEach((el) => {
if (typeof el.searchKey === 'string') {
obj[el.searchKey] = el.searchDefaultValue || ''
} else if (Array.isArray(el.searchKey)) {
obj[el.searchKey.join(',')] = el.searchDefaultValue || ''
} else {
obj[el.key] = el.searchDefaultValue || ''
}
})
condForm.value = obj
emit('condition-change', condForm.value)
break
default:
break
}
}
const isShow = (index: any, item: any) => {
if (extendIndex.value < 0) {
return true
} else {
return index < extendIndex.value
}
}
</script>
<style scoped lang="scss"></style>
TableColumn.vue
<!-- 表格列组件 -->
<template>
<el-table-column
align="center"
:fixed="column.fixed"
:label="column.label"
:prop="column.key"
show-overflow-tooltip
:sortable="column.sortable"
:width="column.width"
>
<template #default="scope">
<div v-if="!column.children">
<!-- 具有优先级 format > unit > 其他 -->
<el-tag
v-if="column.type === 'tag'"
:type="setTagType(scope.row[column.key], column.dict)"
>
<div v-if="column.format">
{{ column.format(scope.row, column.dict) }}{{ column.unit || '' }}
</div>
<div v-else>
{{ setDictValue(scope.row[column.key], column.dict) }}
{{ column.unit || '' }}
</div>
</el-tag>
<el-link
v-else-if="column.type === 'link'"
type="primary"
@click="column.linkClick(scope.row, params)"
>
<div v-if="column.format">
{{ column.format(scope.row) }}{{ column.unit || '' }}
</div>
<div v-else>{{ scope.row[column.key] }}{{ column.unit || '' }}</div>
</el-link>
<div v-else>
<div v-if="column.format">
{{ column.format(scope.row) }}{{ column.unit || '' }}
</div>
<div v-else>{{ scope.row[column.key] }}{{ column.unit || '' }}</div>
</div>
</div>
<template v-else>
<TableColumn
v-for="(child, index) in column.children"
:key="index"
:column="child"
/>
</template>
</template>
</el-table-column>
</template>
<script lang="ts" setup>
const props = defineProps({
column: {
type: Object,
require: true,
default: () => {},
},
params: {
type: Object,
default: () => {},
},
})
const setDictValue = (value: any, opts: any) => {
const idx = opts.findIndex((el: any) => value === el.key)
if (idx !== -1) {
return opts[idx]?.name || '--'
} else {
return value || '--'
}
}
const setTagType = (value: any, opts: any) => {
const idx = opts.findIndex((el: any) => value === el.value)
if (idx !== -1) {
return opts[idx]?.type
} else {
return 'info'
}
}
</script>
<style scoped lang="scss"></style>
TablePagination.vue
<template>
<div class="customer-pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="pageSizes"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
vue
<script setup lang="ts">
const emit = defineEmits(['pageChange'])
const props = defineProps({
total: {
type: Number,
default: 0,
},
resetPage: {
type: Boolean,
default: false,
},
})
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
const pageSizes = [2, 5, 10, 20, 30, 40, 50]
const handleSizeChange = (val: number) => {
pageSize.value = val
// TODO 查询
// getRecordList()
emit('pageChange', {
currentPage: currentPage.value,
pageSize: pageSize.value,
})
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
// TODO 查询
// getRecordList()
emit('pageChange', {
currentPage: currentPage.value,
pageSize: pageSize.value,
})
}
/**
* 页码重置
*/
const reset = () => {
currentPage.value = 1
pageSize.value = 10
}
const returnPage = () => {
return { currentPage: currentPage.value, pageSize: pageSize.value }
}
defineExpose({
returnPage,
reset,
currentPage,
pageSize,
})
</script>
<style lang="scss"></style>
ToolBtn.vue
<template>
<div style="display: flex; margin-bottom: 8px">
<el-button
v-if="types.includes('refresh')"
circle
:icon="Refresh"
@click="handleClick"
/>
<el-button
v-if="types.includes('printer')"
circle
:icon="Printer"
@click="handleClick"
/>
<el-button
v-if="types.includes('operation')"
circle
:icon="Operation"
@click="handleClick"
/>
<el-button
v-if="types.includes('search')"
circle
:icon="Search"
@click="handleClick"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, nextTick, PropType, watch, computed } from 'vue'
import { Printer, Refresh, Operation, Search } from '@element-plus/icons-vue'
const props = defineProps({
config: {
type: Array as PropType<ToolBtnCfg[]>,
default: () => [],
},
params: {
type: Object,
default: () => {},
},
})
const types = computed(() => {
return props.config.map((el) => el.type)
})
const handleClick = () => {
ElMessage('功能开发中...')
}
</script>
<style lang="scss" scoped></style>