|
@@ -0,0 +1,849 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="app-container ota-upgrade-page">
|
|
|
|
|
+ <!-- 说明卡片 -->
|
|
|
|
|
+ <div class="ota-hero">
|
|
|
|
|
+ <div class="ota-hero-left">
|
|
|
|
|
+ <div class="ota-hero-title">软件版本 / OTA 升级</div>
|
|
|
|
|
+ <div class="ota-hero-desc">
|
|
|
|
|
+ 当前页面为前端占位版,用于展示智能巡检机器人小车整机软件模块升级管理方式。
|
|
|
|
|
+ 实际可升级模块、升级包格式、升级流程和进度回传方式,待机器人侧能力确认后接入。
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="risk-tip">
|
|
|
|
|
+ <el-icon><Warning /></el-icon>
|
|
|
|
|
+ <span>
|
|
|
|
|
+ 智驾导航、底盘控制、驱动适配类升级可能影响机器人移动、定位、避障和底盘控制能力。
|
|
|
|
|
+ 真实升级前必须确保机器人处于安全停靠状态。
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="ota-hero-right">
|
|
|
|
|
+ <el-tag type="info" effect="plain">前端占位</el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 当前版本信息 -->
|
|
|
|
|
+ <div class="ota-section-card">
|
|
|
|
|
+ <div class="section-header">
|
|
|
|
|
+ <span class="section-title">当前版本信息</span>
|
|
|
|
|
+ <span class="section-desc">展示机器人本地各软件模块当前版本和运行状态</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="section-toolbar">
|
|
|
|
|
+ <el-button type="primary" plain icon="Refresh" @click="refreshVersions">刷新版本信息</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="table-scroll">
|
|
|
|
|
+ <el-table v-loading="loading" :data="versionList" stripe class="version-table">
|
|
|
|
|
+ <el-table-column label="模块分类" align="center" prop="moduleCategory" width="120">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getCategoryTagType(scope.row.moduleCategory)" effect="plain">
|
|
|
|
|
+ {{ formatModuleCategory(scope.row.moduleCategory) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="模块编码" align="center" prop="moduleCode" min-width="180" />
|
|
|
|
|
+ <el-table-column label="模块名称" align="center" prop="moduleName" min-width="140" />
|
|
|
|
|
+ <el-table-column label="当前版本" align="center" prop="currentVersion" width="120" />
|
|
|
|
|
+ <el-table-column label="运行状态" align="center" prop="runStatus" width="100">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getRunStatusTagType(scope.row.runStatus)" effect="plain">
|
|
|
|
|
+ {{ formatRunStatus(scope.row.runStatus) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="安装时间" align="center" prop="installTime" width="170" />
|
|
|
|
|
+ <el-table-column label="备注" align="left" prop="remark" min-width="220" show-overflow-tooltip />
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 升级包管理 -->
|
|
|
|
|
+ <div class="ota-section-card">
|
|
|
|
|
+ <div class="section-header">
|
|
|
|
|
+ <span class="section-title">升级包管理</span>
|
|
|
|
|
+ <span class="section-desc">展示已上传的升级包,当前为前端模拟,不执行真实上传</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="section-toolbar">
|
|
|
|
|
+ <el-button type="primary" icon="Upload" @click="openUploadDialog">上传升级包</el-button>
|
|
|
|
|
+ <el-button plain icon="Refresh" @click="refreshPackages">刷新升级包</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="table-scroll">
|
|
|
|
|
+ <el-table v-loading="loading" :data="pagedPackageList" stripe class="package-table">
|
|
|
|
|
+ <el-table-column label="安装包名称" align="left" prop="packageName" min-width="260" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="模块分类" align="center" prop="moduleCategory" width="120">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getCategoryTagType(scope.row.moduleCategory)" effect="plain">
|
|
|
|
|
+ {{ formatModuleCategory(scope.row.moduleCategory) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="模块名称" align="center" prop="moduleName" min-width="140" />
|
|
|
|
|
+ <el-table-column label="目标版本" align="center" prop="targetVersion" width="120" />
|
|
|
|
|
+ <el-table-column label="包类型" align="center" prop="packageType" width="90">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ {{ formatPackageType(scope.row.packageType) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="文件大小" align="center" prop="fileSize" width="100" />
|
|
|
|
|
+ <el-table-column label="包状态" align="center" prop="packageStatus" width="90">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getPackageStatusTagType(scope.row.packageStatus)" effect="plain">
|
|
|
|
|
+ {{ formatPackageStatus(scope.row.packageStatus) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="上传时间" align="center" prop="uploadTime" width="170" />
|
|
|
|
|
+ <el-table-column label="操作" align="center" fixed="right" width="170">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button link type="primary" icon="UploadFilled" @click="handleExecuteUpgrade(scope.row)">执行升级</el-button>
|
|
|
|
|
+ <el-button link type="danger" icon="Delete" @click="handleDeletePackage(scope.row)">删除</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="table-pagination">
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="packageList.length > 0"
|
|
|
|
|
+ :total="packageList.length"
|
|
|
|
|
+ v-model:page="packagePage.pageNum"
|
|
|
|
|
+ v-model:limit="packagePage.pageSize"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 升级记录 -->
|
|
|
|
|
+ <div class="ota-section-card">
|
|
|
|
|
+ <div class="section-header">
|
|
|
|
|
+ <span class="section-title">升级记录</span>
|
|
|
|
|
+ <span class="section-desc">展示历史升级执行记录,当前为模拟数据</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="section-toolbar">
|
|
|
|
|
+ <el-button type="primary" plain icon="Refresh" @click="refreshRecords">刷新升级记录</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="table-scroll">
|
|
|
|
|
+ <el-table v-loading="loading" :data="pagedRecordList" stripe class="record-table">
|
|
|
|
|
+ <el-table-column label="开始时间" align="center" prop="startTime" width="170" />
|
|
|
|
|
+ <el-table-column label="模块分类" align="center" prop="moduleCategory" width="120">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getCategoryTagType(scope.row.moduleCategory)" effect="plain">
|
|
|
|
|
+ {{ formatModuleCategory(scope.row.moduleCategory) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="模块名称" align="center" prop="moduleName" min-width="140" />
|
|
|
|
|
+ <el-table-column label="原版本" align="center" prop="currentVersion" width="100" />
|
|
|
|
|
+ <el-table-column label="目标版本" align="center" prop="targetVersion" width="100" />
|
|
|
|
|
+ <el-table-column label="升级状态" align="center" prop="resultStatus" width="110">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getUpgradeStatusTagType(scope.row.resultStatus)" effect="plain">
|
|
|
|
|
+ {{ formatUpgradeStatus(scope.row.resultStatus) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="升级进度" align="center" prop="progressPercent" width="150">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <div class="progress-cell">
|
|
|
|
|
+ <el-progress
|
|
|
|
|
+ :percentage="scope.row.progressPercent || 0"
|
|
|
|
|
+ :color="getProgressColor(scope.row.resultStatus)"
|
|
|
|
|
+ :status="scope.row.resultStatus === '3' ? 'exception' : scope.row.resultStatus === '2' ? 'success' : undefined"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="执行人" align="center" prop="executeBy" width="100" />
|
|
|
|
|
+ <el-table-column label="操作" align="center" fixed="right" width="110">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button link type="primary" icon="View" @click="handleRecordDetail(scope.row)">查看详情</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="table-pagination">
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="recordList.length > 0"
|
|
|
|
|
+ :total="recordList.length"
|
|
|
|
|
+ v-model:page="recordPage.pageNum"
|
|
|
|
|
+ v-model:limit="recordPage.pageSize"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 上传升级包弹窗 -->
|
|
|
|
|
+ <el-dialog title="上传升级包" v-model="uploadOpen" width="620px" append-to-body>
|
|
|
|
|
+ <el-form ref="uploadRef" :model="uploadForm" :rules="uploadRules" label-width="110px">
|
|
|
|
|
+ <el-form-item label="模块分类" prop="moduleCategory">
|
|
|
|
|
+ <el-select v-model="uploadForm.moduleCategory" placeholder="请选择模块分类" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="(name, key) in moduleCategoryMap"
|
|
|
|
|
+ :key="key"
|
|
|
|
|
+ :label="name"
|
|
|
|
|
+ :value="key"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="模块编码" prop="moduleCode">
|
|
|
|
|
+ <el-input v-model="uploadForm.moduleCode" placeholder="请输入模块编码" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="模块名称" prop="moduleName">
|
|
|
|
|
+ <el-input v-model="uploadForm.moduleName" placeholder="请输入模块名称" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="目标版本" prop="targetVersion">
|
|
|
|
|
+ <el-input v-model="uploadForm.targetVersion" placeholder="请输入目标版本,例如 v1.0.1" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="安装包名称" prop="packageName">
|
|
|
|
|
+ <el-input v-model="uploadForm.packageName" placeholder="请输入安装包文件名" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="包类型" prop="packageType">
|
|
|
|
|
+ <el-select v-model="uploadForm.packageType" placeholder="请选择包类型" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="(name, key) in packageTypeMap"
|
|
|
|
|
+ :key="key"
|
|
|
|
|
+ :label="name"
|
|
|
|
|
+ :value="key"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="安装包文件">
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ class="upload-demo"
|
|
|
|
|
+ drag
|
|
|
|
|
+ action="#"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :show-file-list="false"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
|
|
|
|
+ <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
|
|
+ <template #tip>
|
|
|
|
|
+ <div class="el-upload__tip">当前为前端模拟,不执行真实上传</div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="备注">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="uploadForm.remark"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ placeholder="请输入备注信息"
|
|
|
|
|
+ maxlength="200"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button @click="uploadOpen = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="submitUpload">确认上传</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 升级记录详情弹窗 -->
|
|
|
|
|
+ <el-dialog title="升级记录详情" v-model="detailOpen" width="680px" append-to-body>
|
|
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
|
|
+ <el-descriptions-item label="模块分类">
|
|
|
|
|
+ <el-tag :type="getCategoryTagType(detail.moduleCategory)" effect="plain">
|
|
|
|
|
+ {{ formatModuleCategory(detail.moduleCategory) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="模块编码">{{ detail.moduleCode || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="模块名称">{{ detail.moduleName || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="原版本">{{ detail.currentVersion || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="目标版本">{{ detail.targetVersion || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="升级状态">
|
|
|
|
|
+ <el-tag :type="getUpgradeStatusTagType(detail.resultStatus)" effect="plain">
|
|
|
|
|
+ {{ formatUpgradeStatus(detail.resultStatus) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="升级进度">
|
|
|
|
|
+ <el-progress
|
|
|
|
|
+ :percentage="detail.progressPercent || 0"
|
|
|
|
|
+ :color="getProgressColor(detail.resultStatus)"
|
|
|
|
|
+ :status="detail.resultStatus === '3' ? 'exception' : detail.resultStatus === '2' ? 'success' : undefined"
|
|
|
|
|
+ style="width: 200px"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="执行人">{{ detail.executeBy || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="开始时间">{{ detail.startTime || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="结束时间">{{ detail.endTime || (detail.resultStatus === '1' ? '升级中' : '-') }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="结果信息" :span="2">{{ detail.resultMsg || '-' }}</el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="isHighRiskModule(detail.moduleCategory)" class="detail-risk-tip">
|
|
|
|
|
+ <el-icon><Warning /></el-icon>
|
|
|
|
|
+ <span>高风险模块:该升级记录属于智驾导航、底盘控制或驱动适配相关模块。</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button @click="detailOpen = false">关闭</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { ref, reactive, computed, getCurrentInstance } from 'vue'
|
|
|
|
|
+import { Warning, UploadFilled } from '@element-plus/icons-vue'
|
|
|
|
|
+
|
|
|
|
|
+const { proxy } = getCurrentInstance()
|
|
|
|
|
+
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+const moduleCategoryMap = {
|
|
|
|
|
+ '1': '业务应用',
|
|
|
|
|
+ '2': '安防感知',
|
|
|
|
|
+ '3': '智驾导航',
|
|
|
|
|
+ '4': '底盘控制',
|
|
|
|
|
+ '5': '驱动适配',
|
|
|
|
|
+ '9': '其他'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const runStatusMap = {
|
|
|
|
|
+ '1': '运行中',
|
|
|
|
|
+ '2': '已停止',
|
|
|
|
|
+ '3': '异常',
|
|
|
|
|
+ '4': '未知'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const packageTypeMap = {
|
|
|
|
|
+ '1': 'zip',
|
|
|
|
|
+ '2': 'tar.gz',
|
|
|
|
|
+ '3': 'deb',
|
|
|
|
|
+ '4': 'sh',
|
|
|
|
|
+ '5': 'apk',
|
|
|
|
|
+ '9': '其他'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const packageStatusMap = {
|
|
|
|
|
+ '1': '可用',
|
|
|
|
|
+ '0': '停用'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const upgradeStatusMap = {
|
|
|
|
|
+ '1': '升级中',
|
|
|
|
|
+ '2': '升级成功',
|
|
|
|
|
+ '3': '升级失败',
|
|
|
|
|
+ '4': '已取消'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatModuleCategory(value) {
|
|
|
|
|
+ return moduleCategoryMap[value] || '-'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatRunStatus(value) {
|
|
|
|
|
+ return runStatusMap[value] || '-'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatPackageType(value) {
|
|
|
|
|
+ return packageTypeMap[value] || '-'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatPackageStatus(value) {
|
|
|
|
|
+ return packageStatusMap[value] || '-'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatUpgradeStatus(value) {
|
|
|
|
|
+ return upgradeStatusMap[value] || '-'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getCategoryTagType(category) {
|
|
|
|
|
+ if (category === '3' || category === '4' || category === '5') return 'warning'
|
|
|
|
|
+ if (category === '9') return 'info'
|
|
|
|
|
+ return ''
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getRunStatusTagType(status) {
|
|
|
|
|
+ const map = { '1': 'success', '2': 'info', '3': 'danger', '4': 'warning' }
|
|
|
|
|
+ return map[status] || 'info'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getPackageStatusTagType(status) {
|
|
|
|
|
+ return status === '1' ? 'success' : 'info'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getUpgradeStatusTagType(status) {
|
|
|
|
|
+ const map = { '1': 'warning', '2': 'success', '3': 'danger', '4': 'info' }
|
|
|
|
|
+ return map[status] || 'info'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getProgressColor(status) {
|
|
|
|
|
+ const map = { '1': '#E6A23C', '2': '#67C23A', '3': '#F56C6C', '4': '#909399' }
|
|
|
|
|
+ return map[status] || '#409EFF'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function isHighRiskModule(category) {
|
|
|
|
|
+ return category === '3' || category === '4' || category === '5'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const versionList = ref([
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '1',
|
|
|
|
|
+ moduleCode: 'web-admin',
|
|
|
|
|
+ moduleName: '运维端后台',
|
|
|
|
|
+ currentVersion: 'v1.0.0',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-20 09:00:00',
|
|
|
|
|
+ remark: '本地 Web 运维后台'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '1',
|
|
|
|
|
+ moduleCode: 'screen-app',
|
|
|
|
|
+ moduleName: '机身屏应用',
|
|
|
|
|
+ currentVersion: 'v1.0.0',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-20 09:10:00',
|
|
|
|
|
+ remark: '机器人 8 寸屏应用'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '1',
|
|
|
|
|
+ moduleCode: 'main-service',
|
|
|
|
|
+ moduleName: '主控业务服务',
|
|
|
|
|
+ currentVersion: 'v1.0.0',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-20 09:20:00',
|
|
|
|
|
+ remark: '本地业务主服务'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '2',
|
|
|
|
|
+ moduleCode: 'vision-service',
|
|
|
|
|
+ moduleName: '视觉识别服务',
|
|
|
|
|
+ currentVersion: 'v0.9.3',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-18 10:00:00',
|
|
|
|
|
+ remark: '摄像头识别和安防告警能力'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '3',
|
|
|
|
|
+ moduleCode: 'navigation-service',
|
|
|
|
|
+ moduleName: '导航服务',
|
|
|
|
|
+ currentVersion: 'v0.8.5',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-18 10:30:00',
|
|
|
|
|
+ remark: '定位、路径规划、导航控制相关服务'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '4',
|
|
|
|
|
+ moduleCode: 'chassis-service',
|
|
|
|
|
+ moduleName: '底盘控制服务',
|
|
|
|
|
+ currentVersion: 'v0.7.2',
|
|
|
|
|
+ runStatus: '1',
|
|
|
|
|
+ installTime: '2026-05-18 11:00:00',
|
|
|
|
|
+ remark: '底盘控制、运动控制相关服务'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ moduleCategory: '5',
|
|
|
|
|
+ moduleCode: 'lidar-driver',
|
|
|
|
|
+ moduleName: '激光雷达驱动',
|
|
|
|
|
+ currentVersion: 'v0.6.1',
|
|
|
|
|
+ runStatus: '4',
|
|
|
|
|
+ installTime: '2026-05-18 11:30:00',
|
|
|
|
|
+ remark: '雷达数据采集和驱动适配'
|
|
|
|
|
+ }
|
|
|
|
|
+])
|
|
|
|
|
+
|
|
|
|
|
+const packageList = ref([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ packageName: 'screen-app-v1.0.1.zip',
|
|
|
|
|
+ moduleCategory: '1',
|
|
|
|
|
+ moduleCode: 'screen-app',
|
|
|
|
|
+ moduleName: '机身屏应用',
|
|
|
|
|
+ targetVersion: 'v1.0.1',
|
|
|
|
|
+ packageType: '1',
|
|
|
|
|
+ fileSize: '36.5MB',
|
|
|
|
|
+ packageStatus: '1',
|
|
|
|
|
+ uploadBy: 'admin',
|
|
|
|
|
+ uploadTime: '2026-05-20 10:00:00',
|
|
|
|
|
+ remark: '屏幕端显示优化包'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ packageName: 'navigation-service-v0.8.6.tar.gz',
|
|
|
|
|
+ moduleCategory: '3',
|
|
|
|
|
+ moduleCode: 'navigation-service',
|
|
|
|
|
+ moduleName: '导航服务',
|
|
|
|
|
+ targetVersion: 'v0.8.6',
|
|
|
|
|
+ packageType: '2',
|
|
|
|
|
+ fileSize: '128.4MB',
|
|
|
|
|
+ packageStatus: '1',
|
|
|
|
|
+ uploadBy: 'admin',
|
|
|
|
|
+ uploadTime: '2026-05-20 10:20:00',
|
|
|
|
|
+ remark: '智驾导航服务升级包'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ packageName: 'chassis-service-v0.7.3.deb',
|
|
|
|
|
+ moduleCategory: '4',
|
|
|
|
|
+ moduleCode: 'chassis-service',
|
|
|
|
|
+ moduleName: '底盘控制服务',
|
|
|
|
|
+ targetVersion: 'v0.7.3',
|
|
|
|
|
+ packageType: '3',
|
|
|
|
|
+ fileSize: '82.7MB',
|
|
|
|
|
+ packageStatus: '1',
|
|
|
|
|
+ uploadBy: 'admin',
|
|
|
|
|
+ uploadTime: '2026-05-20 10:40:00',
|
|
|
|
|
+ remark: '底盘控制服务升级包'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 4,
|
|
|
|
|
+ packageName: 'lidar-driver-v0.6.2.sh',
|
|
|
|
|
+ moduleCategory: '5',
|
|
|
|
|
+ moduleCode: 'lidar-driver',
|
|
|
|
|
+ moduleName: '激光雷达驱动',
|
|
|
|
|
+ targetVersion: 'v0.6.2',
|
|
|
|
|
+ packageType: '4',
|
|
|
|
|
+ fileSize: '18.2MB',
|
|
|
|
|
+ packageStatus: '0',
|
|
|
|
|
+ uploadBy: 'admin',
|
|
|
|
|
+ uploadTime: '2026-05-19 16:30:00',
|
|
|
|
|
+ remark: '驱动适配测试包'
|
|
|
|
|
+ }
|
|
|
|
|
+])
|
|
|
|
|
+
|
|
|
|
|
+const recordList = ref([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ moduleCategory: '1',
|
|
|
|
|
+ moduleCode: 'screen-app',
|
|
|
|
|
+ moduleName: '机身屏应用',
|
|
|
|
|
+ currentVersion: 'v1.0.0',
|
|
|
|
|
+ targetVersion: 'v1.0.1',
|
|
|
|
|
+ startTime: '2026-05-20 11:00:00',
|
|
|
|
|
+ endTime: '2026-05-20 11:03:20',
|
|
|
|
|
+ resultStatus: '2',
|
|
|
|
|
+ progressPercent: 100,
|
|
|
|
|
+ executeBy: 'admin',
|
|
|
|
|
+ resultMsg: '升级成功'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ moduleCategory: '3',
|
|
|
|
|
+ moduleCode: 'navigation-service',
|
|
|
|
|
+ moduleName: '导航服务',
|
|
|
|
|
+ currentVersion: 'v0.8.5',
|
|
|
|
|
+ targetVersion: 'v0.8.6',
|
|
|
|
|
+ startTime: '2026-05-20 11:20:00',
|
|
|
|
|
+ endTime: '',
|
|
|
|
|
+ resultStatus: '1',
|
|
|
|
|
+ progressPercent: 65,
|
|
|
|
|
+ executeBy: 'admin',
|
|
|
|
|
+ resultMsg: '升级执行中,等待机器人侧回传结果'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ moduleCategory: '4',
|
|
|
|
|
+ moduleCode: 'chassis-service',
|
|
|
|
|
+ moduleName: '底盘控制服务',
|
|
|
|
|
+ currentVersion: 'v0.7.1',
|
|
|
|
|
+ targetVersion: 'v0.7.2',
|
|
|
|
|
+ startTime: '2026-05-19 15:00:00',
|
|
|
|
|
+ endTime: '2026-05-19 15:06:40',
|
|
|
|
|
+ resultStatus: '3',
|
|
|
|
|
+ progressPercent: 48,
|
|
|
|
|
+ executeBy: 'admin',
|
|
|
|
|
+ resultMsg: '升级失败:底盘服务重启后未在规定时间内恢复心跳'
|
|
|
|
|
+ }
|
|
|
|
|
+])
|
|
|
|
|
+
|
|
|
|
|
+const uploadOpen = ref(false)
|
|
|
|
|
+const uploadRef = ref()
|
|
|
|
|
+
|
|
|
|
|
+const uploadForm = reactive({
|
|
|
|
|
+ moduleCategory: '',
|
|
|
|
|
+ moduleCode: '',
|
|
|
|
|
+ moduleName: '',
|
|
|
|
|
+ targetVersion: '',
|
|
|
|
|
+ packageName: '',
|
|
|
|
|
+ packageType: '',
|
|
|
|
|
+ remark: ''
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const uploadRules = {
|
|
|
|
|
+ moduleCategory: [{ required: true, message: '请选择模块分类', trigger: 'change' }],
|
|
|
|
|
+ moduleCode: [{ required: true, message: '请输入模块编码', trigger: 'blur' }],
|
|
|
|
|
+ moduleName: [{ required: true, message: '请输入模块名称', trigger: 'blur' }],
|
|
|
|
|
+ targetVersion: [{ required: true, message: '请输入目标版本', trigger: 'blur' }],
|
|
|
|
|
+ packageName: [{ required: true, message: '请输入安装包名称', trigger: 'blur' }],
|
|
|
|
|
+ packageType: [{ required: true, message: '请选择包类型', trigger: 'change' }]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const packagePage = reactive({
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 5
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const pagedPackageList = computed(() => {
|
|
|
|
|
+ const start = (packagePage.pageNum - 1) * packagePage.pageSize
|
|
|
|
|
+ const end = start + packagePage.pageSize
|
|
|
|
|
+ return packageList.value.slice(start, end)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const recordPage = reactive({
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 5
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const pagedRecordList = computed(() => {
|
|
|
|
|
+ const start = (recordPage.pageNum - 1) * recordPage.pageSize
|
|
|
|
|
+ const end = start + recordPage.pageSize
|
|
|
|
|
+ return recordList.value.slice(start, end)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const detailOpen = ref(false)
|
|
|
|
|
+const detail = ref({})
|
|
|
|
|
+
|
|
|
|
|
+function getNowTime() {
|
|
|
|
|
+ const date = new Date()
|
|
|
|
|
+ const pad = value => String(value).padStart(2, '0')
|
|
|
|
|
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function refreshVersions() {
|
|
|
|
|
+ proxy.$modal.msgWarning('版本信息接口暂未接入,当前为前端模拟数据')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function refreshPackages() {
|
|
|
|
|
+ proxy.$modal.msgWarning('升级包列表接口暂未接入,当前为前端模拟数据')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function refreshRecords() {
|
|
|
|
|
+ proxy.$modal.msgWarning('升级记录接口暂未接入,当前为前端模拟数据')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function openUploadDialog() {
|
|
|
|
|
+ uploadForm.moduleCategory = ''
|
|
|
|
|
+ uploadForm.moduleCode = ''
|
|
|
|
|
+ uploadForm.moduleName = ''
|
|
|
|
|
+ uploadForm.targetVersion = ''
|
|
|
|
|
+ uploadForm.packageName = ''
|
|
|
|
|
+ uploadForm.packageType = ''
|
|
|
|
|
+ uploadForm.remark = ''
|
|
|
|
|
+ uploadOpen.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function submitUpload() {
|
|
|
|
|
+ uploadRef.value.validate(valid => {
|
|
|
|
|
+ if (!valid) return
|
|
|
|
|
+
|
|
|
|
|
+ packageList.value.unshift({
|
|
|
|
|
+ id: Date.now(),
|
|
|
|
|
+ packageName: uploadForm.packageName,
|
|
|
|
|
+ moduleCategory: uploadForm.moduleCategory,
|
|
|
|
|
+ moduleCode: uploadForm.moduleCode,
|
|
|
|
|
+ moduleName: uploadForm.moduleName,
|
|
|
|
|
+ targetVersion: uploadForm.targetVersion,
|
|
|
|
|
+ packageType: uploadForm.packageType,
|
|
|
|
|
+ fileSize: '模拟文件',
|
|
|
|
|
+ packageStatus: '1',
|
|
|
|
|
+ uploadBy: 'admin',
|
|
|
|
|
+ uploadTime: getNowTime(),
|
|
|
|
|
+ remark: uploadForm.remark || '前端模拟上传记录'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ packagePage.pageNum = 1
|
|
|
|
|
+ uploadOpen.value = false
|
|
|
|
|
+ proxy.$modal.msgSuccess('升级包上传接口暂未接入,当前仅临时加入前端列表演示')
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleExecuteUpgrade(row) {
|
|
|
|
|
+ if (row.packageStatus !== '1') {
|
|
|
|
|
+ proxy.$modal.msgError('当前升级包不可用')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isHighRiskModule(row.moduleCategory)) {
|
|
|
|
|
+ proxy.$modal.confirm(
|
|
|
|
|
+ '当前升级对象属于智驾导航、底盘控制或驱动适配相关模块,升级过程可能影响机器人移动、定位、避障或底盘控制能力。请确认机器人处于安全停靠状态后再执行升级。',
|
|
|
|
|
+ '高风险升级确认',
|
|
|
|
|
+ {
|
|
|
|
|
+ confirmButtonText: '确认升级',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning'
|
|
|
|
|
+ }
|
|
|
|
|
+ ).then(() => {
|
|
|
|
|
+ appendMockUpgradeRecord(row)
|
|
|
|
|
+ proxy.$modal.msgWarning('升级执行接口暂未接入,当前仅新增前端模拟升级记录')
|
|
|
|
|
+ }).catch(() => {})
|
|
|
|
|
+ } else {
|
|
|
|
|
+ proxy.$modal.confirm('确认使用该安装包执行升级吗?').then(() => {
|
|
|
|
|
+ appendMockUpgradeRecord(row)
|
|
|
|
|
+ proxy.$modal.msgWarning('升级执行接口暂未接入,当前仅新增前端模拟升级记录')
|
|
|
|
|
+ }).catch(() => {})
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function appendMockUpgradeRecord(row) {
|
|
|
|
|
+ recordList.value.unshift({
|
|
|
|
|
+ id: Date.now(),
|
|
|
|
|
+ moduleCategory: row.moduleCategory,
|
|
|
|
|
+ moduleCode: row.moduleCode,
|
|
|
|
|
+ moduleName: row.moduleName,
|
|
|
|
|
+ currentVersion: '-',
|
|
|
|
|
+ targetVersion: row.targetVersion,
|
|
|
|
|
+ startTime: getNowTime(),
|
|
|
|
|
+ endTime: '',
|
|
|
|
|
+ resultStatus: '1',
|
|
|
|
|
+ progressPercent: 10,
|
|
|
|
|
+ executeBy: 'admin',
|
|
|
|
|
+ resultMsg: '升级执行接口暂未接入,当前为前端模拟升级记录'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ recordPage.pageNum = 1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleDeletePackage(row) {
|
|
|
|
|
+ proxy.$modal.confirm('确认删除该升级包记录吗?').then(() => {
|
|
|
|
|
+ packageList.value = packageList.value.filter(item => item.id !== row.id)
|
|
|
|
|
+
|
|
|
|
|
+ if (pagedPackageList.value.length === 0 && packagePage.pageNum > 1) {
|
|
|
|
|
+ packagePage.pageNum -= 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ proxy.$modal.msgSuccess('升级包删除接口暂未接入,当前仅从前端列表临时移除')
|
|
|
|
|
+ }).catch(() => {})
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleRecordDetail(row) {
|
|
|
|
|
+ detail.value = { ...row }
|
|
|
|
|
+ detailOpen.value = true
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.ota-upgrade-page {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ padding-bottom: 24px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ota-hero {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: 16px 20px;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ota-hero-title {
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ota-hero-desc {
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.6;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ota-hero-right {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin-left: 16px;
|
|
|
|
|
+ padding-top: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.risk-tip {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
|
|
+ color: #e6a23c;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ background: #fdf6ec;
|
|
|
|
|
+ border: 1px solid #f5dab1;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.risk-tip .el-icon {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin-top: 1px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ota-section-card {
|
|
|
|
|
+ padding: 16px 20px;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-desc {
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-toolbar {
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.table-scroll {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ overflow-x: auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.version-table {
|
|
|
|
|
+ min-width: 1100px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.package-table,
|
|
|
|
|
+.record-table {
|
|
|
|
|
+ min-width: 1200px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.table-pagination {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.progress-cell {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-risk-tip {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ margin-top: 14px;
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
|
|
+ color: #e6a23c;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ background: #fdf6ec;
|
|
|
|
|
+ border: 1px solid #f5dab1;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-risk-tip .el-icon {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin-top: 1px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-demo {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|