fix:历史记录界面后端对接
This commit is contained in:
@@ -562,6 +562,8 @@ const local: any = {
|
|||||||
uptime:"Up Time",
|
uptime:"Up Time",
|
||||||
lasttime:"Last seen Time",
|
lasttime:"Last seen Time",
|
||||||
cdr:"CDR Records",
|
cdr:"CDR Records",
|
||||||
|
refresh:"Refresh",
|
||||||
|
mac:"MAC"
|
||||||
},
|
},
|
||||||
records:{
|
records:{
|
||||||
clientID:"Client ID",
|
clientID:"Client ID",
|
||||||
@@ -572,6 +574,7 @@ const local: any = {
|
|||||||
endtime:"End Time",
|
endtime:"End Time",
|
||||||
clienthistory:"Client history",
|
clienthistory:"Client history",
|
||||||
refresh:"Refresh",
|
refresh:"Refresh",
|
||||||
|
dataUsage:"Data Usage",
|
||||||
},
|
},
|
||||||
access:{
|
access:{
|
||||||
devicename:"Device Name",
|
devicename:"Device Name",
|
||||||
|
|||||||
@@ -562,6 +562,8 @@ const local:any = {
|
|||||||
uptime:"接入时间",
|
uptime:"接入时间",
|
||||||
lasttime:"断开时间",
|
lasttime:"断开时间",
|
||||||
cdr:"CDR记录",
|
cdr:"CDR记录",
|
||||||
|
refresh:"刷新",
|
||||||
|
mac:"mac地址"
|
||||||
},
|
},
|
||||||
records:{
|
records:{
|
||||||
clientID:"设备ID",
|
clientID:"设备ID",
|
||||||
@@ -572,6 +574,7 @@ const local:any = {
|
|||||||
endtime:"结束时间",
|
endtime:"结束时间",
|
||||||
clienthistory:"接入历史记录",
|
clienthistory:"接入历史记录",
|
||||||
refresh:"刷新",
|
refresh:"刷新",
|
||||||
|
dataUsage:"流量使用量",
|
||||||
},
|
},
|
||||||
access:{
|
access:{
|
||||||
devicename:"设备名称",
|
devicename:"设备名称",
|
||||||
|
|||||||
@@ -91,3 +91,34 @@ export function doGetCheckCode() {
|
|||||||
url: '/code'
|
url: '/code'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//首页仪表盘
|
||||||
|
/** Get dashboard gauge data */
|
||||||
|
export function fetchDashboardData() {
|
||||||
|
return request<Api.Dashboard.GaugeData>({
|
||||||
|
url: '/u/cdr/getOne',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** Get current connected devices */
|
||||||
|
export function fetchCurrentDevices() {
|
||||||
|
return request<Api.Device.DeviceListResponse>({
|
||||||
|
url: '/u/client/pageCurrentClient',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** Get historical devices */
|
||||||
|
export function fetchHistoricalDevices() {
|
||||||
|
return request<Api.Device.HistoricalDeviceListResponse>({
|
||||||
|
url: '/u/client/pageHistoryClient',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** Get CDR history records */
|
||||||
|
export function fetchCDRHistory(params: Api.CDR.CDRQueryParams) {
|
||||||
|
return request<Api.CDR.CDRListResponse>({
|
||||||
|
url: '/u/cdr/pageHistory',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { localStg } from '@/utils/storage';
|
|||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { useRouteStore } from '../route';
|
import { useRouteStore } from '../route';
|
||||||
import { clearAuthStorage, emptyInfo, getToken } from './shared';
|
import { clearAuthStorage, emptyInfo, getToken } from './shared';
|
||||||
import { doCheckUserRepeat, sendCaptcha } from '@/service/api/auth';
|
import { doCheckUserRepeat, sendCaptcha, fetchDashboardData } from '@/service/api/auth';
|
||||||
|
|
||||||
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
@@ -18,6 +18,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
|
|
||||||
const userInfo: Api.Auth.UserInfo = reactive(emptyInfo);
|
const userInfo: Api.Auth.UserInfo = reactive(emptyInfo);
|
||||||
const permissions = computed(() => userInfo.permissions);
|
const permissions = computed(() => userInfo.permissions);
|
||||||
|
/** Dashboard data */
|
||||||
|
const dashboardData = ref<Api.Dashboard.GaugeData | null>(null);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => token.value,
|
() => token.value,
|
||||||
@@ -159,6 +161,16 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
const { data, error } = await sendCaptcha({ email }); // 这里调用后端接口发送验证码
|
const { data, error } = await sendCaptcha({ email }); // 这里调用后端接口发送验证码
|
||||||
return { data, error };
|
return { data, error };
|
||||||
}
|
}
|
||||||
|
/** Fetch dashboard data */
|
||||||
|
async function getDashboardData() {
|
||||||
|
const { data, error } = await fetchDashboardData();
|
||||||
|
if (!error) {
|
||||||
|
dashboardData.value = data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
@@ -172,6 +184,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
register,
|
register,
|
||||||
captcha,
|
captcha,
|
||||||
checkUserRepeat,
|
checkUserRepeat,
|
||||||
updateUserProfile
|
updateUserProfile,
|
||||||
|
dashboardData,
|
||||||
|
getDashboardData
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
const {authRoutes: staticAuthRoutes} = createStaticRoutes();
|
const {authRoutes: staticAuthRoutes} = createStaticRoutes();
|
||||||
const filteredAuthRoutes = filterAuthRoutesByRoles(staticAuthRoutes, authStore.userInfo.roles ?? []);
|
const filteredAuthRoutes = filterAuthRoutesByRoles(staticAuthRoutes, authStore.userInfo.roles ?? []);
|
||||||
|
console.log (filteredAuthRoutes.concat(routes))
|
||||||
addAuthRoutes(filteredAuthRoutes.concat(routes));
|
addAuthRoutes(filteredAuthRoutes.concat(routes));
|
||||||
|
|
||||||
handleAuthRoutes();
|
handleAuthRoutes();
|
||||||
@@ -184,7 +185,6 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
|
|||||||
const vueRoutes = getAuthVueRoutes(sortRoutes);
|
const vueRoutes = getAuthVueRoutes(sortRoutes);
|
||||||
|
|
||||||
resetVueRoutes();
|
resetVueRoutes();
|
||||||
|
|
||||||
addRoutesToVueRouter(vueRoutes);
|
addRoutesToVueRouter(vueRoutes);
|
||||||
|
|
||||||
getGlobalMenus(sortRoutes);
|
getGlobalMenus(sortRoutes);
|
||||||
|
|||||||
150
src/typings/api.d.ts
vendored
150
src/typings/api.d.ts
vendored
@@ -435,4 +435,154 @@ declare namespace Api {
|
|||||||
|
|
||||||
type DictList = Common.PaginatingQueryRecord<Dict>;
|
type DictList = Common.PaginatingQueryRecord<Dict>;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Namespace Dashboard
|
||||||
|
*
|
||||||
|
* Backend api module: "dashboard"
|
||||||
|
*/
|
||||||
|
namespace Dashboard {
|
||||||
|
/** Dashboard gauge data */
|
||||||
|
interface GaugeData {
|
||||||
|
/** Remaining credit amount */
|
||||||
|
remainingCredit: number;
|
||||||
|
/** Used credit amount */
|
||||||
|
usedCredit: number;
|
||||||
|
/** Remaining flow amount (MB) */
|
||||||
|
remainingFlow: number;
|
||||||
|
/** Used flow amount (MB) */
|
||||||
|
usedFlow: number;
|
||||||
|
/** Current traffic rate (MB/s) */
|
||||||
|
trafficRate: number;
|
||||||
|
/** Peak traffic rate (MB/s) */
|
||||||
|
peakTrafficRate: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace Device
|
||||||
|
*
|
||||||
|
* Backend api module: "device"
|
||||||
|
*/
|
||||||
|
namespace Device {
|
||||||
|
/** Device information */
|
||||||
|
interface DeviceInfo {
|
||||||
|
/** Device unique key */
|
||||||
|
key: string;
|
||||||
|
/** Device name */
|
||||||
|
deviceName: string;
|
||||||
|
/** MAC address */
|
||||||
|
macAddress: string;
|
||||||
|
/** Current speed */
|
||||||
|
speed: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Device list response */
|
||||||
|
interface DeviceListResponse {
|
||||||
|
/** List of devices */
|
||||||
|
rows: DeviceInfo[];
|
||||||
|
/** Total count */
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Historical device information */
|
||||||
|
interface HistoricalDeviceInfo {
|
||||||
|
/** Device unique key */
|
||||||
|
key: string;
|
||||||
|
/** Device name */
|
||||||
|
deviceName: string;
|
||||||
|
/** MAC address */
|
||||||
|
macAddress: string;
|
||||||
|
/** Connection time */
|
||||||
|
connectionTime: string;
|
||||||
|
/** Disconnection time */
|
||||||
|
disconnectionTime: string;
|
||||||
|
/** Data usage */
|
||||||
|
dataUsage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Historical device list response */
|
||||||
|
interface HistoricalDeviceListResponse {
|
||||||
|
/** List of historical devices */
|
||||||
|
rows: HistoricalDeviceInfo[];
|
||||||
|
/** Total count */
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeviceInfo {
|
||||||
|
id: number;
|
||||||
|
clientName: string;
|
||||||
|
clientMac: string;
|
||||||
|
clientDeviceType: string;
|
||||||
|
activity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeviceResponse {
|
||||||
|
total: number;
|
||||||
|
rows: DeviceInfo[];
|
||||||
|
}
|
||||||
|
interface HistoricalDeviceInfo {
|
||||||
|
id: number;
|
||||||
|
clientName: string;
|
||||||
|
clientMac: string;
|
||||||
|
startTime: number; // 时间戳
|
||||||
|
endTime: number; // 时间戳
|
||||||
|
duration: number; // 流量使用量(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HistoricalDeviceResponse {
|
||||||
|
total: number;
|
||||||
|
rows: HistoricalDeviceInfo[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace CDR {
|
||||||
|
/** CDR record information */
|
||||||
|
interface CDRRecord {
|
||||||
|
/** Record ID */
|
||||||
|
id: number;
|
||||||
|
/** AP name */
|
||||||
|
ap_name: string;
|
||||||
|
/** Upload traffic */
|
||||||
|
traffic_up: number;
|
||||||
|
/** Download traffic */
|
||||||
|
traffic_down: number;
|
||||||
|
/** Up time */
|
||||||
|
up_time: string;
|
||||||
|
/** Last seen time */
|
||||||
|
last_seen_time: string;
|
||||||
|
}
|
||||||
|
interface CDRRecord {
|
||||||
|
/** Record ID */
|
||||||
|
id: number;
|
||||||
|
/** Client name */
|
||||||
|
clientName: string;
|
||||||
|
/** Client MAC address */
|
||||||
|
clientMac: string;
|
||||||
|
/** Upload traffic in bytes */
|
||||||
|
trafficUp: number;
|
||||||
|
/** Download traffic in bytes */
|
||||||
|
trafficDown: number;
|
||||||
|
/** Start time timestamp */
|
||||||
|
startTime: number;
|
||||||
|
/** End time timestamp */
|
||||||
|
endTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** CDR record list response */
|
||||||
|
interface CDRListResponse {
|
||||||
|
/** List of CDR records */
|
||||||
|
rows: CDRRecord[];
|
||||||
|
/** Total count */
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** CDR query params */
|
||||||
|
interface CDRQueryParams {
|
||||||
|
/** Page number */
|
||||||
|
pageNum: number;
|
||||||
|
/** Page size */
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/typings/auto-imports.d.ts
vendored
4
src/typings/auto-imports.d.ts
vendored
@@ -92,10 +92,14 @@ declare global {
|
|||||||
const expect: typeof import('vitest')['expect']
|
const expect: typeof import('vitest')['expect']
|
||||||
const extendRef: typeof import('@vueuse/core')['extendRef']
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
const extractTabsByAllRoutes: typeof import('../store/modules/tab/shared')['extractTabsByAllRoutes']
|
const extractTabsByAllRoutes: typeof import('../store/modules/tab/shared')['extractTabsByAllRoutes']
|
||||||
|
const fetchCDRHistory: typeof import('../service/api/auth')['fetchCDRHistory']
|
||||||
|
const fetchCurrentDevices: typeof import('../service/api/auth')['fetchCurrentDevices']
|
||||||
const fetchCustomBackendError: typeof import('../service/api/auth')['fetchCustomBackendError']
|
const fetchCustomBackendError: typeof import('../service/api/auth')['fetchCustomBackendError']
|
||||||
|
const fetchDashboardData: typeof import('../service/api/auth')['fetchDashboardData']
|
||||||
const fetchGetAllPages: typeof import('../service/api/menu')['fetchGetAllPages']
|
const fetchGetAllPages: typeof import('../service/api/menu')['fetchGetAllPages']
|
||||||
const fetchGetConstantRoutes: typeof import('../service/api/route')['fetchGetConstantRoutes']
|
const fetchGetConstantRoutes: typeof import('../service/api/route')['fetchGetConstantRoutes']
|
||||||
const fetchGetMenuTree: typeof import('../service/api/menu')['fetchGetMenuTree']
|
const fetchGetMenuTree: typeof import('../service/api/menu')['fetchGetMenuTree']
|
||||||
|
const fetchHistoricalDevices: typeof import('../service/api/auth')['fetchHistoricalDevices']
|
||||||
const fetchIsRouteExist: typeof import('../service/api/route')['fetchIsRouteExist']
|
const fetchIsRouteExist: typeof import('../service/api/route')['fetchIsRouteExist']
|
||||||
const fetchLogin: typeof import('../service/api/auth')['fetchLogin']
|
const fetchLogin: typeof import('../service/api/auth')['fetchLogin']
|
||||||
const fetchRefreshToken: typeof import('../service/api/auth')['fetchRefreshToken']
|
const fetchRefreshToken: typeof import('../service/api/auth')['fetchRefreshToken']
|
||||||
|
|||||||
@@ -1,133 +1,122 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import type { TableColumnsType } from 'ant-design-vue'
|
import type { TableColumnsType } from 'ant-design-vue'
|
||||||
import { HistoryOutlined } from '@ant-design/icons-vue'
|
import { HistoryOutlined } from '@ant-design/icons-vue'
|
||||||
import type { Dayjs } from 'dayjs'
|
import { useI18n } from "vue-i18n";
|
||||||
import {useI18n} from "vue-i18n";
|
import { fetchHistoricalDevices } from '@/service/api/auth';
|
||||||
|
|
||||||
const {t} = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
interface AccessRecord {
|
// 格式化时间戳
|
||||||
key: string
|
function formatTimestamp(timestamp: number): string {
|
||||||
clientId: string
|
const date = new Date(timestamp);
|
||||||
clientName: string
|
return date.toLocaleString('zh-CN', {
|
||||||
clientDeviceType: string
|
year: 'numeric',
|
||||||
clientMac: string
|
month: '2-digit',
|
||||||
startTime: string
|
day: '2-digit',
|
||||||
endTime: string
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 格式化流量
|
||||||
|
function formatDataUsage(bytes: number): string {
|
||||||
|
if (bytes < 1024) {
|
||||||
|
return `${bytes}B`;
|
||||||
|
} else if (bytes < 1024 * 1024) {
|
||||||
|
return `${(bytes / 1024).toFixed(2)}KB`;
|
||||||
|
} else if (bytes < 1024 * 1024 * 1024) {
|
||||||
|
return `${(bytes / (1024 * 1024)).toFixed(2)}MB`;
|
||||||
|
} else {
|
||||||
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义记录类型
|
||||||
|
interface DeviceRecord {
|
||||||
|
id: number;
|
||||||
|
deviceName: string;
|
||||||
|
macAddress: string;
|
||||||
|
dataUsage: string;
|
||||||
|
connectionTime: string;
|
||||||
|
disconnectionTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备列表数据
|
||||||
|
const deviceList = ref<DeviceRecord[]>([]);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
const columns: TableColumnsType = [
|
const columns: TableColumnsType = [
|
||||||
{
|
|
||||||
title: t('page.records.clientID'),
|
|
||||||
dataIndex: 'clientId',
|
|
||||||
key: 'clientId',
|
|
||||||
width: 100,
|
|
||||||
responsive: ['md']
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('page.records.clientname'),
|
title: t('page.records.clientname'),
|
||||||
dataIndex: 'clientName',
|
dataIndex: 'deviceName',
|
||||||
key: 'clientName',
|
key: 'deviceName',
|
||||||
width: 90,
|
width: '30%'
|
||||||
className: 'wrap-cell'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('page.records.clienttype'),
|
|
||||||
dataIndex: 'clientDeviceType',
|
|
||||||
key: 'clientDeviceType',
|
|
||||||
width: 90,
|
|
||||||
responsive: ['lg']
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('page.records.clientmac'),
|
title: t('page.records.clientmac'),
|
||||||
dataIndex: 'clientMac',
|
dataIndex: 'macAddress',
|
||||||
key: 'clientMac',
|
key: 'macAddress',
|
||||||
width: 140,
|
width: '40%'
|
||||||
responsive: ['md']
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('page.records.starttime'),
|
title: t('page.records.dataUsage'),
|
||||||
dataIndex: 'startTime',
|
dataIndex: 'dataUsage',
|
||||||
key: 'startTime',
|
key: 'dataUsage',
|
||||||
width: 110,
|
width: '30%',
|
||||||
ellipsis: true
|
align: 'right'
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('page.records.endtime'),
|
|
||||||
dataIndex: 'endTime',
|
|
||||||
key: 'endTime',
|
|
||||||
width: 110,
|
|
||||||
ellipsis: true
|
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
// 添加一个存储所有记录的数组和当前显示记录的数组
|
// 添加展开行控制
|
||||||
const allRecords = ref<AccessRecord[]>([
|
const expandedRowKeys = ref<number[]>([]);
|
||||||
{
|
|
||||||
key: '1',
|
// 处理展开行变化
|
||||||
clientId: 'DEV001',
|
function handleExpandChange(expanded: boolean, record: DeviceRecord) {
|
||||||
clientName: 'iPhone 13',
|
if (expanded) {
|
||||||
clientDeviceType: '手机',
|
expandedRowKeys.value = [record.id];
|
||||||
clientMac: '00:11:22:33:44:55',
|
} else {
|
||||||
startTime: '2024-03-20 10:00:00',
|
expandedRowKeys.value = [];
|
||||||
endTime: '2024-03-20 12:30:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
clientId: 'DEV002',
|
|
||||||
clientName: 'MacBook Pro',
|
|
||||||
clientDeviceType: '笔记本',
|
|
||||||
clientMac: '66:77:88:99:AA:BB',
|
|
||||||
startTime: '2024-03-20 14:00:00',
|
|
||||||
endTime: '2024-03-20 18:00:00'
|
|
||||||
}
|
}
|
||||||
])
|
|
||||||
|
|
||||||
const accessRecords = ref<AccessRecord[]>(allRecords.value)
|
|
||||||
|
|
||||||
// 日期范围变更处理函数
|
|
||||||
const onRangeChange = (
|
|
||||||
value: [Dayjs, Dayjs] | [string, string] | null,
|
|
||||||
dateStrings: [string, string]
|
|
||||||
) => {
|
|
||||||
if (!value) {
|
|
||||||
// 如果清空日期范围,显示所有记录
|
|
||||||
accessRecords.value = allRecords.value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用字符串数组进行过滤
|
|
||||||
const [startStr, endStr] = dateStrings
|
|
||||||
|
|
||||||
// 筛选在选定时间范围内的记录
|
|
||||||
accessRecords.value = allRecords.value.filter((record: AccessRecord) => {
|
|
||||||
const recordStart = new Date(record.startTime).getTime()
|
|
||||||
const recordEnd = new Date(record.endTime).getTime()
|
|
||||||
const filterStart = new Date(startStr).getTime()
|
|
||||||
const filterEnd = new Date(endStr).getTime()
|
|
||||||
|
|
||||||
// 记录的时间范围与选择的时间范围有重叠
|
|
||||||
return !(recordEnd < filterStart || recordStart > filterEnd)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新数据的方法
|
// 获取历史设备列表数据
|
||||||
const refreshData = () => {
|
async function getHistoricalDeviceList() {
|
||||||
|
loading.value = true;
|
||||||
// 模拟刷新操作
|
try {
|
||||||
accessRecords.value = allRecords.value
|
const { data, error } = await fetchHistoricalDevices();
|
||||||
console.log('刷新数据')
|
if (!error && data) {
|
||||||
|
deviceList.value = data.rows.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
deviceName: item.clientName,
|
||||||
|
macAddress: item.clientMac,
|
||||||
|
dataUsage: formatDataUsage(item.duration),
|
||||||
|
connectionTime: formatTimestamp(item.startTime),
|
||||||
|
disconnectionTime: formatTimestamp(item.endTime)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch historical device list:', err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认选择时间范围
|
// 刷新设备列表
|
||||||
const onRangeOk = (dates: [Dayjs, Dayjs] | [string, string]) => {
|
async function handleRefresh() {
|
||||||
console.log('确认选择时间范围: ', dates)
|
await getHistoricalDeviceList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
getHistoricalDeviceList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="records-container">
|
<div class="device-list-container">
|
||||||
<a-card :bordered="false">
|
<a-card :bordered="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
@@ -137,42 +126,33 @@ const onRangeOk = (dates: [Dayjs, Dayjs] | [string, string]) => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space>
|
<a-button type="primary" :loading="loading" @click="handleRefresh">
|
||||||
<a-range-picker
|
<template #icon>
|
||||||
style="margin-right: 8px"
|
<span class="i-carbon:refresh"></span>
|
||||||
:show-time="{ format: 'HH:mm' }"
|
</template>
|
||||||
format="YYYY-MM-DD HH:mm"
|
{{ t('page.records.refresh') }}
|
||||||
:placeholder="['开始时间', '结束时间']"
|
</a-button>
|
||||||
@change="onRangeChange"
|
|
||||||
@ok="onRangeOk"
|
|
||||||
/>
|
|
||||||
<a-button type="primary" @click="refreshData">
|
|
||||||
<template #icon>
|
|
||||||
<span class="i-carbon:refresh"></span>
|
|
||||||
</template>
|
|
||||||
{{ t('page.records.refresh') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-table
|
<a-table
|
||||||
|
:loading="loading"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data-source="accessRecords"
|
:data-source="deviceList"
|
||||||
:scroll="{ x: 'max-content' }"
|
:row-key="(record: DeviceRecord) => record.id"
|
||||||
size="small"
|
:expanded-row-keys="expandedRowKeys"
|
||||||
|
@expand="handleExpandChange"
|
||||||
:pagination="{
|
:pagination="{
|
||||||
total: accessRecords.length,
|
total: deviceList.length,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
showSizeChanger: true,
|
showSizeChanger: false,
|
||||||
showQuickJumper: true,
|
showTotal: (total) => `共 ${total} 条`
|
||||||
size: 'small',
|
|
||||||
showTotal: (total) => `共 ${total} 条记录`
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, text }">
|
<template #expandedRowRender="{ record }">
|
||||||
<template v-if="column.key === 'clientDeviceType'">
|
<div class="pl-4">
|
||||||
<a-tag :color="text === '手机' ? 'blue' : 'green'">{{ text }}</a-tag>
|
<div>{{ t('page.records.starttime') }}: {{ record.connectionTime }}</div>
|
||||||
</template>
|
<div>{{ t('page.records.endtime') }}: {{ record.disconnectionTime }}</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -180,7 +160,7 @@ const onRangeOk = (dates: [Dayjs, Dayjs] | [string, string]) => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.records-container {
|
.device-list-container {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background-color: #f5f5f7;
|
background-color: #f5f5f7;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@@ -194,6 +174,10 @@ const onRangeOk = (dates: [Dayjs, Dayjs] | [string, string]) => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pl-4 {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.ant-card-head) {
|
:deep(.ant-card-head) {
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
@@ -208,89 +192,11 @@ const onRangeOk = (dates: [Dayjs, Dayjs] | [string, string]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-table) {
|
:deep(.ant-table) {
|
||||||
font-size: 14px;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wrap-cell) {
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-word;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
.records-container {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-card-head-wrapper) {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-card-extra) {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-top: 8px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-picker) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-space) {
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-space-item) {
|
|
||||||
width: 100%;
|
|
||||||
margin-right: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-btn) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table) {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table th),
|
|
||||||
:deep(.ant-table td:not(.wrap-cell)) {
|
|
||||||
padding: 8px 4px !important;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wrap-cell) {
|
|
||||||
padding: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tag) {
|
|
||||||
margin-right: 0;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-picker-panels) {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-picker-panel) {
|
|
||||||
width: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-picker-datetime-panel) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-picker-time-panel) {
|
|
||||||
width: auto !important;
|
|
||||||
border-left: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.records-container {
|
.device-list-container {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user