wangzhibo
10 天以前 220e4fab27b7f419b3e89e2f2d28ae789aac09d7
xiaozhi-esp32-server/main/manager-mobile/src/pages/index/index.vue
@@ -11,12 +11,13 @@
</route>
<script lang="ts" setup>
import type { Agent } from '@/api/agent/types'
import type { Agent, AgentDevice } from '@/api/agent/types'
import { onShow } from '@dcloudio/uni-app'
import { computed, onMounted, ref } from 'vue'
// 在组件挂载后设置导航栏标题
import { useMessage } from 'wot-design-uni/components/wd-message-box'
import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
import { createAgent, deleteAgent, getAgentList } from '@/api/agent/agent'
import { getBindDevices } from '@/api/device'
import { t } from '@/i18n'
import { toast } from '@/utils/toast'
@@ -68,6 +69,7 @@
    console.log('z-paging获取智能体列表')
    const response = await getAgentList()
    await attachDevices(response)
    // 更新本地列表
    agentList.value = response
@@ -144,6 +146,73 @@
  finally {
    deleteAgentLoading.value = false
  }
}
async function attachDevices(agents: Agent[]) {
  await Promise.all((agents || []).map(async (agent) => {
    if (Array.isArray(agent.devices))
      return
    try {
      const list = await getBindDevices(agent.id)
      agent.devices = (list || []).map(device => ({
        macAddress: device.macAddress,
        alias: device.alias,
        online: device.online,
      }))
      agent.onlineDeviceCount = (agent.devices || []).filter(item => item.online === true).length
      agent.deviceCount = agent.devices?.length || 0
    }
    catch (error) {
      console.error('load home device status failed', error)
      agent.devices = []
    }
  }))
}
function agentPresence(agent: Agent): 'online' | 'offline' | 'none' | 'unknown' {
  const devices = agent.devices
  if (!Array.isArray(devices))
    return 'unknown'
  if (devices.length === 0)
    return 'none'
  if (devices.some(item => item.online === true))
    return 'online'
  if (devices.every(item => item.online === false))
    return 'offline'
  return 'unknown'
}
function agentPresenceTitle(agent: Agent) {
  const state = agentPresence(agent)
  if (state === 'online') {
    const devices = agent.devices || []
    const online = devices.filter(item => item.online === true).length
    if (devices.length > 1)
      return t('home.onlineCount', { online, total: devices.length })
    return t('device.online')
  }
  if (state === 'offline')
    return t('device.offline')
  if (state === 'none')
    return t('home.noBoundDevice')
  return t('device.checkingOnline')
}
function devicePresence(device: AgentDevice): 'online' | 'offline' | 'unknown' {
  if (device.online === true)
    return 'online'
  if (device.online === false)
    return 'offline'
  return 'unknown'
}
function devicePresenceText(device: AgentDevice) {
  const state = devicePresence(device)
  if (state === 'online')
    return t('device.online')
  if (state === 'offline')
    return t('device.offline')
  return t('device.checkingOnline')
}
// 进入编辑页面
@@ -260,6 +329,28 @@
                  <text class="agent-name">
                    {{ agent.agentName }}
                  </text>
                  <view class="presence-chip" :class="agentPresence(agent)">
                    <view class="presence-chip-dot" :class="agentPresence(agent)" />
                    <text class="presence-chip-text" :class="agentPresence(agent)">
                      {{ agentPresenceTitle(agent) }}
                    </text>
                  </view>
                </view>
                <view v-if="agent.devices && agent.devices.length" class="device-presence-list">
                  <view
                    v-for="device in agent.devices"
                    :key="device.macAddress"
                    class="device-presence-row"
                  >
                    <view class="device-presence-dot" :class="devicePresence(device)" />
                    <text class="device-presence-name">
                      {{ device.macAddress }}
                    </text>
                    <text class="device-presence-state" :class="devicePresence(device)">
                      {{ devicePresenceText(device) }}
                    </text>
                  </view>
                </view>
                <view class="model-info">
@@ -557,15 +648,143 @@
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12rpx;
    margin-bottom: 12rpx;
    .agent-name {
      flex: 1;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-size: 32rpx;
      font-weight: 600;
      color: #1a1a1a;
    }
  }
  .presence-chip {
    display: flex;
    align-items: center;
    gap: 8rpx;
    flex-shrink: 0;
    padding: 6rpx 14rpx;
    border-radius: 24rpx;
    &.online {
      background: #e8f8ef;
    }
    &.offline {
      background: #fff1f0;
    }
    &.none,
    &.unknown {
      background: #f5f7fb;
    }
  }
  .presence-chip-dot {
    width: 12rpx;
    height: 12rpx;
    flex-shrink: 0;
    border-radius: 50%;
    &.online {
      background: #1aa34a;
    }
    &.offline {
      background: #d4380d;
    }
    &.none,
    &.unknown {
      background: #c0c4cc;
    }
  }
  .presence-chip-text {
    font-size: 22rpx;
    font-weight: 600;
    line-height: 1.4;
    &.online {
      color: #146c36;
    }
    &.offline {
      color: #d4380d;
    }
    &.none,
    &.unknown {
      color: #65686f;
    }
  }
  .device-presence-list {
    display: flex;
    flex-direction: column;
    gap: 8rpx;
    margin-bottom: 16rpx;
  }
  .device-presence-row {
    display: flex;
    align-items: center;
    gap: 10rpx;
    padding: 8rpx 4rpx;
  }
  .device-presence-dot {
    width: 12rpx;
    height: 12rpx;
    flex-shrink: 0;
    border-radius: 50%;
    &.online {
      background: #1aa34a;
    }
    &.offline {
      background: #d4380d;
    }
    &.unknown {
      background: #c0c4cc;
    }
  }
  .device-presence-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 24rpx;
    color: #232338;
  }
  .device-presence-state {
    flex-shrink: 0;
    font-size: 22rpx;
    font-weight: 600;
    &.online {
      color: #146c36;
    }
    &.offline {
      color: #d4380d;
    }
    &.unknown {
      color: #65686f;
    }
  }
  .model-info {
    margin-bottom: 16rpx;