需求:页面有多处地方需要使用a-table,封装成公用组件
子页面:
1. defineProps中接受父页面columns,dataList,total
2. 子页面定义pagination分页,defineEmits中定义父页面分页change事件
代码如下:
<template>
<div>
<a-table
:columns="columns"
:data-source="dataList"
:pagination="pagination"
bordered
size="middle"
@change="handleTableChange"
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'ip'">
<a>{{ text }}</a>
</template>
<template v-if="column.dataIndex === 'tags'">
<div style="display: flex; flex-wrap: wrap; gap: 5px">
<div v-for="tag in record.tags" :key="tag">
<a-tooltip placement="top">
<template #title>
<span>{{ tag.hasTagTip }}</span>
</template>
<span>{{ tag.name }}</span>
</a-tooltip>
</div>
</div>
</template>
</template>
</a-table>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { ColumnsType } from 'ant-design-vue/es/table'
defineOptions({
name: '',
})
// 使用withDefaults可以给defineProps定义的属性指定ts类型,解决columns ts类型报错
const props = withDefaults(
defineProps<{
columns: ColumnsType<any>
dataList: Array<string>
total: Number
}>(),
{},
)
// 对props.total进行监听,因为defineProps中定义的值没有响应性,如果父组件列表请求还没结束就渲染
// 子页面的话,会导致props已经赋了初始值,不会再重新赋值.
watch(
() => props.total,
(newVal) => {
if (newVal) {
return (pagination.value.total = newVal)
}
},
)
// 分页定义在props中没办法单独给pagination中的total赋值,只能定义在外面
const pagination = ref<any>({
total: props.total,
current: 1,
pageSize: 10, //每页中显示10条数据
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'], //每页中显示的数据
showTotal: (total) => `共有 ${total} 条数据`, //分页中显示总的数据
})
const emit = defineEmits(['handleTableChange'])
// table分页change事件
const handleTableChange = (pageSize) => {
// 切换分页后需要将新的分页属性赋值一遍,因为change事件返回的pageSize中没有showTotal()方法,
// 所以要用Object.assign()
Object.assign(pagination.value, pageSize)
emit('handleTableChange', pageSize)
}
</script>
<style lang="less" scoped></style>
父页面:
1. import引入子页面
2. 父页面分别定义colums,datalist,total,三个table接口入参
3. 父页面定义分页change事件,传入type区分分页和接口入参
代码如下 :
<template>
<div class="content">
<div class="content-tab">
<a-tabs v-model:activeKey="activeKey" @change="handChange">
<a-tab-pane key="1" tab="ICO情报">
<h3>文件情报</h3>
<CustomTable
key="fileKey"
:columns="columns"
:data-list="fileDataList"
:total="fileTotalNum"
@handle-table-change="(pagination) => handleTableChange(pagination, 'file')"
/>
<h3>域名情报</h3>
<CustomTable
key="'domainKey'"
:columns="domainColumns"
:data-list="domainDataList"
:total="domainTotalNum"
@handle-table-change="(pagination) => handleTableChange(pagination,'domain')"
/>
<h3>IP情报</h3>
<CustomTable
key="ipKey"
:columns="ipColumns"
:data-list="ipDataList"
:total="ipTotalNum"
@handle-table-change="(pagination) => handleTableChange(pagination, 'ip')"
/>
</a-tab-pane>
</a-tabs>
</div>
</div>
</template>
<script setup lang="ts">
import { h, ref } from 'vue'
import { UserOutlined, LeftOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import CustomTable from './apt-table/index.vue'
const activeKey = ref('1')
const datalist = ref([
{
link: '',
create_time: '',
title: '',
author: '',
description: '',
},
])
const fileTotalNum = ref()
const domainTotalNum = ref()
const ipTotalNum = ref()
// 文件情报list
const fileDataList = ref([])
// 域名情报table list
const domainDataList = ref([])
// IP情报table list
const ipDataList = ref([])
const columns = ref([
{
title: '文件HASA',
dataIndex: 'sha256',
width: '50%',
},
{
title: '文件类型',
dataIndex: 'file_type',
width: '10%',
},
{
title: '威胁情报',
dataIndex: 'tags',
width: '40%',
},
])
const domainColumns = ref([
{
title: '域名信息',
dataIndex: 'domain',
},
{
title: '威胁情报',
dataIndex: 'tags',
},
])
const ipColumns = ref([
{
title: 'IP地址',
dataIndex: 'ip',
},
{
title: '地理信息',
dataIndex: 'location',
},
{
title: 'ASN',
dataIndex: 'asn',
},
{
title: '威胁情报',
dataIndex: 'tags',
},
])
defineOptions({
title: '海莲花组织近期攻击手法分析',
})
const paramsId = ref<any>('')
// 定义三个table接口查询入参
const createQueryParam = (id) => {
return ref({
id,
page: 1,
size: 10,
})
}
const activityQueryParam = createQueryParam(paramsId)
const fileQueryParam = createQueryParam(paramsId)
const domainQueryParam = createQueryParam(paramsId)
const ipQueryParam = createQueryParam(paramsId)
function onload() {
paramsId.value = route.params.id
getTrajectoryList()
}
onload()
// tabs change事件
const handChange = (targetKey) => {
getICOIntelligence('')
}
// 获取ICO情报列表
const getICOIntelligence = async (type) => {
try {
let requestList: Promise<any>[] = []
if (type === 'file') {
requestList.push(apiGetAptDetailDocument(fileQueryParam.value))
} else if (type === 'domain') {
requestList.push(apiGetAptDetailDomainIntel(domainQueryParam.value))
} else if (type === 'ip') {
requestList.push(apiGetAptDetailIpIntel(ipQueryParam.value))
} else {
requestList = [
apiGetAptDetailDocument(fileQueryParam.value),
apiGetAptDetailDomainIntel(domainQueryParam.value),
apiGetAptDetailIpIntel(ipQueryParam.value),
]
}
const result = await Promise.all(requestList)
if (type === 'file') {
fileDataList.value = result[0].data
fileTotalNum.value = result[0].total
} else if (type === 'domain') {
domainDataList.value = result[0].data
domainTotalNum.value = result[0].total
} else if (type === 'ip') {
ipDataList.value = result[0].data
ipTotalNum.value = result[0].total
} else {
fileDataList.value = result[0].data
fileTotalNum.value = result[0].total
domainDataList.value = result[1].data
domainTotalNum.value = result[1].total
ipDataList.value = result[2].data
ipTotalNum.value = result[2].total
}
} catch (error) {
console.error(error)
}
}
// table子组件分页change事件
const handleTableChange = (pagination, type) => {
if (type === 'file') {
fileQueryParam.value.page = pagination.current
fileQueryParam.value.size = pagination.pageSize
} else if (type === 'domain') {
domainQueryParam.value.page = pagination.current
domainQueryParam.value.size = pagination.pageSize
} else if (type === 'ip') {
ipQueryParam.value.page = pagination.current
ipQueryParam.value.size = pagination.pageSize
}
getICOIntelligence(type)
}
</script>
<style lang="less" scoped></style>
踩过的坑:
1. 分页在父页面定义了三个,导致子页面调用父分页分页change事件后需要判断给三个table分页和入参重新赋值,代码过于冗余。
2. 使用Promise.all()同时处理三个接口,分页切换的时候需要判断调用那个接口,且对返回的结果也要单独判断赋值,建议可以为三个table请求单独定义方法。
3. 子页面在defineProps接收分页并赋初始值,没办法使父页面首次加载的total具有响应性,只能单独分开,在defineProps中接受total,使用ref()定义分页数据,再将props.total监听(监听为了解决defineProps没有响应性问题)赋值给分页pagination对象。
4. 在defineProps中定义columns时eslint提示ts类型格式不对,使用withDefaults为defineProps中的columns重新定义类型。