feat
This commit is contained in:
@@ -1,130 +1,145 @@
|
||||
import { request } from "@/api/request";
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 获取 告警策略列表 */
|
||||
export const fetchPolicyList = (data: {
|
||||
page?: number,
|
||||
page_size?: number,
|
||||
keyword?: string,
|
||||
enabled?: boolean,
|
||||
priority?: number,
|
||||
created_at_start?: string,
|
||||
created_at_end?: string,
|
||||
order_by?: string,
|
||||
export const fetchPolicyList = (data: {
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
enabled?: boolean
|
||||
priority?: number
|
||||
created_at_start?: string
|
||||
created_at_end?: string
|
||||
order_by?: string
|
||||
order?: string
|
||||
}) => request.get("/Alert/v1/policy/list", { params: data });
|
||||
}) => request.get('/Alert/v1/policy/list', { params: data })
|
||||
|
||||
/** 获取 告警策略详情 */
|
||||
export const fetchPolicyDetail = (id: number) => request.get(`/Alert/v1/policy/get/${id}`);
|
||||
export const fetchPolicyDetail = (id: number) => request.get(`/Alert/v1/policy/get/${id}`)
|
||||
|
||||
/** 创建 告警策略 */
|
||||
export const createPolicy = (data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
enabled?: boolean;
|
||||
priority?: number;
|
||||
labels?: string;
|
||||
template_id?: number;
|
||||
auto_create_ticket?: boolean;
|
||||
feedback_template_id?: number;
|
||||
dispatch_rule?: string;
|
||||
}) => request.post("/Alert/v1/policy/create", data);
|
||||
name: string
|
||||
description?: string
|
||||
enabled?: boolean
|
||||
priority?: number
|
||||
labels?: string
|
||||
template_id?: number
|
||||
auto_create_ticket?: boolean
|
||||
feedback_template_id?: number
|
||||
dispatch_rule?: string
|
||||
}) => request.post('/Alert/v1/policy/create', data)
|
||||
|
||||
/** 更新 告警策略 */
|
||||
export const updatePolicy = (data: {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
enabled?: boolean;
|
||||
priority?: number;
|
||||
labels?: string;
|
||||
template_id?: number;
|
||||
auto_create_ticket?: boolean;
|
||||
feedback_template_id?: number;
|
||||
dispatch_rule?: string;
|
||||
}) => request.post("/Alert/v1/policy/update", data);
|
||||
id: number
|
||||
name?: string
|
||||
description?: string
|
||||
enabled?: boolean
|
||||
priority?: number
|
||||
labels?: string
|
||||
template_id?: number
|
||||
auto_create_ticket?: boolean
|
||||
feedback_template_id?: number
|
||||
dispatch_rule?: string
|
||||
}) => request.post('/Alert/v1/policy/update', data)
|
||||
|
||||
/** 删除 告警策略 */
|
||||
export const deletePolicy = (id: number) => request.delete(`/Alert/v1/policy/delete/${id}`);
|
||||
export const deletePolicy = (id: number) => request.delete(`/Alert/v1/policy/delete/${id}`)
|
||||
|
||||
/** 获取 告警规则列表 */
|
||||
export const fetchRuleList = (data: {
|
||||
policy_id?: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
sort?: string;
|
||||
policy_id?: number
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}) => request.get("/Alert/v1/rule/list", { params: data });
|
||||
}) => request.get('/Alert/v1/rule/list', { params: data })
|
||||
|
||||
/** 获取 告警规则详情 */
|
||||
export const fetchRuleDetail = (id: number) => request.get(`/Alert/v1/rule/get/${id}`);
|
||||
export const fetchRuleDetail = (id: number) => request.get(`/Alert/v1/rule/get/${id}`)
|
||||
|
||||
/** 创建 告警规则 */
|
||||
export const createRule = (data: {
|
||||
policy_id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
rule_type: string;
|
||||
severity_id: number;
|
||||
enabled?: boolean;
|
||||
metric_name?: string;
|
||||
query_expr?: string;
|
||||
threshold?: number;
|
||||
compare_op?: string;
|
||||
duration?: number;
|
||||
baseline_config?: string;
|
||||
labels?: string;
|
||||
annotations?: string;
|
||||
}) => request.post("/Alert/v1/rule/create", data);
|
||||
policy_id: number
|
||||
name: string
|
||||
description?: string
|
||||
rule_type: string
|
||||
severity_id: number
|
||||
enabled?: boolean
|
||||
metric_name?: string
|
||||
query_expr?: string
|
||||
threshold?: number
|
||||
compare_op?: string
|
||||
duration?: number
|
||||
baseline_config?: string
|
||||
labels?: string
|
||||
annotations?: string
|
||||
}) => request.post('/Alert/v1/rule/create', data)
|
||||
|
||||
/** 更新 告警规则 */
|
||||
export const updateRule = (data: {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
rule_type?: string;
|
||||
severity_id?: number;
|
||||
enabled?: boolean;
|
||||
metric_name?: string;
|
||||
query_expr?: string;
|
||||
threshold?: number;
|
||||
compare_op?: string;
|
||||
duration?: number;
|
||||
baseline_config?: string;
|
||||
labels?: string;
|
||||
annotations?: string;
|
||||
}) => request.post("/Alert/v1/rule/update", data);
|
||||
id: number
|
||||
name?: string
|
||||
description?: string
|
||||
rule_type?: string
|
||||
severity_id?: number
|
||||
enabled?: boolean
|
||||
metric_name?: string
|
||||
query_expr?: string
|
||||
threshold?: number
|
||||
compare_op?: string
|
||||
duration?: number
|
||||
baseline_config?: string
|
||||
labels?: string
|
||||
annotations?: string
|
||||
}) => request.post('/Alert/v1/rule/update', data)
|
||||
|
||||
/** 删除 告警规则 */
|
||||
export const deleteRule = (id: number) => request.delete(`/Alert/v1/rule/delete/${id}`);
|
||||
export const deleteRule = (id: number) => request.delete(`/Alert/v1/rule/delete/${id}`)
|
||||
|
||||
/** 获取 告警模板列表 */
|
||||
export const fetchTemplateList = (data: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
}) => request.get("/Alert/v1/template/list", { params: data });
|
||||
export const fetchTemplateList = (data: { page?: number; page_size?: number; keyword?: string }) =>
|
||||
request.get('/Alert/v1/template/list', { params: data })
|
||||
|
||||
/** 获取 告警级别列表 */
|
||||
export const fetchSeverityList = (data: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
enabled?: string;
|
||||
}) => request.get("/Alert/v1/severity/list", { params: data });
|
||||
export const fetchSeverityList = (data: { page?: number; page_size?: number; enabled?: string }) =>
|
||||
request.get('/Alert/v1/severity/list', { params: data })
|
||||
|
||||
/** 获取告警级别下拉选项(不分页),支持 keyword 模糊搜索与 enabled=true 过滤 */
|
||||
export const fetchSeverityOptions = (data?: {
|
||||
keyword?: string;
|
||||
enabled?: 'true' | 'false';
|
||||
}) =>
|
||||
request.get("/Alert/v1/severity/options", {
|
||||
export const fetchSeverityOptions = (data?: { keyword?: string; enabled?: 'true' | 'false' }) =>
|
||||
request.get('/Alert/v1/severity/options', {
|
||||
params: {
|
||||
keyword: data?.keyword || undefined,
|
||||
enabled: data?.enabled ?? 'true',
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
/** 获取工单模板下拉选项 */
|
||||
export const fetchFeedbackTemplateOptions = (data?: {
|
||||
status?: 'active' | 'inactive'
|
||||
}) => request.get('/Feedback/v1/templates/options', { params: data || {} });
|
||||
export const fetchFeedbackTemplateOptions = (data?: { status?: 'active' | 'inactive' }) =>
|
||||
request.get('/Feedback/v1/templates/options', { params: data || {} })
|
||||
|
||||
/** 告警策略选项项 */
|
||||
export interface PolicyOptionItem {
|
||||
id: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
name: string
|
||||
description: string
|
||||
enabled: boolean
|
||||
priority: number
|
||||
labels: string
|
||||
template_id: number
|
||||
auto_create_ticket: boolean
|
||||
feedback_template_id: number
|
||||
dispatch_rule: string
|
||||
}
|
||||
|
||||
/** 告警策略选项查询参数 */
|
||||
export interface PolicyOptionsParams {
|
||||
keyword?: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
/** 获取告警策略下拉选项(不分页),支持 keyword 模糊搜索与 enabled 过滤 */
|
||||
export const fetchPolicyOptions = (params?: PolicyOptionsParams) => request.get<PolicyOptionItem[]>('/Alert/v1/policy/options', { params })
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { request } from "@/api/request";
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 资产状态枚举 */
|
||||
export enum AssetStatus {
|
||||
IN_USE = 'in_use', // 在用
|
||||
IDLE = 'idle', // 闲置
|
||||
MAINTAIN = 'maintain', // 维修中
|
||||
SCRAP = 'scrap', // 待报废
|
||||
DISPOSED = 'disposed', // 已报废
|
||||
IN_USE = 'in_use', // 在用
|
||||
IDLE = 'idle', // 闲置
|
||||
MAINTAIN = 'maintain', // 维修中
|
||||
SCRAP = 'scrap', // 待报废
|
||||
DISPOSED = 'disposed', // 已报废
|
||||
}
|
||||
|
||||
/** 资产状态选项 */
|
||||
@@ -16,13 +16,13 @@ export const assetStatusOptions = [
|
||||
{ label: '维修中', value: AssetStatus.MAINTAIN },
|
||||
{ label: '待报废', value: AssetStatus.SCRAP },
|
||||
{ label: '已报废', value: AssetStatus.DISPOSED },
|
||||
];
|
||||
]
|
||||
|
||||
/** 获取资产状态文本 */
|
||||
export const getAssetStatusText = (status: string) => {
|
||||
const item = assetStatusOptions.find(opt => opt.value === status);
|
||||
return item?.label || status;
|
||||
};
|
||||
const item = assetStatusOptions.find((opt) => opt.value === status)
|
||||
return item?.label || status
|
||||
}
|
||||
|
||||
/** 获取资产状态颜色 */
|
||||
export const getAssetStatusColor = (status: string) => {
|
||||
@@ -32,113 +32,117 @@ export const getAssetStatusColor = (status: string) => {
|
||||
[AssetStatus.MAINTAIN]: 'orange',
|
||||
[AssetStatus.SCRAP]: 'red',
|
||||
[AssetStatus.DISPOSED]: 'gray',
|
||||
};
|
||||
return colorMap[status] || 'gray';
|
||||
};
|
||||
}
|
||||
return colorMap[status] || 'gray'
|
||||
}
|
||||
|
||||
/** 资产列表查询参数 */
|
||||
export interface AssetListParams {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
status?: string;
|
||||
category_id?: number;
|
||||
supplier_id?: number;
|
||||
datacenter_id?: number;
|
||||
department?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
status?: string
|
||||
category_id?: number
|
||||
supplier_id?: number
|
||||
datacenter_id?: number
|
||||
floor_id?: number
|
||||
room_id?: number
|
||||
rack_id?: number
|
||||
department?: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}
|
||||
|
||||
/** 资产表单数据 */
|
||||
export interface AssetForm {
|
||||
id?: number;
|
||||
asset_name: string;
|
||||
asset_code: string;
|
||||
category_id?: number;
|
||||
model?: string;
|
||||
manufacturer?: string;
|
||||
serial_number?: string;
|
||||
purchase_date?: string;
|
||||
original_value?: number;
|
||||
supplier_id?: number;
|
||||
warranty_period?: string;
|
||||
warranty_expiry?: string;
|
||||
department?: string;
|
||||
user?: string;
|
||||
status?: string;
|
||||
location?: string;
|
||||
datacenter_id?: number;
|
||||
floor_id?: number;
|
||||
rack_id?: number;
|
||||
unit_start?: number;
|
||||
unit_end?: number;
|
||||
qr_code?: string;
|
||||
rfid_tag?: string;
|
||||
asset_tag?: string;
|
||||
specifications?: string;
|
||||
description?: string;
|
||||
remarks?: string;
|
||||
id?: number
|
||||
asset_name: string
|
||||
asset_code: string
|
||||
category_id?: number
|
||||
model?: string
|
||||
manufacturer?: string
|
||||
serial_number?: string
|
||||
purchase_date?: string
|
||||
original_value?: number
|
||||
supplier_id?: number
|
||||
warranty_period?: string
|
||||
warranty_expiry?: string
|
||||
department?: string
|
||||
user?: string
|
||||
status?: string
|
||||
location?: string
|
||||
datacenter_id?: number
|
||||
floor_id?: number
|
||||
room_id?: number
|
||||
rack_id?: number
|
||||
unit_start?: number
|
||||
unit_end?: number
|
||||
qr_code?: string
|
||||
rfid_tag?: string
|
||||
asset_tag?: string
|
||||
specifications?: string
|
||||
description?: string
|
||||
remarks?: string
|
||||
}
|
||||
|
||||
/** 获取资产列表(分页) */
|
||||
export const fetchAssetList = (data?: AssetListParams) => {
|
||||
return request.post("/Assets/v1/asset/list", data || {});
|
||||
};
|
||||
return request.post('/Assets/v1/asset/list', data || {})
|
||||
}
|
||||
|
||||
/** 获取资产列表(不分页,下拉) */
|
||||
export const fetchAssetAll = (params?: { keyword?: string }) => {
|
||||
return request.get("/Assets/v1/asset/all", { params });
|
||||
};
|
||||
return request.get('/Assets/v1/asset/all', { params })
|
||||
}
|
||||
|
||||
/** 获取资产详情 */
|
||||
export const fetchAssetDetail = (id: number) => {
|
||||
return request.get(`/Assets/v1/asset/detail/${id}`);
|
||||
};
|
||||
return request.get(`/Assets/v1/asset/detail/${id}`)
|
||||
}
|
||||
|
||||
/** 创建资产 */
|
||||
export const createAsset = (data: AssetForm) => {
|
||||
return request.post("/Assets/v1/asset/create", data);
|
||||
};
|
||||
return request.post('/Assets/v1/asset/create', data)
|
||||
}
|
||||
|
||||
/** 更新资产 */
|
||||
export const updateAsset = (data: AssetForm) => {
|
||||
return request.put("/Assets/v1/asset/update", data);
|
||||
};
|
||||
return request.put('/Assets/v1/asset/update', data)
|
||||
}
|
||||
|
||||
/** 删除资产 */
|
||||
export const deleteAsset = (id: number) => {
|
||||
return request.delete(`/Assets/v1/asset/delete/${id}`);
|
||||
};
|
||||
return request.delete(`/Assets/v1/asset/delete/${id}`)
|
||||
}
|
||||
|
||||
/** 导出资产 */
|
||||
export const exportAssets = (keyword?: string) => {
|
||||
const params: any = {};
|
||||
if (keyword) params.keyword = keyword;
|
||||
return request.get("/Assets/v1/asset/export", { params });
|
||||
};
|
||||
const params: any = {}
|
||||
if (keyword) params.keyword = keyword
|
||||
return request.get('/Assets/v1/asset/export', { params })
|
||||
}
|
||||
|
||||
/** 获取资产分类列表(下拉) */
|
||||
export const fetchCategoryOptions = () => {
|
||||
return request.get("/Assets/v1/category/all");
|
||||
};
|
||||
return request.get('/Assets/v1/category/all')
|
||||
}
|
||||
|
||||
/** 获取供应商列表(下拉) */
|
||||
export const fetchSupplierOptions = () => {
|
||||
return request.get("/Assets/v1/supplier/all");
|
||||
};
|
||||
return request.get('/Assets/v1/supplier/all')
|
||||
}
|
||||
|
||||
/** 获取数据中心列表(下拉) */
|
||||
export const fetchDatacenterOptions = () => {
|
||||
return request.get("/Assets/v1/datacenter/list");
|
||||
};
|
||||
return request.get('/Assets/v1/datacenter/list')
|
||||
}
|
||||
|
||||
/** 根据数据中心获取楼层列表 */
|
||||
export const fetchFloorOptions = (datacenterId: number) => {
|
||||
return request.get(`/Assets/v1/datacenter/${datacenterId}`);
|
||||
};
|
||||
return request.get(`/Assets/v1/datacenter/${datacenterId}`)
|
||||
}
|
||||
|
||||
/** 获取机柜列表(下拉) */
|
||||
export const fetchRackOptions = (params?: { datacenter_id?: number; floor_id?: number }) => {
|
||||
return request.post("/Assets/v1/rack/list", params || {});
|
||||
};
|
||||
return request.post('/Assets/v1/rack/list', params || {})
|
||||
}
|
||||
|
||||
@@ -1,73 +1,69 @@
|
||||
import { request } from "@/api/request";
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 获取机柜列表(分页) */
|
||||
export const fetchRackList = (data?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
datacenter_id?: number;
|
||||
floor_id?: number;
|
||||
rack_type?: string;
|
||||
status?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
datacenter_id?: number
|
||||
floor_id?: number
|
||||
room_id?: number
|
||||
rack_type?: string
|
||||
status?: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}) => {
|
||||
return request.post("/Assets/v1/rack/list", data || {});
|
||||
};
|
||||
return request.post('/Assets/v1/rack/list', data || {})
|
||||
}
|
||||
|
||||
/** 根据数据中心获取机柜列表(下拉,不分页) */
|
||||
export const fetchRackListByDatacenter = (
|
||||
datacenterId: number,
|
||||
params?: { name?: string }
|
||||
) => {
|
||||
return request.get(`/Assets/v1/rack/datacenter/${datacenterId}`, { params });
|
||||
};
|
||||
export const fetchRackListByDatacenter = (datacenterId: number, params?: { name?: string }) => {
|
||||
return request.get(`/Assets/v1/rack/datacenter/${datacenterId}`, { params })
|
||||
}
|
||||
|
||||
/** 根据楼层获取机柜列表(下拉,不分页) */
|
||||
export const fetchRackListByFloor = (
|
||||
floorId: number,
|
||||
params?: { name?: string }
|
||||
) => {
|
||||
return request.get(`/Assets/v1/rack/floor/${floorId}`, { params });
|
||||
};
|
||||
/** 根据楼层获取机柜列表(下拉,不分页)- 已废弃,请使用 fetchRackListByRoom */
|
||||
export const fetchRackListByFloor = (floorId: number, params?: { name?: string }) => {
|
||||
return request.get(`/Assets/v1/rack/floor/${floorId}`, { params })
|
||||
}
|
||||
|
||||
/** 根据机房获取机柜列表(下拉,不分页) */
|
||||
export const fetchRackListByRoom = (roomId: number, params?: { name?: string }) => {
|
||||
return request.get(`/Assets/v1/rack/room/${roomId}`, { params })
|
||||
}
|
||||
|
||||
/** 获取机柜详情 */
|
||||
export const fetchRackDetail = (id: number) => {
|
||||
return request.get(`/Assets/v1/rack/detail/${id}`);
|
||||
};
|
||||
return request.get(`/Assets/v1/rack/detail/${id}`)
|
||||
}
|
||||
|
||||
/** 创建机柜 */
|
||||
export const createRack = (data: any) => {
|
||||
return request.post("/Assets/v1/rack/create", data);
|
||||
};
|
||||
return request.post('/Assets/v1/rack/create', data)
|
||||
}
|
||||
|
||||
/** 更新机柜 */
|
||||
export const updateRack = (data: any) => {
|
||||
return request.put("/Assets/v1/rack/update", data);
|
||||
};
|
||||
return request.put('/Assets/v1/rack/update', data)
|
||||
}
|
||||
|
||||
/** 删除机柜 */
|
||||
export const deleteRack = (id: number) => {
|
||||
return request.delete(`/Assets/v1/rack/delete/${id}`);
|
||||
};
|
||||
return request.delete(`/Assets/v1/rack/delete/${id}`)
|
||||
}
|
||||
|
||||
/** 获取供应商列表(用于下拉选择) */
|
||||
export const fetchSupplierList = () => {
|
||||
return request.get("/Assets/v1/supplier/all");
|
||||
};
|
||||
return request.get('/Assets/v1/supplier/all')
|
||||
}
|
||||
|
||||
/** 获取数据中心列表(用于下拉选择) */
|
||||
export const fetchDatacenterList = (params?: { keyword?: string; name?: string }) => {
|
||||
const normalizedParams = params?.keyword
|
||||
? { ...params, name: params.name ?? params.keyword }
|
||||
: params
|
||||
return request.get("/Assets/v1/datacenter/all", { params: normalizedParams });
|
||||
};
|
||||
const normalizedParams = params?.keyword ? { ...params, name: params.name ?? params.keyword } : params
|
||||
return request.get('/Assets/v1/datacenter/all', { params: normalizedParams })
|
||||
}
|
||||
|
||||
/** 获取楼层列表(用于下拉选择) */
|
||||
export const fetchFloorListForSelect = (params?: { datacenter_id?: number; keyword?: string; name?: string }) => {
|
||||
const normalizedParams = params?.keyword
|
||||
? { ...params, name: params.name ?? params.keyword }
|
||||
: params
|
||||
return request.get("/Assets/v1/floor/all", { params: normalizedParams });
|
||||
};
|
||||
const normalizedParams = params?.keyword ? { ...params, name: params.name ?? params.keyword } : params
|
||||
return request.get('/Assets/v1/floor/all', { params: normalizedParams })
|
||||
}
|
||||
|
||||
155
src/api/ops/room-device.ts
Normal file
155
src/api/ops/room-device.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 机房设备服务项 */
|
||||
export interface RoomDeviceItem {
|
||||
id: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
deleted_at: string | null
|
||||
service_identity: string
|
||||
name: string
|
||||
description: string
|
||||
room_id: string
|
||||
device_category: string
|
||||
agent_config: string
|
||||
enabled: boolean
|
||||
collect_on: boolean
|
||||
collect_interval: number
|
||||
collect_last_result: string
|
||||
policy_ids?: number[]
|
||||
}
|
||||
|
||||
/** 机房设备列表响应 */
|
||||
export interface RoomDeviceListResponse {
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
data: RoomDeviceItem[]
|
||||
}
|
||||
|
||||
/** 机房设备列表查询参数 */
|
||||
export interface RoomDeviceListParams {
|
||||
page?: number
|
||||
size?: number
|
||||
keyword?: string
|
||||
enabled?: boolean
|
||||
room_id?: string
|
||||
device_category?: string
|
||||
}
|
||||
|
||||
/** 机房设备创建数据 */
|
||||
export interface RoomDeviceCreateData {
|
||||
service_identity?: string
|
||||
name: string
|
||||
description?: string
|
||||
room_id: string
|
||||
device_category: string
|
||||
agent_config?: string
|
||||
enabled?: boolean
|
||||
collect_on?: boolean
|
||||
collect_interval?: number
|
||||
policy_ids?: number[]
|
||||
}
|
||||
|
||||
/** 机房设备更新数据 */
|
||||
export interface RoomDeviceUpdateData {
|
||||
name?: string
|
||||
description?: string
|
||||
room_id?: string
|
||||
device_category?: string
|
||||
agent_config?: string
|
||||
enabled?: boolean
|
||||
collect_on?: boolean
|
||||
collect_interval?: number
|
||||
policy_ids?: number[]
|
||||
}
|
||||
|
||||
/** 机房设备采集配置数据 */
|
||||
export interface RoomDeviceCollectData {
|
||||
collect_on?: boolean
|
||||
collect_interval?: number
|
||||
agent_config?: string
|
||||
}
|
||||
|
||||
/** 指标数据项 */
|
||||
export interface MetricItem {
|
||||
timestamp: string
|
||||
service_identity: string
|
||||
room_id: string
|
||||
device_category: string
|
||||
type?: string
|
||||
metric_name: string
|
||||
metric_value: number
|
||||
metric_unit?: string
|
||||
}
|
||||
|
||||
/** 指标上报数据 */
|
||||
export interface MetricsUploadData {
|
||||
metrics: MetricItem[]
|
||||
}
|
||||
|
||||
/** 获取机房设备列表(分页) */
|
||||
export const fetchRoomDeviceList = (params?: RoomDeviceListParams) => {
|
||||
return request.get<RoomDeviceListResponse>('/DC-Control/v1/room-devices', { params })
|
||||
}
|
||||
|
||||
/** 获取机房设备详情 */
|
||||
export const fetchRoomDeviceDetail = (id: number) => {
|
||||
return request.get<RoomDeviceItem>(`/DC-Control/v1/room-devices/${id}`)
|
||||
}
|
||||
|
||||
/** 创建机房设备 */
|
||||
export const createRoomDevice = (data: RoomDeviceCreateData) => {
|
||||
return request.post<{ message: string; id: number }>('/DC-Control/v1/room-devices', data)
|
||||
}
|
||||
|
||||
/** 更新机房设备 */
|
||||
export const updateRoomDevice = (id: number, data: RoomDeviceUpdateData) => {
|
||||
return request.put<{ message: string }>(`/DC-Control/v1/room-devices/${id}`, data)
|
||||
}
|
||||
|
||||
/** 删除机房设备 */
|
||||
export const deleteRoomDevice = (id: number) => {
|
||||
return request.delete<{ message: string }>(`/DC-Control/v1/room-devices/${id}`)
|
||||
}
|
||||
|
||||
/** 更新采集配置 */
|
||||
export const patchRoomDeviceCollect = (id: number, data: RoomDeviceCollectData) => {
|
||||
return request.patch<{ message: string }>(`/DC-Control/v1/room-devices/${id}/collect`, data)
|
||||
}
|
||||
|
||||
/** 查询最新指标 */
|
||||
export const fetchLatestMetrics = (serviceIdentity: string) => {
|
||||
return request.get<MetricItem[]>('/DC-Control/v1/room-devices/metrics/latest', {
|
||||
params: { service_identity: serviceIdentity },
|
||||
})
|
||||
}
|
||||
|
||||
/** 上报指标(匿名接口) */
|
||||
export const uploadMetrics = (data: MetricsUploadData) => {
|
||||
return request.post<{ message: string }>('/DC-Control/v1/room-devices/metrics/upload', data)
|
||||
}
|
||||
|
||||
/** 设备分类选项 */
|
||||
export const DEVICE_CATEGORY_OPTIONS = [
|
||||
{ label: '电力', value: 'power' },
|
||||
{ label: 'UPS', value: 'ups' },
|
||||
{ label: '空调', value: 'air_conditioner' },
|
||||
{ label: '温湿度', value: 'temp_humidity' },
|
||||
{ label: '消防', value: 'fire_control' },
|
||||
{ label: '门禁', value: 'access_control' },
|
||||
{ label: '漏水', value: 'water_leak' },
|
||||
{ label: '有害气体', value: 'hazardous_gas' },
|
||||
]
|
||||
|
||||
/** 设备分类映射 */
|
||||
export const DEVICE_CATEGORY_MAP: Record<string, string> = {
|
||||
power: '电力',
|
||||
ups: 'UPS',
|
||||
air_conditioner: '空调',
|
||||
temp_humidity: '温湿度',
|
||||
fire_control: '消防',
|
||||
access_control: '门禁',
|
||||
water_leak: '漏水',
|
||||
hazardous_gas: '有害气体',
|
||||
}
|
||||
87
src/api/ops/room.ts
Normal file
87
src/api/ops/room.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 机房项 */
|
||||
export interface RoomItem {
|
||||
id: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
deleted_at: string | null
|
||||
name: string
|
||||
code: string
|
||||
description?: string
|
||||
floor_id: number
|
||||
datacenter_id: number
|
||||
status?: string
|
||||
floor?: { id: number; name: string }
|
||||
datacenter?: { id: number; name: string }
|
||||
}
|
||||
|
||||
/** 机房列表响应 */
|
||||
export interface RoomListResponse {
|
||||
code: number
|
||||
details: {
|
||||
data: RoomItem[]
|
||||
total: number
|
||||
}
|
||||
}
|
||||
|
||||
/** 机房列表查询参数 */
|
||||
export interface RoomListParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
datacenter_id?: number
|
||||
floor_id?: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
/** 机房创建数据 */
|
||||
export interface RoomCreateData {
|
||||
name: string
|
||||
code: string
|
||||
description?: string
|
||||
floor_id: number
|
||||
datacenter_id: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
/** 机房更新数据 */
|
||||
export interface RoomUpdateData {
|
||||
id: number
|
||||
name?: string
|
||||
code?: string
|
||||
description?: string
|
||||
floor_id?: number
|
||||
datacenter_id?: number
|
||||
status?: string
|
||||
}
|
||||
|
||||
/** 获取机房列表(分页) */
|
||||
export const fetchRoomList = (data?: RoomListParams) => {
|
||||
return request.post<RoomListResponse>('/Assets/v1/room/list', data || {})
|
||||
}
|
||||
|
||||
/** 获取机房详情 */
|
||||
export const fetchRoomDetail = (id: number) => {
|
||||
return request.get<RoomItem>(`/Assets/v1/room/detail/${id}`)
|
||||
}
|
||||
|
||||
/** 根据楼层获取机房列表(下拉,不分页) */
|
||||
export const fetchRoomListByFloor = (floorId: number, params?: { name?: string }) => {
|
||||
return request.get<RoomItem[]>(`/Assets/v1/room/floor/${floorId}`, { params })
|
||||
}
|
||||
|
||||
/** 创建机房 */
|
||||
export const createRoom = (data: RoomCreateData) => {
|
||||
return request.post('/Assets/v1/room/create', data)
|
||||
}
|
||||
|
||||
/** 更新机房 */
|
||||
export const updateRoom = (data: RoomUpdateData) => {
|
||||
return request.put('/Assets/v1/room/update', data)
|
||||
}
|
||||
|
||||
/** 删除机房 */
|
||||
export const deleteRoom = (id: number) => {
|
||||
return request.delete(`/Assets/v1/room/delete/${id}`)
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
<!-- 机柜选择卡片 -->
|
||||
<a-card class="rack-select-card">
|
||||
<template #title>
|
||||
<icon-storage /> 选择机柜
|
||||
<icon-storage />
|
||||
选择机柜
|
||||
</template>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model="selectedDatacenterId"
|
||||
placeholder="请选择数据中心"
|
||||
@@ -16,16 +17,12 @@
|
||||
@change="handleDatacenterChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-option
|
||||
v-for="datacenter in datacenterList"
|
||||
:key="datacenter.value"
|
||||
:value="datacenter.value"
|
||||
>
|
||||
<a-option v-for="datacenter in datacenterList" :key="datacenter.value" :value="datacenter.value">
|
||||
{{ datacenter.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model="selectedFloorId"
|
||||
placeholder="请选择楼层"
|
||||
@@ -36,49 +33,47 @@
|
||||
@change="handleFloorChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-option
|
||||
v-for="floor in floorList"
|
||||
:key="floor.value"
|
||||
:value="floor.value"
|
||||
>
|
||||
<a-option v-for="floor in floorList" :key="floor.value" :value="floor.value">
|
||||
{{ floor.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model="selectedRoomId"
|
||||
placeholder="请选择机房"
|
||||
:loading="roomListLoading"
|
||||
:disabled="!selectedFloorId"
|
||||
allow-search
|
||||
@search="handleRoomSearch"
|
||||
@change="handleRoomChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-option v-for="room in roomList" :key="room.id" :value="room.id">{{ room.name }} ({{ room.code }})</a-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model="selectedRackId"
|
||||
placeholder="请选择机柜"
|
||||
:loading="rackListLoading"
|
||||
:disabled="!selectedFloorId"
|
||||
:disabled="!selectedRoomId"
|
||||
allow-search
|
||||
@search="handleRackSearch"
|
||||
@change="handleRackChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-option
|
||||
v-for="rack in rackList"
|
||||
:key="rack.id"
|
||||
:value="rack.id"
|
||||
>
|
||||
{{ rack.name }} ({{ rack.code }})
|
||||
</a-option>
|
||||
<a-option v-for="rack in rackList" :key="rack.id" :value="rack.id">{{ rack.name }} ({{ rack.code }})</a-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="16">
|
||||
</a-row>
|
||||
<a-row :gutter="16" style="margin-top: 16px">
|
||||
<a-col :span="24">
|
||||
<a-space>
|
||||
<a-tag v-if="rackInfo.height">
|
||||
总U位: {{ rackInfo.height }}
|
||||
</a-tag>
|
||||
<a-tag v-if="usedUnits" color="blue">
|
||||
已使用: {{ usedUnits }}
|
||||
</a-tag>
|
||||
<a-tag v-if="availableUnits" color="green">
|
||||
空余: {{ availableUnits }}
|
||||
</a-tag>
|
||||
<a-tag v-if="usagePercentage" :color="usagePercentage > 80 ? 'red' : 'orange'">
|
||||
使用率: {{ usagePercentage }}%
|
||||
</a-tag>
|
||||
<a-tag v-if="rackInfo.height">总U位: {{ rackInfo.height }}</a-tag>
|
||||
<a-tag v-if="usedUnits" color="blue">已使用: {{ usedUnits }}</a-tag>
|
||||
<a-tag v-if="availableUnits" color="green">空余: {{ availableUnits }}</a-tag>
|
||||
<a-tag v-if="usagePercentage" :color="usagePercentage > 80 ? 'red' : 'orange'">使用率: {{ usagePercentage }}%</a-tag>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -111,17 +106,11 @@
|
||||
<!-- U位列表 -->
|
||||
<a-card class="u-position-card" :loading="loading">
|
||||
<template #title>
|
||||
<icon-apps /> U位列表
|
||||
<icon-apps />
|
||||
U位列表
|
||||
</template>
|
||||
|
||||
<a-table
|
||||
:data="unitList"
|
||||
:pagination="false"
|
||||
:bordered="{ cell: true }"
|
||||
:scroll="{ x: 1400 }"
|
||||
:loading="loading"
|
||||
size="small"
|
||||
>
|
||||
|
||||
<a-table :data="unitList" :pagination="false" :bordered="{ cell: true }" :scroll="{ x: 1400 }" :loading="loading" size="small">
|
||||
<template #columns>
|
||||
<!-- <a-table-column title="序号" :width="80">
|
||||
<template #cell="{ rowIndex }">
|
||||
@@ -146,34 +135,14 @@
|
||||
<template #cell="{ record }">
|
||||
<a-space size="small">
|
||||
<!-- 禁用/启用按钮(所有状态都显示) -->
|
||||
<a-button
|
||||
v-if="record.status !== 'disabled'"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDisable(record)"
|
||||
>
|
||||
禁用
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleEnable(record)"
|
||||
>
|
||||
启用
|
||||
</a-button>
|
||||
|
||||
<a-button v-if="record.status !== 'disabled'" type="text" size="small" @click="handleDisable(record)">禁用</a-button>
|
||||
<a-button v-else type="text" size="small" @click="handleEnable(record)">启用</a-button>
|
||||
|
||||
<!-- 已占用状态:显示释放按钮 -->
|
||||
<a-button
|
||||
v-if="record.status === 'occupied'"
|
||||
type="text"
|
||||
size="small"
|
||||
status="danger"
|
||||
@click="handleRelease(record)"
|
||||
>
|
||||
<a-button v-if="record.status === 'occupied'" type="text" size="small" status="danger" @click="handleRelease(record)">
|
||||
释放
|
||||
</a-button>
|
||||
|
||||
|
||||
<!-- 已预留状态:显示取消预留按钮 -->
|
||||
<a-button
|
||||
v-if="record.status === 'reserved'"
|
||||
@@ -213,19 +182,10 @@
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { IconStorage, IconApps, IconPlus, IconLock, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||
import {
|
||||
fetchUnitList,
|
||||
allocateUnit,
|
||||
reserveUnit,
|
||||
cancelReservation,
|
||||
releaseUnit,
|
||||
updateUnitStatus,
|
||||
} from '@/api/ops/unit'
|
||||
import {
|
||||
fetchDatacenterList,
|
||||
fetchRackListByFloor,
|
||||
} from '@/api/ops/rack'
|
||||
import { fetchUnitList, allocateUnit, reserveUnit, cancelReservation, releaseUnit, updateUnitStatus } from '@/api/ops/unit'
|
||||
import { fetchDatacenterList, fetchRackListByRoom } from '@/api/ops/rack'
|
||||
import { fetchFloorListByDatacenter } from '@/api/ops/floor'
|
||||
import { fetchRoomListByFloor } from '@/api/ops/room'
|
||||
import { normalizeUnitList } from './utils/unitFallback'
|
||||
import AllocateUnitDialog from './components/AllocateUnitDialog.vue'
|
||||
import ReserveUnitDialog from './components/ReserveUnitDialog.vue'
|
||||
@@ -235,16 +195,20 @@ const loading = ref(false)
|
||||
const rackListLoading = ref(false)
|
||||
const datacenterListLoading = ref(false)
|
||||
const floorListLoading = ref(false)
|
||||
const roomListLoading = ref(false)
|
||||
const selectedDatacenterId = ref<number | undefined>(undefined)
|
||||
const selectedFloorId = ref<number | undefined>(undefined)
|
||||
const selectedRoomId = ref<number | undefined>(undefined)
|
||||
const selectedRackId = ref<number | undefined>(undefined)
|
||||
const rackInfo = ref<any>({})
|
||||
const datacenterList = ref<{ label: string; value: number }[]>([])
|
||||
const floorList = ref<{ label: string; value: number }[]>([])
|
||||
const roomList = ref<any[]>([])
|
||||
const rackList = ref<any[]>([])
|
||||
const unitList = ref<any[]>([])
|
||||
let datacenterSearchTimer: number | undefined
|
||||
let floorSearchTimer: number | undefined
|
||||
let roomSearchTimer: number | undefined
|
||||
let rackSearchTimer: number | undefined
|
||||
|
||||
// 对话框可见性
|
||||
@@ -253,9 +217,7 @@ const reserveVisible = ref(false)
|
||||
|
||||
// 已使用U位
|
||||
const usedUnits = computed(() => {
|
||||
return unitList.value.filter(
|
||||
(unit) => unit.status === 'occupied' || unit.status === 'reserved'
|
||||
).length
|
||||
return unitList.value.filter((unit) => unit.status === 'occupied' || unit.status === 'reserved').length
|
||||
})
|
||||
|
||||
// 空余U位
|
||||
@@ -270,12 +232,7 @@ const usagePercentage = computed(() => {
|
||||
})
|
||||
|
||||
const extractList = (res: any): any[] => {
|
||||
const candidate =
|
||||
res?.data?.data ??
|
||||
res?.details?.data ??
|
||||
res?.data ??
|
||||
res?.details ??
|
||||
[]
|
||||
const candidate = res?.data?.data ?? res?.details?.data ?? res?.data ?? res?.details ?? []
|
||||
return Array.isArray(candidate) ? candidate : []
|
||||
}
|
||||
|
||||
@@ -303,14 +260,14 @@ const getUnitStatusText = (status?: string) => {
|
||||
|
||||
// 获取机柜列表
|
||||
const fetchRacks = async (keyword?: string) => {
|
||||
if (!selectedFloorId.value) {
|
||||
if (!selectedRoomId.value) {
|
||||
rackList.value = []
|
||||
return
|
||||
}
|
||||
rackListLoading.value = true
|
||||
|
||||
|
||||
try {
|
||||
const res: any = await fetchRackListByFloor(selectedFloorId.value, { name: keyword })
|
||||
const res: any = await fetchRackListByRoom(selectedRoomId.value, { name: keyword })
|
||||
rackList.value = extractList(res)
|
||||
} catch (error) {
|
||||
console.error('获取机柜列表失败:', error)
|
||||
@@ -363,6 +320,26 @@ const fetchFloors = async (keyword?: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchRooms = async (keyword?: string) => {
|
||||
if (!selectedFloorId.value) {
|
||||
roomList.value = []
|
||||
return
|
||||
}
|
||||
roomListLoading.value = true
|
||||
try {
|
||||
const res: any = await fetchRoomListByFloor(selectedFloorId.value, {
|
||||
name: keyword || undefined,
|
||||
})
|
||||
roomList.value = extractList(res)
|
||||
} catch (error) {
|
||||
console.error('获取机房列表失败:', error)
|
||||
Message.error('获取机房列表失败')
|
||||
roomList.value = []
|
||||
} finally {
|
||||
roomListLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDatacenterSearch = (keyword: string) => {
|
||||
if (datacenterSearchTimer) {
|
||||
window.clearTimeout(datacenterSearchTimer)
|
||||
@@ -382,8 +359,18 @@ const handleFloorSearch = (keyword: string) => {
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const handleRackSearch = (keyword: string) => {
|
||||
const handleRoomSearch = (keyword: string) => {
|
||||
if (!selectedFloorId.value) return
|
||||
if (roomSearchTimer) {
|
||||
window.clearTimeout(roomSearchTimer)
|
||||
}
|
||||
roomSearchTimer = window.setTimeout(() => {
|
||||
fetchRooms(keyword?.trim() || undefined)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const handleRackSearch = (keyword: string) => {
|
||||
if (!selectedRoomId.value) return
|
||||
if (rackSearchTimer) {
|
||||
window.clearTimeout(rackSearchTimer)
|
||||
}
|
||||
@@ -397,9 +384,11 @@ const handleDatacenterChange = async (datacenterId?: number | string) => {
|
||||
selectedDatacenterId.value = Number(datacenterId)
|
||||
}
|
||||
selectedFloorId.value = undefined
|
||||
selectedRoomId.value = undefined
|
||||
selectedRackId.value = undefined
|
||||
rackInfo.value = {}
|
||||
rackList.value = []
|
||||
roomList.value = []
|
||||
floorList.value = []
|
||||
unitList.value = []
|
||||
await fetchFloors()
|
||||
@@ -409,6 +398,19 @@ const handleFloorChange = async (floorId?: number | string) => {
|
||||
if (floorId !== undefined && floorId !== null && floorId !== '') {
|
||||
selectedFloorId.value = Number(floorId)
|
||||
}
|
||||
selectedRoomId.value = undefined
|
||||
selectedRackId.value = undefined
|
||||
rackInfo.value = {}
|
||||
rackList.value = []
|
||||
roomList.value = []
|
||||
unitList.value = []
|
||||
await fetchRooms()
|
||||
}
|
||||
|
||||
const handleRoomChange = async (roomId?: number | string) => {
|
||||
if (roomId !== undefined && roomId !== null && roomId !== '') {
|
||||
selectedRoomId.value = Number(roomId)
|
||||
}
|
||||
selectedRackId.value = undefined
|
||||
rackInfo.value = {}
|
||||
rackList.value = []
|
||||
@@ -429,14 +431,14 @@ const handleRackChange = (rackId: number) => {
|
||||
// 获取U位列表
|
||||
const fetchUnits = async (rackId?: number) => {
|
||||
const targetRackId = rackId || selectedRackId.value
|
||||
|
||||
|
||||
if (!targetRackId) return
|
||||
|
||||
|
||||
loading.value = true
|
||||
|
||||
|
||||
try {
|
||||
const res = await fetchUnitList(targetRackId)
|
||||
|
||||
|
||||
if (res.code === 0) {
|
||||
const payload = res?.details ?? res?.data ?? {}
|
||||
rackInfo.value = payload?.rack || {}
|
||||
@@ -489,14 +491,14 @@ const handleDisable = async (record: any) => {
|
||||
Message.warning('请先选择机柜')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const res = await updateUnitStatus({
|
||||
rack_id: selectedRackId.value,
|
||||
start_unit: record.unit_number,
|
||||
end_unit: record.unit_number,
|
||||
status: 'disabled',
|
||||
})
|
||||
|
||||
|
||||
if (res.code === 0) {
|
||||
Message.success('禁用成功')
|
||||
fetchUnits()
|
||||
@@ -521,14 +523,14 @@ const handleEnable = async (record: any) => {
|
||||
Message.warning('请先选择机柜')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const res = await updateUnitStatus({
|
||||
rack_id: selectedRackId.value,
|
||||
start_unit: record.unit_number,
|
||||
end_unit: record.unit_number,
|
||||
status: 'available',
|
||||
})
|
||||
|
||||
|
||||
if (res.code === 0) {
|
||||
Message.success('启用成功')
|
||||
fetchUnits()
|
||||
@@ -549,21 +551,19 @@ const handleRelease = async (record: any) => {
|
||||
title: '确认释放',
|
||||
content: `确认释放 U位 ${record.unit_number} 吗?`,
|
||||
onOk: async () => {
|
||||
const endUnit = record.occupied_units > 1
|
||||
? record.unit_number + record.occupied_units - 1
|
||||
: record.unit_number
|
||||
|
||||
const endUnit = record.occupied_units > 1 ? record.unit_number + record.occupied_units - 1 : record.unit_number
|
||||
|
||||
if (!selectedRackId.value) {
|
||||
Message.warning('请先选择机柜')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const res = await releaseUnit({
|
||||
rack_id: selectedRackId.value,
|
||||
start_unit: record.unit_number,
|
||||
end_unit: endUnit,
|
||||
})
|
||||
|
||||
|
||||
if (res.code === 0) {
|
||||
Message.success('释放成功')
|
||||
fetchUnits()
|
||||
@@ -584,21 +584,19 @@ const handleCancelReservation = async (record: any) => {
|
||||
title: '确认取消预留',
|
||||
content: `确认取消 U位 ${record.unit_number} 的预留吗?`,
|
||||
onOk: async () => {
|
||||
const endUnit = record.occupied_units > 1
|
||||
? record.unit_number + record.occupied_units - 1
|
||||
: record.unit_number
|
||||
|
||||
const endUnit = record.occupied_units > 1 ? record.unit_number + record.occupied_units - 1 : record.unit_number
|
||||
|
||||
if (!selectedRackId.value) {
|
||||
Message.warning('请先选择机柜')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const res = await cancelReservation({
|
||||
rack_id: selectedRackId.value,
|
||||
start_unit: record.unit_number,
|
||||
end_unit: endUnit,
|
||||
})
|
||||
|
||||
|
||||
if (res.code === 0) {
|
||||
Message.success('取消预留成功')
|
||||
fetchUnits()
|
||||
@@ -637,7 +635,7 @@ export default {
|
||||
|
||||
.action-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
:deep(.arco-card-body) {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
132
src/views/ops/pages/dc/device-collect/components/Detail.vue
Normal file
132
src/views/ops/pages/dc/device-collect/components/Detail.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="detail-container">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="ID">{{ record.id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="服务标识">{{ record.service_identity }}</a-descriptions-item>
|
||||
<a-descriptions-item label="服务名称">{{ record.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="设备分类">
|
||||
{{ DEVICE_CATEGORY_MAP[record.device_category] || record.device_category }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="机房ID">{{ record.room_id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="描述信息" :span="2">{{ record.description || '-' }}</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="采集地址" :span="2">
|
||||
<a-link v-if="record.agent_config" :href="record.agent_config" target="_blank">{{ record.agent_config }}</a-link>
|
||||
<span v-else>-</span>
|
||||
</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="启用状态">
|
||||
<a-tag :color="record.enabled ? 'green' : 'gray'">
|
||||
{{ record.enabled ? '已启用' : '已禁用' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="参与周期采集">
|
||||
<a-tag :color="record.collect_on ? 'green' : 'gray'">
|
||||
{{ record.collect_on ? '已启用' : '未启用' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="采集间隔">{{ record.collect_interval }}秒</a-descriptions-item>
|
||||
<a-descriptions-item label="采集结果摘要" :span="2">{{ record.collect_last_result || '-' }}</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="创建时间">{{ formatTime(record.created_at) }}</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间">{{ formatTime(record.updated_at) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
|
||||
<div class="action-bar">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="$emit('edit')">
|
||||
<template #icon><icon-edit /></template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button type="outline" @click="$emit('quick-config')">
|
||||
<template #icon><icon-settings /></template>
|
||||
采集配置
|
||||
</a-button>
|
||||
<a-button type="outline" @click="handleViewMetrics">
|
||||
<template #icon><icon-eye /></template>
|
||||
查看最新指标
|
||||
</a-button>
|
||||
<a-button type="outline" status="danger" @click="$emit('delete')">
|
||||
<template #icon><icon-delete /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-drawer v-model:visible="metricsVisible" :width="800" title="最新指标数据" :footer="false" unmount-on-close>
|
||||
<a-spin :loading="metricsLoading" style="width: 100%">
|
||||
<div v-if="metricsData.length > 0">
|
||||
<a-list :bordered="false">
|
||||
<a-list-item v-for="(item, index) in metricsData" :key="index">
|
||||
<a-descriptions :column="3" size="small">
|
||||
<a-descriptions-item label="指标名称">{{ item.metric_name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="指标值">{{ item.metric_value }} {{ item.metric_unit || '' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="类型">{{ item.type || '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="时间">{{ formatTime(item.timestamp) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
<a-empty v-else description="暂无指标数据" />
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { IconEdit, IconDelete, IconSettings, IconEye } from '@arco-design/web-vue/es/icon'
|
||||
import { DEVICE_CATEGORY_MAP, fetchLatestMetrics, type RoomDeviceItem, type MetricItem } from '@/api/ops/room-device'
|
||||
|
||||
interface Props {
|
||||
record: RoomDeviceItem
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
defineEmits(['edit', 'quick-config', 'delete'])
|
||||
|
||||
const metricsVisible = ref(false)
|
||||
const metricsLoading = ref(false)
|
||||
const metricsData = ref<MetricItem[]>([])
|
||||
|
||||
const handleViewMetrics = async () => {
|
||||
metricsVisible.value = true
|
||||
metricsLoading.value = true
|
||||
try {
|
||||
const response = await fetchLatestMetrics(props.record.service_identity)
|
||||
metricsData.value = (response as any)?.details?.data || []
|
||||
} catch (error) {
|
||||
console.error('获取最新指标失败:', error)
|
||||
Message.error('获取最新指标失败')
|
||||
metricsData.value = []
|
||||
} finally {
|
||||
metricsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const formatTime = (time?: string) => {
|
||||
if (!time) return '-'
|
||||
const date = new Date(time)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.detail-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
margin-top: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
</style>
|
||||
232
src/views/ops/pages/dc/device-collect/components/FormDialog.vue
Normal file
232
src/views/ops/pages/dc/device-collect/components/FormDialog.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="isEdit ? '编辑机房设备' : '新增机房设备'"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
@update:visible="handleUpdateVisible"
|
||||
:confirm-loading="confirmLoading"
|
||||
width="800px"
|
||||
>
|
||||
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical">
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="name" label="服务名称">
|
||||
<a-input v-model="formData.name" placeholder="请输入服务名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="room_id" label="机房ID">
|
||||
<a-input v-model="formData.room_id" placeholder="请输入机房ID" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="device_category" label="设备分类">
|
||||
<a-select v-model="formData.device_category" placeholder="请选择设备分类">
|
||||
<a-option v-for="item in DEVICE_CATEGORY_OPTIONS" :key="item.value" :value="item.value">
|
||||
{{ item.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item field="description" label="描述信息">
|
||||
<a-textarea v-model="formData.description" placeholder="请输入描述信息" :rows="2" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item field="agent_config" label="采集地址(HTTP/HTTPS GET)">
|
||||
<a-input v-model="formData.agent_config" placeholder="周期采集地址,返回 JSON 格式指标数组" />
|
||||
</a-form-item>
|
||||
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="enabled" label="启用设备">
|
||||
<a-switch v-model="formData.enabled" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="collect_on" label="参与周期采集">
|
||||
<a-switch v-model="formData.collect_on" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="12">
|
||||
<a-form-item v-if="formData.collect_on" field="collect_interval" label="采集间隔(秒)">
|
||||
<a-input-number v-model="formData.collect_interval" placeholder="默认60秒" :min="10" :max="3600" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item field="policy_ids" label="告警策略">
|
||||
<a-select v-model="formData.policy_ids" placeholder="请选择告警策略" multiple allow-clear>
|
||||
<a-option v-for="policy in policyOptions" :key="policy.id" :value="policy.id">
|
||||
{{ policy.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import type { FormInstance } from '@arco-design/web-vue'
|
||||
import {
|
||||
createRoomDevice,
|
||||
updateRoomDevice,
|
||||
DEVICE_CATEGORY_OPTIONS,
|
||||
type RoomDeviceCreateData,
|
||||
type RoomDeviceUpdateData,
|
||||
} from '@/api/ops/room-device'
|
||||
import { fetchPolicyOptions, type PolicyOptionItem } from '@/api/ops/alertPolicy'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
record?: any
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
record: () => ({}),
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'success'])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const confirmLoading = ref(false)
|
||||
const policyOptions = ref<PolicyOptionItem[]>([])
|
||||
|
||||
const isEdit = computed(() => !!props.record?.id)
|
||||
|
||||
const formData = reactive({
|
||||
name: '',
|
||||
description: '',
|
||||
room_id: '',
|
||||
device_category: '',
|
||||
agent_config: '',
|
||||
enabled: true,
|
||||
collect_on: true,
|
||||
collect_interval: 60,
|
||||
policy_ids: [] as number[],
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入服务名称' }],
|
||||
room_id: [{ required: true, message: '请输入机房ID' }],
|
||||
device_category: [{ required: true, message: '请选择设备分类' }],
|
||||
}
|
||||
|
||||
const loadPolicyOptions = async () => {
|
||||
try {
|
||||
const response: any = await fetchPolicyOptions({ enabled: true })
|
||||
if (Array.isArray(response)) {
|
||||
policyOptions.value = response
|
||||
} else if (response && response.details) {
|
||||
policyOptions.value = Array.isArray(response.details) ? response.details : response.details.data || []
|
||||
} else {
|
||||
policyOptions.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载告警策略列表失败:', error)
|
||||
policyOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (isEdit.value && props.record) {
|
||||
Object.assign(formData, {
|
||||
name: props.record.name || '',
|
||||
description: props.record.description || '',
|
||||
room_id: props.record.room_id || '',
|
||||
device_category: props.record.device_category || '',
|
||||
agent_config: props.record.agent_config || '',
|
||||
enabled: props.record.enabled ?? true,
|
||||
collect_on: props.record.collect_on ?? true,
|
||||
collect_interval: props.record.collect_interval || 60,
|
||||
policy_ids: props.record.policy_ids || [],
|
||||
})
|
||||
} else {
|
||||
Object.assign(formData, {
|
||||
name: '',
|
||||
description: '',
|
||||
room_id: '',
|
||||
device_category: '',
|
||||
agent_config: '',
|
||||
enabled: true,
|
||||
collect_on: true,
|
||||
collect_interval: 60,
|
||||
policy_ids: [],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
|
||||
confirmLoading.value = true
|
||||
|
||||
if (isEdit.value) {
|
||||
const updateData: RoomDeviceUpdateData = {
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
room_id: formData.room_id,
|
||||
device_category: formData.device_category,
|
||||
agent_config: formData.agent_config,
|
||||
enabled: formData.enabled,
|
||||
collect_on: formData.collect_on,
|
||||
collect_interval: formData.collect_interval,
|
||||
policy_ids: formData.policy_ids,
|
||||
}
|
||||
await updateRoomDevice(props.record.id, updateData)
|
||||
Message.success('更新成功')
|
||||
} else {
|
||||
const createData: RoomDeviceCreateData = {
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
room_id: formData.room_id,
|
||||
device_category: formData.device_category,
|
||||
agent_config: formData.agent_config,
|
||||
enabled: formData.enabled,
|
||||
collect_on: formData.collect_on,
|
||||
collect_interval: formData.collect_interval,
|
||||
policy_ids: formData.policy_ids,
|
||||
}
|
||||
await createRoomDevice(createData)
|
||||
Message.success('创建成功')
|
||||
}
|
||||
|
||||
emit('success')
|
||||
handleCancel()
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
Message.error('操作失败')
|
||||
} finally {
|
||||
confirmLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleUpdateVisible = (value: boolean) => {
|
||||
emit('update:visible', value)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadPolicyOptions()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<a-modal :visible="visible" title="采集配置" :mask-closable="false" :ok-loading="loading" @ok="handleSubmit" @cancel="handleCancel">
|
||||
<a-form :model="form" layout="vertical">
|
||||
<a-form-item label="参与周期采集">
|
||||
<a-switch v-model="form.collect_on" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="form.collect_on" label="采集间隔(秒)">
|
||||
<a-input-number v-model="form.collect_interval" placeholder="默认60秒" :min="10" :max="3600" style="width: 100%" allow-clear />
|
||||
<template #extra>
|
||||
<span style="color: #86909c">分桶间隔(秒),系统将按此间隔周期采集</span>
|
||||
</template>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="采集地址(HTTP/HTTPS GET)">
|
||||
<a-input v-model="form.agent_config" placeholder="周期采集地址,返回 JSON 格式指标数组" />
|
||||
<template #extra>
|
||||
<span style="color: #86909c">采集地址应返回 JSON 格式的指标数据,支持 {"metrics":[...]} 或数组形式</span>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { patchRoomDeviceCollect, type RoomDeviceCollectData } from '@/api/ops/room-device'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
record: any
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['update:visible', 'success'])
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref({
|
||||
collect_on: true,
|
||||
collect_interval: 60,
|
||||
agent_config: '',
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val && props.record) {
|
||||
form.value.collect_on = props.record.collect_on ?? true
|
||||
form.value.collect_interval = props.record.collect_interval || 60
|
||||
form.value.agent_config = props.record.agent_config || ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data: RoomDeviceCollectData = {
|
||||
collect_on: form.value.collect_on,
|
||||
collect_interval: form.value.collect_interval,
|
||||
agent_config: form.value.agent_config,
|
||||
}
|
||||
|
||||
await patchRoomDeviceCollect(props.record.id, data)
|
||||
|
||||
Message.success('配置成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
} catch (error) {
|
||||
Message.error('配置失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
}
|
||||
</script>
|
||||
66
src/views/ops/pages/dc/device-collect/config/columns.ts
Normal file
66
src/views/ops/pages/dc/device-collect/config/columns.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { DEVICE_CATEGORY_MAP } from '@/api/ops/room-device'
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
dataIndex: 'id',
|
||||
title: 'ID',
|
||||
width: 80,
|
||||
slotName: 'id',
|
||||
},
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '服务名称',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
dataIndex: 'room_id',
|
||||
title: '机房ID',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataIndex: 'device_category',
|
||||
title: '设备分类',
|
||||
width: 100,
|
||||
render: ({ record }: any) => {
|
||||
return DEVICE_CATEGORY_MAP[record.device_category] || record.device_category
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'agent_config',
|
||||
title: '采集地址',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
dataIndex: 'enabled',
|
||||
title: '启用状态',
|
||||
width: 100,
|
||||
slotName: 'enabled',
|
||||
},
|
||||
{
|
||||
dataIndex: 'collect_on',
|
||||
title: '数据采集',
|
||||
width: 100,
|
||||
slotName: 'data_collection',
|
||||
},
|
||||
{
|
||||
dataIndex: 'collect_interval',
|
||||
title: '采集间隔(秒)',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataIndex: 'collect_last_result',
|
||||
title: '采集结果',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right' as const,
|
||||
slotName: 'actions',
|
||||
},
|
||||
]
|
||||
31
src/views/ops/pages/dc/device-collect/config/search-form.ts
Normal file
31
src/views/ops/pages/dc/device-collect/config/search-form.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import { DEVICE_CATEGORY_OPTIONS } from '@/api/ops/room-device'
|
||||
|
||||
export const searchFormConfig: FormItem[] = [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: '关键词',
|
||||
type: 'input',
|
||||
placeholder: '请输入服务名称',
|
||||
span: 6,
|
||||
},
|
||||
{
|
||||
field: 'enabled',
|
||||
label: '启用状态',
|
||||
type: 'select',
|
||||
placeholder: '请选择启用状态',
|
||||
options: [
|
||||
{ label: '已启用', value: true },
|
||||
{ label: '已禁用', value: false },
|
||||
],
|
||||
span: 6,
|
||||
},
|
||||
{
|
||||
field: 'device_category',
|
||||
label: '设备分类',
|
||||
type: 'select',
|
||||
placeholder: '请选择设备分类',
|
||||
options: DEVICE_CATEGORY_OPTIONS,
|
||||
span: 6,
|
||||
},
|
||||
]
|
||||
263
src/views/ops/pages/dc/device-collect/index.vue
Normal file
263
src/views/ops/pages/dc/device-collect/index.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<search-table
|
||||
:form-model="formModel"
|
||||
:form-items="formItems"
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
title="机房设备数据采集"
|
||||
search-button-text="查询"
|
||||
reset-button-text="重置"
|
||||
@update:form-model="handleFormModelUpdate"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
@page-change="handlePageChange"
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
<template #toolbar-left>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
新增设备
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #id="{ record }">
|
||||
{{ record.id }}
|
||||
</template>
|
||||
|
||||
<template #enabled="{ record }">
|
||||
<a-tag :color="record.enabled ? 'green' : 'gray'">
|
||||
{{ record.enabled ? '已启用' : '已禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template #data_collection="{ record }">
|
||||
<a-tag :color="record.collect_on ? 'green' : 'gray'">
|
||||
{{ record.collect_on ? '已启用' : '未启用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template #actions="{ record }">
|
||||
<a-space>
|
||||
<a-dropdown trigger="hover">
|
||||
<a-button type="primary" size="small">
|
||||
管理
|
||||
<icon-down />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption @click="handleDetail(record)">
|
||||
<template #icon>
|
||||
<icon-eye />
|
||||
</template>
|
||||
详情
|
||||
</a-doption>
|
||||
<a-doption @click="handleEdit(record)">
|
||||
<template #icon>
|
||||
<icon-edit />
|
||||
</template>
|
||||
编辑
|
||||
</a-doption>
|
||||
<a-doption @click="handleQuickConfig(record)">
|
||||
<template #icon>
|
||||
<icon-settings />
|
||||
</template>
|
||||
采集配置
|
||||
</a-doption>
|
||||
<a-doption @click="handleDelete(record)" style="color: rgb(var(--danger-6))">
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
删除
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</search-table>
|
||||
|
||||
<FormDialog v-model:visible="formDialogVisible" :record="currentRecord" @success="handleFormSuccess" />
|
||||
|
||||
<QuickConfigDialog v-model:visible="quickConfigVisible" :record="currentRecord" @success="handleFormSuccess" />
|
||||
|
||||
<a-drawer v-model:visible="detailVisible" :width="800" title="机房设备详情" :footer="false" unmount-on-close>
|
||||
<Detail
|
||||
v-if="currentRecord"
|
||||
:record="currentRecord"
|
||||
@edit="handleDetailEdit"
|
||||
@quick-config="handleDetailQuickConfig"
|
||||
@delete="handleDetailDelete"
|
||||
/>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { IconPlus, IconDown, IconEdit, IconDelete, IconEye, IconSettings } from '@arco-design/web-vue/es/icon'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
import { searchFormConfig } from './config/search-form'
|
||||
import FormDialog from './components/FormDialog.vue'
|
||||
import QuickConfigDialog from './components/QuickConfigDialog.vue'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { columns as columnsConfig } from './config/columns'
|
||||
import { fetchRoomDeviceList, deleteRoomDevice, type RoomDeviceItem, type RoomDeviceListParams } from '@/api/ops/room-device'
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref<RoomDeviceItem[]>([])
|
||||
const formDialogVisible = ref(false)
|
||||
const quickConfigVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const currentRecord = ref<RoomDeviceItem | null>(null)
|
||||
const formModel = ref({
|
||||
keyword: '',
|
||||
enabled: undefined as boolean | undefined,
|
||||
device_category: undefined as string | undefined,
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formItems = computed<FormItem[]>(() => searchFormConfig)
|
||||
const columns = computed(() => columnsConfig)
|
||||
|
||||
const fetchRoomDeviceData = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const params: RoomDeviceListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
keyword: formModel.value.keyword,
|
||||
enabled: formModel.value.enabled,
|
||||
device_category: formModel.value.device_category,
|
||||
}
|
||||
|
||||
const response: any = await fetchRoomDeviceList(params)
|
||||
|
||||
if (response && response.details) {
|
||||
tableData.value = response.details?.data || []
|
||||
pagination.total = response.details?.total || 0
|
||||
} else {
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取机房设备列表失败:', error)
|
||||
Message.error('获取机房设备列表失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchRoomDeviceData()
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (value: any) => {
|
||||
formModel.value = value
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
formModel.value = {
|
||||
keyword: '',
|
||||
enabled: undefined,
|
||||
device_category: undefined,
|
||||
}
|
||||
pagination.current = 1
|
||||
fetchRoomDeviceData()
|
||||
}
|
||||
|
||||
const handlePageChange = (current: number) => {
|
||||
pagination.current = current
|
||||
fetchRoomDeviceData()
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
fetchRoomDeviceData()
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
currentRecord.value = null
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleQuickConfig = (record: RoomDeviceItem) => {
|
||||
currentRecord.value = record
|
||||
quickConfigVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (record: RoomDeviceItem) => {
|
||||
currentRecord.value = record
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetail = (record: RoomDeviceItem) => {
|
||||
currentRecord.value = record
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetailEdit = () => {
|
||||
detailVisible.value = false
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetailQuickConfig = () => {
|
||||
detailVisible.value = false
|
||||
quickConfigVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetailDelete = () => {
|
||||
detailVisible.value = false
|
||||
if (currentRecord.value) {
|
||||
handleDelete(currentRecord.value)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormSuccess = () => {
|
||||
fetchRoomDeviceData()
|
||||
}
|
||||
|
||||
const handleDelete = (record: RoomDeviceItem) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确认删除机房设备 "${record.name}" 吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await deleteRoomDevice(record.id)
|
||||
Message.success('删除成功')
|
||||
fetchRoomDeviceData()
|
||||
} catch (error) {
|
||||
console.error('删除机房设备失败:', error)
|
||||
Message.error('删除失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fetchRoomDeviceData()
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DeviceCollectManagement',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user