|
|
|
|
@ -3,71 +3,80 @@
|
|
|
|
|
<!-- 任务信息 -->
|
|
|
|
|
<ContentWrap title="任务信息" class="mb-20px">
|
|
|
|
|
<el-descriptions :column="3" v-loading="taskLoading">
|
|
|
|
|
<el-descriptions-item label="任务ID">{{ taskInfo.id }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务名称">{{ taskInfo.name }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务类型">版本升级</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="设备数量">{{
|
|
|
|
|
taskInfo.deviceTotalCount
|
|
|
|
|
}}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="预定时间">-</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="添加时间">{{
|
|
|
|
|
formatTime(taskInfo.createTime)
|
|
|
|
|
}}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务描述" :span="3">{{
|
|
|
|
|
taskInfo.description || '-'
|
|
|
|
|
}}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务编号">{{ task.id }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务名称">{{ task.name }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="升级范围">
|
|
|
|
|
<dict-tag :type="DICT_TYPE.IOT_OTA_TASK_DEVICE_SCOPE" :value="task.deviceScope" />
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务状态">
|
|
|
|
|
<dict-tag :type="DICT_TYPE.IOT_OTA_TASK_STATUS" :value="task.status" />
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="创建时间">
|
|
|
|
|
{{ task.createTime ? formatDate(task.createTime) : '-' }}
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="任务描述" :span="3">
|
|
|
|
|
{{ task.description }}
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
|
|
|
|
|
<!-- 任务升级设备统计 -->
|
|
|
|
|
<ContentWrap title="升级设备统计" class="mb-20px">
|
|
|
|
|
<el-row :gutter="20" class="py-20px" v-loading="statisticsLoading">
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-row :gutter="20" class="py-20px" v-loading="taskStatisticsLoading">
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-blue-500">
|
|
|
|
|
{{ statisticsData.total }}
|
|
|
|
|
{{ Object.values(taskStatistics).reduce((sum, count) => sum + (count || 0), 0) || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">升级设备总数</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-gray-400">
|
|
|
|
|
{{ statisticsData.pending }}
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.PENDING.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">待推送</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-blue-400">
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.PUSHED.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">已推送</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-yellow-500">
|
|
|
|
|
{{ statisticsData.upgrading }}
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.UPGRADING.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">正在升级</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-green-500">
|
|
|
|
|
{{ statisticsData.success }}
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.SUCCESS.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">升级成功</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-red-500">
|
|
|
|
|
{{ statisticsData.failure }}
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.FAILURE.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">升级失败</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-col :span="3">
|
|
|
|
|
<div class="text-center p-20px border border-solid border-gray-200 rounded bg-gray-50">
|
|
|
|
|
<div class="text-32px font-bold mb-8px text-gray-400">
|
|
|
|
|
{{ statisticsData.stopped }}
|
|
|
|
|
{{ taskStatistics[IoTOtaTaskRecordStatusEnum.CANCELED.value] || 0 }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-14px text-gray-600">停止</div>
|
|
|
|
|
<div class="text-14px text-gray-600">升级取消</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
@ -89,10 +98,10 @@
|
|
|
|
|
:show-overflow-tooltip="true"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column label="设备名称" align="center" prop="deviceName" />
|
|
|
|
|
<el-table-column label="当前版本" align="center" prop="currentVersion" />
|
|
|
|
|
<el-table-column label="当前版本" align="center" prop="fromFirmwareVersion" />
|
|
|
|
|
<el-table-column label="升级状态" align="center" prop="status" width="120">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<dict-tag :type="DICT_TYPE.IOT_OTA_RECORD_STATUS" :value="scope.row.status" />
|
|
|
|
|
<dict-tag :type="DICT_TYPE.IOT_OTA_TASK_RECORD_STATUS" :value="scope.row.status" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="升级进度" align="center" prop="progress" width="120">
|
|
|
|
|
@ -100,7 +109,9 @@
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="状态描述" align="center" prop="description" />
|
|
|
|
|
<el-table-column label="更新时间" align="center" prop="updateTime" width="180">
|
|
|
|
|
<template #default="scope"> {{ formatTime(scope.row.updateTime) }} </template>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ formatDate(scope.row.updateTime) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" align="center" width="80">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
@ -116,7 +127,6 @@
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
<!-- 分页 -->
|
|
|
|
|
<div class="flex justify-center mt-20px">
|
|
|
|
|
<Pagination
|
|
|
|
|
:total="recordTotal"
|
|
|
|
|
v-model:page="queryParams.pageNo"
|
|
|
|
|
@ -124,7 +134,6 @@
|
|
|
|
|
@pagination="getRecordList"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
</Dialog>
|
|
|
|
|
</template>
|
|
|
|
|
@ -144,90 +153,48 @@ import { formatDate } from '@/utils/formatTime'
|
|
|
|
|
/** OTA 任务详情组件 */
|
|
|
|
|
defineOptions({ name: 'OtaTaskDetail' })
|
|
|
|
|
|
|
|
|
|
const message = useMessage()
|
|
|
|
|
const message = useMessage() // 消息弹窗
|
|
|
|
|
|
|
|
|
|
// 弹窗相关
|
|
|
|
|
const dialogVisible = ref(false)
|
|
|
|
|
const taskId = ref<number>()
|
|
|
|
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
|
|
|
|
|
|
|
|
// 任务信息
|
|
|
|
|
const taskLoading = ref(false)
|
|
|
|
|
const taskInfo = ref<OtaTask>({})
|
|
|
|
|
const taskId = ref<number>() // 任务编号
|
|
|
|
|
const taskLoading = ref(false) // 任务加载状态
|
|
|
|
|
const task = ref<OtaTask>({} as OtaTask) // 任务信息
|
|
|
|
|
|
|
|
|
|
// 统计加载状态
|
|
|
|
|
const statisticsLoading = ref(false)
|
|
|
|
|
const taskStatisticsLoading = ref(false) // 任务统计加载状态
|
|
|
|
|
const taskStatistics = ref<Record<string, number>>({}) // 任务统计数据
|
|
|
|
|
|
|
|
|
|
// 统计数据
|
|
|
|
|
const statisticsData = ref({
|
|
|
|
|
total: 0,
|
|
|
|
|
pending: 0,
|
|
|
|
|
pushed: 0,
|
|
|
|
|
upgrading: 0,
|
|
|
|
|
success: 0,
|
|
|
|
|
failure: 0,
|
|
|
|
|
stopped: 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 当前选中的标签
|
|
|
|
|
const activeTab = ref('')
|
|
|
|
|
|
|
|
|
|
// 记录列表相关
|
|
|
|
|
const recordLoading = ref(false)
|
|
|
|
|
const recordList = ref<OtaTaskRecord[]>([])
|
|
|
|
|
const recordTotal = ref(0)
|
|
|
|
|
const recordLoading = ref(false) // 记录列表加载状态
|
|
|
|
|
const recordList = ref<OtaTaskRecord[]>([]) // 记录列表数据
|
|
|
|
|
const recordTotal = ref(0) // 记录总数
|
|
|
|
|
const queryParams = reactive({
|
|
|
|
|
pageNo: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
taskId: undefined as number | undefined,
|
|
|
|
|
status: undefined as number | undefined,
|
|
|
|
|
deviceNumber: ''
|
|
|
|
|
status: undefined as number | undefined
|
|
|
|
|
}) // 查询参数
|
|
|
|
|
const activeTab = ref('') // 当前激活的标签页
|
|
|
|
|
|
|
|
|
|
/** 状态标签配置 */
|
|
|
|
|
const statusTabs = computed(() => {
|
|
|
|
|
const tabs = [{ key: '', label: '全部设备' }]
|
|
|
|
|
Object.values(IoTOtaTaskRecordStatusEnum).forEach((status) => {
|
|
|
|
|
tabs.push({
|
|
|
|
|
key: status.value.toString(),
|
|
|
|
|
label: status.label
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
return tabs
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 状态标签配置
|
|
|
|
|
const statusTabs = computed(() => [
|
|
|
|
|
{ key: '', label: '全部设备' },
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.PENDING.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.PENDING.label
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.PUSHED.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.PUSHED.label
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.UPGRADING.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.UPGRADING.label
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.SUCCESS.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.SUCCESS.label
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.FAILURE.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.FAILURE.label
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: IoTOtaTaskRecordStatusEnum.CANCELED.value.toString(),
|
|
|
|
|
label: IoTOtaTaskRecordStatusEnum.CANCELED.label
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
/** 时间格式化 */
|
|
|
|
|
const formatTime = (time: string | undefined) => {
|
|
|
|
|
if (!time) return '-'
|
|
|
|
|
return formatDate(new Date(time))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 获取任务详情 */
|
|
|
|
|
const getTaskInfo = async () => {
|
|
|
|
|
if (!taskId.value) return
|
|
|
|
|
|
|
|
|
|
if (!taskId.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
taskLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const data = await IoTOtaTaskApi.getOtaTask(taskId.value)
|
|
|
|
|
taskInfo.value = data
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取任务详情失败', error)
|
|
|
|
|
task.value = await IoTOtaTaskApi.getOtaTask(taskId.value)
|
|
|
|
|
} finally {
|
|
|
|
|
taskLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
@ -235,61 +202,31 @@ const getTaskInfo = async () => {
|
|
|
|
|
|
|
|
|
|
/** 获取统计数据 */
|
|
|
|
|
const getStatistics = async () => {
|
|
|
|
|
if (!taskId.value) return
|
|
|
|
|
|
|
|
|
|
statisticsLoading.value = true
|
|
|
|
|
if (!taskId.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
taskStatisticsLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const data = await IoTOtaTaskRecordApi.getOtaTaskRecordStatusStatistics(undefined, taskId.value)
|
|
|
|
|
statisticsData.value = {
|
|
|
|
|
total: data.total || 0,
|
|
|
|
|
pending: data.pending || 0,
|
|
|
|
|
pushed: data.pushed || 0,
|
|
|
|
|
upgrading: data.upgrading || 0,
|
|
|
|
|
success: data.success || 0,
|
|
|
|
|
failure: data.failure || 0,
|
|
|
|
|
stopped: data.stopped || 0
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取统计数据失败', error)
|
|
|
|
|
// 模拟数据
|
|
|
|
|
statisticsData.value = {
|
|
|
|
|
total: 1,
|
|
|
|
|
pending: 0,
|
|
|
|
|
pushed: 0,
|
|
|
|
|
upgrading: 0,
|
|
|
|
|
success: 0,
|
|
|
|
|
failure: 1,
|
|
|
|
|
stopped: 0
|
|
|
|
|
}
|
|
|
|
|
taskStatistics.value = await IoTOtaTaskRecordApi.getOtaTaskRecordStatusStatistics(
|
|
|
|
|
undefined,
|
|
|
|
|
taskId.value
|
|
|
|
|
)
|
|
|
|
|
} finally {
|
|
|
|
|
statisticsLoading.value = false
|
|
|
|
|
taskStatisticsLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 获取记录列表 */
|
|
|
|
|
/** 获取升级记录列表 */
|
|
|
|
|
const getRecordList = async () => {
|
|
|
|
|
if (!taskId.value) return
|
|
|
|
|
|
|
|
|
|
if (!taskId.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
recordLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
queryParams.taskId = taskId.value
|
|
|
|
|
const data = await IoTOtaTaskRecordApi.getOtaTaskRecordPage(queryParams)
|
|
|
|
|
recordList.value = data.list || []
|
|
|
|
|
recordTotal.value = data.total || 0
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取记录列表失败', error)
|
|
|
|
|
// 模拟数据
|
|
|
|
|
recordList.value = [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
taskId: taskId.value,
|
|
|
|
|
deviceId: '1',
|
|
|
|
|
status: IoTOtaTaskRecordStatusEnum.FAILURE.value,
|
|
|
|
|
progress: 0,
|
|
|
|
|
description: '升级失败'
|
|
|
|
|
} as OtaTaskRecord
|
|
|
|
|
]
|
|
|
|
|
recordTotal.value = 1
|
|
|
|
|
} finally {
|
|
|
|
|
recordLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
@ -300,14 +237,7 @@ const handleTabClick = (tab: TabsPaneContext) => {
|
|
|
|
|
const tabKey = tab.paneName as string
|
|
|
|
|
activeTab.value = tabKey
|
|
|
|
|
queryParams.pageNo = 1
|
|
|
|
|
|
|
|
|
|
// 设置状态过滤:使用 IoTOtaTaskRecordStatusEnum 的值作为 tab key
|
|
|
|
|
if (tabKey === '') {
|
|
|
|
|
queryParams.status = undefined // 全部
|
|
|
|
|
} else {
|
|
|
|
|
queryParams.status = parseInt(tabKey) // 直接使用枚举值
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queryParams.status = activeTab.value === '' ? undefined : parseInt(tabKey)
|
|
|
|
|
getRecordList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -315,9 +245,12 @@ const handleTabClick = (tab: TabsPaneContext) => {
|
|
|
|
|
const handleCancelUpgrade = async (record: OtaTaskRecord) => {
|
|
|
|
|
try {
|
|
|
|
|
await message.confirm('确认要取消该设备的升级任务吗?')
|
|
|
|
|
// TODO: 调用取消升级接口
|
|
|
|
|
await IoTOtaTaskRecordApi.cancelOtaTaskRecord(record.id!)
|
|
|
|
|
message.success('取消成功')
|
|
|
|
|
getRecordList()
|
|
|
|
|
// 刷新数据
|
|
|
|
|
await getRecordList()
|
|
|
|
|
await getStatistics()
|
|
|
|
|
// TODO @AI:需要 succes 不断刷新出去
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('取消升级失败', error)
|
|
|
|
|
}
|
|
|
|
|
@ -327,12 +260,10 @@ const handleCancelUpgrade = async (record: OtaTaskRecord) => {
|
|
|
|
|
const open = (id: number) => {
|
|
|
|
|
taskId.value = id
|
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
|
|
|
|
|
// 重置数据
|
|
|
|
|
activeTab.value = ''
|
|
|
|
|
queryParams.pageNo = 1
|
|
|
|
|
queryParams.status = undefined
|
|
|
|
|
queryParams.deviceNumber = ''
|
|
|
|
|
|
|
|
|
|
// 加载数据
|
|
|
|
|
getTaskInfo()
|
|
|
|
|
@ -341,7 +272,5 @@ const open = (id: number) => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 暴露方法 */
|
|
|
|
|
defineExpose({
|
|
|
|
|
open
|
|
|
|
|
})
|
|
|
|
|
defineExpose({ open })
|
|
|
|
|
</script>
|
|
|
|
|
|