Files
front/src/components/search-table/index.vue
2026-03-21 17:39:39 +08:00

251 lines
5.8 KiB
Vue

<template>
<div class="search-table-container">
<a-card class="general-card" :title="title">
<!-- 搜索表单 -->
<SearchForm
:model-value="formModel"
:form-items="formItems"
:show-buttons="showSearchButtons"
:search-button-text="searchButtonText"
:reset-button-text="resetButtonText"
@update:model-value="handleFormModelUpdate"
@search="handleSearch"
@reset="handleReset"
>
<template #form-items>
<slot name="form-items" />
</template>
</SearchForm>
<a-divider style="margin-top: 0" />
<!-- 数据表格 -->
<DataTable
:data="data"
:columns="columns"
:loading="loading"
:pagination="pagination"
:bordered="bordered"
:row-selection="rowSelection"
:scroll="scroll"
:show-toolbar="showToolbar"
:show-download="showDownload"
:show-refresh="showRefresh"
:show-density="showDensity"
:show-column-setting="showColumnSetting"
:download-button-text="downloadButtonText"
:refresh-tooltip-text="refreshTooltipText"
:density-tooltip-text="densityTooltipText"
:column-setting-tooltip-text="columnSettingTooltipText"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
@refresh="handleRefresh"
@download="handleDownload"
@density-change="handleDensityChange"
@column-change="handleColumnChange"
>
<template #toolbar-left>
<slot name="toolbar-left" />
</template>
<template #toolbar-right>
<slot name="toolbar-right" />
</template>
<!-- 动态插槽透传 -->
<template v-for="col in slotColumns" :key="col.dataIndex" #[String(col.slotName)]="slotProps">
<slot :name="col.slotName" v-bind="slotProps" />
</template>
</DataTable>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { computed, PropType } from 'vue'
import SearchForm from '../search-form/index.vue'
import type { FormItem } from '../search-form/types'
import DataTable from '../data-table/index.vue'
import type { TableColumnData, TableRowSelection } from '@arco-design/web-vue/es/table/interface'
type SizeProps = 'mini' | 'small' | 'medium' | 'large'
const props = defineProps({
// 表单相关
formModel: {
type: Object as PropType<Record<string, any>>,
required: true,
},
formItems: {
type: Array as PropType<FormItem[]>,
default: () => [],
},
showSearchButtons: {
type: Boolean,
default: true,
},
searchButtonText: {
type: String,
default: '查询',
},
resetButtonText: {
type: String,
default: '重置',
},
// 表格相关
data: {
type: Array as PropType<any[]>,
default: () => [],
},
columns: {
type: Array as PropType<TableColumnData[]>,
required: true,
},
loading: {
type: Boolean,
default: false,
},
pagination: {
type: Object as PropType<{
current: number
pageSize: number
total?: number
}>,
default: () => ({
current: 1,
pageSize: 20,
}),
},
bordered: {
type: Boolean,
default: false,
},
rowSelection: {
type: Object as PropType<TableRowSelection | undefined>,
default: undefined,
},
scroll: {
type: Object as PropType<{ x?: number | string; y?: number | string } | undefined>,
default: undefined,
},
// 工具栏相关
showToolbar: {
type: Boolean,
default: true,
},
showDownload: {
type: Boolean,
default: false,
},
showRefresh: {
type: Boolean,
default: true,
},
showDensity: {
type: Boolean,
default: true,
},
showColumnSetting: {
type: Boolean,
default: true,
},
// 文本配置
title: {
type: String,
default: '',
},
downloadButtonText: {
type: String,
default: '下载',
},
refreshTooltipText: {
type: String,
default: '刷新',
},
densityTooltipText: {
type: String,
default: '密度',
},
columnSettingTooltipText: {
type: String,
default: '列设置',
},
})
const emit = defineEmits<{
(e: 'update:formModel', value: Record<string, any>): void
(e: 'search'): void
(e: 'reset'): void
(e: 'page-change', current: number): void
(e: 'page-size-change', pageSize: number): void
(e: 'selection-change', rowKeys: (string | number)[]): void
(e: 'row-click', record: any, ev: Event): void
(e: 'refresh'): void
(e: 'download'): void
(e: 'density-change', size: SizeProps): void
(e: 'column-change', columns: TableColumnData[]): void
}>()
// 计算需要插槽的列(动态插槽透传)
const slotColumns = computed(() => {
return props.columns.filter(col => col.slotName)
})
const handleFormModelUpdate = (value: Record<string, any>) => {
emit('update:formModel', value)
}
const handleSearch = () => {
emit('search')
}
const handleReset = () => {
emit('reset')
}
const handlePageChange = (current: number) => {
emit('page-change', current)
}
const handlePageSizeChange = (pageSize: number) => {
emit('page-size-change', pageSize)
}
const handleSelectionChange = (rowKeys: (string | number)[]) => {
emit('selection-change', rowKeys)
}
const handleRowClick = (record: any, ev: Event) => {
emit('row-click', record, ev)
}
const handleRefresh = () => {
emit('refresh')
}
const handleDownload = () => {
emit('download')
}
const handleDensityChange = (size: SizeProps) => {
emit('density-change', size)
}
const handleColumnChange = (columns: TableColumnData[]) => {
emit('column-change', columns)
}
</script>
<script lang="ts">
export default {
name: 'SearchTable',
}
</script>
<style scoped lang="less">
.search-table-container {
padding: 0 20px 20px 20px;
}
</style>