zhzf/client/src/components/BusinessTable.vue

458 lines
12 KiB
Vue
Raw Normal View History

2025-02-21 11:25:09 +08:00
<template>
<div class="business-table">
<el-table
ref="tableRef"
:data="cData"
:height="'100%'"
:width="'100%'"
:cell-style="tableConfig.cellStyleFunc"
:row-style="tableConfig.rowStyleFunc"
:id="tableConfig.tableId || tableId"
:default-sort="tableConfig.defaultSort"
class="el-table--scrollable-y"
:show-summary="tableConfig.hasSummary"
:summary-method="tableConfig.summaryMethod"
:span-method="tableConfig.spanMethod"
:tree-props="tableConfig.treeProps"
:lazy="tableConfig.lazy"
:load="tableConfig.load"
:row-class-name="tableConfig.rowClassNameFun"
:row-key="tableConfig.rowKey"
header-row-class-name="table-header"
v-bind="$attrs"
@cell-click="tableConfig.cellClick"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
>
<el-table-column
v-if="tableConfig.multipleSelect"
:reserve-selection="tableConfig.reserveSelect"
:selectable="tableConfig.selectable"
type="selection"
header-align="center"
align="center"
/>
<slot />
<slot
name="control-column"
:canDelete="mergePermissions.delete"
:canModify="mergePermissions.modify"
:canDetail="mergePermissions.detail"
:modifyMethod="modify"
:removeMethod="remove"
:detailMethod="showDetail"
>
<el-table-column
fixed="right"
align="center"
class-name="control-column"
label="操作"
:width="tableConfig.controlWidth"
v-if="tableConfig.hasControlColumn"
>
<template #default="scope">
<template v-if="mergePermissions.canModifyCustom(scope.row)">
<el-link
type="primary"
v-if="mergePermissions.modify"
@click="modify(scope.row)"
title="修改"
>
修改
</el-link>
</template>
<template v-if="mergePermissions.canDeleteCustom(scope.row)">
<el-link
type="primary"
v-if="mergePermissions.delete"
@click="remove(scope.row)"
title="删除"
>
删除
</el-link>
</template>
<template v-if="mergePermissions.canDetailCustom(scope.row)">
<el-link
type="primary"
@click="showDetail(scope.row)"
title="详情"
v-if="mergePermissions.detail"
>
详情
</el-link>
</template>
<slot
name="otherControlButtons"
:data="scope"
/>
</template>
</el-table-column>
</slot>
</el-table>
<el-pagination
v-if="hasPagerBar"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
v-model:current-page="pagination.page"
:page-sizes="tableConfig.pageSizes"
v-model:page-size="pagination.pageSize"
:layout="tableConfig.pagerLayout"
:total="pagination.total"
background
>
<template #default>
<span
class="pager total"
:class="{ link: pageUnlimitCondition }"
@click="queryUnLimit"
>
{{ pagerTotal }}
</span>
</template>
</el-pagination>
</div>
</template>
<script setup>
import {ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import { DATA_FLAGS } from '../utils/Constants'
import watermark from '@/utils/WarterMark'
import { v4 as uuid } from 'uuid'
import { merge } from 'lodash'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { useUserStore } from '@/stores/modules/user'
import {
exportExcelFile,
toExportExcelFileByUrl,
toExportXmlAllFileByUrl,
toExportXmlSelectFileByUrl
} from "@/utils/ExportExcel.js";
const userStore = useUserStore()
const emit = defineEmits([
'query-params',
'do-query',
'show-detail',
'show-modify',
'do-remove',
'table-selection',
'do-customize',
])
const defaultConfig = {
tableLoading: false,
hasControlColumn: false,
controlWidth: '300',
pageSizes: [20, 40, 60, 100],
usePage: true,
multipleSelect: true,
selectable: null,
reserveSelect: false,
cellStyleFunc: null,
rowStyleFunc: null,
rowClassNameFun: null,
hasSummary: false,
summaryMethod: null,
spanMethod: null,
userInfo: {},
defaultSort: {},
treeProps: { children: 'children', hasChildren: 'hasChildren' },
useWaterMark: true,
istick: false,
pagerLayout: '->, slot, sizes, prev, pager, next'
}
const props = defineProps({
queryParams: {
type: Object,
default: () => ({}),
},
config: Object,
dataRes: {
type: Object,
default: () => ({}),
},
permissions: {
type: Object,
default: () => ({
modify: false,
delete: false,
detail: true,
canDeleteCustom: () => true,
canDetailCustom: () => true,
canModifyCustom: () => true,
}),
},
})
const defaultPermissions = {
modify: false,
delete: false,
detail: true,
canDeleteCustom: () => true,
canDetailCustom: () => true,
canModifyCustom: () => true,
}
const tableConfig = reactive(merge({}, defaultConfig, props.config))
const mergePermissions = reactive(merge({}, defaultPermissions, props.permissions))
// 响应式状态
const pagination = reactive({
page: 1,
pageSize: tableConfig.pageSizes[0] || 20,
total: 0,
})
const { proxy } = getCurrentInstance()
props.queryParams
const data = ref([])
const once = ref(true)
const tableHeaderLabel = ref([])
const tableHeaderProps = ref([])
// 计算属性
const cData = computed(() => data.value.filter((d) => !d.dataFlag || d.dataFlag !== DATA_FLAGS.REMOVE))
const pagerTotal = computed(() => {
const totalVal = pagination.total || 0
return totalVal === 0 || props.dataRes.relation === 'eq' ? `${totalVal}` : `>=${totalVal}`
})
const pageUnlimitCondition = computed(() => props.dataRes.relation === 'gte' && props.queryParams.limit && !tableConfig.tableLoading)
const hasPagerBar = computed(() => tableConfig.usePage)
const tableId = computed(() => uuid())
// 生命周期
onMounted(() => {
if (tableConfig.useWaterMark) {
watermark.set('el-table', { username: userStore.username, mobile: userStore.mobile })
}
initQueryParams()
})
// 方法
const initQueryParams = () => {
emit('query-params', {
// 查询参数
...props.queryParams,
//
page: pagination.page,
pageSize: pagination.pageSize,
// 兼容旧接口
pagesize: pagination.pageSize,
// 初始化默认排序
sort: tableConfig.defaultSort.prop,
dir: tableConfig.defaultSort.order,
})
}
const query = (pageNum) => {
emit('query-params', {
...props.queryParams,
page: pageNum,
total: pagination.total,
})
emit('do-query')
}
const handleSizeChange = (size) => {
pagination.pageSize = size
pagination.page = 1
emit('query-params', { pagesize: size, pageSize: size })
}
const handleCurrentChange = (current) => {
pagination.page = current
query(current)
}
const showDetail = (row) => {
if (mergePermissions.detail) {
emit('show-detail', row)
}
}
const modify = (row) => {
if (mergePermissions.modify) {
emit('show-modify', row)
}
}
const remove = (row) => {
if (mergePermissions.delete) {
ElMessageBox.confirm('是否要删除选中的数据?', '确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(() => emit('do-remove', row))
.catch(() => {})
}
}
const handleSelectionChange = (val) => {
if (tableConfig.multipleSelect) {
emit('table-selection', val)
}
}
const sortChange = ({ prop, order, column }) => {
const dir = order === 'descending' ? 'desc' : 'asc'
emit('query-params', {
...props.queryParams,
sort: column.sortBy || prop,
dir: dir,
})
if (!once.value) emit('do-query')
once.value = false
}
const queryUnLimit = () => {
if (pageUnlimitCondition.value) {
emit('query-params', {
...props.queryParams,
limit: false,
total: 0,
})
emit('do-query')
}
}
const resetQuery = () => {
// 重置时不保留之前的查询参数,只设置分页相关参数
emit('query-params', {
page: 1,
total: 0,
pageSize: pagination.pageSize,
})
pagination.page = 1
once.value = true
}
const exportExcelFileForCurrentPage = (tableId, excelFileName) => { // 导出当前页数据为excel(不访问后台)
if (pagination.total === 0) {
ElMessage.warning('没有数据可以导出!')
return
}
exportExcelFile(tableId, excelFileName)
}
const exportExcelFileForAllData = (url, excelFileName, params) => { // 导出table页中所有数据需要传入到后台进行查询并组装excel,params为自定义参数用于配合在后台对应的查询条件
if (pagination.total === 0) {
ElMessage.warning('没有数据可以导出!')
return
}
toExportExcelFileByUrl(url, tableHeaderLabel.value, tableHeaderProps.value, excelFileName, params, proxy)
}
const exportXmlSelectFileForData = (url, codeList) => {
if (codeList.length === 0) {
ElMessage.warning('没有数据可以导出!')
return
}
toExportXmlSelectFileByUrl(url, codeList, '企业XML格式数据', {}, proxy)
}
const exportXmlAllFileForData = (url, params) => {
toExportXmlAllFileByUrl(url, '企业XML格式数据', params, proxy)
}
// 数据监听
watch(
() => props.dataRes,
(val) => {
if (val?.success) {
data.value = tableConfig.istick ? [] : val.data
pagination.total = val.total
if (once.value) {
once.value = false
}
}
},
{ immediate: true, deep: true }
)
watch(
() => pagination.page,
(newVal, oldVal) => {
if (newVal !== oldVal) query(newVal)
}
)
defineExpose({
query,
resetQuery,
exportExcelFileForCurrentPage,
exportExcelFileForAllData,
exportXmlSelectFileForData,
exportXmlAllFileForData
})
</script>
<style lang="scss" scoped>
.business-table {
// 给表格添加圆角
border-radius: 8px;
overflow: hidden;
:deep(.el-table) {
// 给表格添加圆角
border-radius: 8px;
overflow: hidden;
// 设置表头背景色
.el-table__header th {
background-color: #D1E5FB !important;
}
// 设置表格内容行的样式
.el-table__row {
background-color: #ffffff;
// 斑马纹样式
&:nth-child(even) {
background-color: #D9E1F3;
}
}
.el-table__cell {
font-size: 1rem;
}
}
// 给分页组件添加圆角
::v-deep(.el-pagination) {
border-radius: 8px;
overflow: hidden;
margin-top: 10px;
padding: 10px;
background-color: transparent;
}
.control-column {
.el-link {
margin-right: 10px;
&:last-child {
margin-right: 0;
}
&.el-link--primary {
color: var(--el-color-primary);
}
&.el-link--danger {
color: var(--el-color-danger);
}
&.el-link--info {
color: var(--el-color-info);
}
svg {
margin-right: 2px;
}
}
}
}
</style>