Files
front/src/components/search-table/index.vue

293 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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="responsiveScroll"
: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"
:status="stateStatus"
:loading-text="loadingText"
:empty-text="emptyText"
:error-text="errorText"
:partial-text="partialText"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
@refresh="handleRefresh"
@download="handleDownload"
@density-change="handleDensityChange"
@column-change="handleColumnChange"
@retry="handleRefresh"
>
<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,
},
status: {
type: String as PropType<'loading' | 'empty' | 'error' | 'success' | 'partial' | 'unauthorized' | undefined>,
default: undefined,
},
loadingText: {
type: String,
default: '加载中',
},
emptyText: {
type: String,
default: '暂无数据',
},
errorText: {
type: String,
default: '数据暂时不可用,请稍后重试。',
},
partialText: {
type: String,
default: '部分数据加载失败,当前页面展示可用数据。',
},
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 stateStatus = computed(() => {
if (props.status) return props.status
if (props.loading) return 'loading'
if (!props.data.length) return 'empty'
return 'success'
})
const responsiveScroll = computed(() => {
return props.scroll || { x: 'max-content' }
})
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;
}
@media (max-width: 768px) {
.search-table-container {
padding: 0 12px 12px;
}
}
</style>