diff --git a/admin/src/api/tracker/visitors.ts b/admin/src/api/tracker/visitors.ts
index 7ad50ef..54f5ddb 100644
--- a/admin/src/api/tracker/visitors.ts
+++ b/admin/src/api/tracker/visitors.ts
@@ -11,6 +11,9 @@ import request from '@/utils/http'
/** 访客行(visitors.py `_row`:列表/详情共用行字段) */
export interface VisitorRow {
visitor_id: string
+ /** 站点归属(多站点下分辨访客来自哪个站) */
+ site_id: string
+ site_name: string
country: string
province: string
city: string
@@ -97,6 +100,8 @@ export interface VisitorListResp {
*/
export interface VisitorListParams {
page?: number
+ /** 站点过滤(site_id;不传 = 全部站点,站点不存在时后端 404 site_not_found) */
+ site?: string
country?: string
device_type?: string
/** 仅 "1" 生效:5 分钟内有行为的在线访客 */
diff --git a/admin/src/views/tracker/visitor/index.vue b/admin/src/views/tracker/visitor/index.vue
index 998d249..52906c3 100644
--- a/admin/src/views/tracker/visitor/index.vue
+++ b/admin/src/views/tracker/visitor/index.vue
@@ -4,6 +4,20 @@
+
+
+
+
([])
+ const sitesLoading = ref(false)
+
+ const loadSites = async () => {
+ sitesLoading.value = true
+ try {
+ sites.value = await getSites()
+ } catch {
+ // 失败已由请求层统一 ElMessage 提示,下拉保持"全部站点"
+ } finally {
+ sitesLoading.value = false
+ }
+ }
+
const filters = reactive({
+ site: ALL_SITES,
search: '',
deviceType: '',
country: '',
@@ -119,6 +151,7 @@
/** 组装请求参数:online/show_bot 用后端哨兵 '1'(类型已锁字面量),空值不发送 */
const buildFilterParams = (): VisitorListParams => {
const params: VisitorListParams = {}
+ if (filters.site !== ALL_SITES) params.site = filters.site
const search = filters.search.trim()
if (search) params.search = search
if (filters.deviceType) params.device_type = filters.deviceType
@@ -190,6 +223,9 @@
const renderSource = (val: string) => h('span', { class: 'whitespace-nowrap' }, val || '—')
+ const renderSite = (row: VisitorRow) =>
+ h('span', { class: 'whitespace-nowrap' }, row.site_name || '—')
+
// ---------- 表格 ----------
/** 后端页大小固定 20(PAGE_SIZE):useTable 侧同步 size=20 才能算对总页数;size 不实际发送 */
const fetchVisitors = (params: VisitorListParams & { size?: number }) => getVisitors(params)
@@ -226,6 +262,12 @@
minWidth: 200,
formatter: renderLocation
},
+ {
+ prop: 'site_name',
+ label: '所属站点',
+ minWidth: 110,
+ formatter: (row: VisitorRow) => renderSite(row)
+ },
{
prop: 'device_type',
label: '设备',
@@ -282,6 +324,7 @@
const resetFilters = () => {
Object.assign(filters, {
+ site: ALL_SITES,
search: '',
deviceType: '',
country: '',
@@ -291,6 +334,8 @@
})
applyFilters()
}
+
+ onMounted(loadSites)