diff --git a/src/api/ops/server.ts b/src/api/ops/server.ts index 0275aa6..e927d01 100644 --- a/src/api/ops/server.ts +++ b/src/api/ops/server.ts @@ -17,6 +17,7 @@ export interface ServerItem { server_type: string tags: string location: string + asset_id?: number remote_access: string remote_port: number agent_config: string @@ -60,6 +61,7 @@ export interface ServerFormData { server_type?: string tags?: string location?: string + asset_id?: number remote_access?: string remote_port?: number agent_config?: string diff --git a/src/router/local-menu-flat.ts b/src/router/local-menu-flat.ts index df36902..0cae7d5 100644 --- a/src/router/local-menu-flat.ts +++ b/src/router/local-menu-flat.ts @@ -766,6 +766,22 @@ export const localMenuFlatItems: MenuItem[] = [ sort_key: 39, created_at: '2025-12-26T13:23:52.220159+08:00', }, + { + id: 53, + identity: '019d0000-0000-7000-8000-000000000053', + title: '机房管理', + title_en: 'Room Management', + code: 'ops:数据中心管理:机房管理', + description: '数据中心管理 - 机房管理', + app_id: 2, + parent_id: 49, + menu_path: '/datacenter/room', + menu_icon: 'appstore', + component: 'ops/pages/datacenter/room', + type: 1, + sort_key: 39.5, + created_at: '2026-04-14T10:00:00+08:00', + }, { id: 54, identity: '019b591d-0343-7ce7-91bd-d82497ea0a11', diff --git a/src/router/local-menu-items.ts b/src/router/local-menu-items.ts index 736ef7a..aea9399 100644 --- a/src/router/local-menu-items.ts +++ b/src/router/local-menu-items.ts @@ -824,6 +824,23 @@ export const localMenuItems: MenuItem[] = [ created_at: '2025-12-26T13:23:52.220159+08:00', children: [], }, + { + id: 53, + identity: '019d0000-0000-7000-8000-000000000053', + title: '机房管理', + title_en: 'Room Management', + code: 'ops:数据中心管理:机房管理', + description: '数据中心管理 - 机房管理', + app_id: 2, + parent_id: 49, + menu_path: '/datacenter/room', + menu_icon: 'appstore', + component: 'ops/pages/datacenter/room', + type: 1, + sort_key: 9, + created_at: '2026-04-14T10:00:00+08:00', + children: [], + }, ], }, { diff --git a/src/views/ops/pages/assets/device/form/index.vue b/src/views/ops/pages/assets/device/form/index.vue index 614abbf..b962c59 100644 --- a/src/views/ops/pages/assets/device/form/index.vue +++ b/src/views/ops/pages/assets/device/form/index.vue @@ -208,7 +208,7 @@ - + - + - + + + + + {{ item.name }} ({{ item.code }}) + + + + + @@ -273,7 +296,7 @@ - + - + ([]) const supplierOptions = ref([]) const datacenterOptions = ref<{ label: string; value: number }[]>([]) const floorOptions = ref<{ label: string; value: number }[]>([]) +const roomOptions = ref([]) const rackOptions = ref([]) // 加载状态 @@ -413,11 +438,13 @@ const categoryLoading = ref(false) const supplierLoading = ref(false) const datacenterLoading = ref(false) const floorLoading = ref(false) +const roomLoading = ref(false) const rackLoading = ref(false) // 搜索防抖定时器 let datacenterSearchTimer: number | undefined let floorSearchTimer: number | undefined +let roomSearchTimer: number | undefined let rackSearchTimer: number | undefined // 表单数据 @@ -574,13 +601,13 @@ const handleFloorSearch = (keyword: string) => { // 加载机柜列表 const fetchRacks = async (keyword?: string) => { - if (!form.value.floor_id) { + if (!form.value.room_id) { rackOptions.value = [] return } rackLoading.value = true try { - const res: any = await fetchRackListByFloor(form.value.floor_id, { name: keyword }) + const res: any = await fetchRackListByRoom(form.value.room_id, { name: keyword }) rackOptions.value = extractList(res) } catch (error) { console.error('获取机柜列表失败:', error) @@ -592,7 +619,7 @@ const fetchRacks = async (keyword?: string) => { // 机柜搜索(带防抖) const handleRackSearch = (keyword: string) => { - if (!form.value.floor_id) return + if (!form.value.room_id) return if (rackSearchTimer) { window.clearTimeout(rackSearchTimer) } @@ -601,11 +628,44 @@ const handleRackSearch = (keyword: string) => { }, 300) } +// 加载机房列表 +const fetchRooms = async (keyword?: string) => { + if (!form.value.floor_id) { + roomOptions.value = [] + return + } + roomLoading.value = true + try { + const res: any = await fetchRoomListByFloor(form.value.floor_id, { + name: keyword || undefined, + }) + roomOptions.value = extractList(res) + } catch (error) { + console.error('获取机房列表失败:', error) + roomOptions.value = [] + } finally { + roomLoading.value = false + } +} + +// 机房搜索(带防抖) +const handleRoomSearch = (keyword: string) => { + if (!form.value.floor_id) return + if (roomSearchTimer) { + window.clearTimeout(roomSearchTimer) + } + roomSearchTimer = window.setTimeout(() => { + fetchRooms(keyword?.trim() || undefined) + }, 300) +} + // 数据中心变化时加载楼层 const handleDatacenterChange = async (value: number | undefined) => { form.value.floor_id = undefined + form.value.room_id = undefined form.value.rack_id = undefined floorOptions.value = [] + roomOptions.value = [] rackOptions.value = [] if (value) { @@ -615,6 +675,18 @@ const handleDatacenterChange = async (value: number | undefined) => { // 楼层变化时加载机柜 const handleFloorChange = async (value: number | undefined) => { + form.value.room_id = undefined + form.value.rack_id = undefined + roomOptions.value = [] + rackOptions.value = [] + + if (value) { + await fetchRooms() + } +} + +// 机房变化时加载机柜 +const handleRoomChange = async (value: number | undefined) => { form.value.rack_id = undefined rackOptions.value = [] @@ -675,7 +747,17 @@ const loadDeviceDetail = async () => { // 如果有楼层,加载机柜 if (res.details.floor_id) { try { - const rackRes: any = await fetchRackListByFloor(res.details.floor_id) + const roomRes: any = await fetchRoomListByFloor(res.details.floor_id) + roomOptions.value = extractList(roomRes) + } catch (error) { + console.error('加载机房失败:', error) + } + } + + // 如果有机房,加载机柜 + if (res.details.room_id) { + try { + const rackRes: any = await fetchRackListByRoom(res.details.room_id) rackOptions.value = extractList(rackRes) } catch (error) { console.error('加载机柜失败:', error) diff --git a/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue b/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue index 808e71a..3d42fdd 100644 --- a/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue +++ b/src/views/ops/pages/datacenter/floor/components/FloorFormDialog.vue @@ -56,6 +56,16 @@ /> + + + 规划中 + 建设中 + 运营中 + 维护中 + 已下线 + + + { name: form.value.name, datacenter_id: form.value.datacenter_id, floor_number: form.value.floor_number, + status: form.value.status, area: form.value.area, height: form.value.height, load_bearing: form.value.load_bearing, diff --git a/src/views/ops/pages/datacenter/rack/components/RackFormDialog.vue b/src/views/ops/pages/datacenter/rack/components/RackFormDialog.vue index 1dee5a2..25dea6e 100644 --- a/src/views/ops/pages/datacenter/rack/components/RackFormDialog.vue +++ b/src/views/ops/pages/datacenter/rack/components/RackFormDialog.vue @@ -74,7 +74,9 @@ v-model="form.floor_id" placeholder="请选择所属楼层" :loading="loadingFloors" + :disabled="!form.datacenter_id" allow-search + @change="handleFloorChange" @search="handleFloorSearch" > + + + + + + {{ item.name }} + + + + + 规格参数 @@ -438,9 +466,10 @@ import { Message } from '@arco-design/web-vue' import { createRack, updateRack } from '@/api/ops/rack' import { fetchDatacenterList, - fetchRackListByDatacenter, fetchSupplierList, } from '@/api/ops/rack' +import { fetchFloorListByDatacenter } from '@/api/ops/floor' +import { fetchRoomListByFloor } from '@/api/ops/room' interface Rack { id?: number @@ -448,6 +477,7 @@ interface Rack { code?: string datacenter_id?: number floor_id?: number + room_id?: number height?: number width?: number depth?: number @@ -496,12 +526,15 @@ const emit = defineEmits() const formRef = ref() const loadingDatacenters = ref(false) const loadingFloors = ref(false) +const loadingRooms = ref(false) const loadingSuppliers = ref(false) const submitting = ref(false) const datacenterList = ref([]) const floorList = ref([]) +const roomList = ref([]) const supplierList = ref([]) let floorSearchTimer: number | undefined +let roomSearchTimer: number | undefined // 表单数据 const form = ref({ @@ -509,6 +542,7 @@ const form = ref({ code: '', datacenter_id: undefined as number | undefined, floor_id: undefined as number | undefined, + room_id: undefined as number | undefined, height: 42, width: undefined as number | undefined, depth: undefined as number | undefined, @@ -559,7 +593,7 @@ const loadDatacenterList = async () => { } } -// 加载楼层列表(通过机柜下拉接口提取去重楼层) +// 加载楼层列表 const loadFloorList = async (datacenterId?: number, keyword?: string) => { if (!datacenterId) { floorList.value = [] @@ -567,20 +601,10 @@ const loadFloorList = async (datacenterId?: number, keyword?: string) => { } loadingFloors.value = true try { - const res: any = await fetchRackListByDatacenter(datacenterId, { name: keyword }) + const res: any = await fetchFloorListByDatacenter(datacenterId, { name: keyword }) if (res.code === 0) { const list = res.details?.data ?? res.data ?? res.details ?? [] - const rows = Array.isArray(list) ? list : [] - const floorMap = new Map() - rows.forEach((rack: any) => { - const floor = rack?.floor - if (!floor?.id || floorMap.has(floor.id)) return - floorMap.set(floor.id, { - id: floor.id, - name: floor.name || String(floor.id), - }) - }) - floorList.value = Array.from(floorMap.values()) + floorList.value = Array.isArray(list) ? list : [] } } catch (error) { console.error('获取楼层列表失败:', error) @@ -607,9 +631,36 @@ const loadSupplierList = async () => { // 数据中心变化时重新加载楼层列表 const handleDatacenterChange = async (value: number) => { form.value.floor_id = undefined + form.value.room_id = undefined + roomList.value = [] await loadFloorList(value) } +const loadRoomList = async (floorId?: number, keyword?: string) => { + if (!floorId) { + roomList.value = [] + return + } + loadingRooms.value = true + try { + const res: any = await fetchRoomListByFloor(floorId, { name: keyword }) + if (res.code === 0) { + const list = res.details?.data ?? res.data ?? res.details ?? [] + roomList.value = Array.isArray(list) ? list : [] + } + } catch (error) { + console.error('获取机房列表失败:', error) + roomList.value = [] + } finally { + loadingRooms.value = false + } +} + +const handleFloorChange = async (value: number) => { + form.value.room_id = undefined + await loadRoomList(value) +} + const handleFloorSearch = (keyword: string) => { if (!form.value.datacenter_id) return if (floorSearchTimer) { @@ -620,6 +671,16 @@ const handleFloorSearch = (keyword: string) => { }, 300) } +const handleRoomSearch = (keyword: string) => { + if (!form.value.floor_id) return + if (roomSearchTimer) { + window.clearTimeout(roomSearchTimer) + } + roomSearchTimer = window.setTimeout(() => { + loadRoomList(form.value.floor_id, keyword?.trim() || undefined) + }, 300) +} + // 监听对话框显示状态 watch( () => props.visible, @@ -632,6 +693,7 @@ watch( code: props.rack.code || '', datacenter_id: props.rack.datacenter_id, floor_id: props.rack.floor_id, + room_id: props.rack.room_id, height: props.rack.height || 42, width: props.rack.width, depth: props.rack.depth, @@ -667,6 +729,9 @@ watch( if (props.rack.datacenter_id) { loadFloorList(props.rack.datacenter_id) } + if (props.rack.floor_id) { + loadRoomList(props.rack.floor_id) + } } else { // 新建模式:重置表单 form.value = { @@ -674,6 +739,7 @@ watch( code: '', datacenter_id: undefined, floor_id: undefined, + room_id: undefined, height: 42, width: undefined, depth: undefined, @@ -705,6 +771,8 @@ watch( description: '', remarks: '', } + floorList.value = [] + roomList.value = [] } } } @@ -722,6 +790,7 @@ const handleOk = async () => { code: form.value.code, datacenter_id: form.value.datacenter_id, floor_id: form.value.floor_id, + room_id: form.value.room_id, height: form.value.height, width: form.value.width, depth: form.value.depth, diff --git a/src/views/ops/pages/datacenter/rack/config/search-form.ts b/src/views/ops/pages/datacenter/rack/config/search-form.ts index 4495830..a3476cc 100644 --- a/src/views/ops/pages/datacenter/rack/config/search-form.ts +++ b/src/views/ops/pages/datacenter/rack/config/search-form.ts @@ -24,6 +24,14 @@ export const searchFormConfig: FormItem[] = [ options: [], // 需要动态加载 span: 6, }, + { + field: 'room_id', + label: '机房', + type: 'select', + placeholder: '请选择机房', + options: [], // 需要动态加载 + span: 6, + }, { field: 'rack_type', label: '机柜类型', diff --git a/src/views/ops/pages/datacenter/rack/index.vue b/src/views/ops/pages/datacenter/rack/index.vue index 0ea8806..17f2ef5 100644 --- a/src/views/ops/pages/datacenter/rack/index.vue +++ b/src/views/ops/pages/datacenter/rack/index.vue @@ -113,9 +113,10 @@ import { columns as columnsConfig } from './config/columns' import { fetchRackList, fetchDatacenterList, - fetchRackListByDatacenter, deleteRack, } from '@/api/ops/rack' +import { fetchFloorListByDatacenter } from '@/api/ops/floor' +import { fetchRoomListByFloor } from '@/api/ops/room' import RackDetailDialog from './components/RackDetailDialog.vue' import RackFormDialog from './components/RackFormDialog.vue' @@ -128,6 +129,7 @@ const formModel = ref({ keyword: '', datacenter_id: undefined, floor_id: undefined, + room_id: undefined, rack_type: undefined, status: undefined, }) @@ -141,8 +143,10 @@ const pagination = reactive({ // 表单项配置 const datacenterSelectOptions = ref<{ label: string; value: number }[]>([]) const floorSelectOptions = ref<{ label: string; value: number }[]>([]) +const roomSelectOptions = ref<{ label: string; value: number }[]>([]) let datacenterSearchTimer: number | undefined let floorSearchTimer: number | undefined +let roomSearchTimer: number | undefined const formItems = computed(() => searchFormConfig.map((item) => { @@ -163,6 +167,15 @@ const formItems = computed(() => disabled: !formModel.value.datacenter_id, } } + if (item.field === 'room_id') { + return { + ...item, + options: roomSelectOptions.value, + allowSearch: true, + onSearch: handleRoomSearch, + disabled: !formModel.value.floor_id, + } + } return item }), ) @@ -199,25 +212,18 @@ const loadDatacenterOptions = async (keyword?: string) => { const loadFloorOptions = async (datacenterId?: number, keyword?: string) => { if (!datacenterId) { floorSelectOptions.value = [] + roomSelectOptions.value = [] return } try { - const res: any = await fetchRackListByDatacenter(datacenterId, { - name: keyword, - }) + const res: any = await fetchFloorListByDatacenter(datacenterId, { name: keyword }) if (res.code === 0) { const list = res.details?.data ?? res.data ?? res.details ?? [] const rows = Array.isArray(list) ? list : [] - const floorMap = new Map() - rows.forEach((rack: any) => { - const floor = rack?.floor - if (!floor?.id || floorMap.has(floor.id)) return - floorMap.set(floor.id, { - label: floor.name || String(floor.id), - value: floor.id, - }) - }) - floorSelectOptions.value = Array.from(floorMap.values()) + floorSelectOptions.value = rows.map((floor: any) => ({ + label: floor.name || String(floor.id), + value: floor.id, + })) } } catch (error) { console.error('加载楼层列表失败:', error) @@ -226,6 +232,28 @@ const loadFloorOptions = async (datacenterId?: number, keyword?: string) => { } } +const loadRoomOptions = async (floorId?: number, keyword?: string) => { + if (!floorId) { + roomSelectOptions.value = [] + return + } + try { + const res: any = await fetchRoomListByFloor(floorId, { name: keyword }) + if (res.code === 0) { + const list = res.details?.data ?? res.data ?? res.details ?? [] + const rows = Array.isArray(list) ? list : [] + roomSelectOptions.value = rows.map((room: any) => ({ + label: room.name || String(room.id), + value: room.id, + })) + } + } catch (error) { + console.error('加载机房列表失败:', error) + Message.error('加载机房列表失败') + roomSelectOptions.value = [] + } +} + const handleDatacenterSearch = (keyword: string) => { if (datacenterSearchTimer) { window.clearTimeout(datacenterSearchTimer) @@ -245,16 +273,37 @@ const handleFloorSearch = (keyword: string) => { }, 300) } +const handleRoomSearch = (keyword: string) => { + if (!formModel.value.floor_id) return + if (roomSearchTimer) { + window.clearTimeout(roomSearchTimer) + } + roomSearchTimer = window.setTimeout(() => { + loadRoomOptions(formModel.value.floor_id, keyword?.trim() || undefined) + }, 300) +} + watch( () => formModel.value.datacenter_id, (newId, oldId) => { if (newId !== oldId) { formModel.value.floor_id = undefined + formModel.value.room_id = undefined } loadFloorOptions(newId) }, ) +watch( + () => formModel.value.floor_id, + (newId, oldId) => { + if (newId !== oldId) { + formModel.value.room_id = undefined + } + loadRoomOptions(newId) + }, +) + // 获取机柜类型颜色 const getRackTypeColor = (type?: string) => { const colorMap: Record = { @@ -314,6 +363,7 @@ const fetchRacks = async () => { keyword: formModel.value.keyword || undefined, datacenter_id: formModel.value.datacenter_id ?? undefined, floor_id: formModel.value.floor_id ?? undefined, + room_id: formModel.value.room_id ?? undefined, rack_type: formModel.value.rack_type || undefined, status: formModel.value.status || undefined, } @@ -350,6 +400,7 @@ const handleReset = () => { keyword: '', datacenter_id: undefined, floor_id: undefined, + room_id: undefined, rack_type: undefined, status: undefined, } @@ -411,7 +462,7 @@ const handleDelete = async (record: any) => { // U位管理 const handleUnitManagement = (record: any) => { router.push({ - path: '/ops/datacenter/u-position', + path: '/datacenter/u-position', query: { rack_id: record.id, rack_name: record.name }, }) } diff --git a/src/views/ops/pages/datacenter/room/components/RoomDetailDialog.vue b/src/views/ops/pages/datacenter/room/components/RoomDetailDialog.vue new file mode 100644 index 0000000..bb5590c --- /dev/null +++ b/src/views/ops/pages/datacenter/room/components/RoomDetailDialog.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/views/ops/pages/datacenter/room/components/RoomFormDialog.vue b/src/views/ops/pages/datacenter/room/components/RoomFormDialog.vue new file mode 100644 index 0000000..55e3569 --- /dev/null +++ b/src/views/ops/pages/datacenter/room/components/RoomFormDialog.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/views/ops/pages/datacenter/room/config/columns.ts b/src/views/ops/pages/datacenter/room/config/columns.ts new file mode 100644 index 0000000..4247659 --- /dev/null +++ b/src/views/ops/pages/datacenter/room/config/columns.ts @@ -0,0 +1,52 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' + +export const columns: TableColumnData[] = [ + { + title: '序号', + dataIndex: 'index', + slotName: 'index', + width: 80, + }, + { + title: '机房名称', + dataIndex: 'name', + width: 180, + }, + { + title: '机房编码', + dataIndex: 'code', + width: 140, + }, + { + title: '所属中心', + dataIndex: 'datacenter', + slotName: 'datacenter', + width: 160, + }, + { + title: '所属楼层', + dataIndex: 'floor', + slotName: 'floor', + width: 160, + }, + { + title: '状态', + dataIndex: 'status', + slotName: 'status', + width: 120, + }, + { + title: '描述', + dataIndex: 'description', + ellipsis: true, + tooltip: true, + width: 220, + }, + { + title: '操作', + dataIndex: 'actions', + slotName: 'actions', + width: 240, + fixed: 'right' as const, + }, +] diff --git a/src/views/ops/pages/datacenter/room/config/search-form.ts b/src/views/ops/pages/datacenter/room/config/search-form.ts new file mode 100644 index 0000000..ebc6a56 --- /dev/null +++ b/src/views/ops/pages/datacenter/room/config/search-form.ts @@ -0,0 +1,37 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入机房名称或编码', + }, + { + field: 'datacenter_id', + label: '数据中心', + type: 'select', + placeholder: '请选择数据中心', + options: [], + }, + { + field: 'floor_id', + label: '所属楼层', + type: 'select', + placeholder: '请选择所属楼层', + options: [], + }, + { + field: 'status', + label: '状态', + type: 'select', + placeholder: '请选择状态', + options: [ + { label: '规划中', value: 'planning' }, + { label: '建设中', value: 'construction' }, + { label: '运营中', value: 'operating' }, + { label: '维护中', value: 'maintenance' }, + { label: '已下线', value: 'offline' }, + ], + }, +] diff --git a/src/views/ops/pages/datacenter/room/index.vue b/src/views/ops/pages/datacenter/room/index.vue new file mode 100644 index 0000000..729f472 --- /dev/null +++ b/src/views/ops/pages/datacenter/room/index.vue @@ -0,0 +1,326 @@ + + + + + + + diff --git a/src/views/ops/pages/datacenter/u-position/index.vue b/src/views/ops/pages/datacenter/u-position/index.vue index b82ec4b..4f1b5c6 100644 --- a/src/views/ops/pages/datacenter/u-position/index.vue +++ b/src/views/ops/pages/datacenter/u-position/index.vue @@ -182,6 +182,7 @@ 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 { useRoute } from 'vue-router' import { fetchUnitList, allocateUnit, reserveUnit, cancelReservation, releaseUnit, updateUnitStatus } from '@/api/ops/unit' import { fetchDatacenterList, fetchRackListByRoom } from '@/api/ops/rack' import { fetchFloorListByDatacenter } from '@/api/ops/floor' @@ -210,6 +211,7 @@ let datacenterSearchTimer: number | undefined let floorSearchTimer: number | undefined let roomSearchTimer: number | undefined let rackSearchTimer: number | undefined +const route = useRoute() // 对话框可见性 const allocateVisible = ref(false) @@ -613,6 +615,11 @@ const handleCancelReservation = async (record: any) => { // 初始化 onMounted(() => { fetchDatacenters() + const rackId = Number(route.query.rack_id) + if (Number.isFinite(rackId) && rackId > 0) { + selectedRackId.value = rackId + fetchUnits(rackId) + } }) diff --git a/src/views/ops/pages/dc/server/components/ServerFormDialog.vue b/src/views/ops/pages/dc/server/components/ServerFormDialog.vue index 4b92734..d445bfd 100644 --- a/src/views/ops/pages/dc/server/components/ServerFormDialog.vue +++ b/src/views/ops/pages/dc/server/components/ServerFormDialog.vue @@ -75,6 +75,23 @@ + + + + + + + + @@ -147,6 +164,7 @@ import { Message } from '@arco-design/web-vue' import type { FormInstance } from '@arco-design/web-vue' import { createServer, updateServer } from '@/api/ops/server' import type { ServerFormData, ServerItem } from '@/api/ops/server' +import { fetchAssetAll } from '@/api/ops/asset' interface Props { visible: boolean @@ -161,6 +179,9 @@ const emit = defineEmits(['update:visible', 'success']) const formRef = ref() const confirmLoading = ref(false) +const assetLoading = ref(false) +const assetOptions = ref<{ label: string; value: number }[]>([]) +let assetSearchTimer: ReturnType | null = null const isEdit = computed(() => !!props.record?.id) @@ -176,6 +197,7 @@ const formData = reactive({ server_type: '', tags: '', location: '', + asset_id: undefined, remote_access: '', remote_port: 0, agent_config: '', @@ -280,6 +302,7 @@ watch( () => props.visible, (val) => { if (val) { + loadAssetOptions() if (isEdit.value && props.record) { Object.assign(formData, { server_identity: props.record.server_identity || '', @@ -293,6 +316,7 @@ watch( server_type: props.record.server_type || '', tags: props.record.tags || '', location: props.record.location || '', + asset_id: props.record.asset_id, remote_access: props.record.remote_access || '', remote_port: props.record.remote_port || 0, agent_config: props.record.agent_config || '', @@ -316,6 +340,7 @@ watch( server_type: '', tags: '', location: '', + asset_id: undefined, remote_access: '', remote_port: 0, agent_config: '', @@ -330,6 +355,39 @@ watch( } ) +async function loadAssetOptions(keyword?: string) { + assetLoading.value = true + try { + const res: any = await fetchAssetAll({ keyword: (keyword || '').trim() || undefined }) + if (res?.code !== 0) { + Message.warning(res?.message || '加载资产列表失败') + assetOptions.value = [] + return + } + const rows = res?.details || [] + assetOptions.value = rows.map((item: any) => ({ + value: Number(item.id), + label: `${item.asset_code || item.id} | ${item.asset_name || '-'}`, + })) + } catch (error) { + console.error('加载资产列表失败:', error) + Message.warning('加载资产列表失败') + assetOptions.value = [] + } finally { + assetLoading.value = false + } +} + +// 资产下拉使用远程模糊查询,避免一次加载全部资产。 +function handleAssetSearch(keyword: string) { + if (assetSearchTimer) { + clearTimeout(assetSearchTimer) + } + assetSearchTimer = setTimeout(() => { + loadAssetOptions(keyword) + }, 300) +} + watch( () => formData.host, () => { @@ -362,6 +420,7 @@ const handleOk = async () => { server_type: formData.server_type, tags: formData.tags, location: formData.location, + asset_id: formData.asset_id ?? (isEdit.value ? 0 : undefined), remote_access: formData.remote_access, remote_port: formData.remote_port, agent_config: formData.agent_config, diff --git a/src/views/ops/pages/monitor/host-hardware/index.vue b/src/views/ops/pages/monitor/host-hardware/index.vue index 30efc4f..ddfcde5 100644 --- a/src/views/ops/pages/monitor/host-hardware/index.vue +++ b/src/views/ops/pages/monitor/host-hardware/index.vue @@ -289,8 +289,6 @@ const subStatusItems = computed(() => { { key: 'cpu_status', label: 'CPU' }, { key: 'memory_status', label: '内存' }, { key: 'disk_status', label: '磁盘' }, - { key: 'network_status', label: '网络' }, - { key: 'raid_status', label: 'RAID' }, ] return fields.map((f) => ({ key: String(f.key),