feat(device): connect collector discovery flow
| | |
| | | CollectorOnlineStatus, |
| | | CollectorStatus, |
| | | CollectorTestStatus, |
| | | CollectorPayload, |
| | | DeviceCollectorRecord, |
| | | } from '@/stores/deviceCollectors' |
| | | import { request, type ApiResponse } from './request' |
| | |
| | | data: CollectorDto |
| | | } |
| | | |
| | | interface DiscoveredCollectorDto { |
| | | deviceCode: string |
| | | ip: string |
| | | mac: string |
| | | name: string |
| | | manufacturer: string |
| | | model: string |
| | | channelCount: number |
| | | onlineStatus: CollectorOnlineStatus |
| | | testStatus: CollectorTestStatus |
| | | currentTest: string |
| | | } |
| | | |
| | | interface DiscoveredCollectorListResponse extends ApiResponse { |
| | | total: number |
| | | rows: DiscoveredCollectorDto[] |
| | | } |
| | | |
| | | export interface DeviceCollectorInput { |
| | | deviceCode: string |
| | | ip: string |
| | |
| | | export interface DeviceCollectorListResult { |
| | | total: number |
| | | rows: DeviceCollectorRecord[] |
| | | } |
| | | |
| | | export interface DiscoveredCollectorListResult { |
| | | total: number |
| | | rows: CollectorPayload[] |
| | | } |
| | | |
| | | const onlineStatusMap: Record<string, CollectorOnlineStatus> = { '0': '离线', '1': '在线' } |
| | |
| | | return mapCollector(result.data) |
| | | } |
| | | |
| | | export async function getDiscoveredCollectorList(pageNum: number, pageSize: number): Promise<DiscoveredCollectorListResult> { |
| | | const result = await request<DiscoveredCollectorListResponse>( |
| | | `/system/collector/discovered/list?pageNum=${pageNum}&pageSize=${pageSize}`, |
| | | ) |
| | | return { |
| | | total: Number(result.total) || 0, |
| | | rows: (result.rows || []).map(item => ({ |
| | | deviceCode: item.deviceCode, |
| | | ip: item.ip, |
| | | mac: item.mac, |
| | | name: item.name || '', |
| | | manufacturer: item.manufacturer || '', |
| | | model: item.model || '', |
| | | channelCount: item.channelCount || 1, |
| | | onlineStatus: item.onlineStatus || '离线', |
| | | testStatus: item.testStatus || '空闲', |
| | | currentTest: item.currentTest || '-', |
| | | status: '启用', |
| | | remark: '', |
| | | })), |
| | | } |
| | | } |
| | | |
| | | export function createDeviceCollector(input: DeviceCollectorInput): Promise<ApiResponse> { |
| | | return request<ApiResponse>('/system/collector', { |
| | | method: 'POST', |
| | |
| | | |
| | | export type CollectorPayload = Pick<DeviceCollectorRecord, 'deviceCode' | 'ip' | 'mac' | 'name' | 'manufacturer' | 'model' | 'channelCount' | 'remark' | 'onlineStatus' | 'testStatus' | 'currentTest' | 'status'> |
| | | |
| | | export const discoveredCollectors: CollectorPayload[] = [ |
| | | { deviceCode: '45643176', ip: '192.1.1.162', mac: '41:F5:D7:A1:00:01', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '在线', testStatus: '空闲', currentTest: '-', status: '启用' }, |
| | | { deviceCode: '45643177', ip: '192.1.1.163', mac: '41:F5:D7:A2:00:02', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '在线', testStatus: '待检测', currentTest: '-', status: '启用' }, |
| | | { deviceCode: '45643178', ip: '192.1.1.164', mac: '41:F5:D7:A3:00:03', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '离线', testStatus: '空闲', currentTest: '-', status: '启用' }, |
| | | { deviceCode: '45643179', ip: '192.1.1.165', mac: '41:F5:D7:A4:00:04', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '在线', testStatus: '空闲', currentTest: '-', status: '启用' }, |
| | | { deviceCode: '45643180', ip: '192.1.1.166', mac: '41:F5:D7:A5:00:05', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '在线', testStatus: '待检测', currentTest: '-', status: '启用' }, |
| | | { deviceCode: '45643181', ip: '192.1.1.167', mac: '41:F5:D7:A6:00:06', name: '', manufacturer: '', model: '', channelCount: 1, remark: '', onlineStatus: '在线', testStatus: '空闲', currentTest: '-', status: '启用' }, |
| | | ] |
| | | |
| | | const STORAGE_KEY = 'battery_detection_device_collectors_v1' |
| | | |
| | | const seedRecords: DeviceCollectorRecord[] = [ |
| | |
| | | import { computed, onMounted, reactive, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { createDeviceCollector, getDeviceCollector, updateDeviceCollector } from '@/api/deviceCollectors' |
| | | import { discoveredCollectors, type CollectorPayload } from '@/stores/deviceCollectors' |
| | | import { createDeviceCollector, getDeviceCollector, getDiscoveredCollectorList, updateDeviceCollector } from '@/api/deviceCollectors' |
| | | import type { CollectorPayload } from '@/stores/deviceCollectors' |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | | const loading = ref(false) |
| | | const saving = ref(false) |
| | | const deviceDialogVisible = ref(false) |
| | | const deviceLoading = ref(false) |
| | | const devicePage = ref(1) |
| | | const devicePageSize = 5 |
| | | const devices = ref<CollectorPayload[]>([]) |
| | | const deviceTotal = ref(0) |
| | | const channelOptions = Array.from({ length: 99 }, (_, index) => index + 1) |
| | | const editingId = computed(() => Number(route.params.id) || null) |
| | | const isEditing = computed(() => editingId.value !== null) |
| | | const pagedDevices = computed(() => discoveredCollectors.slice((devicePage.value - 1) * devicePageSize, devicePage.value * devicePageSize)) |
| | | |
| | | const form = reactive<CollectorPayload>({ |
| | | deviceCode: '', ip: '', mac: '', name: '', manufacturer: '', model: '', channelCount: 1, |
| | |
| | | |
| | | onMounted(loadEditingCollector) |
| | | |
| | | function openDeviceDialog() { |
| | | async function loadDiscoveredCollectors() { |
| | | deviceLoading.value = true |
| | | try { |
| | | const result = await getDiscoveredCollectorList(devicePage.value, devicePageSize) |
| | | devices.value = result.rows |
| | | deviceTotal.value = result.total |
| | | } catch (error) { |
| | | devices.value = [] |
| | | deviceTotal.value = 0 |
| | | ElMessage.error(error instanceof Error ? error.message : '可发现采集器加载失败') |
| | | } finally { |
| | | deviceLoading.value = false |
| | | } |
| | | } |
| | | |
| | | async function openDeviceDialog() { |
| | | devicePage.value = 1 |
| | | deviceDialogVisible.value = true |
| | | await loadDiscoveredCollectors() |
| | | } |
| | | |
| | | async function handleDevicePageChange(page: number) { |
| | | devicePage.value = page |
| | | await loadDiscoveredCollectors() |
| | | } |
| | | |
| | | function selectDevice(device: CollectorPayload) { |
| | |
| | | deviceCode: device.deviceCode, |
| | | ip: device.ip, |
| | | mac: device.mac, |
| | | name: device.name, |
| | | manufacturer: device.manufacturer, |
| | | model: device.model, |
| | | channelCount: device.channelCount, |
| | | onlineStatus: device.onlineStatus, |
| | | testStatus: device.testStatus, |
| | | currentTest: device.currentTest, |
| | |
| | | </section> |
| | | |
| | | <el-dialog v-model="deviceDialogVisible" class="device-dialog" title="选择设备" width="720px" align-center> |
| | | <div class="device-table-wrap"> |
| | | <div v-loading="deviceLoading" class="device-table-wrap"> |
| | | <table class="device-table"> |
| | | <thead><tr><th>设备编码</th><th>IP</th><th>MAC</th><th>操作</th></tr></thead> |
| | | <tbody><tr v-for="device in pagedDevices" :key="device.deviceCode"><td>{{ device.deviceCode }}</td><td>{{ device.ip }}</td><td>{{ device.mac }}</td><td><button type="button" @click="selectDevice(device)">选择</button></td></tr></tbody> |
| | | <tbody> |
| | | <tr v-for="device in devices" :key="device.deviceCode"><td>{{ device.deviceCode }}</td><td>{{ device.ip }}</td><td>{{ device.mac }}</td><td><button type="button" @click="selectDevice(device)">选择</button></td></tr> |
| | | <tr v-if="!deviceLoading && !devices.length"><td colspan="4" class="empty-cell">暂无可发现采集器</td></tr> |
| | | </tbody> |
| | | </table> |
| | | </div> |
| | | <footer class="device-pagination"><span>共 {{ discoveredCollectors.length }} 个设备</span><el-pagination v-model:current-page="devicePage" :page-size="devicePageSize" :total="discoveredCollectors.length" layout="prev, pager, next" background /></footer> |
| | | <footer class="device-pagination"><span>共 {{ deviceTotal }} 个设备</span><el-pagination :current-page="devicePage" :page-size="devicePageSize" :total="deviceTotal" layout="prev, pager, next" background @current-change="handleDevicePageChange" /></footer> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | .device-table th:last-child, .device-table td:last-child { width: 88px; text-align: center; } |
| | | .device-table td:last-child button { border: 0; color: var(--blue); background: transparent; font-size: 12px; font-weight: 500; cursor: pointer; } |
| | | .device-table tbody tr:hover { background: #f7fbff; } |
| | | .device-table .empty-cell { height: 96px; color: #7b8794; text-align: center; } |
| | | .device-pagination { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 18px; } |
| | | .device-pagination > span { color: #7b8794; font-size: 11px; } |
| | | .device-pagination :deep(.el-pagination) { --el-pagination-button-bg-color: #edf4fa; --el-pagination-hover-color: var(--blue); } |