fix: 资产相关页面微调
This commit is contained in:
@@ -1,22 +1,27 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** 许可证配置(与 DC-Control `LicenceConfig` / 接口 `data` 一致;字段按实际响应可能部分缺失) */
|
||||
export interface LicenceConfig {
|
||||
title?: string
|
||||
version?: string
|
||||
company_name?: string
|
||||
create_time?: string
|
||||
expire_time?: string
|
||||
machine_code?: string
|
||||
max_database?: number
|
||||
max_middleware?: number
|
||||
max_pc?: number
|
||||
max_server?: number
|
||||
max_client?: number
|
||||
max_user?: number
|
||||
max_role?: number
|
||||
max_permission?: number
|
||||
max_menu?: number
|
||||
export interface LicenceQuotas {
|
||||
max_database: number
|
||||
max_middleware: number
|
||||
max_network_device: number
|
||||
max_security: number
|
||||
max_storage: number
|
||||
max_pc: number
|
||||
max_server: number
|
||||
max_user: number
|
||||
max_role: number
|
||||
max_permission: number
|
||||
max_menu: number
|
||||
}
|
||||
|
||||
export interface LicenceInfo {
|
||||
id: string
|
||||
platform_name: string
|
||||
workspace: string
|
||||
issued_on: string
|
||||
valid_from: string
|
||||
expires_on: string
|
||||
quotas: LicenceQuotas
|
||||
}
|
||||
|
||||
/** 获取 采集器 */
|
||||
@@ -39,7 +44,8 @@ export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collec
|
||||
export const fetchCollectorStatistics = () => request.get('/DC-Control/v1/statistics')
|
||||
|
||||
/** 获取 许可证信息 */
|
||||
export const fetchLicenseInfo = () => request.get<{ code?: number; data?: LicenceConfig; message?: string }>('/DC-Control/v1/license')
|
||||
export const fetchLicenseInfo = () =>
|
||||
request.get<{ code: number; details: LicenceInfo; message: string; timeseq: number }>('/DC-Control/v1/license')
|
||||
|
||||
export interface PageResult<T> {
|
||||
total: number
|
||||
@@ -182,7 +188,9 @@ export const fetchControlResources = (params?: ResourceListParams) =>
|
||||
request.get<{ code?: number; details?: PageResult<ControlResource>; message?: string }>('/DC-Control/v1/resources', { params })
|
||||
|
||||
export const fetchControlResourceOptions = (params?: { resource_category?: string }) =>
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resources/options', { params })
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resources/options', {
|
||||
params,
|
||||
})
|
||||
|
||||
export const createControlResource = (data: ControlResourcePayload) =>
|
||||
request.post<{ code?: number; details?: ControlResource; message?: string }>('/DC-Control/v1/resources', data)
|
||||
@@ -203,10 +211,9 @@ export const fetchControlResourceTypeOptions = () =>
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resource-types/options')
|
||||
|
||||
export const fetchControlMetricDefinitions = (params?: { page?: number; size?: number; keyword?: string; resource_category?: string }) =>
|
||||
request.get<{ code?: number; details?: PageResult<ControlMetricDefinition>; message?: string }>(
|
||||
'/DC-Control/v1/metric-definitions',
|
||||
{ params }
|
||||
)
|
||||
request.get<{ code?: number; details?: PageResult<ControlMetricDefinition>; message?: string }>('/DC-Control/v1/metric-definitions', {
|
||||
params,
|
||||
})
|
||||
|
||||
export const fetchControlMetricDefinitionOptions = (params?: { resource_category?: string }) =>
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
|
||||
@@ -218,10 +225,14 @@ export const fetchControlMetricSeries = (params?: MetricSeriesListParams) =>
|
||||
request.get<{ code?: number; details?: PageResult<ControlMetricSeries>; message?: string }>('/DC-Control/v1/metric-series', { params })
|
||||
|
||||
export const fetchControlBusinessSystems = (params?: { page?: number; size?: number; keyword?: string; status?: string }) =>
|
||||
request.get<{ code?: number; details?: PageResult<ControlBusinessSystem>; message?: string }>('/DC-Control/v1/business-systems', { params })
|
||||
request.get<{ code?: number; details?: PageResult<ControlBusinessSystem>; message?: string }>('/DC-Control/v1/business-systems', {
|
||||
params,
|
||||
})
|
||||
|
||||
export const fetchControlBusinessSystemOptions = () =>
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/business-systems/options')
|
||||
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
|
||||
'/DC-Control/v1/business-systems/options'
|
||||
)
|
||||
|
||||
export const fetchCollectionStatus = (params?: {
|
||||
page?: number
|
||||
@@ -231,7 +242,6 @@ export const fetchCollectionStatus = (params?: {
|
||||
resource_uid?: string
|
||||
status?: string
|
||||
}) =>
|
||||
request.get<{ code?: number; details?: PageResult<ControlCollectionStatus>; message?: string }>(
|
||||
'/DC-Control/v1/collection/status',
|
||||
{ params }
|
||||
)
|
||||
request.get<{ code?: number; details?: PageResult<ControlCollectionStatus>; message?: string }>('/DC-Control/v1/collection/status', {
|
||||
params,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** IPAM服务统一响应 */
|
||||
export interface IPAMResponse<T> {
|
||||
code: number
|
||||
details: T
|
||||
message?: string
|
||||
timeseq?: number
|
||||
}
|
||||
|
||||
/** IP地址运行状态 */
|
||||
export type IPStatus = 'online' | 'offline' | 'unknown'
|
||||
|
||||
@@ -118,7 +126,8 @@ export interface IPGroupListParams {
|
||||
|
||||
/** IP分组列表响应 */
|
||||
export interface IPGroupListResponse {
|
||||
data: IPGroupItem[]
|
||||
list: IPGroupItem[]
|
||||
count: number
|
||||
}
|
||||
|
||||
/** IP分组表单数据 */
|
||||
@@ -359,92 +368,96 @@ export interface IPAnomalyFormData {
|
||||
/** ========== 概览 API ========== */
|
||||
|
||||
/** 获取IPAM概览统计 */
|
||||
export const fetchIPAMOverview = () => request.get<IPAMOverview>('/DC-Control/v1/ipam/overview')
|
||||
export const fetchIPAMOverview = () => request.get<IPAMResponse<IPAMOverview>>('/DC-Control/v1/ipam/overview')
|
||||
|
||||
/** ========== IP地址 API ========== */
|
||||
|
||||
/** 获取IP地址列表 */
|
||||
export const fetchIPAddressList = (params?: IPAddressListParams) =>
|
||||
request.get<IPAddressListResponse>('/DC-Control/v1/ipaddresses', { params })
|
||||
request.get<IPAMResponse<IPAddressListResponse>>('/DC-Control/v1/ipaddresses', { params })
|
||||
|
||||
/** 获取IP地址详情 */
|
||||
export const fetchIPAddressDetail = (id: number) => request.get<IPAddressItem>(`/DC-Control/v1/ipaddresses/${id}`)
|
||||
export const fetchIPAddressDetail = (id: number) => request.get<IPAMResponse<IPAddressItem>>(`/DC-Control/v1/ipaddresses/${id}`)
|
||||
|
||||
/** 创建IP地址 */
|
||||
export const createIPAddress = (data: IPAddressFormData) => request.post<IPAddressItem>('/DC-Control/v1/ipaddresses', data)
|
||||
export const createIPAddress = (data: IPAddressFormData) => request.post<IPAMResponse<IPAddressItem>>('/DC-Control/v1/ipaddresses', data)
|
||||
|
||||
/** 更新IP地址 */
|
||||
export const updateIPAddress = (id: number, data: Partial<IPAddressFormData>) =>
|
||||
request.put<{ message: string }>(`/DC-Control/v1/ipaddresses/${id}`, data)
|
||||
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ipaddresses/${id}`, data)
|
||||
|
||||
/** 删除IP地址 */
|
||||
export const deleteIPAddress = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ipaddresses/${id}`)
|
||||
export const deleteIPAddress = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ipaddresses/${id}`)
|
||||
|
||||
/** ========== IP分组 API ========== */
|
||||
|
||||
/** 获取IP分组列表(树形) */
|
||||
export const fetchIPGroupList = (params?: IPGroupListParams) => request.get<IPGroupListResponse>('/DC-Control/v1/ip-groups', { params })
|
||||
export const fetchIPGroupList = (params?: IPGroupListParams) =>
|
||||
request.get<IPAMResponse<IPGroupListResponse>>('/DC-Control/v1/ip-groups', { params })
|
||||
|
||||
/** 创建IP分组 */
|
||||
export const createIPGroup = (data: IPGroupFormData) => request.post<IPGroupItem>('/DC-Control/v1/ip-groups', data)
|
||||
export const createIPGroup = (data: IPGroupFormData) => request.post<IPAMResponse<IPGroupItem>>('/DC-Control/v1/ip-groups', data)
|
||||
|
||||
/** 更新IP分组 */
|
||||
export const updateIPGroup = (id: number, data: Partial<IPGroupFormData>) =>
|
||||
request.put<{ message: string }>(`/DC-Control/v1/ip-groups/${id}`, data)
|
||||
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-groups/${id}`, data)
|
||||
|
||||
/** 删除IP分组 */
|
||||
export const deleteIPGroup = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ip-groups/${id}`)
|
||||
export const deleteIPGroup = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-groups/${id}`)
|
||||
|
||||
/** ========== IP子网 API ========== */
|
||||
|
||||
/** 获取IP子网列表 */
|
||||
export const fetchIPSubnetList = (params?: IPSubnetListParams) => request.get<IPSubnetListResponse>('/DC-Control/v1/ip-subnets', { params })
|
||||
export const fetchIPSubnetList = (params?: IPSubnetListParams) =>
|
||||
request.get<IPAMResponse<IPSubnetListResponse>>('/DC-Control/v1/ip-subnets', { params })
|
||||
|
||||
/** 获取IP子网详情 */
|
||||
export const fetchIPSubnetDetail = (id: number) => request.get<IPSubnetItem>(`/DC-Control/v1/ip-subnets/${id}`)
|
||||
export const fetchIPSubnetDetail = (id: number) => request.get<IPAMResponse<IPSubnetItem>>(`/DC-Control/v1/ip-subnets/${id}`)
|
||||
|
||||
/** 创建IP子网 */
|
||||
export const createIPSubnet = (data: IPSubnetFormData) => request.post<IPSubnetItem>('/DC-Control/v1/ip-subnets', data)
|
||||
export const createIPSubnet = (data: IPSubnetFormData) => request.post<IPAMResponse<IPSubnetItem>>('/DC-Control/v1/ip-subnets', data)
|
||||
|
||||
/** 更新IP子网 */
|
||||
export const updateIPSubnet = (id: number, data: Partial<IPSubnetFormData>) =>
|
||||
request.put<{ message: string }>(`/DC-Control/v1/ip-subnets/${id}`, data)
|
||||
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-subnets/${id}`, data)
|
||||
|
||||
/** 删除IP子网 */
|
||||
export const deleteIPSubnet = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ip-subnets/${id}`)
|
||||
export const deleteIPSubnet = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-subnets/${id}`)
|
||||
|
||||
/** ========== DHCP租约 API ========== */
|
||||
|
||||
/** 获取DHCP租约列表 */
|
||||
export const fetchDHCPLeaseList = (params?: DHCPLeaseListParams) =>
|
||||
request.get<DHCPLeaseListResponse>('/DC-Control/v1/ipam/dhcp-leases', { params })
|
||||
request.get<IPAMResponse<DHCPLeaseListResponse>>('/DC-Control/v1/ipam/dhcp-leases', { params })
|
||||
|
||||
/** 创建DHCP租约 */
|
||||
export const createDHCPLease = (data: DHCPLeaseFormData) => request.post<DHCPLeaseItem>('/DC-Control/v1/ipam/dhcp-leases', data)
|
||||
export const createDHCPLease = (data: DHCPLeaseFormData) =>
|
||||
request.post<IPAMResponse<DHCPLeaseItem>>('/DC-Control/v1/ipam/dhcp-leases', data)
|
||||
|
||||
/** ========== IP冲突 API ========== */
|
||||
|
||||
/** 获取IP冲突列表 */
|
||||
export const fetchIPConflictList = (params?: IPConflictListParams) =>
|
||||
request.get<IPConflictListResponse>('/DC-Control/v1/ipam/conflicts', { params })
|
||||
request.get<IPAMResponse<IPConflictListResponse>>('/DC-Control/v1/ipam/conflicts', { params })
|
||||
|
||||
/** 创建IP冲突记录 */
|
||||
export const createIPConflict = (data: IPConflictFormData) => request.post<IPConflictItem>('/DC-Control/v1/ipam/conflicts', data)
|
||||
export const createIPConflict = (data: IPConflictFormData) =>
|
||||
request.post<IPAMResponse<IPConflictItem>>('/DC-Control/v1/ipam/conflicts', data)
|
||||
|
||||
/** ========== IP变更 API ========== */
|
||||
|
||||
/** 获取IP变更列表 */
|
||||
export const fetchIPChangeList = (params?: IPChangeListParams) =>
|
||||
request.get<IPChangeListResponse>('/DC-Control/v1/ipam/changes', { params })
|
||||
request.get<IPAMResponse<IPChangeListResponse>>('/DC-Control/v1/ipam/changes', { params })
|
||||
|
||||
/** 创建IP变更记录 */
|
||||
export const createIPChange = (data: IPChangeFormData) => request.post<IPChangeItem>('/DC-Control/v1/ipam/changes', data)
|
||||
export const createIPChange = (data: IPChangeFormData) => request.post<IPAMResponse<IPChangeItem>>('/DC-Control/v1/ipam/changes', data)
|
||||
|
||||
/** ========== IP异常 API ========== */
|
||||
|
||||
/** 获取IP异常列表 */
|
||||
export const fetchIPAnomalyList = (params?: IPAnomalyListParams) =>
|
||||
request.get<IPAnomalyListResponse>('/DC-Control/v1/ipam/anomalies', { params })
|
||||
request.get<IPAMResponse<IPAnomalyListResponse>>('/DC-Control/v1/ipam/anomalies', { params })
|
||||
|
||||
/** 创建IP异常记录 */
|
||||
export const createIPAnomaly = (data: IPAnomalyFormData) => request.post<IPAnomalyItem>('/DC-Control/v1/ipam/anomalies', data)
|
||||
export const createIPAnomaly = (data: IPAnomalyFormData) => request.post<IPAMResponse<IPAnomalyItem>>('/DC-Control/v1/ipam/anomalies', data)
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface RoomDeviceItem {
|
||||
service_identity: string
|
||||
name: string
|
||||
description: string
|
||||
room_id: string
|
||||
room_id: number
|
||||
device_code?: string
|
||||
device_category: string
|
||||
type?: string
|
||||
@@ -69,7 +69,7 @@ export interface RoomDeviceCreateData {
|
||||
service_identity?: string
|
||||
name: string
|
||||
description?: string
|
||||
room_id: string
|
||||
room_id: number
|
||||
device_category: string
|
||||
agent_config?: string
|
||||
collect_method?: 'api' | 'snmp'
|
||||
@@ -97,7 +97,7 @@ export interface RoomDeviceCreateData {
|
||||
export interface RoomDeviceUpdateData {
|
||||
name?: string
|
||||
description?: string
|
||||
room_id?: string
|
||||
room_id?: number
|
||||
device_category?: string
|
||||
agent_config?: string
|
||||
collect_method?: 'api' | 'snmp'
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface StorageListParams {
|
||||
|
||||
/** 创建存储设备请求参数 */
|
||||
export interface StorageCreateData {
|
||||
service_identity: string
|
||||
service_identity?: string
|
||||
name: string
|
||||
category?: string
|
||||
type: string
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface LoginData {
|
||||
/** 用户信息 */
|
||||
export interface UserItem {
|
||||
id?: number
|
||||
user_id?: number
|
||||
account?: string
|
||||
password?: string
|
||||
confirmPassword?: string
|
||||
|
||||
@@ -93,7 +93,8 @@
|
||||
<li>
|
||||
<a-dropdown trigger="click">
|
||||
<a-avatar :size="32" :style="{ marginRight: '8px' }">
|
||||
<img alt="avatar" :src="avatar" />
|
||||
<img v-if="avatar" alt="avatar" :src="avatar" />
|
||||
<icon-user v-else />
|
||||
</a-avatar>
|
||||
<template #content>
|
||||
<a-doption>
|
||||
@@ -149,7 +150,7 @@ const { changeLocale, currentLocale }: any = useLocale()
|
||||
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen()
|
||||
const locales = [...LOCALE_OPTIONS]
|
||||
const avatar = computed(() => {
|
||||
return userStore.avatar || '//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image'
|
||||
return userStore.avatar
|
||||
})
|
||||
const theme = computed(() => {
|
||||
return appStore.theme
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { resetUserPassword } from '@/api/module/user'
|
||||
import type { UserItem } from '@/api/types'
|
||||
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
|
||||
|
||||
interface Props {
|
||||
@@ -79,12 +80,12 @@ const handleSavePassword = async () => {
|
||||
|
||||
try {
|
||||
// 从 SafeStorage 获取登录用户信息
|
||||
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
|
||||
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
|
||||
const res = await resetUserPassword({
|
||||
account: userInfo.account || '',
|
||||
account: userInfo?.account || '',
|
||||
code: '123456', // 暂时没校验,随便传
|
||||
password: form.value.newPassword,
|
||||
phone: userInfo.phone || '13800138000', // 暂时没校验,随便传
|
||||
phone: userInfo?.phone || '13800138000', // 暂时没校验,随便传
|
||||
})
|
||||
|
||||
if (res.code === 0) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { modifyUser } from '@/api/module/user'
|
||||
import type { UserItem } from '@/api/types'
|
||||
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
|
||||
|
||||
interface Props {
|
||||
@@ -56,11 +57,11 @@ watch(
|
||||
visible.value = val
|
||||
if (val) {
|
||||
// 打开时从存储中获取用户信息
|
||||
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
|
||||
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
|
||||
form.value = {
|
||||
account: userInfo.account || '',
|
||||
name: userInfo.name || '',
|
||||
email: userInfo.email || '',
|
||||
account: userInfo?.account || '',
|
||||
name: userInfo?.name || '',
|
||||
email: userInfo?.email || '',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,11 +77,11 @@ const handleSaveProfile = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
|
||||
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
|
||||
const updatedUserInfo = {
|
||||
...userInfo,
|
||||
...form.value,
|
||||
id: userInfo.user_id,
|
||||
id: userInfo?.user_id ?? userInfo?.id,
|
||||
}
|
||||
|
||||
const res = await modifyUser(updatedUserInfo)
|
||||
|
||||
@@ -19,8 +19,14 @@ import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import bannerImage from '@/assets/images/login-banner.png'
|
||||
|
||||
interface CarouselItem {
|
||||
slogan?: string
|
||||
subSlogan?: string
|
||||
image: string
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
const carouselItem = computed(() => [
|
||||
const carouselItem = computed<CarouselItem[]>(() => [
|
||||
{
|
||||
// slogan: t('login.banner.slogan1'),
|
||||
// subSlogan: t('login.banner.subSlogan1'),
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<a-button type="text" size="small" :disabled="isActionDisabled(record, 'comment')" @click.stop="handleComment(record)">
|
||||
评论
|
||||
</a-button>
|
||||
<a-dropdown @select="(v) => handleMoreSelect(v, record)">
|
||||
<a-dropdown @select="handleMoreSelect($event, record)">
|
||||
<a-button type="text" size="small" @click.stop>更多</a-button>
|
||||
<template #content>
|
||||
<a-doption value="detail">详情</a-doption>
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
<!-- 分配U位对话框 -->
|
||||
<allocate-unit-dialog
|
||||
v-model:visible="allocateVisible"
|
||||
:rack-id="selectedRackId"
|
||||
:rack-id="selectedRackId!"
|
||||
:rack-height="rackInfo.height"
|
||||
@success="handleRefresh"
|
||||
/>
|
||||
@@ -171,7 +171,7 @@
|
||||
<!-- 预留U位对话框 -->
|
||||
<reserve-unit-dialog
|
||||
v-model:visible="reserveVisible"
|
||||
:rack-id="selectedRackId"
|
||||
:rack-id="selectedRackId!"
|
||||
:rack-height="rackInfo.height"
|
||||
@success="handleRefresh"
|
||||
/>
|
||||
|
||||
@@ -265,7 +265,7 @@ const loadPolicyOptions = async () => {
|
||||
|
||||
const loadRoomOptions = async () => {
|
||||
try {
|
||||
const response: any = await fetchRoomOptions({ enabled: true })
|
||||
const response: any = await fetchRoomOptions()
|
||||
if (Array.isArray(response)) {
|
||||
roomOptions.value = response
|
||||
} else if (response && response.details) {
|
||||
@@ -347,6 +347,11 @@ watch(
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
const roomId = formData.room_id
|
||||
if (!roomId) {
|
||||
Message.warning('请选择机房')
|
||||
return
|
||||
}
|
||||
if (formData.collect_method === 'api' && !formData.agent_config?.trim()) {
|
||||
Message.warning('API 模式下请填写采集地址')
|
||||
return
|
||||
@@ -384,7 +389,7 @@ const handleOk = async () => {
|
||||
const updateData: RoomDeviceUpdateData = {
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
room_id: formData.room_id,
|
||||
room_id: roomId,
|
||||
device_category: formData.device_category,
|
||||
agent_config: formData.agent_config,
|
||||
collect_method: formData.collect_method,
|
||||
@@ -417,7 +422,7 @@ const handleOk = async () => {
|
||||
const createData: RoomDeviceCreateData = {
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
room_id: formData.room_id,
|
||||
room_id: roomId,
|
||||
device_category: formData.device_category,
|
||||
agent_config: formData.agent_config,
|
||||
collect_method: formData.collect_method,
|
||||
|
||||
@@ -157,7 +157,7 @@ const handleViewMetrics = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const formatTime = (time?: string) => {
|
||||
const formatTime = (time?: string | null) => {
|
||||
if (!time) return '-'
|
||||
if (time.startsWith('0001-01-01')) return '-'
|
||||
const date = new Date(time)
|
||||
|
||||
@@ -160,7 +160,7 @@ const handleViewMetrics = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const formatTime = (time?: string) => {
|
||||
const formatTime = (time?: string | null) => {
|
||||
if (!time) return '-'
|
||||
if (time.startsWith('0001-01-01')) return '-'
|
||||
const date = new Date(time)
|
||||
|
||||
@@ -443,7 +443,7 @@ async function loadHardware() {
|
||||
if (isHostHardwareApiSuccess(colRes)) {
|
||||
const col = unwrapHostHardwareDetails(colRes)
|
||||
const did = col?.device_id
|
||||
deviceId.value = col?.device_id
|
||||
deviceId.value = did ?? null
|
||||
if (did) {
|
||||
const detailOk = await loadDeviceDetailIntoForm(did, gen)
|
||||
if (gen !== hardwareLoadGeneration) return
|
||||
|
||||
@@ -181,7 +181,9 @@ let assetSearchTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
const isEdit = computed(() => !!props.record?.id)
|
||||
|
||||
const formData = reactive<ServerFormData>({
|
||||
type ServerDialogFormData = ServerFormData & Required<Pick<ServerFormData, 'host'>>
|
||||
|
||||
const formData = reactive<ServerDialogFormData>({
|
||||
server_identity: '',
|
||||
name: '',
|
||||
host: '',
|
||||
|
||||
@@ -331,16 +331,16 @@ function formatDateTime(v?: string) {
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||
}
|
||||
|
||||
function formatStatusText(status: string) {
|
||||
function formatStatusText(status?: string) {
|
||||
const m: Record<string, string> = {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
unknown: '未知',
|
||||
}
|
||||
return m[status] || status || '-'
|
||||
return m[status || ''] || status || '-'
|
||||
}
|
||||
|
||||
function getStatusColor(status: string) {
|
||||
function getStatusColor(status?: string) {
|
||||
const colorMap: Record<string, string> = {
|
||||
online: 'green',
|
||||
offline: 'red',
|
||||
@@ -348,7 +348,7 @@ function getStatusColor(status: string) {
|
||||
warning: 'orange',
|
||||
success: 'green',
|
||||
}
|
||||
return colorMap[status] || 'gray'
|
||||
return colorMap[status || ''] || 'gray'
|
||||
}
|
||||
|
||||
function isMetricsTarget(d: RoomDeviceItem) {
|
||||
|
||||
@@ -409,7 +409,15 @@ function mergeMonitorOptionAsRow(opt: StorageMonitorOptionItem) {
|
||||
collect_method: 'api',
|
||||
snmp_target: '',
|
||||
snmp_port: 161,
|
||||
snmp_version: 'v2c',
|
||||
snmp_community: '',
|
||||
snmp_v3_security_level: '',
|
||||
snmp_v3_security_name: '',
|
||||
snmp_v3_auth_protocol: '',
|
||||
snmp_v3_auth_password: '',
|
||||
snmp_v3_priv_protocol: '',
|
||||
snmp_v3_priv_password: '',
|
||||
snmp_v3_context_name: '',
|
||||
snmp_timeout_ms: 3000,
|
||||
snmp_retries: 1,
|
||||
snmp_oids: '[]',
|
||||
|
||||
@@ -476,7 +476,7 @@ const handleScan = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const formatRange = (a?: string, b?: string) => {
|
||||
const formatRange = (a?: string, b?: string | null) => {
|
||||
if (!a) return ''
|
||||
const sa = dayjs(a).format('YYYY-MM-DD HH:mm:ss')
|
||||
const sb = b ? dayjs(b).format('YYYY-MM-DD HH:mm:ss') : '进行中'
|
||||
|
||||
@@ -81,7 +81,14 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface AnomalySearchForm {
|
||||
keyword: string
|
||||
subnet_id: number | ''
|
||||
anomaly_type: string
|
||||
status: string
|
||||
}
|
||||
|
||||
const formModel = reactive<AnomalySearchForm>({
|
||||
keyword: '',
|
||||
subnet_id: '',
|
||||
anomaly_type: '',
|
||||
@@ -144,7 +151,7 @@ const loadSubnets = async () => {
|
||||
try {
|
||||
const response = await fetchIPSubnetList({ size: 1000 })
|
||||
if (response && response.code === 0) {
|
||||
subnets.value = response.details?.data || response.data || []
|
||||
subnets.value = response.details.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -157,7 +164,10 @@ const loadData = async () => {
|
||||
const params: IPAnomalyListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
subnet_id: formModel.subnet_id || undefined,
|
||||
anomaly_type: formModel.anomaly_type,
|
||||
status: formModel.status,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -168,8 +178,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchIPAnomalyList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load anomalies:', error)
|
||||
@@ -178,7 +188,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,13 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface ChangeSearchForm {
|
||||
keyword: string
|
||||
subnet_id: number | ''
|
||||
change_type: string
|
||||
}
|
||||
|
||||
const formModel = reactive<ChangeSearchForm>({
|
||||
keyword: '',
|
||||
subnet_id: '',
|
||||
change_type: '',
|
||||
@@ -130,7 +136,7 @@ const loadSubnets = async () => {
|
||||
try {
|
||||
const response = await fetchIPSubnetList({ size: 1000 })
|
||||
if (response && response.code === 0) {
|
||||
subnets.value = response.details?.data || response.data || []
|
||||
subnets.value = response.details.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -143,7 +149,9 @@ const loadData = async () => {
|
||||
const params: IPChangeListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
subnet_id: formModel.subnet_id || undefined,
|
||||
change_type: formModel.change_type,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -154,8 +162,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchIPChangeList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load changes:', error)
|
||||
@@ -164,7 +172,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,13 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface ConflictSearchForm {
|
||||
keyword: string
|
||||
subnet_id: number | ''
|
||||
status: string
|
||||
}
|
||||
|
||||
const formModel = reactive<ConflictSearchForm>({
|
||||
keyword: '',
|
||||
subnet_id: '',
|
||||
status: '',
|
||||
@@ -132,7 +138,7 @@ const loadSubnets = async () => {
|
||||
try {
|
||||
const response = await fetchIPSubnetList({ size: 1000 })
|
||||
if (response && response.code === 0) {
|
||||
subnets.value = response.details?.data || response.data || []
|
||||
subnets.value = response.details.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -145,7 +151,9 @@ const loadData = async () => {
|
||||
const params: IPConflictListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
subnet_id: formModel.subnet_id || undefined,
|
||||
status: formModel.status,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -156,8 +164,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchIPConflictList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load conflicts:', error)
|
||||
@@ -166,7 +174,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,12 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface DHCPLeaseSearchForm {
|
||||
keyword: string
|
||||
subnet_id: number | ''
|
||||
}
|
||||
|
||||
const formModel = reactive<DHCPLeaseSearchForm>({
|
||||
keyword: '',
|
||||
subnet_id: '',
|
||||
})
|
||||
@@ -114,7 +119,7 @@ const loadSubnets = async () => {
|
||||
try {
|
||||
const response = await fetchIPSubnetList({ size: 1000 })
|
||||
if (response && response.code === 0) {
|
||||
subnets.value = response.details?.data || response.data || []
|
||||
subnets.value = response.details.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -127,7 +132,8 @@ const loadData = async () => {
|
||||
const params: DHCPLeaseListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
subnet_id: formModel.subnet_id || undefined,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -138,8 +144,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchDHCPLeaseList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load DHCP leases:', error)
|
||||
@@ -148,7 +154,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,15 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface IPAddressSearchForm {
|
||||
keyword: string
|
||||
subnet_id: number | ''
|
||||
allocation_status: AllocationStatus | ''
|
||||
status: IPStatus | ''
|
||||
usage_status: UsageStatus | ''
|
||||
}
|
||||
|
||||
const formModel = reactive<IPAddressSearchForm>({
|
||||
keyword: '',
|
||||
subnet_id: '',
|
||||
allocation_status: '',
|
||||
@@ -169,7 +177,7 @@ const loadSubnets = async () => {
|
||||
try {
|
||||
const response = await fetchIPSubnetList({ size: 1000 })
|
||||
if (response && response.code === 0) {
|
||||
subnets.value = response.details?.data || response.data || []
|
||||
subnets.value = response.details.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -182,7 +190,11 @@ const loadData = async () => {
|
||||
const params: IPAddressListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
subnet_id: formModel.subnet_id || undefined,
|
||||
allocation_status: formModel.allocation_status || undefined,
|
||||
status: formModel.status || undefined,
|
||||
usage_status: formModel.usage_status || undefined,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -193,8 +205,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchIPAddressList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load IP addresses:', error)
|
||||
@@ -203,7 +215,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ const loadData = async () => {
|
||||
try {
|
||||
const response = await fetchIPAMOverview()
|
||||
if (response && response.code === 0) {
|
||||
overview.value = response.details || response.data || response
|
||||
overview.value = response.details
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load overview:', error)
|
||||
|
||||
@@ -84,7 +84,12 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formModel = reactive({
|
||||
interface SubnetSearchForm {
|
||||
keyword: string
|
||||
group_id: number | ''
|
||||
}
|
||||
|
||||
const formModel = reactive<SubnetSearchForm>({
|
||||
keyword: '',
|
||||
group_id: '',
|
||||
})
|
||||
@@ -113,7 +118,7 @@ const loadGroups = async () => {
|
||||
try {
|
||||
const response = await fetchIPGroupList()
|
||||
if (response && response.code === 0) {
|
||||
groups.value = response.details?.data || response.data || []
|
||||
groups.value = response.details.list || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load groups:', error)
|
||||
@@ -126,7 +131,8 @@ const loadData = async () => {
|
||||
const params: IPSubnetListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
...formModel,
|
||||
keyword: formModel.keyword,
|
||||
group_id: formModel.group_id || undefined,
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => {
|
||||
@@ -137,8 +143,8 @@ const loadData = async () => {
|
||||
|
||||
const response = await fetchIPSubnetList(params)
|
||||
if (response && response.code === 0) {
|
||||
tableData.value = response.details?.data || response.data || []
|
||||
pagination.total = response.details?.total || response.total || 0
|
||||
tableData.value = response.details.data || []
|
||||
pagination.total = response.details.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subnets:', error)
|
||||
@@ -147,7 +153,7 @@ const loadData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (model: typeof formModel) => {
|
||||
const handleFormModelUpdate = (model: Record<string, unknown>) => {
|
||||
Object.assign(formModel, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ const formData = reactive<Partial<TopologyGroup> & { parent_id: number }>({
|
||||
name: '',
|
||||
description: '',
|
||||
sort: 0,
|
||||
enable: 2,
|
||||
enable: true,
|
||||
parent_id: 0,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { computed, ComputedRef, unref, Ref } from 'vue'
|
||||
import { Edge } from '@vue-flow/core'
|
||||
|
||||
type EdgeType = 'default' | 'straight' | 'step' | 'simplebezier'
|
||||
import type { EdgeType } from '../types'
|
||||
|
||||
/**
|
||||
* 边样式计算Hook
|
||||
|
||||
@@ -140,7 +140,7 @@ import '@vue-flow/core/dist/theme-default.css'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import * as TopoAPI from '@/api/ops/netarchTopo'
|
||||
|
||||
import { NodeData, DeviceType } from './types'
|
||||
import { NodeData, DeviceType, EdgeType } from './types'
|
||||
import { DEVICE_TYPE_CONFIG } from './config'
|
||||
import { CustomNode } from './components'
|
||||
import { useTopoLayout, useEdgeStyles } from './hooks'
|
||||
@@ -184,7 +184,7 @@ const edges = ref<any[]>([])
|
||||
// UI控制状态
|
||||
const selectedGroup = ref<string | null>(null)
|
||||
const expandedGroups = ref<Set<string>>(new Set())
|
||||
const edgeType = ref<'default' | 'straight' | 'step' | 'simplebezier'>('default')
|
||||
const edgeType = ref<EdgeType>('default')
|
||||
|
||||
// 节点操作状态
|
||||
const selectedNode = ref<any>(null)
|
||||
@@ -422,7 +422,7 @@ const handleLayout = (value: string | number | Record<string, any> | undefined)
|
||||
|
||||
// 设置边类型
|
||||
const setEdgeType = (value: string | number | Record<string, any> | undefined) => {
|
||||
const type = value as 'default' | 'straight' | 'step' | 'smoothstep' | 'simplebezier'
|
||||
const type = value as EdgeType
|
||||
edgeType.value = type
|
||||
}
|
||||
|
||||
|
||||
@@ -42,4 +42,4 @@ export interface LinkData {
|
||||
}
|
||||
|
||||
// 链路类型(用于边样式)
|
||||
export type EdgeType = 'default' | 'straight' | 'step' | 'simplebezier'
|
||||
export type EdgeType = 'default' | 'straight' | 'step' | 'smoothstep' | 'simplebezier'
|
||||
|
||||
@@ -555,7 +555,7 @@ const loadTrafficData = async () => {
|
||||
if (!dashboard?.protocols?.length && latest?.protocol_stats) {
|
||||
const protocol = parseJsonObject(latest.protocol_stats)
|
||||
if (protocol && typeof protocol === 'object') {
|
||||
const total = Object.values(protocol).reduce((sum, v) => sum + Number(v || 0), 0)
|
||||
const total = Object.values(protocol).reduce<number>((sum, v) => sum + Number(v || 0), 0)
|
||||
if (total > 0) {
|
||||
protocolData.value = Object.entries(protocol).map(([name, value], index) => ({
|
||||
name,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="stat-title">
|
||||
<span class="stat-name">{{ card.title }}</span>
|
||||
<a-tag v-if="card.title === '警告' || card.title === '工单' || card.title === '审核'" size="small" class="stat-tag">
|
||||
待处理/总数
|
||||
{{ card.title === '警告' ? '告警中/总数' : '待处理/总数' }}
|
||||
</a-tag>
|
||||
<a-tag v-else size="small" class="stat-tag">启用/总数</a-tag>
|
||||
</div>
|
||||
@@ -506,11 +506,11 @@ const handleChange = (value: string) => {
|
||||
// 处理统计卡片点击
|
||||
const handleCardClick = (cardTitle: string) => {
|
||||
const routeMap: any = {
|
||||
服务器及PC: '/ops/monitor/server',
|
||||
数据库服务: '/ops/dc/database',
|
||||
网络设备: '/ops/monitor/network',
|
||||
服务器及PC: '/dc/server',
|
||||
数据库服务: '/dc/database',
|
||||
网络设备: '/monitor/network',
|
||||
警告: '/alert/tackle',
|
||||
工单: '/ops/ticket',
|
||||
工单: '/feedback/all',
|
||||
审核: '/kb/review',
|
||||
}
|
||||
const route = routeMap[cardTitle]
|
||||
@@ -559,7 +559,7 @@ const loadStatistics = async () => {
|
||||
fetchAlertCount().catch((e) => ({ error: e, success: false })),
|
||||
fetchCollectorStatistics().catch((e) => ({ error: e, success: false })),
|
||||
fetchFeedbackTicketStatistics().catch((e) => ({ error: e, success: false })),
|
||||
fetchHistories({ page: 1, page_size: 5, status: 'pending' }).catch((e) => ({ error: e, success: false })),
|
||||
fetchHistories({ page: 1, page_size: 5, status: 'firing' }).catch((e) => ({ error: e, success: false })),
|
||||
fetchReviewStats({ resource_type: 'all' }).catch((e: unknown) => ({ error: e, success: false as const })),
|
||||
fetchNetworkDeviceList({ page: 1, size: 1 }).catch((e) => ({ error: e, success: false })),
|
||||
fetchNetworkDeviceList({ page: 1, size: 1, enabled: true }).catch((e) => ({ error: e, success: false })),
|
||||
@@ -591,7 +591,7 @@ const loadStatistics = async () => {
|
||||
// 处理告警统计数据
|
||||
if (alertData.status === 'fulfilled' && !isFailedRequest(alertData.value)) {
|
||||
statistics.alert = {
|
||||
pending: alertData.value?.details?.status_counts?.pending || 0,
|
||||
pending: alertData.value?.details?.status_counts?.firing || 0,
|
||||
total: alertData.value?.details?.total || 0,
|
||||
}
|
||||
} else {
|
||||
@@ -667,6 +667,7 @@ const getStatusColor = (status: any) => {
|
||||
const statusStr = typeof status === 'object' ? status?.code || status?.name : status
|
||||
const colorMap: any = {
|
||||
pending: '#FF4D4F',
|
||||
firing: '#FF4D4F',
|
||||
processing: '#1890FF',
|
||||
resolved: '#52C41A',
|
||||
closed: '#8C8C8C',
|
||||
@@ -679,6 +680,7 @@ const getStatusTagColor = (status: any) => {
|
||||
const statusStr = typeof status === 'object' ? status?.code || status?.name : status
|
||||
const colorMap: any = {
|
||||
pending: 'red',
|
||||
firing: 'red',
|
||||
processing: 'blue',
|
||||
resolved: 'green',
|
||||
closed: 'gray',
|
||||
@@ -693,6 +695,7 @@ const getStatusText = (status: any) => {
|
||||
}
|
||||
const statusMap: any = {
|
||||
pending: '待处理',
|
||||
firing: '告警中',
|
||||
processing: '处理中',
|
||||
resolved: '已解决',
|
||||
closed: '已关闭',
|
||||
|
||||
@@ -35,7 +35,7 @@ import { computed, ref } from 'vue'
|
||||
|
||||
export interface PermissionItem {
|
||||
id: number
|
||||
name: string
|
||||
title: string
|
||||
children?: PermissionItem[]
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="kv-list">
|
||||
<div v-for="row in basicRows" :key="row.key" class="basic-row">
|
||||
<span class="label">{{ row.label }}</span>
|
||||
<span class="value" :class="{ 'value-code': row.key === 'machine_code' }">{{ row.display }}</span>
|
||||
<span class="value" :class="{ 'value-code': row.key === 'id' }">{{ row.display }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
@@ -35,7 +35,7 @@
|
||||
<template #title>
|
||||
<span class="section-title">资源限制</span>
|
||||
</template>
|
||||
<p class="quota-hint">配额项为 0 时表示该维度不按许可证限制数量(由业务逻辑决定)。</p>
|
||||
<p class="quota-hint">配额为 0 表示该资源未授权,不能新增。</p>
|
||||
<div class="kv-list">
|
||||
<div v-for="row in quotaRows" :key="row.key" class="quota-row">
|
||||
<span class="label">{{ row.label }}</span>
|
||||
@@ -53,135 +53,42 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||
import { fetchLicenseInfo, type LicenceConfig } from '@/api/ops/dcControl'
|
||||
import { fetchLicenseInfo, type LicenceInfo } from '@/api/ops/dcControl'
|
||||
|
||||
const loading = ref(false)
|
||||
const loadError = ref(false)
|
||||
const license = ref<LicenceConfig | null>(null)
|
||||
const license = ref<LicenceInfo | null>(null)
|
||||
|
||||
const dash = (v: string | undefined | null) => (v != null && String(v).length > 0 ? String(v) : '—')
|
||||
|
||||
const formatQuota = (n: number | undefined) => {
|
||||
if (n == null || Number.isNaN(Number(n))) return '—'
|
||||
const v = Number(n)
|
||||
return v > 0 ? String(v) : '无限制'
|
||||
}
|
||||
|
||||
function pickStr(o: Record<string, unknown>, snake: string, camel: string): string | undefined {
|
||||
const v = o[snake] ?? o[camel]
|
||||
return typeof v === 'string' ? v : v != null ? String(v) : undefined
|
||||
}
|
||||
|
||||
function pickNum(o: Record<string, unknown>, snake: string, camel: string): number | undefined {
|
||||
const v = o[snake] ?? o[camel]
|
||||
if (v == null || v === '') return undefined
|
||||
const n = Number(v)
|
||||
return Number.isNaN(n) ? undefined : n
|
||||
}
|
||||
|
||||
/** 将接口对象统一为 LicenceConfig(兼容 snake_case / camelCase) */
|
||||
function normalizeLicensePayload(raw: Record<string, unknown>): LicenceConfig {
|
||||
return {
|
||||
title: pickStr(raw, 'title', 'title'),
|
||||
version: pickStr(raw, 'version', 'version'),
|
||||
company_name: pickStr(raw, 'company_name', 'companyName'),
|
||||
create_time: pickStr(raw, 'create_time', 'createTime'),
|
||||
expire_time: pickStr(raw, 'expire_time', 'expireTime'),
|
||||
machine_code: pickStr(raw, 'machine_code', 'machineCode'),
|
||||
max_database: pickNum(raw, 'max_database', 'maxDatabase'),
|
||||
max_middleware: pickNum(raw, 'max_middleware', 'maxMiddleware'),
|
||||
max_pc: pickNum(raw, 'max_pc', 'maxPc'),
|
||||
max_server: pickNum(raw, 'max_server', 'maxServer'),
|
||||
max_client: pickNum(raw, 'max_client', 'maxClient'),
|
||||
max_user: pickNum(raw, 'max_user', 'maxUser'),
|
||||
max_role: pickNum(raw, 'max_role', 'maxRole'),
|
||||
max_permission: pickNum(raw, 'max_permission', 'maxPermission'),
|
||||
max_menu: pickNum(raw, 'max_menu', 'maxMenu'),
|
||||
}
|
||||
}
|
||||
|
||||
function isLicensePayloadRaw(v: unknown): v is Record<string, unknown> {
|
||||
if (v == null || typeof v !== 'object' || Array.isArray(v)) return false
|
||||
const o = v as Record<string, unknown>
|
||||
const strHit = (s: string, c: string) => {
|
||||
const x = o[s] ?? o[c]
|
||||
return typeof x === 'string' && x.length > 0
|
||||
}
|
||||
const numHit = (s: string, c: string) => {
|
||||
const x = o[s] ?? o[c]
|
||||
return typeof x === 'number' && !Number.isNaN(x)
|
||||
}
|
||||
return (
|
||||
strHit('company_name', 'companyName') ||
|
||||
strHit('machine_code', 'machineCode') ||
|
||||
strHit('title', 'title') ||
|
||||
strHit('version', 'version') ||
|
||||
numHit('max_database', 'maxDatabase') ||
|
||||
numHit('max_middleware', 'maxMiddleware') ||
|
||||
numHit('max_pc', 'maxPc') ||
|
||||
numHit('max_server', 'maxServer') ||
|
||||
numHit('max_client', 'maxClient') ||
|
||||
numHit('max_user', 'maxUser') ||
|
||||
numHit('max_role', 'maxRole') ||
|
||||
numHit('max_permission', 'maxPermission') ||
|
||||
numHit('max_menu', 'maxMenu')
|
||||
)
|
||||
}
|
||||
|
||||
function parseLicenseResponse(res: unknown): {
|
||||
code?: number
|
||||
success?: boolean
|
||||
data: unknown
|
||||
message?: string
|
||||
} {
|
||||
if (res == null || typeof res !== 'object') {
|
||||
return { data: undefined, message: undefined }
|
||||
}
|
||||
const r = res as Record<string, unknown>
|
||||
const code = typeof r.code === 'number' ? r.code : undefined
|
||||
const success = typeof r.success === 'boolean' ? r.success : undefined
|
||||
const message = typeof r.message === 'string' ? r.message : typeof r.msg === 'string' ? r.msg : undefined
|
||||
|
||||
let data: unknown = r.data ?? r.details ?? r.result
|
||||
if (data == null && isLicensePayloadRaw(r)) {
|
||||
data = r
|
||||
}
|
||||
return { code, success, data, message }
|
||||
}
|
||||
|
||||
function responseIndicatesSuccess(code: number | undefined, success: boolean | undefined): boolean {
|
||||
if (success === false) return false
|
||||
if (success === true) return true
|
||||
if (code === undefined) return true
|
||||
return code === 200 || code === 0
|
||||
}
|
||||
const formatQuota = (value: number) => (value === 0 ? '未授权' : String(value))
|
||||
|
||||
const basicRows = computed(() => {
|
||||
const L = license.value
|
||||
if (!L) return []
|
||||
const current = license.value
|
||||
if (!current) return []
|
||||
return [
|
||||
{ key: 'company_name', label: '公司名称', display: dash(L.company_name) },
|
||||
{ key: 'title', label: '版本标题', display: dash(L.title) },
|
||||
{ key: 'version', label: '版本号', display: dash(L.version) },
|
||||
{ key: 'machine_code', label: '机器码', display: dash(L.machine_code) },
|
||||
{ key: 'create_time', label: '创建时间', display: dash(L.create_time) },
|
||||
{ key: 'expire_time', label: '过期时间', display: dash(L.expire_time) },
|
||||
{ key: 'id', label: '许可证 ID', display: current.id },
|
||||
{ key: 'platform_name', label: '平台名称', display: current.platform_name },
|
||||
{ key: 'workspace', label: 'Workspace', display: current.workspace },
|
||||
{ key: 'issued_on', label: '签发日期', display: current.issued_on },
|
||||
{ key: 'valid_from', label: '生效日期', display: current.valid_from },
|
||||
{ key: 'expires_on', label: '到期日期', display: current.expires_on },
|
||||
]
|
||||
})
|
||||
|
||||
const quotaRows = computed(() => {
|
||||
const L = license.value
|
||||
if (!L) return []
|
||||
const quotas = license.value?.quotas
|
||||
if (!quotas) return []
|
||||
return [
|
||||
{ key: 'max_database', label: '数据库', display: formatQuota(L.max_database) },
|
||||
{ key: 'max_middleware', label: '中间件', display: formatQuota(L.max_middleware) },
|
||||
{ key: 'max_pc', label: 'PC', display: formatQuota(L.max_pc) },
|
||||
{ key: 'max_server', label: '服务器', display: formatQuota(L.max_server) },
|
||||
{ key: 'max_client', label: '客户端', display: formatQuota(L.max_client) },
|
||||
{ key: 'max_user', label: '用户', display: formatQuota(L.max_user) },
|
||||
{ key: 'max_role', label: '角色', display: formatQuota(L.max_role) },
|
||||
{ key: 'max_permission', label: '权限', display: formatQuota(L.max_permission) },
|
||||
{ key: 'max_menu', label: '菜单', display: formatQuota(L.max_menu) },
|
||||
{ key: 'max_database', label: '数据库', display: formatQuota(quotas.max_database) },
|
||||
{ key: 'max_middleware', label: '中间件', display: formatQuota(quotas.max_middleware) },
|
||||
{ key: 'max_network_device', label: '网络设备', display: formatQuota(quotas.max_network_device) },
|
||||
{ key: 'max_security', label: '安全设备', display: formatQuota(quotas.max_security) },
|
||||
{ key: 'max_storage', label: '存储设备', display: formatQuota(quotas.max_storage) },
|
||||
{ key: 'max_pc', label: 'PC', display: formatQuota(quotas.max_pc) },
|
||||
{ key: 'max_server', label: '服务器', display: formatQuota(quotas.max_server) },
|
||||
{ key: 'max_user', label: '用户', display: formatQuota(quotas.max_user) },
|
||||
{ key: 'max_role', label: '角色', display: formatQuota(quotas.max_role) },
|
||||
{ key: 'max_permission', label: '权限', display: formatQuota(quotas.max_permission) },
|
||||
{ key: 'max_menu', label: '菜单', display: formatQuota(quotas.max_menu) },
|
||||
]
|
||||
})
|
||||
|
||||
@@ -189,28 +96,22 @@ async function loadLicense() {
|
||||
loading.value = true
|
||||
loadError.value = false
|
||||
try {
|
||||
const res = await fetchLicenseInfo()
|
||||
const { code, success, data, message } = parseLicenseResponse(res)
|
||||
const ok = responseIndicatesSuccess(code, success)
|
||||
if (data != null && typeof data === 'object' && !Array.isArray(data) && isLicensePayloadRaw(data) && ok) {
|
||||
license.value = normalizeLicensePayload(data as Record<string, unknown>)
|
||||
loadError.value = false
|
||||
} else {
|
||||
loadError.value = true
|
||||
Message.error(message || '获取许可证失败')
|
||||
const response = await fetchLicenseInfo()
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.message || '获取许可证失败')
|
||||
}
|
||||
} catch (e) {
|
||||
license.value = response.details
|
||||
} catch (error) {
|
||||
loadError.value = true
|
||||
console.error('[license-center] fetchLicenseInfo', e)
|
||||
Message.error('获取许可证失败')
|
||||
license.value = null
|
||||
console.error('[license-center] fetchLicenseInfo', error)
|
||||
Message.error(error instanceof Error ? error.message : '获取许可证失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadLicense()
|
||||
})
|
||||
onMounted(loadLicense)
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
|
||||
Reference in New Issue
Block a user