|
@@ -1,45 +1,65 @@
|
|
<template>
|
|
<template>
|
|
- <div class="layout-padding">
|
|
|
|
- <div class="layout-padding-auto layout-padding-view">
|
|
|
|
- <el-row class="ml10" v-show="showSearch">
|
|
|
|
- <el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
|
|
|
|
- <el-form-item :label="t('systoken.ip')" prop="ip">
|
|
|
|
- <el-input :placeholder="t('systoken.inputIpTip')" v-model="state.queryForm.ip"></el-input>
|
|
|
|
- </el-form-item>
|
|
|
|
- <el-form-item :label="t('systoken.domain')" prop="domain">
|
|
|
|
- <el-input :placeholder="t('systoken.inputDomainTip')" v-model="state.queryForm.domain"></el-input>
|
|
|
|
- </el-form-item>
|
|
|
|
- <el-form-item :label="t('systoken.referrer')" prop="referrer">
|
|
|
|
- <el-input :placeholder="t('systoken.inputReferrer')" v-model="state.queryForm.referrer"></el-input>
|
|
|
|
- </el-form-item>
|
|
|
|
- <el-form-item>
|
|
|
|
- <el-button @click="getDataList" icon="Search" type="primary">{{ t('common.queryBtn') }} </el-button>
|
|
|
|
- <el-button @click="resetQuery" icon="Refresh">{{ t('common.resetBtn') }}</el-button>
|
|
|
|
- </el-form-item>
|
|
|
|
- </el-form>
|
|
|
|
- </el-row>
|
|
|
|
- <el-table
|
|
|
|
- :data="state.dataList"
|
|
|
|
- @sort-change="sortChangeHandle"
|
|
|
|
- style="width: 100%"
|
|
|
|
- v-loading="state.loading"
|
|
|
|
- border
|
|
|
|
- :cell-style="tableStyle.cellStyle"
|
|
|
|
- :header-cell-style="tableStyle.headerCellStyle"
|
|
|
|
- >
|
|
|
|
- <!-- <el-table-column align="center" type="selection" width="40" /> -->
|
|
|
|
- <el-table-column :label="t('systoken.ip')" prop="ip" show-overflow-tooltip></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.domain')" prop="domain" show-overflow-tooltip ></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.content')" prop="total" show-overflow-tooltip></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
|
|
|
|
- <el-table-column :label="t('systoken.referrer')" prop="referrer" show-overflow-tooltip></el-table-column>
|
|
|
|
- </el-table>
|
|
|
|
-
|
|
|
|
- <pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle" v-bind="state.pagination"> </pagination>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="layout-padding">
|
|
|
|
+ <div class="layout-padding-auto layout-padding-view">
|
|
|
|
+ <el-row class="ml10" v-show="showSearch">
|
|
|
|
+ <el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
|
|
|
|
+ <el-form-item :label="t('systoken.ip')" prop="ip">
|
|
|
|
+ <el-input :placeholder="t('systoken.inputIpTip')" v-model="state.queryForm.ip"></el-input>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item :label="t('systoken.domain')" prop="domain">
|
|
|
|
+ <el-input :placeholder="t('systoken.inputDomainTip')" v-model="state.queryForm.domain"></el-input>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item :label="t('systoken.referrer')" prop="referrer">
|
|
|
|
+ <el-input :placeholder="t('systoken.inputReferrer')" v-model="state.queryForm.referrer"></el-input>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item>
|
|
|
|
+ <el-button @click="getDataList" icon="Search" type="primary">{{ t('common.queryBtn') }} </el-button>
|
|
|
|
+ <el-button @click="resetQuery" icon="Refresh">{{ t('common.resetBtn') }}</el-button>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ </el-row>
|
|
|
|
+ <el-table class="statistics-table" :data="state.dataList" row-key="ip" @sort-change="sortChangeHandle" style="width: 100%"
|
|
|
|
+ v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
|
|
|
+ :header-cell-style="tableStyle.headerCellStyle" @row-click="toggleRowExpansion"
|
|
|
|
+ :expand-row-keys="expandedRowKeys"
|
|
|
|
+ @expand-change="handleExpandChange"
|
|
|
|
+ >
|
|
|
|
+ <!-- <el-table-column align="center" type="selection" width="40" /> -->
|
|
|
|
+ <el-table-column show-overflow-tooltip type="expand">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <div class="child-table-container">
|
|
|
|
+ <el-table :data="row.childTableData" v-loading="row.childTableData.childLoading" border :cell-style="tableStyle.cellStyle"
|
|
|
|
+ :header-cell-style="tableStyle.headerCellStyle">
|
|
|
|
+ <!-- <el-table-column :label="t('systoken.ip')" prop="ip" show-overflow-tooltip></el-table-column> -->
|
|
|
|
+ <el-table-column :label="t('systoken.domain')" prop="domain" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.fingerprint')" prop="finger" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.referrer')" prop="referrer" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.content')" prop="total" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
|
|
|
|
+ </el-table>
|
|
|
|
+ <div class="pagination-container" @click.stop>
|
|
|
|
+ <pagination v-bind="row.childPagination" @size-change="size => handlePageSizeChange(row, size)"
|
|
|
|
+ @current-change="page => handlePageChange(row, page)" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.ip')" prop="ip" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <!-- <el-table-column :label="t('systoken.domain')" prop="domain" show-overflow-tooltip></el-table-column> -->
|
|
|
|
+ <!-- <el-table-column :label="t('systoken.fingerprint')" show-overflow-tooltip>--</el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.referrer')" show-overflow-tooltip>--</el-table-column> -->
|
|
|
|
+ <el-table-column :label="t('systoken.content')" prop="total" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.dayActive')" prop="daily" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.hourActive')" prop="hourly" show-overflow-tooltip></el-table-column>
|
|
|
|
+ <el-table-column :label="t('systoken.fifteenOnline')" prop="online" show-overflow-tooltip></el-table-column>
|
|
|
|
+ </el-table>
|
|
|
|
+
|
|
|
|
+ <pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle" v-bind="state.pagination">
|
|
|
|
+ </pagination>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
@@ -54,18 +74,185 @@ const showSearch = ref(true);
|
|
|
|
|
|
// table hook
|
|
// table hook
|
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
|
- queryForm: {
|
|
|
|
- ip: '',
|
|
|
|
- domain:''
|
|
|
|
- },
|
|
|
|
- pageList: pageList,
|
|
|
|
|
|
+ queryForm: {
|
|
|
|
+ ip: '',
|
|
|
|
+ domain: ''
|
|
|
|
+ },
|
|
|
|
+ pageList: pageList,
|
|
});
|
|
});
|
|
const { getDataList, currentChangeHandle, sortChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
|
const { getDataList, currentChangeHandle, sortChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
|
|
|
|
|
// 清空搜索条件
|
|
// 清空搜索条件
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
- queryRef.value?.resetFields();
|
|
|
|
- getDataList();
|
|
|
|
|
|
+ queryRef.value?.resetFields();
|
|
|
|
+ getDataList();
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 初始化子表格数据和分页
|
|
|
|
+const initChildData = (parentRow) => {
|
|
|
|
+ parentRow.expanded = false;
|
|
|
|
+ parentRow.childTableData = [
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "192.168.3.9",
|
|
|
|
+ "referrer": "http://192.168.3.9:5174/",
|
|
|
|
+ "total": "1",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "192.168.3.9",
|
|
|
|
+ "referrer": "http://192.168.3.9:5174/www.baidu.com",
|
|
|
|
+ "total": "2",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "localhost",
|
|
|
|
+ "referrer": "",
|
|
|
|
+ "total": "73",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "192.168.3.9",
|
|
|
|
+ "referrer": "",
|
|
|
|
+ "total": "75",
|
|
|
|
+ "daily": "1",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "192.168.3.9",
|
|
|
|
+ "referrer": "",
|
|
|
|
+ "total": "2",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "ip": "",
|
|
|
|
+ "domain": "192.168.0.77",
|
|
|
|
+ "referrer": "",
|
|
|
|
+ "total": "25",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": null
|
|
|
|
+ }
|
|
|
|
+ ];
|
|
|
|
+ parentRow.childLoading = false;
|
|
|
|
+ parentRow.childPagination = {
|
|
|
|
+ page: 1,
|
|
|
|
+ size: 5,
|
|
|
|
+ total: 6,
|
|
|
|
+ pageSizes: [1, 5, 10, 20]
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 初始化所有父行
|
|
|
|
+watchEffect(() => {
|
|
|
|
+ state?.dataList?.forEach(row => {
|
|
|
|
+ initChildData(row);
|
|
|
|
+ })
|
|
|
|
+}, [state.pageList])
|
|
|
|
+
|
|
|
|
+// 切换行展开状态
|
|
|
|
+const toggleRowExpansion = (row: any, column: any, event: Event) => {
|
|
|
|
+ console.log(row);
|
|
|
|
+ if ((event.target as HTMLElement).closest('.pagination-container')) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ row.expanded = !row.expanded;
|
|
|
|
+ if (row.expanded && row.childTableData.length === 0) {
|
|
|
|
+ loadChildData(row);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 加载子表格数据(模拟API请求)
|
|
|
|
+const loadChildData = async (parentRow) => {
|
|
|
|
+ parentRow.childLoading = true;
|
|
|
|
+ try {
|
|
|
|
+ // 模拟API请求参数
|
|
|
|
+ const params = {
|
|
|
|
+ parentId: parentRow.id,
|
|
|
|
+ page: parentRow.childPagination.page,
|
|
|
|
+ pageSize: parentRow.childPagination.size
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 模拟API请求延迟
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 800));
|
|
|
|
+
|
|
|
|
+ // 生成模拟数据
|
|
|
|
+ const mockData = [];
|
|
|
|
+ const startIndex = (params.page - 1) * params.pageSize;
|
|
|
|
+
|
|
|
|
+ for (let i = 0; i < params.pageSize; i++) {
|
|
|
|
+ const index = startIndex + i;
|
|
|
|
+ if (index >= parentRow.childPagination.total) break;
|
|
|
|
+
|
|
|
|
+ mockData.push({
|
|
|
|
+ "ip": parentRow.ip,
|
|
|
|
+ "domain": "192.168.3.9",
|
|
|
|
+ "referrer": "http://192.168.3.9:5174/",
|
|
|
|
+ "total": "1",
|
|
|
|
+ "daily": "0",
|
|
|
|
+ "hourly": "0",
|
|
|
|
+ "online": Math.random() < 0.5 ? null : Math.floor(Math.random() * 100)
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 更新数据
|
|
|
|
+ parentRow.childTableData = mockData;
|
|
|
|
+ parentRow.childPagination.total = parentRow.childPagination.total+=0;
|
|
|
|
+ } finally {
|
|
|
|
+ parentRow.childLoading = false;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 处理分页大小变化
|
|
|
|
+const handlePageSizeChange = (row: any, size: number) => {
|
|
|
|
+ row.childPagination.size = size;
|
|
|
|
+ row.childPagination.page = 1; // 重置为第一页
|
|
|
|
+ loadChildData(row);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 处理页码变化
|
|
|
|
+const handlePageChange = (row: any, page: number) => {
|
|
|
|
+ row.childPagination.page = page;
|
|
|
|
+ console.log('page', page);
|
|
|
|
+
|
|
|
|
+ loadChildData(row);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const expandedRowKeys = ref<number[]>([]);
|
|
|
|
+const handleExpandChange = (row: any, expandedRows: any[]) => {
|
|
|
|
+ expandedRowKeys.value = expandedRows.map(item => item.ip);
|
|
};
|
|
};
|
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss">
|
|
|
|
+// .statistics-table {
|
|
|
|
+// tr>.el-table__cell.el-table__expanded-cell {
|
|
|
|
+// padding-top: 0;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+.child-table-container {
|
|
|
|
+ margin: 20px;
|
|
|
|
+}
|
|
|
|
+// .child-table-container {
|
|
|
|
+// margin: -1px 0 0 48px;
|
|
|
|
+
|
|
|
|
+// .el-table__header {
|
|
|
|
+// display: none;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+</style>
|