2356 lines
92 KiB
HTML
2356 lines
92 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>风电叶片检查智能管理平台 - 施工操作台</title>
|
||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.3.2/dist/echarts.min.js"></script>
|
||
<style>
|
||
/* 基础样式保持不变,新增以下样式 */
|
||
.app-container {
|
||
display: flex;
|
||
min-height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
.sidebar {
|
||
width: 220px;
|
||
background-color: #304156;
|
||
color: #fff;
|
||
padding-top: 20px;
|
||
transition: width 0.3s;
|
||
flex-shrink: 0;
|
||
overflow-y: auto;
|
||
}
|
||
.sidebar.collapsed {
|
||
width: 64px;
|
||
}
|
||
.main-content {
|
||
flex: 1;
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
transition: margin-left 0.3s;
|
||
}
|
||
.sidebar.collapsed + .main-content {
|
||
margin-left: -156px;
|
||
}
|
||
.logo {
|
||
padding: 10px 0;
|
||
text-align: center;
|
||
position: relative;
|
||
}
|
||
.logo h2 {
|
||
font-size: 18px;
|
||
margin: 5px 0;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
padding: 0 10px;
|
||
}
|
||
.logo p {
|
||
font-size: 12px;
|
||
margin: 0;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
padding: 0 10px;
|
||
}
|
||
.toggle-sidebar {
|
||
position: absolute;
|
||
right: 10px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
cursor: pointer;
|
||
}
|
||
.menu-item {
|
||
position: relative;
|
||
padding: 12px 20px;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s;
|
||
}
|
||
.menu-item:hover {
|
||
background-color: rgba(0, 0, 0, 0.1);
|
||
}
|
||
.menu-item.active {
|
||
background-color: rgba(0, 0, 0, 0.2);
|
||
}
|
||
.menu-item i {
|
||
margin-right: 10px;
|
||
}
|
||
.menu-item .el-badge {
|
||
position: absolute;
|
||
right: 20px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
}
|
||
.sidebar.collapsed .menu-item .el-badge {
|
||
right: 5px;
|
||
}
|
||
.status-badge {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
margin-right: 5px;
|
||
}
|
||
.status-online {
|
||
background-color: #67C23A;
|
||
}
|
||
.status-offline {
|
||
background-color: #F56C6C;
|
||
}
|
||
.status-warning {
|
||
background-color: #E6A23C;
|
||
}
|
||
.real-time-data {
|
||
display: flex;
|
||
margin-bottom: 15px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.data-card {
|
||
flex: 1;
|
||
min-width: 200px;
|
||
margin-right: 15px;
|
||
margin-bottom: 15px;
|
||
padding: 15px;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
}
|
||
.data-card:last-child {
|
||
margin-right: 0;
|
||
}
|
||
.data-title {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
margin-bottom: 10px;
|
||
}
|
||
.data-value {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
}
|
||
.data-unit {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
margin-left: 5px;
|
||
}
|
||
.chart-container {
|
||
height: 300px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.equipment-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
.equipment-item {
|
||
width: calc(33.33% - 10px);
|
||
margin-right: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.equipment-item:nth-child(3n) {
|
||
margin-right: 0;
|
||
}
|
||
.equipment-card {
|
||
height: 100%;
|
||
}
|
||
.equipment-image {
|
||
height: 120px;
|
||
background-color: #f5f7fa;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.equipment-image img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
}
|
||
.equipment-info {
|
||
padding: 10px;
|
||
}
|
||
.equipment-name {
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
.equipment-status {
|
||
font-size: 12px;
|
||
}
|
||
.task-progress {
|
||
margin-top: 10px;
|
||
}
|
||
.defect-image-preview {
|
||
width: 100px;
|
||
height: 100px;
|
||
margin-right: 10px;
|
||
margin-bottom: 10px;
|
||
border: 1px solid #ebeef5;
|
||
cursor: pointer;
|
||
}
|
||
.defect-image-preview:hover {
|
||
border-color: #409EFF;
|
||
}
|
||
.image-preview-dialog .el-dialog__body {
|
||
padding: 0;
|
||
}
|
||
.full-image {
|
||
width: 100%;
|
||
display: block;
|
||
}
|
||
.user-info {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10px;
|
||
border-top: 1px solid rgba(255,255,255,0.1);
|
||
}
|
||
.user-avatar {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
margin-right: 10px;
|
||
background-color: #409EFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-weight: bold;
|
||
}
|
||
.user-name {
|
||
flex: 1;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.user-logout {
|
||
cursor: pointer;
|
||
padding: 5px;
|
||
}
|
||
.sidebar.collapsed .user-info {
|
||
justify-content: center;
|
||
padding: 10px 5px;
|
||
}
|
||
.sidebar.collapsed .user-name,
|
||
.sidebar.collapsed .user-logout {
|
||
display: none;
|
||
}
|
||
.sidebar.collapsed .user-avatar {
|
||
margin-right: 0;
|
||
}
|
||
.sidebar.collapsed .menu-item span {
|
||
display: none;
|
||
}
|
||
.sidebar.collapsed .menu-item i {
|
||
margin-right: 0;
|
||
font-size: 18px;
|
||
}
|
||
.sidebar.collapsed .menu-item {
|
||
text-align: center;
|
||
padding: 12px 0;
|
||
}
|
||
.context-menu {
|
||
position: fixed;
|
||
z-index: 9999;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
padding: 5px 0;
|
||
}
|
||
.context-menu-item {
|
||
padding: 8px 20px;
|
||
cursor: pointer;
|
||
}
|
||
.context-menu-item:hover {
|
||
background-color: #ecf5ff;
|
||
color: #409EFF;
|
||
}
|
||
/* 响应式调整 */
|
||
@media (max-width: 992px) {
|
||
.equipment-item {
|
||
width: calc(50% - 10px);
|
||
}
|
||
.equipment-item:nth-child(3n) {
|
||
margin-right: 15px;
|
||
}
|
||
.equipment-item:nth-child(2n) {
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
@media (max-width: 768px) {
|
||
.sidebar {
|
||
position: fixed;
|
||
z-index: 1000;
|
||
height: 100vh;
|
||
}
|
||
.sidebar.collapsed + .main-content {
|
||
margin-left: 0;
|
||
}
|
||
.main-content {
|
||
margin-left: 220px;
|
||
}
|
||
.sidebar.collapsed {
|
||
width: 0;
|
||
overflow: hidden;
|
||
}
|
||
.real-time-data {
|
||
flex-direction: column;
|
||
}
|
||
.data-card {
|
||
margin-right: 0;
|
||
margin-bottom: 15px;
|
||
}
|
||
.equipment-item {
|
||
width: 100%;
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
.module-title {
|
||
margin-bottom: 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
}
|
||
.card-container {
|
||
background: #fff;
|
||
padding: 20px;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
margin-bottom: 20px;
|
||
}
|
||
.process-steps {
|
||
margin-bottom: 20px;
|
||
}
|
||
.turbine-list {
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 4px;
|
||
}
|
||
.turbine-item {
|
||
padding: 15px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
}
|
||
.turbine-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.turbine-title {
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
}
|
||
.blade-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 8px 0;
|
||
border-bottom: 1px dashed #ebeef5;
|
||
}
|
||
.blade-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.blade-info {
|
||
flex: 1;
|
||
}
|
||
.blade-status {
|
||
width: 80px;
|
||
text-align: center;
|
||
margin: 0 10px;
|
||
padding: 2px 5px;
|
||
border-radius: 3px;
|
||
font-size: 12px;
|
||
}
|
||
.status-待开始 {
|
||
background-color: #f5f5f5;
|
||
color: #909399;
|
||
}
|
||
.status-进行中 {
|
||
background-color: #ecf5ff;
|
||
color: #409EFF;
|
||
}
|
||
.status-已完成 {
|
||
background-color: #f0f9eb;
|
||
color: #67C23A;
|
||
}
|
||
.upload-area {
|
||
margin-bottom: 20px;
|
||
}
|
||
.upload-icon {
|
||
font-size: 50px;
|
||
color: #409EFF;
|
||
margin: 20px 0;
|
||
}
|
||
.report-preview {
|
||
border: 1px solid #ebeef5;
|
||
padding: 20px;
|
||
margin-top: 20px;
|
||
}
|
||
.report-section {
|
||
margin-bottom: 20px;
|
||
}
|
||
.report-title {
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
padding-bottom: 5px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
}
|
||
.report-content {
|
||
padding: 0 10px;
|
||
}
|
||
.defect-item {
|
||
margin-bottom: 15px;
|
||
padding-bottom: 15px;
|
||
border-bottom: 1px dashed #ebeef5;
|
||
}
|
||
.defect-title {
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
.defect-desc {
|
||
margin-bottom: 10px;
|
||
color: #606266;
|
||
}
|
||
.defect-images {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="app" class="app-container">
|
||
<!-- 侧边栏导航 -->
|
||
<div class="sidebar" :class="{collapsed: isCollapse}">
|
||
<div class="logo">
|
||
<h2>风电叶片检查</h2>
|
||
<p>智能管理平台</p>
|
||
<div class="toggle-sidebar" @click="toggleSidebar">
|
||
<i :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'dashboard'}"
|
||
@click="switchModule('dashboard')"
|
||
>
|
||
<i class="el-icon-data-line"></i>
|
||
<span>工作台</span>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'project'}"
|
||
@click="switchModule('project')"
|
||
>
|
||
<i class="el-icon-s-order"></i>
|
||
<span>项目列表</span>
|
||
<el-badge :value="3" :max="99" class="item"></el-badge>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'fieldwork'}"
|
||
@click="switchModule('fieldwork')"
|
||
>
|
||
<i class="el-icon-map-location"></i>
|
||
<span>外业施工</span>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'data'}"
|
||
@click="switchModule('data')"
|
||
>
|
||
<i class="el-icon-upload"></i>
|
||
<span>数据入库</span>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'inspection'}"
|
||
@click="switchModule('inspection')"
|
||
>
|
||
<i class="el-icon-search"></i>
|
||
<span>智能巡检</span>
|
||
<el-badge :value="5" :max="99" class="item"></el-badge>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'report'}"
|
||
@click="switchModule('report')"
|
||
>
|
||
<i class="el-icon-document"></i>
|
||
<span>报告审核</span>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'delivery'}"
|
||
@click="switchModule('delivery')"
|
||
>
|
||
<i class="el-icon-finished"></i>
|
||
<span>项目交付</span>
|
||
</div>
|
||
<div
|
||
class="menu-item"
|
||
:class="{active: activeModule === 'tools'}"
|
||
@click="switchModule('tools')"
|
||
>
|
||
<i class="el-icon-set-up"></i>
|
||
<span>工具与资源</span>
|
||
</div>
|
||
|
||
<div class="user-info">
|
||
<div class="user-avatar">{{ userInfo.name.substring(0,1) }}</div>
|
||
<div class="user-name">{{ userInfo.name }} ({{ userInfo.role }})</div>
|
||
<div class="user-logout" @click="logout">
|
||
<i class="el-icon-switch-button"></i>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="main-content">
|
||
<!-- 工作台模块 -->
|
||
<div v-if="activeModule === 'dashboard'">
|
||
<h2 class="module-title">工作台</h2>
|
||
|
||
<div class="real-time-data">
|
||
<div class="data-card">
|
||
<div class="data-title">今日检查机组</div>
|
||
<div class="data-value">12<span class="data-unit">台</span></div>
|
||
</div>
|
||
<div class="data-card">
|
||
<div class="data-title">发现缺陷</div>
|
||
<div class="data-value">8<span class="data-unit">处</span></div>
|
||
</div>
|
||
<div class="data-card">
|
||
<div class="data-title">待审核报告</div>
|
||
<div class="data-value">3<span class="data-unit">份</span></div>
|
||
</div>
|
||
<div class="data-card">
|
||
<div class="data-title">设备在线</div>
|
||
<div class="data-value">5/6<span class="data-unit">台</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-container">
|
||
<el-tabs v-model="dashboardTab">
|
||
<el-tab-pane label="任务进度" name="tasks">
|
||
<div class="chart-container" ref="taskChart"></div>
|
||
|
||
<h3>我的任务</h3>
|
||
<el-table :data="myTasks" border style="width: 100%">
|
||
<el-table-column prop="name" label="任务名称" width="180"></el-table-column>
|
||
<el-table-column prop="project" label="所属项目"></el-table-column>
|
||
<el-table-column prop="progress" label="进度" width="120">
|
||
<template slot-scope="scope">
|
||
<el-progress :percentage="scope.row.progress" :status="scope.row.status"></el-progress>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="deadline" label="截止时间" width="180"></el-table-column>
|
||
<el-table-column label="操作" width="120">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewTaskDetail(scope.row)">查看</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="缺陷统计" name="defects">
|
||
<div class="chart-container" ref="defectChart"></div>
|
||
|
||
<h3>最新缺陷</h3>
|
||
<el-table :data="recentDefects" border style="width: 100%">
|
||
<el-table-column prop="type" label="缺陷类型" width="180"></el-table-column>
|
||
<el-table-column prop="position" label="位置"></el-table-column>
|
||
<el-table-column prop="severity" label="严重程度" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.severity === '高' ? 'danger' : scope.row.severity === '中' ? 'warning' : 'success'">
|
||
{{ scope.row.severity }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="time" label="发现时间" width="180"></el-table-column>
|
||
<el-table-column label="操作" width="120">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewDefectDetail(scope.row)">详情</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="消息通知" name="notifications">
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="(notification, index) in notifications"
|
||
:key="index"
|
||
:timestamp="notification.time"
|
||
placement="top"
|
||
>
|
||
<el-card>
|
||
<h4>{{ notification.title }}</h4>
|
||
<p>{{ notification.content }}</p>
|
||
<el-button size="mini" v-if="notification.action" @click="handleNotification(notification)">{{ notification.action }}</el-button>
|
||
</el-card>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 项目列表模块 -->
|
||
<div v-if="activeModule === 'project'">
|
||
<h2 class="module-title">项目列表</h2>
|
||
|
||
<div class="card-container">
|
||
<div style="display: flex; justify-content: space-between; margin-bottom: 20px;">
|
||
<el-input
|
||
placeholder="搜索项目名称"
|
||
v-model="projectSearch"
|
||
clearable
|
||
style="width: 300px;"
|
||
>
|
||
<el-button slot="append" icon="el-icon-search"></el-button>
|
||
</el-input>
|
||
|
||
<div>
|
||
<el-button type="primary" icon="el-icon-plus" @click="showProjectDialog">新建项目</el-button>
|
||
<el-button icon="el-icon-refresh" @click="refreshProjects">刷新</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<el-table :data="filteredProjects" border style="width: 100%">
|
||
<el-table-column prop="name" label="项目名称" width="200"></el-table-column>
|
||
<el-table-column prop="turbineCount" label="机组数量" width="100"></el-table-column>
|
||
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
|
||
<el-table-column prop="endDate" label="结束日期" width="150"></el-table-column>
|
||
<el-table-column prop="status" label="状态" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.status === '进行中' ? 'primary' : scope.row.status === '已完成' ? 'success' : 'info'">
|
||
{{ scope.row.status }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="progress" label="进度" width="180">
|
||
<template slot-scope="scope">
|
||
<el-progress :percentage="scope.row.progress" :status="scope.row.status === '已完成' ? 'success' : ''"></el-progress>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="180">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewProjectDetail(scope.row)">查看</el-button>
|
||
<el-button size="mini" type="primary" @click="editProject(scope.row)">编辑</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 外业施工模块 -->
|
||
<div v-if="activeModule === 'fieldwork'">
|
||
<h2 class="module-title">外业施工 - {{ currentProject.name }}</h2>
|
||
|
||
<div class="card-container">
|
||
<el-steps :active="fieldworkStep" finish-status="success" class="process-steps">
|
||
<el-step title="准备工作" description="确认检查计划和装备"></el-step>
|
||
<el-step title="现场检查" description="执行叶片检查工作"></el-step>
|
||
<el-step title="数据收集" description="采集检查数据"></el-step>
|
||
<el-step title="初步报告" description="提交初步检查结果"></el-step>
|
||
</el-steps>
|
||
|
||
<div style="margin: 20px 0;">
|
||
<el-button-group>
|
||
<el-button type="primary" icon="el-icon-tickets" @click="showWorkPlan">查看工作计划</el-button>
|
||
<el-button type="primary" icon="el-icon-edit" @click="showDailyReportDialog">填写日报</el-button>
|
||
<el-button type="primary" icon="el-icon-picture" @click="showPhotoUpload">上传现场照片</el-button>
|
||
<el-button type="primary" icon="el-icon-warning" @click="showIssueReport">报告问题</el-button>
|
||
</el-button-group>
|
||
</div>
|
||
|
||
<el-tabs v-model="fieldworkTab">
|
||
<el-tab-pane label="机组列表" name="turbines">
|
||
<div class="turbine-list">
|
||
<div v-for="turbine in currentProject.turbines" :key="turbine.id" class="turbine-item">
|
||
<div class="turbine-title">
|
||
<span class="status-badge" :class="'status-' + turbine.status"></span>
|
||
机组 {{ turbine.code }}
|
||
<span style="float: right; font-size: 12px; color: #909399;">最后检查: {{ turbine.lastInspection }}</span>
|
||
</div>
|
||
|
||
<div v-for="blade in turbine.blades" :key="blade.id" class="blade-item">
|
||
<div class="blade-info">
|
||
{{ blade.position }}叶片 - {{ blade.type }}检查
|
||
<span v-if="blade.inspector">(检查员: {{ blade.inspector }})</span>
|
||
</div>
|
||
<div class="blade-status" :class="'status-' + blade.status.replace(' ', '-')">
|
||
{{ blade.status }}
|
||
</div>
|
||
<el-button size="mini" @click="startInspection(blade)">开始检查</el-button>
|
||
<el-button size="mini" type="text" @click="showBladeContextMenu($event, blade)">
|
||
<i class="el-icon-more"></i>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="地图视图" name="map">
|
||
<div style="height: 500px; background: #f5f7fa; display: flex; align-items: center; justify-content: center;">
|
||
<span style="color: #909399;">地图展示区域 - 显示机组位置和检查状态</span>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="检查记录" name="records">
|
||
<el-table :data="inspectionRecords" border style="width: 100%">
|
||
<el-table-column prop="turbine" label="机组" width="120"></el-table-column>
|
||
<el-table-column prop="blade" label="叶片" width="120"></el-table-column>
|
||
<el-table-column prop="type" label="检查类型" width="150"></el-table-column>
|
||
<el-table-column prop="inspector" label="检查员" width="120"></el-table-column>
|
||
<el-table-column prop="time" label="检查时间" width="180"></el-table-column>
|
||
<el-table-column prop="result" label="检查结果">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" :type="scope.row.result === '正常' ? 'success' : 'danger'">
|
||
{{ scope.row.result }}
|
||
</el-tag>
|
||
<span v-if="scope.row.result !== '正常'" style="margin-left: 5px;">{{ scope.row.defects }}处缺陷</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="120">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewInspectionRecord(scope.row)">详情</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 数据处理模块 -->
|
||
<div v-if="activeModule === 'data'">
|
||
<h2 class="module-title">数据入库 - {{ currentBlade ? currentBlade.position + '叶片' + currentBlade.type + '检查' : '请选择检查项' }}</h2>
|
||
|
||
<div class="card-container" v-if="currentBlade">
|
||
<el-tabs v-model="activeDataType" class="data-type-tabs">
|
||
<el-tab-pane label="图片" name="image"></el-tab-pane>
|
||
<el-tab-pane label="视频" name="video"></el-tab-pane>
|
||
<el-tab-pane label="语音" name="audio"></el-tab-pane>
|
||
<el-tab-pane label="文档" name="document"></el-tab-pane>
|
||
<el-tab-pane label="其他" name="other"></el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<el-upload
|
||
class="upload-area"
|
||
drag
|
||
action=""
|
||
multiple
|
||
:auto-upload="false"
|
||
:on-change="handleFileChange"
|
||
:file-list="fileList"
|
||
:before-upload="beforeUpload"
|
||
:on-remove="handleRemove"
|
||
>
|
||
<i class="el-icon-upload upload-icon"></i>
|
||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||
<div class="el-upload__tip" slot="tip">支持上传{{ activeDataType === 'image' ? 'JPG/PNG/BMP (不超过10MB)' : activeDataType === 'video' ? 'MP4/AVI/MOV (不超过100MB)' : activeDataType === 'audio' ? 'MP3/WAV (不超过20MB)' : activeDataType === 'document' ? 'PDF/DOC/XLS (不超过50MB)' : '任意 (不超过100MB)' }}格式文件</div>
|
||
</el-upload>
|
||
|
||
<div style="margin: 20px 0;">
|
||
<el-button type="primary" @click="submitUpload">开始上传</el-button>
|
||
<el-button @click="clearFiles">清空文件</el-button>
|
||
<el-button type="text" @click="showBatchImportDialog">批量导入...</el-button>
|
||
</div>
|
||
|
||
<h3>已上传数据</h3>
|
||
<el-table :data="uploadedData" border style="width: 100%; margin-bottom: 20px;">
|
||
<el-table-column prop="name" label="文件名" width="200"></el-table-column>
|
||
<el-table-column prop="type" label="类型" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.type === 'image' ? 'primary' : scope.row.type === 'video' ? 'success' : scope.row.type === 'audio' ? 'warning' : 'info'">
|
||
{{ scope.row.type }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="size" label="大小" width="120"></el-table-column>
|
||
<el-table-column prop="time" label="上传时间" width="180"></el-table-column>
|
||
<el-table-column prop="status" label="状态" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.status === '成功' ? 'success' : scope.row.status === '失败' ? 'danger' : 'warning'">
|
||
{{ scope.row.status }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="180">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="previewFile(scope.row)">预览</el-button>
|
||
<el-button size="mini" type="danger" @click="deleteFile(scope.row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-divider></el-divider>
|
||
|
||
<h3>数据预处理</h3>
|
||
<el-button type="primary" @click="preprocessData" :loading="preprocessing">一键预处理</el-button>
|
||
<el-button @click="showPreprocessSettings">预处理设置</el-button>
|
||
|
||
<div v-if="preprocessing" style="margin-top: 20px;">
|
||
<el-progress :percentage="preprocessProgress" :status="preprocessStatus === '完成' ? 'success' : ''"></el-progress>
|
||
<div style="margin-top: 10px; color: #909399;">
|
||
正在处理: {{ preprocessStatus }}
|
||
<span v-if="preprocessCurrentFile">(当前文件: {{ preprocessCurrentFile }})</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="preprocessResults.length > 0" style="margin-top: 20px;">
|
||
<h4>预处理结果</h4>
|
||
<el-collapse v-model="activePreprocessResult">
|
||
<el-collapse-item v-for="(result, index) in preprocessResults" :key="index" :title="result.type" :name="index">
|
||
<div>{{ result.content }}</div>
|
||
<el-button v-if="result.details" size="mini" @click="showPreprocessDetails(result)">查看详情</el-button>
|
||
</el-collapse-item>
|
||
</el-collapse>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-container" v-else>
|
||
<el-alert
|
||
title="请先选择要上传数据的检查项"
|
||
type="info"
|
||
show-icon
|
||
:closable="false"
|
||
>
|
||
</el-alert>
|
||
<el-button type="primary" @click="activeModule = 'fieldwork'">返回外业施工</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 智能巡检模块 -->
|
||
<div v-if="activeModule === 'inspection'">
|
||
<h2 class="module-title">智能巡检平台</h2>
|
||
|
||
<div class="card-container">
|
||
<el-tabs v-model="inspectionTab">
|
||
<el-tab-pane label="缺陷检测" name="defect">
|
||
<div style="margin-bottom: 20px;">
|
||
<el-button type="primary" @click="runDefectDetection" icon="el-icon-cpu">运行缺陷检测算法</el-button>
|
||
<el-button @click="showDetectionSettings" icon="el-icon-setting">检测设置</el-button>
|
||
<el-button @click="exportDetectionResults" icon="el-icon-download" :disabled="defectDetectionResults.length === 0">导出结果</el-button>
|
||
</div>
|
||
|
||
<div v-if="detectionRunning" style="margin-bottom: 20px;">
|
||
<el-progress :percentage="detectionProgress"></elgress>
|
||
<div style="margin-top: 10px; color: #909399;">
|
||
正在检测: {{ detectionStatus }}
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="defectDetectionResults.length > 0">
|
||
<h3>检测结果</h3>
|
||
<el-table :data="defectDetectionResults" border style="width: 100%">
|
||
<el-table-column prop="type" label="缺陷类型" width="180"></el-table-column>
|
||
<el-table-column prop="position" label="位置"></el-table-column>
|
||
<el-table-column prop="size" label="尺寸" width="120"></el-table-column>
|
||
<el-table-column prop="severity" label="严重程度" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.severity === '高' ? 'danger' : scope.row.severity === '中' ? 'warning' : 'success'">
|
||
{{ scope.row.severity }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="图片" width="120">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="previewDefectImages(scope.row)">查看({{ scope.row.images.length }})</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="180">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewDefectDetail(scope.row)">详情</el-button>
|
||
<el-button size="mini" type="danger" @click="markAsFalseAlarm(scope.row)">误报</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="树状可视化管理" name="tree">
|
||
<div style="height: 600px; display: flex;">
|
||
<div style="width: 300px; border-right: 1px solid #ebeef5; padding-right: 20px; overflow-y: auto;">
|
||
<el-input
|
||
placeholder="搜索机组或叶片"
|
||
v-model="treeSearch"
|
||
clearable
|
||
style="margin-bottom: 15px;"
|
||
>
|
||
<el-button slot="append" icon="el-icon-search"></el-button>
|
||
</el-input>
|
||
|
||
<el-tree
|
||
:data="treeData"
|
||
:props="treeProps"
|
||
node-key="id"
|
||
default-expand-all
|
||
:filter-node-method="filterTreeNode"
|
||
@node-click="handleTreeNodeClick"
|
||
:expand-on-click-node="false"
|
||
>
|
||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||
<span>
|
||
<i :class="data.type === 'turbine' ? 'el-icon-office-building' : 'el-icon-wind-power'" style="margin-right: 5px;"></i>
|
||
{{ node.label }}
|
||
</span>
|
||
<span v-if="data.type === 'blade' && data.status" style="margin-left: 10px;">
|
||
<el-tag size="mini" :type="data.status === '正常' ? 'success' : 'danger'">
|
||
{{ data.status }}
|
||
</el-tag>
|
||
</span>
|
||
</span>
|
||
</el-tree>
|
||
</div>
|
||
|
||
<div style="flex: 1; padding-left: 20px;">
|
||
<div v-if="selectedTreeNode" style="margin-bottom: 20px;">
|
||
<h3>{{ selectedTreeNode.type === 'turbine' ? '机组' : '叶片' }}详情</h3>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="名称">{{ selectedTreeNode.label }}</el-descriptions-item>
|
||
<el-descriptions-item label="类型">{{ selectedTreeNode.type === 'turbine' ? '机组' : '叶片' }}</el-descriptions-item>
|
||
<el-descriptions-item label="状态" v-if="selectedTreeNode.status">
|
||
<el-tag :type="selectedTreeNode.status === '正常' ? 'success' : 'danger'">
|
||
{{ selectedTreeNode.status }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="最后检查时间" v-if="selectedTreeNode.lastInspection">
|
||
{{ selectedTreeNode.lastInspection }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="缺陷数量" v-if="selectedTreeNode.defects">
|
||
{{ selectedTreeNode.defects }}处
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<div v-if="selectedTreeNode.type === 'blade' && selectedTreeNode.images" style="margin-top: 20px;">
|
||
<h4>检查图片</h4>
|
||
<div style="display: flex; flex-wrap: wrap;">
|
||
<div
|
||
v-for="(img, index) in selectedTreeNode.images"
|
||
:key="index"
|
||
class="defect-image-preview"
|
||
@click="previewImage(img)"
|
||
>
|
||
<img :src="img.thumbnail" style="width: 100%; height: 100%; object-fit: cover;">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else style="height: 500px; display: flex; align-items: center; justify-content: center; color: #909399;">
|
||
请从左侧选择机组或叶片查看详情
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="标准信息库" name="standard">
|
||
<el-input
|
||
placeholder="搜索标准信息"
|
||
v-model="standardSearch"
|
||
clearable
|
||
style="width: 300px; margin-bottom: 20px;"
|
||
>
|
||
<el-button slot="append" icon="el-icon-search"></el-button>
|
||
</el-input>
|
||
|
||
<el-collapse accordion>
|
||
<el-collapse-item v-for="(item, index) in filteredStandards" :key="index" :title="item.title">
|
||
<div style="padding: 10px;">
|
||
<div v-html="item.content"></div>
|
||
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
|
||
最后更新: {{ item.updateTime }} | 版本: {{ item.version }}
|
||
</div>
|
||
<div style="margin-top: 10px;">
|
||
<el-button size="mini" @click="downloadStandard(item)">下载PDF</el-button>
|
||
<el-button size="mini" type="primary" @click="viewStandardDetail(item)">查看详情</el-button>
|
||
</div>
|
||
</div>
|
||
</el-collapse-item>
|
||
</el-collapse>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="全生命周期管理" name="lifecycle">
|
||
<el-steps direction="vertical" :active="4" style="margin-top: 20px;">
|
||
<el-step title="设计制造" description="叶片设计和制造阶段"></el-step>
|
||
<el-step title="安装调试" description="现场安装和调试阶段"></el-step>
|
||
<el-step title="运行维护" description="正常运行和维护阶段"></el-step>
|
||
<el-step title="检修记录" description="历史检修记录"></el-step>
|
||
<el-step title="退役评估" description="退役评估和处置"></el-step>
|
||
</el-steps>
|
||
|
||
<div style="margin-top: 30px;">
|
||
<h3>缺陷跟踪</h3>
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="(defect, index) in lifecycleDefects"
|
||
:key="index"
|
||
:timestamp="defect.time"
|
||
placement="top"
|
||
>
|
||
<el-card>
|
||
<h4>{{ defect.type }} - {{ defect.position }}</h4>
|
||
<p>{{ defect.description }}</p>
|
||
<p>处理状态: <el-tag :type="defect.status === '已修复' ? 'success' : defect.status === '处理中' ? 'warning' : 'danger'">{{ defect.status }}</el-tag></p>
|
||
<div v-if="defect.images" style="margin-top: 10px;">
|
||
<el-button size="mini" @click="previewDefectImages(defect)">查看图片({{ defect.images.length }})</el-button>
|
||
</div>
|
||
</el-card>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 报告审核模块 -->
|
||
<div v-if="activeModule === 'report'">
|
||
<h2 class="module-title">报告修改审核</h2>
|
||
|
||
<div class="card-container">
|
||
<el-steps :active="reportStep" finish-status="success" class="process-steps">
|
||
<el-step title="报告生成" description="自动生成初步报告"></el-step>
|
||
<el-step title="报告修改" description="检查人员修改报告"></el-step>
|
||
<el-step title="报告审核" description="质量审核人员审核"></el-step>
|
||
<el-step title="报告确认" description="最终确认报告"></el-step>
|
||
</el-steps>
|
||
|
||
<div style="margin: 20px 0;">
|
||
<el-button-group>
|
||
<el-button type="primary" icon="el-icon-document" @click="generateReport">生成报告</el-button>
|
||
<el-button type="primary" icon="el-icon-edit" @click="editReport">编辑报告</el-button>
|
||
<el-button type="primary" icon="el-icon-view" @click="previewReport">预览报告</el-button>
|
||
<el-button type="primary" icon="el-icon-check" @click="submitReport" :disabled="!reportGenerated">提交审核</el-button>
|
||
</el-button-group>
|
||
|
||
<el-button-group style="margin-left: 15px;">
|
||
<el-button icon="el-icon-download" @click="exportReport" :disabled="!reportGenerated">导出Word</el-button>
|
||
<el-button icon="el-icon-picture" @click="exportReportImages" :disabled="!reportGenerated">导出图片</el-button>
|
||
<el-button icon="el-icon-printer" @click="printReport" :disabled="!reportGenerated">打印</el-button>
|
||
</el-button-group>
|
||
</div>
|
||
|
||
<div class="report-preview">
|
||
<div class="report-section">
|
||
<div class="report-title">检查概况</div>
|
||
<div class="report-content">
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="项目名称">{{ currentProject.name }}</el-descriptions-item>
|
||
<el-descriptions-item label="检查时间">{{ new Date().toLocaleDateString() }}</el-descriptions-item>
|
||
<el-descriptions-item label="检查人员">{{ userInfo.name }}</el-descriptions-item>
|
||
<el-descriptions-item label="检查类型">{{ currentBlade.type }}检查</el-descriptions-item>
|
||
<el-descriptions-item label="检查机组" :span="2">{{ currentBlade.turbineCode }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="report-section">
|
||
<div class="report-title">检查结果</div>
|
||
<div class="report-content">
|
||
<p v-if="defectDetectionResults.length === 0">未发现明显缺陷</p>
|
||
<div v-else>
|
||
<p>共发现 {{ defectDetectionResults.length }} 处缺陷:</p>
|
||
<div v-for="(defect, index) in defectDetectionResults" :key="index" class="defect-item">
|
||
<div class="defect-title">{{ index + 1 }}. {{ defect.type }} (严重程度: {{ defect.severity }})</div>
|
||
<div class="defect-desc">位置: {{ defect.position }}; 尺寸: {{ defect.size }}; 描述: {{ defect.description }}</div>
|
||
<div class="defect-images">
|
||
<div
|
||
v-for="(img, imgIndex) in defect.images"
|
||
:key="imgIndex"
|
||
class="defect-image-preview"
|
||
@click="previewImage(img)"
|
||
>
|
||
<img :src="img.thumbnail" style="width: 100%; height: 100%; object-fit: cover;">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="report-section">
|
||
<div class="report-title">处理建议</div>
|
||
<div class="report-content">
|
||
<el-input
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入处理建议"
|
||
v-model="reportSuggestions"
|
||
></el-input>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="report-section">
|
||
<div class="report-title">审核意见</div>
|
||
<div class="report-content">
|
||
<el-input
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入审核意见"
|
||
v-model="reportReviewComments"
|
||
:disabled="reportStep < 3"
|
||
></el-input>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top: 20px; text-align: center;">
|
||
<el-button type="primary" @click="saveReport" :loading="reportSaving">保存报告</el-button>
|
||
<el-button type="success" @click="submitReport" :disabled="!reportGenerated || reportSubmitting" :loading="reportSubmitting">提交审核</el-button>
|
||
<el-button @click="exportReport" :disabled="!reportGenerated">导出报告</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 项目交付模块 -->
|
||
<div v-if="activeModule === 'delivery'">
|
||
<h2 class="module-title">项目交付验收</h2>
|
||
|
||
<div class="card-container">
|
||
<el-steps :active="deliveryStep" finish-status="success" class="process-steps">
|
||
<el-step title="可靠性评估" description="检查过程有无遗漏"></el-step>
|
||
<el-step title="数据质量评估" description="确认原数据是否入库"></el-step>
|
||
<el-step title="缺陷入库" description="审核人员确认缺陷"></el-step>
|
||
<el-step title="项目验收" description="完成项目交付"></el-step>
|
||
</el-steps>
|
||
|
||
<div v-if="deliveryStep === 0">
|
||
<h3>可靠性评估</h3>
|
||
<el-form label-width="120px">
|
||
<el-form-item label="数据生产负责人">
|
||
<el-input v-model="deliveryInfo.producer" style="width: 300px;"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="经手人">
|
||
<el-input v-model="deliveryInfo.handler" style="width: 300px;"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="生产时间">
|
||
<el-date-picker v-model="deliveryInfo.productionTime" type="datetime" style="width: 300px;"></el-date-picker>
|
||
</el-form-item>
|
||
<el-form-item label="生产地点">
|
||
<el-input v-model="deliveryInfo.location" style="width: 300px;"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="检查过程完整性">
|
||
<el-checkbox-group v-model="deliveryInfo.checkItems">
|
||
<el-checkbox label="检查计划完整"></el-checkbox>
|
||
<el-checkbox label="检查记录完整"></el-checkbox>
|
||
<el-checkbox label="数据采集完整"></el-checkbox>
|
||
<el-checkbox label="无遗漏项目"></el-checkbox>
|
||
</el-checkbox-group>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="completeReliabilityCheck">完成评估</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
|
||
<div v-else-if="deliveryStep === 1">
|
||
<h3>数据质量评估</h3>
|
||
<el-table :data="dataQualityItems" border style="width: 100%; margin-bottom: 20px;">
|
||
<el-table-column prop="item" label="评估项目" width="180"></el-table-column>
|
||
<el-table-column prop="status" label="状态" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.status === '通过' ? 'success' : 'danger'">{{ scope.row.status }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="comment" label="说明"></el-table-column>
|
||
<el-table-column label="操作" width="120">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="editDataQualityItem(scope.row)">编辑</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-button type="primary" @click="completeDataQualityCheck">完成评估</el-button>
|
||
</div>
|
||
|
||
<div v-else-if="deliveryStep === 2">
|
||
<h3>缺陷入库确认</h3>
|
||
<el-table :data="defectDetectionResults" border style="width: 100%; margin-bottom: 20px;">
|
||
<el-table-column prop="type" label="缺陷类型" width="180"></el-table-column>
|
||
<el-table-column prop="position" label="位置"></el-table-column>
|
||
<el-table-column prop="severity" label="严重程度" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.severity === '高' ? 'danger' : scope.row.severity === '中' ? 'warning' : 'success'">
|
||
{{ scope.row.severity }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="入库确认" width="120">
|
||
<template slot-scope="scope">
|
||
<el-checkbox v-model="scope.row.confirmed"></el-checkbox>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-button type="primary" @click="completeDefectConfirmation">确认入库</el-button>
|
||
</div>
|
||
|
||
<div v-else-if="deliveryStep === 3">
|
||
<h3>项目验收</h3>
|
||
<el-result icon="success" title="所有验收步骤已完成" subTitle="可以提交项目交付">
|
||
<template slot="extra">
|
||
<el-button type="primary" size="medium" @click="submitProjectDelivery">提交项目交付</el-button>
|
||
<el-button size="medium" @click="downloadDeliveryPackage">下载交付包</el-button>
|
||
</template>
|
||
</el-result>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 工具与资源模块 -->
|
||
<div v-if="activeModule === 'tools'">
|
||
<h2 class="module-title">工具与资源</h2>
|
||
|
||
<div class="card-container">
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>航线规划</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="openRoutePlanning">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=航线规划" alt="航线规划">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">无人机检查航线规划工具</div>
|
||
<div class="equipment-status">版本: 2.3.1</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>三维模型</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="open3DViewer">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=三维模型" alt="三维模型">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">叶片三维模型查看与分析</div>
|
||
<div class="equipment-status">版本: 1.5.0</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>报告模板库</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="openReportTemplates">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=报告模板" alt="报告模板">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">各类检查报告模板下载</div>
|
||
<div class="equipment-status">最近更新: 2023-05-15</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8" style="margin-top: 20px;">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>缺陷数据库</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="openDefectDatabase">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=缺陷数据库" alt="缺陷数据库">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">历史缺陷案例查询</div>
|
||
<div class="equipment-status">记录: 1,258条</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8" style="margin-top: 20px;">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>风速预测</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="openWindForecast">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=风速预测" alt="风速预测">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">未来72小时风速预测</div>
|
||
<div class="equipment-status">更新于: 今天 08:00</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8" style="margin-top: 20px;">
|
||
<el-card shadow="hover" class="equipment-item">
|
||
<div slot="header" class="clearfix">
|
||
<span>培训资料</span>
|
||
<el-button style="float: right; padding: 3px 0" type="text" @click="openTrainingMaterials">进入</el-button>
|
||
</div>
|
||
<div class="equipment-image">
|
||
<img src="https://via.placeholder.com/200x120?text=培训资料" alt="培训资料">
|
||
</div>
|
||
<div class="equipment-info">
|
||
<div class="equipment-name">检查技术培训资料</div>
|
||
<div class="equipment-status">资料: 32份</div>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-divider></el-divider>
|
||
|
||
<h3>塔下监测视频</h3>
|
||
<el-table :data="monitoringVideos" border style="width: 100%">
|
||
<el-table-column prop="batch" label="监测批次" width="120"></el-table-column>
|
||
<el-table-column prop="time" label="监测时间" width="180"></el-table-column>
|
||
<el-table-column prop="duration" label="时长" width="100"></el-table-column>
|
||
<el-table-column prop="location" label="位置"></el-table-column>
|
||
<el-table-column label="状态" width="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.status === '已分析' ? 'success' : 'warning'">
|
||
{{ scope.row.status }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="180">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" @click="viewMonitoringVideo(scope.row)">查看</el-button>
|
||
<el-button size="mini" type="primary" @click="analyzeVideo(scope.row)">分析</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 各种对话框 -->
|
||
<el-dialog title="项目详情" :visible.sync="projectDialogVisible" width="70%">
|
||
<el-form :model="currentProject" label-width="120px">
|
||
<el-form-item label="项目名称">
|
||
<el-input v-model="currentProject.name"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="项目描述">
|
||
<el-input type="textarea" v-model="currentProject.description"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="开始日期">
|
||
<el-date-picker v-model="currentProject.startDate" type="date"></el-date-picker>
|
||
</el-form-item>
|
||
<el-form-item label="结束日期">
|
||
<el-date-picker v-model="currentProject.endDate" type="date"></el-date-picker>
|
||
</el-form-item>
|
||
<el-form-item label="项目状态">
|
||
<el-select v-model="currentProject.status">
|
||
<el-option label="准备中" value="准备中"></el-option>
|
||
<el-option label="进行中" value="进行中"></el-option>
|
||
<el-option label="已完成" value="已完成"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="项目进度">
|
||
<el-slider v-model="currentProject.progress" :step="10" show-stops></el-slider>
|
||
</el-form-item>
|
||
</el-form>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="projectDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="saveProject">保存</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
|
||
<el-dialog title="图片预览" :visible.sync="imagePreviewVisible" class="image-preview-dialog" width="80%">
|
||
<img :src="currentPreviewImage" class="full-image" alt="预览图片">
|
||
</el-dialog>
|
||
|
||
<el-dialog title="缺陷图片" :visible.sync="defectImagesVisible" width="80%">
|
||
<div style="display: flex; flex-wrap: wrap;">
|
||
<div
|
||
v-for="(img, index) in currentDefectImages"
|
||
:key="index"
|
||
class="defect-image-preview"
|
||
@click="previewImage(img)"
|
||
style="width: 150px; height: 150px;"
|
||
>
|
||
<img :src="img.thumbnail" style="width: 100%; height: 100%; object-fit: cover;">
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<script>
|
||
new Vue({
|
||
el: '#app',
|
||
data() {
|
||
return {
|
||
isCollapse: false,
|
||
activeModule: 'dashboard',
|
||
dashboardTab: 'tasks',
|
||
projectSearch: '',
|
||
projects: [
|
||
{
|
||
id: 1,
|
||
name: '风电场A区2023年度检查',
|
||
description: '对A区12台风机进行全面检查',
|
||
turbineCount: 12,
|
||
startDate: '2023-03-15',
|
||
endDate: '2023-06-30',
|
||
status: '进行中',
|
||
progress: 65,
|
||
turbines: [
|
||
{
|
||
id: 1,
|
||
code: 'F01',
|
||
status: '在线',
|
||
lastInspection: '2023-06-15 10:23',
|
||
blades: [
|
||
{ id: 1, position: '1号', type: '内部', inspector: '张三', status: '待开始' },
|
||
{ id: 2, position: '2号', type: '内部', inspector: '李四', status: '进行中' },
|
||
{ id: 3, position: '3号', type: '内部', inspector: '王五', status: '已完成' }
|
||
]
|
||
},
|
||
{
|
||
id: 2,
|
||
code: 'F02',
|
||
status: '在线',
|
||
lastInspection: '2023-06-14 14:45',
|
||
blades: [
|
||
{ id: 4, position: '外部检查', type: '外部', inspector: '赵六', status: '进行中' }
|
||
]
|
||
},
|
||
{
|
||
id: 3,
|
||
code: 'F03',
|
||
status: '离线',
|
||
lastInspection: '2023-06-10 09:12',
|
||
blades: [
|
||
{ id: 5, position: '防雷检测', type: '防雷', inspector: '钱七', status: '待开始' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '风电场B区专项检查',
|
||
description: '针对B区8台风机特定问题的专项检查',
|
||
turbineCount: 8,
|
||
startDate: '2023-04-01',
|
||
endDate: '2023-07-15',
|
||
status: '准备中',
|
||
progress: 20,
|
||
turbines: []
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '风电场C区防雷检测',
|
||
description: 'C区15台风机的防雷系统专项检测',
|
||
turbineCount: 15,
|
||
startDate: '2023-05-10',
|
||
endDate: '2023-05-30',
|
||
status: '已完成',
|
||
progress: 100,
|
||
turbines: []
|
||
}
|
||
],
|
||
currentProject: {},
|
||
currentBlade: null,
|
||
fieldworkStep: 1,
|
||
fieldworkTab: 'turbines',
|
||
inspectionRecords: [
|
||
{ turbine: 'F01', blade: '1号', type: '内部检查', inspector: '张三', time: '2023-06-15 10:23', result: '正常', defects: 0 },
|
||
{ turbine: 'F01', blade: '2号', type: '内部检查', inspector: '李四', time: '2023-06-14 14:45', result: '异常', defects: 2 },
|
||
{ turbine: 'F02', blade: '外部检查', type: '外部检查', inspector: '赵六', time: '2023-06-13 09:12', result: '异常', defects: 1 }
|
||
],
|
||
activeDataType: 'image',
|
||
fileList: [],
|
||
uploadedData: [
|
||
{ name: '检查照片1.jpg', type: 'image', size: '2.5MB', time: '2023-06-01 10:23', status: '成功' },
|
||
{ name: '检查视频1.mp4', type: 'video', size: '45.6MB', time: '2023-06-01 10:45', status: '成功' },
|
||
{ name: '检查录音1.mp3', type: 'audio', size: '8.2MB', time: '2023-06-01 11:12', status: '成功' },
|
||
{ name: '检查报告.docx', type: 'document', size: '1.8MB', time: '2023-06-01 13:45', status: '失败' }
|
||
],
|
||
preprocessing: false,
|
||
preprocessProgress: 0,
|
||
preprocessStatus: '',
|
||
preprocessCurrentFile: '',
|
||
preprocessResults: [],
|
||
activePreprocessResult: '',
|
||
inspectionTab: 'defect',
|
||
treeSearch: '',
|
||
treeData: [
|
||
{
|
||
id: 1,
|
||
label: '风电场A区',
|
||
type: 'farm',
|
||
children: [
|
||
{
|
||
id: 2,
|
||
label: 'F01机组',
|
||
type: 'turbine',
|
||
status: '正常',
|
||
lastInspection: '2023-06-15',
|
||
children: [
|
||
{ id: 3, label: '1号叶片', type: 'blade', status: '正常', lastInspection: '2023-06-15', images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=叶片1-1', full: 'https://via.placeholder.com/800x600?text=叶片1-1' },
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=叶片1-2', full: 'https://via.placeholder.com/800x600?text=叶片1-2' }
|
||
] },
|
||
{ id: 4, label: '2号叶片', type: 'blade', status: '异常', defects: 2, lastInspection: '2023-06-14', images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=叶片2-1', full: 'https://via.placeholder.com/800x600?text=叶片2-1' }
|
||
] },
|
||
{ id: 5, label: '3号叶片', type: 'blade', status: '正常', lastInspection: '2023-06-15', images: [] }
|
||
]
|
||
},
|
||
{
|
||
id: 6,
|
||
label: 'F02机组',
|
||
type: 'turbine',
|
||
status: '异常',
|
||
defects: 1,
|
||
lastInspection: '2023-06-14',
|
||
children: [
|
||
{ id: 7, label: '外部检查', type: 'blade', status: '异常', defects: 1, lastInspection: '2023-06-13', images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=外部1-1', full: 'https://via.placeholder.com/800x600?text=外部1-1' },
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=外部1-2', full: 'https://via.placeholder.com/800x600?text=外部1-2' }
|
||
] }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
],
|
||
treeProps: {
|
||
children: 'children',
|
||
label: 'label'
|
||
},
|
||
selectedTreeNode: null,
|
||
standardSearch: '',
|
||
standards: [
|
||
{
|
||
title: '叶片内部检查标准',
|
||
content: '<p>1. 检查叶片内部结构是否完整</p><p>2. 检查是否有裂纹、变形等缺陷</p><p>3. 检查连接部位是否牢固</p>',
|
||
updateTime: '2023-05-20',
|
||
version: '2.1'
|
||
},
|
||
{
|
||
title: '外部检查无人机操作规范',
|
||
content: '<p>1. 飞行前检查无人机状态</p><p>2. 按照预定航线飞行</p><p>3. 保持安全距离</p>',
|
||
updateTime: '2023-04-15',
|
||
version: '1.5'
|
||
},
|
||
{
|
||
title: '防雷检测技术规范',
|
||
content: '<p>1. 检查防雷系统完整性</p><p>2. 测量接地电阻</p><p>3. 检查连接部位</p>',
|
||
updateTime: '2023-03-10',
|
||
version: '3.0'
|
||
}
|
||
],
|
||
defectDetectionResults: [
|
||
{
|
||
type: '表面裂纹',
|
||
position: '叶片根部',
|
||
size: '5cm×0.2cm',
|
||
severity: '高',
|
||
description: '长约5cm的表面裂纹,深度约2mm',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=裂纹1', full: 'https://via.placeholder.com/800x600?text=裂纹1' },
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=裂纹2', full: 'https://via.placeholder.com/800x600?text=裂纹2' }
|
||
],
|
||
confirmed: true
|
||
},
|
||
{
|
||
type: '涂层脱落',
|
||
position: '叶片中部',
|
||
size: '10cm×10cm',
|
||
severity: '中',
|
||
description: '约10cm×10cm的涂层脱落区域',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=涂层脱落', full: 'https://via.placeholder.com/800x600?text=涂层脱落' }
|
||
],
|
||
confirmed: true
|
||
},
|
||
{
|
||
type: '雷击损伤',
|
||
position: '叶片尖部',
|
||
size: '3cm×3cm',
|
||
severity: '低',
|
||
description: '轻微雷击痕迹',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=雷击1', full: 'https://via.placeholder.com/800x600?text=雷击1' },
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=雷击2', full: 'https://via.placeholder.com/800x600?text=雷击2' }
|
||
],
|
||
confirmed: false
|
||
}
|
||
],
|
||
lifecycleDefects: [
|
||
{
|
||
type: '表面裂纹',
|
||
position: '叶片根部',
|
||
description: '长约5cm的表面裂纹',
|
||
time: '2023-06-01',
|
||
status: '待处理',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=裂纹1', full: 'https://via.placeholder.com/800x600?text=裂纹1' }
|
||
]
|
||
},
|
||
{
|
||
type: '涂层脱落',
|
||
position: '叶片中部',
|
||
description: '约10cm×10cm的涂层脱落区域',
|
||
time: '2023-05-15',
|
||
status: '处理中',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=涂层脱落', full: 'https://via.placeholder.com/800x600?text=涂层脱落' }
|
||
]
|
||
},
|
||
{
|
||
type: '雷击损伤',
|
||
position: '叶片尖部',
|
||
description: '轻微雷击痕迹',
|
||
time: '2023-04-20',
|
||
status: '已修复',
|
||
images: [
|
||
{ thumbnail: 'https://via.placeholder.com/150x150?text=雷击1', full: 'https://via.placeholder.com/800x600?text=雷击1' }
|
||
]
|
||
}
|
||
],
|
||
detectionRunning: false,
|
||
detectionProgress: 0,
|
||
detectionStatus: '',
|
||
reportStep: 1,
|
||
reportSuggestions: '1. 对裂纹部位进行修补\n2. 重新喷涂脱落涂层区域\n3. 检查雷击损伤是否扩大',
|
||
reportReviewComments: '',
|
||
reportGenerated: true,
|
||
reportSaving: false,
|
||
reportSubmitting: false,
|
||
deliveryStep: 0,
|
||
deliveryInfo: {
|
||
producer: '张三',
|
||
handler: '李四',
|
||
productionTime: '2023-06-15',
|
||
location: '风电场A区',
|
||
checkItems: ['检查计划完整', '检查记录完整', '数据采集完整']
|
||
},
|
||
dataQualityItems: [
|
||
{ item: '数据完整性', status: '通过', comment: '所有检查数据已完整上传' },
|
||
{ item: '数据准确性', status: '通过', comment: '数据与实际情况相符' },
|
||
{ item: '数据规范性', status: '未通过', comment: '部分数据命名不规范' },
|
||
{ item: '数据一致性', status: '通过', comment: '数据之间无矛盾' },
|
||
{ item: '数据时效性', status: '通过', comment: '数据均为最新检查结果' }
|
||
],
|
||
monitoringVideos: [
|
||
{ batch: 'B20230601', time: '2023-06-01 08:30', duration: '45分钟', location: 'A区1号机组', status: '已分析' },
|
||
{ batch: 'B20230515', time: '2023-05-15 14:20', duration: '30分钟', location: 'B区3号机组', status: '已分析' },
|
||
{ batch: 'B20230420', time: '2023-04-20 10:15', duration: '60分钟', location: 'C区5号机组', status: '未分析' }
|
||
],
|
||
userInfo: {
|
||
name: '张三',
|
||
role: '检查员'
|
||
},
|
||
myTasks: [
|
||
{ id: 1, name: 'A区1号机组检查', project: '风电场A区2023年度检查', progress: 80, status: 'success', deadline: '2023-06-20' },
|
||
{ id: 2, name: 'B区专项检查准备', project: '风电场B区专项检查', progress: 30, status: '', deadline: '2023-06-25' },
|
||
{ id: 3, name: 'C区防雷检测报告', project: '风电场C区防雷检测', progress: 100, status: 'success', deadline: '2023-06-10' }
|
||
],
|
||
recentDefects: [
|
||
{ type: '表面裂纹', position: 'F01-1号叶片根部', severity: '高', time: '2023-06-15 10:23' },
|
||
{ type: '涂层脱落', position: 'F02-外部检查中部', severity: '中', time: '2023-06-14 14:45' },
|
||
{ type: '雷击损伤', position: 'F03-防雷检测尖部', severity: '低', time: '2023-06-13 09:12' }
|
||
],
|
||
notifications: [
|
||
{
|
||
id: 1,
|
||
title: '新任务分配',
|
||
content: '您被分配到风电场A区2号机组的检查任务',
|
||
time: '2023-06-15 09:00',
|
||
action: '查看任务'
|
||
},
|
||
{
|
||
id: 2,
|
||
title: '报告审核通过',
|
||
content: '您提交的C区防雷检测报告已通过审核',
|
||
time: '2023-06-14 16:30',
|
||
action: '查看报告'
|
||
},
|
||
{
|
||
id: 3,
|
||
title: '设备维护提醒',
|
||
content: '您使用的无人机(UAV-001)需要定期维护',
|
||
time: '2023-06-13 14:15',
|
||
action: '安排维护'
|
||
}
|
||
],
|
||
projectDialogVisible: false,
|
||
imagePreviewVisible: false,
|
||
currentPreviewImage: '',
|
||
defectImagesVisible: false,
|
||
currentDefectImages: [],
|
||
contextMenuVisible: false,
|
||
contextMenuPosition: { x: 0, y: 0 },
|
||
currentContextBlade: null
|
||
}
|
||
},
|
||
computed: {
|
||
filteredProjects() {
|
||
if (!this.projectSearch) return this.projects;
|
||
return this.projects.filter(project =>
|
||
project.name.toLowerCase().includes(this.projectSearch.toLowerCase()) ||
|
||
project.description.toLowerCase().includes(this.projectSearch.toLowerCase())
|
||
);
|
||
},
|
||
filteredStandards() {
|
||
if (!this.standardSearch) return this.standards;
|
||
return this.standards.filter(standard =>
|
||
standard.title.toLowerCase().includes(this.standardSearch.toLowerCase()) ||
|
||
standard.content.toLowerCase().includes(this.standardSearch.toLowerCase())
|
||
);
|
||
}
|
||
},
|
||
mounted() {
|
||
this.initCharts();
|
||
this.currentProject = this.projects[0];
|
||
|
||
// 模拟实时数据更新
|
||
setInterval(() => {
|
||
this.updateRealTimeData();
|
||
}, 5000);
|
||
},
|
||
watch: {
|
||
treeSearch(val) {
|
||
this.$refs.tree.filter(val);
|
||
}
|
||
},
|
||
methods: {
|
||
toggleSidebar() {
|
||
this.isCollapse = !this.isCollapse;
|
||
},
|
||
switchModule(module) {
|
||
this.activeModule = module;
|
||
if (module === 'dashboard') {
|
||
this.$nextTick(() => {
|
||
this.initCharts();
|
||
});
|
||
}
|
||
},
|
||
initCharts() {
|
||
// 任务进度图表
|
||
const taskChart = echarts.init(this.$refs.taskChart);
|
||
taskChart.setOption({
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
legend: {
|
||
data: ['已完成', '进行中', '未开始']
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'value'
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
data: ['A区检查', 'B区检查', 'C区检查', '防雷检测', '专项检查']
|
||
},
|
||
series: [
|
||
{
|
||
name: '已完成',
|
||
type: 'bar',
|
||
stack: 'total',
|
||
label: {
|
||
show: true
|
||
},
|
||
emphasis: {
|
||
focus: 'series'
|
||
},
|
||
data: [80, 30, 100, 75, 20],
|
||
itemStyle: {
|
||
color: '#67C23A'
|
||
}
|
||
},
|
||
{
|
||
name: '进行中',
|
||
type: 'bar',
|
||
stack: 'total',
|
||
label: {
|
||
show: true
|
||
},
|
||
emphasis: {
|
||
focus: 'series'
|
||
},
|
||
data: [20, 50, 0, 25, 30],
|
||
itemStyle: {
|
||
color: '#409EFF'
|
||
}
|
||
},
|
||
{
|
||
name: '未开始',
|
||
type: 'bar',
|
||
stack: 'total',
|
||
label: {
|
||
show: true
|
||
},
|
||
emphasis: {
|
||
focus: 'series'
|
||
},
|
||
data: [0, 20, 0, 0, 50],
|
||
itemStyle: {
|
||
color: '#909399'
|
||
}
|
||
}
|
||
]
|
||
});
|
||
|
||
// 缺陷统计图表
|
||
const defectChart = echarts.init(this.$refs.defectChart);
|
||
defectChart.setOption({
|
||
tooltip: {
|
||
trigger: 'item'
|
||
},
|
||
legend: {
|
||
top: '5%',
|
||
left: 'center'
|
||
},
|
||
series: [
|
||
{
|
||
name: '缺陷类型',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: false,
|
||
position: 'center'
|
||
},
|
||
emphasis: {
|
||
label: {
|
||
show: true,
|
||
fontSize: '18',
|
||
fontWeight: 'bold'
|
||
}
|
||
},
|
||
labelLine: {
|
||
show: false
|
||
},
|
||
data: [
|
||
{ value: 12, name: '表面裂纹' },
|
||
{ value: 8, name: '涂层脱落' },
|
||
{ value: 5, name: '雷击损伤' },
|
||
{ value: 3, name: '结构变形' },
|
||
{ value: 2, name: '其他' }
|
||
]
|
||
}
|
||
]
|
||
});
|
||
|
||
// 窗口大小变化时重新调整图表大小
|
||
window.addEventListener('resize', function() {
|
||
taskChart.resize();
|
||
defectChart.resize();
|
||
});
|
||
},
|
||
updateRealTimeData() {
|
||
// 模拟实时数据更新
|
||
this.equipmentList.forEach(equipment => {
|
||
if (equipment.status === '在线') {
|
||
// 随机更新最后活跃时间
|
||
if (Math.random() > 0.7) {
|
||
equipment.lastActive = new Date().toLocaleString();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
viewProjectDetail(project) {
|
||
this.currentProject = project;
|
||
this.activeModule = 'fieldwork';
|
||
},
|
||
showProjectDialog() {
|
||
this.projectDialogVisible = true;
|
||
this.currentProject = {
|
||
name: '',
|
||
description: '',
|
||
startDate: '',
|
||
endDate: '',
|
||
status: '准备中',
|
||
progress: 0
|
||
};
|
||
},
|
||
editProject(project) {
|
||
this.projectDialogVisible = true;
|
||
this.currentProject = JSON.parse(JSON.stringify(project));
|
||
},
|
||
saveProject() {
|
||
this.$message.success('项目保存成功');
|
||
this.projectDialogVisible = false;
|
||
|
||
// 如果是新建项目,添加到项目列表
|
||
if (!this.currentProject.id) {
|
||
this.currentProject.id = this.projects.length + 1;
|
||
this.currentProject.turbineCount = 0;
|
||
this.currentProject.turbines = [];
|
||
this.projects.push(this.currentProject);
|
||
} else {
|
||
// 更新现有项目
|
||
const index = this.projects.findIndex(p => p.id === this.currentProject.id);
|
||
this.$set(this.projects, index, this.currentProject);
|
||
}
|
||
},
|
||
refreshProjects() {
|
||
this.$message.success('项目列表已刷新');
|
||
},
|
||
startInspection(blade) {
|
||
this.currentBlade = blade;
|
||
this.activeModule = 'data';
|
||
// 更新叶片状态
|
||
const turbine = this.currentProject.turbines.find(t => t.blades.some(b => b.id === blade.id));
|
||
const bladeIndex = turbine.blades.findIndex(b => b.id === blade.id);
|
||
this.$set(turbine.blades[bladeIndex], 'status', '进行中');
|
||
},
|
||
showBladeContextMenu(event, blade) {
|
||
this.currentContextBlade = blade;
|
||
this.contextMenuPosition = {
|
||
x: event.clientX,
|
||
y: event.clientY
|
||
};
|
||
this.contextMenuVisible = true;
|
||
|
||
// 点击其他地方关闭菜单
|
||
document.addEventListener('click', this.closeContextMenu);
|
||
},
|
||
closeContextMenu() {
|
||
this.contextMenuVisible = false;
|
||
document.removeEventListener('click', this.closeContextMenu);
|
||
},
|
||
handleBladeAction(action) {
|
||
switch(action) {
|
||
case 'view':
|
||
this.viewBladeDetail(this.currentContextBlade);
|
||
break;
|
||
case 'history':
|
||
this.viewBladeHistory(this.currentContextBlade);
|
||
break;
|
||
case 'complete':
|
||
this.completeBladeInspection(this.currentContextBlade);
|
||
break;
|
||
}
|
||
this.closeContextMenu();
|
||
},
|
||
viewBladeDetail(blade) {
|
||
this.$message.info(`查看叶片详情: ${blade.position}叶片`);
|
||
},
|
||
viewBladeHistory(blade) {
|
||
this.$message.info(`查看历史记录: ${blade.position}叶片`);
|
||
},
|
||
completeBladeInspection(blade) {
|
||
const turbine = this.currentProject.turbines.find(t => t.blades.some(b => b.id === blade.id));
|
||
const bladeIndex = turbine.blades.findIndex(b => b.id === blade.id);
|
||
this.$set(turbine.blades[bladeIndex], 'status', '已完成');
|
||
this.$message.success(`${blade.position}叶片检查已完成`);
|
||
},
|
||
showWorkPlan() {
|
||
this.$message.info('显示工作计划');
|
||
},
|
||
showDailyReportDialog() {
|
||
this.$message.info('打开日报填写对话框');
|
||
},
|
||
showPhotoUpload() {
|
||
this.$message.info('打开照片上传界面');
|
||
},
|
||
showIssueReport() {
|
||
this.$message.info('打开问题报告界面');
|
||
},
|
||
viewInspectionRecord(record) {
|
||
this.$message.info(`查看检查记录: ${record.turbine} ${record.blade}`);
|
||
},
|
||
beforeUpload(file) {
|
||
const isImage = file.type.includes('image');
|
||
const isVideo = file.type.includes('video');
|
||
const isAudio = file.type.includes('audio');
|
||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||
const isLt100M = file.size / 1024 / 1024 < 100;
|
||
|
||
if (this.activeDataType === 'image' && !isImage) {
|
||
this.$message.error('只能上传图片文件!');
|
||
return false;
|
||
}
|
||
|
||
if (this.activeDataType === 'video' && !isVideo) {
|
||
this.$message.error('只能上传视频文件!');
|
||
return false;
|
||
}
|
||
|
||
if (this.activeDataType === 'audio' && !isAudio) {
|
||
this.$message.error('只能上传音频文件!');
|
||
return false;
|
||
}
|
||
|
||
if (this.activeDataType === 'image' && !isLt10M) {
|
||
this.$message.error('图片大小不能超过10MB!');
|
||
return false;
|
||
}
|
||
|
||
if ((this.activeDataType === 'video' || this.activeDataType === 'other') && !isLt100M) {
|
||
this.$message.error('文件大小不能超过100MB!');
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
handleFileChange(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
handleRemove(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
submitUpload() {
|
||
if (this.fileList.length === 0) {
|
||
this.$message.warning('请先选择要上传的文件');
|
||
return;
|
||
}
|
||
|
||
this.$message.info('开始上传文件...');
|
||
|
||
// 模拟上传过程
|
||
let uploadedCount = 0;
|
||
this.fileList.forEach(file => {
|
||
setTimeout(() => {
|
||
const newFile = {
|
||
name: file.name,
|
||
type: this.activeDataType,
|
||
size: this.formatFileSize(file.size),
|
||
time: new Date().toLocaleString(),
|
||
status: '成功'
|
||
};
|
||
this.uploadedData.unshift(newFile);
|
||
uploadedCount++;
|
||
|
||
if (uploadedCount === this.fileList.length) {
|
||
this.$message.success('所有文件上传完成');
|
||
this.fileList = [];
|
||
}
|
||
}, Math.random() * 2000);
|
||
});
|
||
},
|
||
clearFiles() {
|
||
this.fileList = [];
|
||
},
|
||
showBatchImportDialog() {
|
||
this.$message.info('打开批量导入对话框');
|
||
},
|
||
formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 Bytes';
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
},
|
||
previewFile(file) {
|
||
if (file.type === 'image') {
|
||
this.currentPreviewImage = 'https://via.placeholder.com/800x600?text=' + file.name.split('.')[0];
|
||
this.imagePreviewVisible = true;
|
||
} else {
|
||
this.$message.info(`预览 ${file.type} 文件: ${file.name}`);
|
||
}
|
||
},
|
||
deleteFile(file) {
|
||
this.$confirm(`确定要删除文件 ${file.name} 吗?`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
const index = this.uploadedData.findIndex(f => f.name === file.name);
|
||
this.uploadedData.splice(index, 1);
|
||
this.$message.success('文件已删除');
|
||
});
|
||
},
|
||
preprocessData() {
|
||
if (this.uploadedData.length === 0) {
|
||
this.$message.warning('没有可处理的数据');
|
||
return;
|
||
}
|
||
|
||
this.preprocessing = true;
|
||
this.preprocessProgress = 0;
|
||
this.preprocessStatus = '准备中...';
|
||
this.preprocessResults = [];
|
||
|
||
// 模拟预处理过程
|
||
const totalSteps = 10;
|
||
let currentStep = 0;
|
||
|
||
const processInterval = setInterval(() => {
|
||
currentStep++;
|
||
this.preprocessProgress = (currentStep / totalSteps) * 100;
|
||
|
||
switch(currentStep) {
|
||
case 1:
|
||
this.preprocessStatus = '正在解析文件...';
|
||
this.preprocessCurrentFile = this.uploadedData[0].name;
|
||
break;
|
||
case 3:
|
||
this.preprocessStatus = '语音转文字处理中...';
|
||
this.preprocessCurrentFile = this.uploadedData[1].name;
|
||
break;
|
||
case 5:
|
||
this.preprocessStatus = '图像特征提取中...';
|
||
this.preprocessCurrentFile = this.uploadedData[0].name;
|
||
break;
|
||
case 7:
|
||
this.preprocessStatus = '关键信息提取中...';
|
||
break;
|
||
case 9:
|
||
this.preprocessStatus = '生成处理结果...';
|
||
break;
|
||
case 10:
|
||
this.preprocessStatus = '完成';
|
||
this.preprocessing = false;
|
||
this.preprocessResults = [
|
||
{
|
||
type: '语音转文字',
|
||
content: '检查录音1.mp3 转文字结果: 叶片根部发现约5cm裂纹...',
|
||
details: '详细转写内容...'
|
||
},
|
||
{
|
||
type: '关键信息提取',
|
||
content: '提取到关键缺陷信息: 裂纹, 5cm, 根部',
|
||
details: '提取的详细信息...'
|
||
},
|
||
{
|
||
type: '图文匹配',
|
||
content: '已为缺陷图片匹配对应文字描述',
|
||
details: '匹配结果详情...'
|
||
}
|
||
];
|
||
this.activePreprocessResult = 0;
|
||
clearInterval(processInterval);
|
||
break;
|
||
default:
|
||
this.preprocessStatus = '处理中...';
|
||
}
|
||
}, 500);
|
||
},
|
||
showPreprocessSettings() {
|
||
this.$message.info('打开预处理设置对话框');
|
||
},
|
||
showPreprocessDetails(result) {
|
||
this.$alert(result.details, `${result.type}详情`, {
|
||
customClass: 'preprocess-details-dialog',
|
||
showConfirmButton: false,
|
||
closeOnClickModal: true
|
||
});
|
||
},
|
||
runDefectDetection() {
|
||
if (this.uploadedData.filter(f => f.type === 'image').length === 0) {
|
||
this.$message.warning('没有可用的图片数据进行缺陷检测');
|
||
return;
|
||
}
|
||
|
||
this.detectionRunning = true;
|
||
this.detectionProgress = 0;
|
||
this.detectionStatus = '初始化算法...';
|
||
|
||
// 模拟检测过程
|
||
const totalSteps = 20;
|
||
let currentStep = 0;
|
||
|
||
const detectInterval = setInterval(() => {
|
||
currentStep++;
|
||
this.detectionProgress = (currentStep / totalSteps) * 100;
|
||
|
||
switch(currentStep) {
|
||
case 5:
|
||
this.detectionStatus = '正在分析图片1...';
|
||
break;
|
||
case 10:
|
||
this.detectionStatus = '正在分析图片2...';
|
||
break;
|
||
case 15:
|
||
this.detectionStatus = '正在分析图片3...';
|
||
break;
|
||
case 20:
|
||
this.detectionStatus = '分析完成';
|
||
this.detectionRunning = false;
|
||
this.$message.success('缺陷检测完成');
|
||
clearInterval(detectInterval);
|
||
break;
|
||
default:
|
||
this.detectionStatus = '分析中...';
|
||
}
|
||
}, 300);
|
||
},
|
||
showDetectionSettings() {
|
||
this.$message.info('打开检测设置对话框');
|
||
},
|
||
exportDetectionResults() {
|
||
this.$message.success('检测结果导出成功');
|
||
},
|
||
viewDefectDetail(defect) {
|
||
this.$alert(`<strong>缺陷类型:</strong> ${defect.type}<br>
|
||
<strong>位置:</strong> ${defect.position}<br>
|
||
<strong>尺寸:</strong> ${defect.size}<br>
|
||
<strong>严重程度:</strong> ${defect.severity}<br>
|
||
<strong>描述:</strong> ${defect.description}`, '缺陷详情', {
|
||
dangerouslyUseHTMLString: true,
|
||
customClass: 'defect-detail-dialog'
|
||
});
|
||
},
|
||
previewDefectImages(defect) {
|
||
this.currentDefectImages = defect.images;
|
||
this.defectImagesVisible = true;
|
||
},
|
||
markAsFalseAlarm(defect) {
|
||
this.$confirm(`确定将 ${defect.type} 标记为误报吗?`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
const index = this.defectDetectionResults.findIndex(d => d.type === defect.type && d.position === defect.position);
|
||
this.defectDetectionResults.splice(index, 1);
|
||
this.$message.success('已标记为误报');
|
||
});
|
||
},
|
||
filterTreeNode(value, data) {
|
||
if (!value) return true;
|
||
return data.label.indexOf(value) !== -1;
|
||
},
|
||
handleTreeNodeClick(data) {
|
||
this.selectedTreeNode = data;
|
||
},
|
||
downloadStandard(standard) {
|
||
this.$message.info(`下载标准: ${standard.title}`);
|
||
},
|
||
viewStandardDetail(standard) {
|
||
this.$alert(standard.content, standard.title, {
|
||
dangerouslyUseHTMLString: true,
|
||
customClass: 'standard-detail-dialog',
|
||
width: '60%'
|
||
});
|
||
},
|
||
generateReport() {
|
||
this.reportGenerated = true;
|
||
this.reportStep = 2;
|
||
this.$message.success('报告生成成功');
|
||
},
|
||
editReport() {
|
||
this.$message.info('打开报告编辑器');
|
||
},
|
||
previewReport() {
|
||
this.$message.info('预览报告内容');
|
||
},
|
||
saveReport() {
|
||
this.reportSaving = true;
|
||
setTimeout(() => {
|
||
this.reportSaving = false;
|
||
this.$message.success('报告保存成功');
|
||
}, 1000);
|
||
},
|
||
submitReport() {
|
||
this.reportSubmitting = true;
|
||
setTimeout(() => {
|
||
this.reportSubmitting = false;
|
||
this.reportStep = 3;
|
||
this.$message.success('报告已提交审核');
|
||
}, 1500);
|
||
},
|
||
exportReport() {
|
||
this.$message.success('报告导出成功');
|
||
},
|
||
exportReportImages() {
|
||
this.$message.success('报告图片导出成功');
|
||
},
|
||
printReport() {
|
||
this.$message.info('打印报告');
|
||
},
|
||
completeReliabilityCheck() {
|
||
this.deliveryStep = 1;
|
||
this.$message.success('可靠性评估完成');
|
||
},
|
||
editDataQualityItem(item) {
|
||
this.$prompt('请输入说明', '编辑数据质量项', {
|
||
inputValue: item.comment
|
||
}).then(({ value }) => {
|
||
item.comment = value;
|
||
this.$message.success('修改成功');
|
||
});
|
||
},
|
||
completeDataQualityCheck() {
|
||
if (this.dataQualityItems.some(item => item.status === '未通过')) {
|
||
this.$confirm('存在未通过的质量项,确定要继续吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.deliveryStep = 2;
|
||
this.$message.success('数据质量评估完成');
|
||
});
|
||
} else {
|
||
this.deliveryStep = 2;
|
||
this.$message.success('数据质量评估完成');
|
||
}
|
||
},
|
||
completeDefectConfirmation() {
|
||
if (this.defectDetectionResults.some(d => !d.confirmed)) {
|
||
this.$confirm('存在未确认的缺陷,确定要继续吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.deliveryStep = 3;
|
||
this.$message.success('缺陷入库确认完成');
|
||
});
|
||
} else {
|
||
this.deliveryStep = 3;
|
||
this.$message.success('缺陷入库确认完成');
|
||
}
|
||
},
|
||
submitProjectDelivery() {
|
||
this.$message.success('项目交付提交成功');
|
||
},
|
||
downloadDeliveryPackage() {
|
||
this.$message.success('交付包下载开始');
|
||
},
|
||
openRoutePlanning() {
|
||
this.$message.info('打开航线规划工具');
|
||
},
|
||
open3DViewer() {
|
||
this.$message.info('打开三维模型查看器');
|
||
},
|
||
openReportTemplates() {
|
||
this.$message.info('打开报告模板库');
|
||
},
|
||
openDefectDatabase() {
|
||
this.$message.info('打开缺陷数据库');
|
||
},
|
||
openWindForecast() {
|
||
this.$message.info('打开风速预测工具');
|
||
},
|
||
openTrainingMaterials() {
|
||
this.$message.info('打开培训资料库');
|
||
},
|
||
viewMonitoringVideo(video) {
|
||
this.$message.info(`查看监测视频: ${video.batch}`);
|
||
},
|
||
analyzeVideo(video) {
|
||
this.$message.info(`分析监测视频: ${video.batch}`);
|
||
},
|
||
previewImage(img) {
|
||
this.currentPreviewImage = img.full;
|
||
this.imagePreviewVisible = true;
|
||
},
|
||
viewTaskDetail(task) {
|
||
this.$message.info(`查看任务详情: ${task.name}`);
|
||
},
|
||
handleNotification(notification) {
|
||
this.$message.info(`处理通知: ${notification.title}`);
|
||
},
|
||
logout() {
|
||
this.$confirm('确定要退出系统吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.$message.success('已退出系统');
|
||
// 实际应用中这里应该跳转到登录页面
|
||
});
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |