458 lines
12 KiB
Vue
458 lines
12 KiB
Vue
<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>
|