yeg
2026-08-27 eb001ee347e7def1dd3e70cc64df0d6d93caaeab
feat(device): connect collector discovery flow
3个文件已修改
101 ■■■■■ 已修改文件
src/api/deviceCollectors.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/deviceCollectors.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/DeviceCollectorForm.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/deviceCollectors.ts
@@ -2,6 +2,7 @@
  CollectorOnlineStatus,
  CollectorStatus,
  CollectorTestStatus,
  CollectorPayload,
  DeviceCollectorRecord,
} from '@/stores/deviceCollectors'
import { request, type ApiResponse } from './request'
@@ -43,6 +44,24 @@
  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
@@ -71,6 +90,11 @@
export interface DeviceCollectorListResult {
  total: number
  rows: DeviceCollectorRecord[]
}
export interface DiscoveredCollectorListResult {
  total: number
  rows: CollectorPayload[]
}
const onlineStatusMap: Record<string, CollectorOnlineStatus> = { '0': '离线', '1': '在线' }
@@ -129,6 +153,29 @@
  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',
src/stores/deviceCollectors.ts
@@ -23,15 +23,6 @@
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[] = [
src/views/DeviceCollectorForm.vue
@@ -2,20 +2,22 @@
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,
@@ -37,9 +39,30 @@
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) {
@@ -47,6 +70,10 @@
    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,
@@ -115,13 +142,16 @@
    </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>
@@ -173,6 +203,7 @@
.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); }