yuanxingsheji/施工操作台/新施工操作台.html

3468 lines
138 KiB
HTML
Raw Normal View History

2025-07-08 10:16:29 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
2025-07-25 17:57:25 +08:00
<title>风电叶片检查智能管理平台</title>
2025-07-08 10:16:29 +08:00
<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>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
color: #333;
background-color: #f0f2f5;
}
.app-container {
display: flex;
min-height: 100vh;
overflow: hidden;
}
/* 侧边栏样式 */
.sidebar {
width: 220px;
background-color: #304156;
color: #fff;
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;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.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;
color: #fff;
}
/* 菜单项样式 */
.el-menu {
border-right: none;
background-color: transparent;
}
.el-menu-item, .el-submenu__title {
color: #fff;
height: 46px;
line-height: 46px;
}
.el-menu-item:hover, .el-submenu__title:hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
.el-menu-item.is-active {
background-color: rgba(0, 0, 0, 0.2) !important;
color: #409EFF !important;
}
.el-submenu .el-menu-item {
min-width: 0 !important;
padding-left: 50px !important;
background-color: #1f2d3d !important;
}
.el-submenu .el-menu-item:hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
.el-submenu__title {
padding-left: 20px !important;
}
.el-menu-item [class^=el-icon-], .el-submenu [class^=el-icon-] {
margin-right: 10px;
}
/* 二级菜单样式 */
.el-menu--inline .el-menu-item {
padding-left: 60px !important;
}
/* 折叠状态下的样式 */
.sidebar.collapsed .el-submenu__icon-arrow {
display: none;
}
.sidebar.collapsed .el-submenu > .el-menu {
display: none;
}
/* 徽标样式 */
.el-badge {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
}
.sidebar.collapsed .el-badge {
right: 5px;
}
/* 用户信息样式 */
.user-info {
display: flex;
align-items: center;
padding: 10px;
border-top: 1px solid rgba(255,255,255,0.1);
position: absolute;
bottom: 0;
width: 220px;
background-color: #304156;
}
.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;
width: 64px;
}
.sidebar.collapsed .user-name,
.sidebar.collapsed .user-logout {
display: none;
}
.sidebar.collapsed .user-avatar {
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;
}
/* 实时数据卡片 */
.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;
}
/* 表格样式 */
.el-table {
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;
}
.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;
}
2025-07-25 17:57:25 +08:00
/* 施工任务卡片 */
.task-card {
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
transition: all 0.3s;
}
.task-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.task-card-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-card-body {
padding: 15px;
}
.task-progress {
margin: 10px 0;
}
/* 监测数据卡片 */
.monitor-card {
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.monitor-card-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
font-weight: bold;
}
.monitor-card-body {
padding: 15px;
}
2025-07-08 10:16:29 +08:00
/* 响应式调整 */
@media (max-width: 992px) {
.data-card {
width: calc(50% - 10px);
}
.data-card: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;
width: 100%;
}
}
</style>
</head>
<body>
<div id="app" class="app-container">
<!-- 侧边栏导航 - 优化后的版本 -->
<div class="sidebar" :class="{collapsed: isCollapse}">
<div class="logo">
2025-07-25 17:57:25 +08:00
<h2>风电叶片检查</h2>
<p>智能管理平台</p>
2025-07-08 10:16:29 +08:00
<div class="toggle-sidebar" @click="toggleSidebar">
<i :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"></i>
</div>
</div>
<!-- 使用Element UI的菜单组件重构导航 -->
<el-menu
:default-active="activeModule"
class="el-menu-vertical"
:collapse="isCollapse"
:collapse-transition="false"
background-color="#304156"
text-color="#fff"
active-text-color="#409EFF"
>
<!-- 工作台 -->
<el-menu-item index="dashboard" @click="switchModule('dashboard')">
<i class="el-icon-data-line"></i>
<span slot="title">工作台</span>
</el-menu-item>
2025-07-25 17:57:25 +08:00
<!-- 我的项目 -->
<el-submenu index="project">
2025-07-08 10:16:29 +08:00
<template slot="title">
2025-07-25 17:57:25 +08:00
<i class="el-icon-folder-opened"></i>
<span>我的项目</span>
2025-07-08 10:16:29 +08:00
</template>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="project-list"
@click="switchModule('project-list')"
2025-07-08 10:16:29 +08:00
>
<span>项目列表</span>
2025-07-25 17:57:25 +08:00
<el-badge :value="newProjectCount" :max="99" class="item"></el-badge>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="project-construction"
@click="switchModule('project-construction')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>我的施工</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
2025-07-25 17:57:25 +08:00
<!-- 我的业务数据 -->
<el-submenu index="data-processing">
<template slot="title">我的业务数据</template>
2025-07-08 10:16:29 +08:00
<el-menu-item
2025-07-25 17:57:25 +08:00
index="data-import"
@click="switchModule('data-import')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>数据入库</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="data-preprocess"
@click="switchModule('data-preprocess')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>数据预处理</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
2025-07-25 17:57:25 +08:00
<!-- 数据分析或图像检测 - 二级下拉菜单 -->
<el-submenu index="inspection">
<template slot="title">数据分析或图像检测</template>
<el-menu-item
index="inspection-tree"
@click="switchModule('inspection-tree')"
>
<span>可视化管理</span>
</el-menu-item>
<el-menu-item
index="inspection-defect"
@click="switchModule('inspection-defect')"
>
<span>缺陷检测</span>
</el-menu-item>
<el-menu-item
index="inspection-report"
@click="switchModule('inspection-report')"
>
<span>报告生成</span>
</el-menu-item>
<el-menu-item
index="inspection-standard"
@click="switchModule('inspection-standard')"
>
<span>标准信息库</span>
</el-menu-item>
<el-menu-item
index="report-review"
@click="switchModule('report-review')"
>
<span>报告修改审核</span>
</el-menu-item>
<el-menu-item
index="inspection-lifecycle"
@click="switchModule('inspection-lifecycle')"
>
<span>全生命周期管理</span>
</el-menu-item>
<el-menu-item
index="drone-service"
@click="switchModule('drone-service')"
>
<span>无人机云服务</span>
<span>航线规划</span>
<span>机场管理</span>
</el-menu-item>
</el-submenu>
</el-submenu>
<!-- 项目交付 -->
<el-submenu index="delivery">
<template slot="title">项目交付</template>
2025-07-08 10:16:29 +08:00
<el-menu-item
2025-07-25 17:57:25 +08:00
index="delivery-reliability"
@click="switchModule('delivery-reliability')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>可靠性评估</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="delivery-quality"
@click="switchModule('delivery-quality')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>数据质量评估</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="delivery-storage"
@click="switchModule('delivery-storage')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>质量入库</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
</el-submenu>
2025-07-25 17:57:25 +08:00
</el-submenu>
<!-- 我的在线监测业务 -->
<el-submenu index="monitor-project">
<template slot="title">
<i class="el-icon-monitor"></i>
<span>我的在线监测业务/项目</span>
</template>
</el-menu-item>
</el-submenu>
<!-- 塔下监测预报 -->
<el-submenu index="monitor">
<template slot="title">
<i class="el-icon-monitor"></i>
<span>塔下监测预报</span>
</template>
<el-menu-item
index="monitor-clearance"
@click="switchModule('monitor-clearance')"
>
<span>净空距离</span>
</el-menu-item>
2025-07-08 10:16:29 +08:00
<el-menu-item
2025-07-25 17:57:25 +08:00
index="monitor-deformation"
@click="switchModule('monitor-deformation')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>形变监测</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="monitor-image"
@click="switchModule('monitor-image')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>图像检测</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
</el-submenu>
2025-07-25 17:57:25 +08:00
<!-- 其他 -->
<el-submenu index="others">
2025-07-08 10:16:29 +08:00
<template slot="title">
2025-07-25 17:57:25 +08:00
<i class="el-icon-set-up"></i>
<span>其他</span>
2025-07-08 10:16:29 +08:00
</template>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="others-route"
@click="switchModule('others-route')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>航线规划</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="others-model"
@click="switchModule('others-model')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>三维模型</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
<el-menu-item
2025-07-25 17:57:25 +08:00
index="others-template"
@click="switchModule('others-template')"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<span>报告模板库</span>
</el-menu-item>
<el-menu-item
index="others-settings"
@click="switchModule('others-settings')"
>
<span>系统设置</span>
2025-07-08 10:16:29 +08:00
</el-menu-item>
</el-submenu>
</el-menu>
<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">
2025-07-25 17:57:25 +08:00
<div class="data-title">我的项目</div>
<div class="data-value">{{ myProjects.length }}<span class="data-unit"></span></div>
2025-07-08 10:16:29 +08:00
</div>
<div class="data-card">
2025-07-25 17:57:25 +08:00
<div class="data-title">进行中任务</div>
<div class="data-value">{{ ongoingTasks.length }}<span class="data-unit"></span></div>
2025-07-08 10:16:29 +08:00
</div>
<div class="data-card">
<div class="data-title">待审核报告</div>
2025-07-25 17:57:25 +08:00
<div class="data-value">{{ pendingReports.length }}<span class="data-unit"></span></div>
2025-07-08 10:16:29 +08:00
</div>
<div class="data-card">
2025-07-25 17:57:25 +08:00
<div class="data-title">发现缺陷</div>
<div class="data-value">{{ defectCount }}<span class="data-unit"></span></div>
2025-07-08 10:16:29 +08:00
</div>
</div>
<div class="card-container">
<el-tabs v-model="dashboardTab">
2025-07-25 17:57:25 +08:00
<el-tab-pane label="项目进度" name="projects">
<div class="chart-container" ref="projectChart"></div>
2025-07-08 10:16:29 +08:00
2025-07-25 17:57:25 +08:00
<h3>我的项目</h3>
<el-table :data="myProjects" 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">
2025-07-08 10:16:29 +08:00
<template slot-scope="scope">
2025-07-25 17:57:25 +08:00
<el-tag :type="scope.row.status === '进行中' ? 'primary' : scope.row.status === '已完成' ? 'success' : 'info'">
{{ scope.row.status }}
</el-tag>
2025-07-08 10:16:29 +08:00
</template>
</el-table-column>
2025-07-25 17:57:25 +08:00
<el-table-column prop="progress" label="进度" width="180">
2025-07-08 10:16:29 +08:00
<template slot-scope="scope">
2025-07-25 17:57:25 +08:00
<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="enterProject(scope.row)">进入</el-button>
2025-07-08 10:16:29 +08:00
</template>
</el-table-column>
</el-table>
</el-tab-pane>
2025-07-25 17:57:25 +08:00
<el-tab-pane label="施工任务" name="tasks">
<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="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="progress" label="进度" width="120">
2025-07-08 10:16:29 +08:00
<template slot-scope="scope">
2025-07-25 17:57:25 +08:00
<el-progress :percentage="scope.row.progress" :status="scope.row.status"></el-progress>
2025-07-08 10:16:29 +08:00
</template>
</el-table-column>
2025-07-25 17:57:25 +08:00
<el-table-column prop="deadline" label="截止时间" width="180"></el-table-column>
2025-07-08 10:16:29 +08:00
<el-table-column label="操作" width="120">
<template slot-scope="scope">
2025-07-25 17:57:25 +08:00
<el-button size="mini" @click="viewTaskDetail(scope.row)">查看</el-button>
2025-07-08 10:16:29 +08:00
</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>
2025-07-25 17:57:25 +08:00
<!-- 项目列表模块 -->
<div v-if="activeModule === 'project-list'">
2025-07-08 10:16:29 +08:00
<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>
2025-07-25 17:57:25 +08:00
<el-tabs v-model="projectTab">
<el-tab-pane label="全部项目" name="all">
<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="enterProject(scope.row)">进入</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="我的项目" name="mine">
<el-table :data="myProjects" 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="enterProject(scope.row)">进入</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="待开工项目" name="pending">
<el-table :data="pendingProjects" 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="info">{{ scope.row.status }}</el-tag>
</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="enterProject(scope.row)">进入</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
2025-07-08 10:16:29 +08:00
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 我的施工模块 -->
<div v-if="activeModule === 'project-construction'">
<h2 class="module-title">我的施工 - {{ currentProject ? currentProject.name : '请选择项目' }}</h2>
2025-07-08 10:16:29 +08:00
2025-07-25 17:57:25 +08:00
<div class="card-container" v-if="currentProject">
<el-tabs v-model="constructionTab">
<el-tab-pane label="任务机组" name="turbines">
<div style="margin-bottom: 20px;">
<el-input
placeholder="搜索机组编号"
v-model="turbineSearch"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-table :data="filteredTurbines" border style="width: 100%">
<el-table-column prop="code" label="机组编号" width="120"></el-table-column>
<el-table-column prop="type" label="机组类型" width="120"></el-table-column>
<el-table-column prop="location" label="位置"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '待施工' ? 'info' : scope.row.status === '施工中' ? 'primary' : 'success'">
{{ 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"
type="primary"
@click="startConstruction(scope.row)"
v-if="scope.row.status === '待施工'"
>
开始施工
</el-button>
<el-button
size="mini"
type="success"
@click="completeConstruction(scope.row)"
v-if="scope.row.status === '施工中'"
>
完成施工
</el-button>
<el-button
size="mini"
@click="viewTurbineDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="施工报告" name="reports">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="createConstructionReport">新建报告</el-button>
<el-button icon="el-icon-refresh" @click="refreshReports">刷新</el-button>
</div>
<el-table :data="constructionReports" border style="width: 100%">
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="date" label="施工日期" width="150"></el-table-column>
<el-table-column prop="inspector" label="检查人员" width="120"></el-table-column>
<el-table-column prop="defectCount" label="缺陷数量" width="120"></el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '草稿' ? 'info' : scope.row.status === '已提交' ? 'primary' : 'success'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewReport(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editReport(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteReport(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="施工台账" name="records">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="recordDateRange"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
>
</el-date-picker>
<el-button type="primary" style="margin-left: 20px;" @click="searchRecords">查询</el-button>
<el-button @click="exportRecords">导出台账</el-button>
</div>
<el-table :data="constructionRecords" border style="width: 100%">
<el-table-column prop="date" label="日期" width="150"></el-table-column>
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="type" label="工作类型" width="120"></el-table-column>
<el-table-column prop="content" label="工作内容"></el-table-column>
<el-table-column prop="operator" label="操作人" width="120"></el-table-column>
<el-table-column label="操作" width="120">
<template slot-scope="scope">
<el-button size="mini" @click="viewRecordDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
<div class="card-container" v-else>
2025-07-08 10:16:29 +08:00
<el-alert
2025-07-25 17:57:25 +08:00
title="请先选择要操作的项目"
2025-07-08 10:16:29 +08:00
type="info"
show-icon
:closable="false"
>
</el-alert>
2025-07-25 17:57:25 +08:00
<el-button type="primary" @click="switchModule('project-list')">选择项目</el-button>
2025-07-08 10:16:29 +08:00
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 数据入库模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'data-import'">
<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>
</div>
<div class="card-container" v-else>
<el-alert
title="请先选择要上传数据的检查项"
type="info"
show-icon
:closable="false"
>
</el-alert>
2025-07-25 17:57:25 +08:00
<el-button type="primary" @click="switchModule('project-list')">返回项目列表</el-button>
2025-07-08 10:16:29 +08:00
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 数据预处理模块 -->
<div v-if="activeModule === 'data-preprocess'">
<h2 class="module-title">数据预处理</h2>
<div class="card-container">
<el-steps :active="preprocessStep" finish-status="success" style="margin-bottom: 20px;">
<el-step title="选择数据"></el-step>
<el-step title="预处理设置"></el-step>
<el-step title="执行预处理"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<div v-if="preprocessStep === 0">
<h3>选择要预处理的数据</h3>
<el-select v-model="selectedProject" placeholder="请选择项目" style="width: 300px;">
<el-option
v-for="project in projects"
:key="project.id"
:label="project.name"
:value="project.id"
></el-option>
</el-select>
<div style="margin-top: 20px;">
<el-table :data="preprocessData" border style="width: 100%">
<el-table-column type="selection" width="55"></el-table-column>
<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' : '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>
</div>
<div style="margin-top: 20px;">
<el-button type="primary" @click="preprocessStep = 1">下一步</el-button>
</div>
</div>
<div v-if="preprocessStep === 1">
<h3>预处理设置</h3>
<el-form :model="preprocessForm" label-width="120px">
<el-form-item label="图像处理">
<el-checkbox-group v-model="preprocessForm.imageOptions">
<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 label="视频处理">
<el-checkbox-group v-model="preprocessForm.videoOptions">
<el-checkbox label="关键帧提取"></el-checkbox>
<el-checkbox label="稳定化"></el-checkbox>
<el-checkbox label="分辨率调整"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="输出格式">
<el-select v-model="preprocessForm.outputFormat" placeholder="请选择输出格式">
<el-option label="JPG" value="jpg"></el-option>
<el-option label="PNG" value="png"></el-option>
<el-option label="MP4" value="mp4"></el-option>
</el-select>
</el-form-item>
<el-form-item label="输出目录">
<el-input v-model="preprocessForm.outputPath" placeholder="请输入输出目录"></el-input>
</el-form-item>
</el-form>
<div style="margin-top: 20px;">
<el-button @click="preprocessStep = 0">上一步</el-button>
<el-button type="primary" @click="preprocessStep = 2">下一步</el-button>
</div>
</div>
<div v-if="preprocessStep === 2">
<h3>执行预处理</h3>
<div style="margin-bottom: 20px;">
<el-progress :percentage="preprocessProgress"></el-progress>
<div style="margin-top: 10px; color: #909399;">
{{ preprocessStatus }}
</div>
</div>
<el-button @click="preprocessStep = 1">上一步</el-button>
<el-button type="primary" @click="startPreprocess" :disabled="preprocessRunning">开始预处理</el-button>
</div>
<div v-if="preprocessStep === 3">
<el-result
icon="success"
title="预处理完成"
subTitle="数据预处理已成功完成"
>
<template slot="extra">
<el-button type="primary" size="medium" @click="viewPreprocessResults">查看结果</el-button>
<el-button size="medium" @click="preprocessStep = 0">继续处理</el-button>
</template>
</el-result>
</div>
</div>
</div>
<!-- 缺陷检测算法模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'inspection-defect'">
<h2 class="module-title">缺陷检测算法</h2>
<div class="card-container">
<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"></el-progress>
<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>
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 树状可视化管理模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'inspection-tree'">
<h2 class="module-title">树状可视化管理</h2>
<div class="card-container">
<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"
2025-07-25 17:57:25 +08:00
ref="tree"
2025-07-08 10:16:29 +08:00
>
<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>
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 标准信息库模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'inspection-standard'">
<h2 class="module-title">标准信息库</h2>
<div class="card-container">
<el-tabs v-model="standardTab">
<el-tab-pane label="缺陷标准" name="defect">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="showAddStandardDialog('defect')">新增缺陷标准</el-button>
<el-button icon="el-icon-download" @click="exportStandards('defect')">导出标准</el-button>
<el-button icon="el-icon-upload2">导入标准</el-button>
</div>
<el-table :data="defectStandards" border style="width: 100%">
<el-table-column prop="code" label="缺陷代码" width="120"></el-table-column>
<el-table-column prop="name" label="缺陷名称" width="180"></el-table-column>
<el-table-column prop="category" 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 prop="description" label="缺陷描述"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="editStandard(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteStandard(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="检查标准" name="inspection">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="showAddStandardDialog('inspection')">新增检查标准</el-button>
<el-button icon="el-icon-download" @click="exportStandards('inspection')">导出标准</el-button>
<el-button icon="el-icon-upload2">导入标准</el-button>
</div>
<el-table :data="inspectionStandards" border style="width: 100%">
<el-table-column prop="code" label="标准代码" width="120"></el-table-column>
<el-table-column prop="name" label="标准名称" width="180"></el-table-column>
<el-table-column prop="category" label="适用部位" width="120"></el-table-column>
<el-table-column prop="method" label="检查方法" width="120"></el-table-column>
<el-table-column prop="description" label="标准描述"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="editStandard(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteStandard(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="维修标准" name="repair">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="showAddStandardDialog('repair')">新增维修标准</el-button>
<el-button icon="el-icon-download" @click="exportStandards('repair')">导出标准</el-button>
<el-button icon="el-icon-upload2">导入标准</el-button>
</div>
<el-table :data="repairStandards" border style="width: 100%">
<el-table-column prop="code" label="标准代码" width="120"></el-table-column>
<el-table-column prop="name" label="标准名称" width="180"></el-table-column>
<el-table-column prop="defectType" label="适用缺陷" width="120"></el-table-column>
<el-table-column prop="method" label="维修方法" width="120"></el-table-column>
<el-table-column prop="description" label="标准描述"></el-table-column>
<el-table-column prop="material" label="所需材料" width="180"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="editStandard(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteStandard(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 报告生成模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'inspection-report'">
<h2 class="module-title">报告生成</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="generateNewReport">生成新报告</el-button>
<el-button icon="el-icon-refresh" @click="refreshReportsList">刷新列表</el-button>
<el-button icon="el-icon-download" @click="exportAllReports">导出全部</el-button>
</div>
<el-table :data="reportList" border style="width: 100%">
<el-table-column prop="name" label="报告名称" width="200"></el-table-column>
<el-table-column prop="project" label="所属项目" width="180"></el-table-column>
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="date" label="生成日期" width="150"></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 === '待审核' ? 'warning' : 'info'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="250">
<template slot-scope="scope">
<el-button size="mini" @click="viewReport(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editReport(scope.row)">编辑</el-button>
<el-button size="mini" type="success" @click="publishReport(scope.row)" :disabled="scope.row.status === '已发布'">发布</el-button>
<el-button size="mini" type="danger" @click="deleteReport(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 报告预览对话框 -->
<el-dialog title="报告预览" :visible.sync="reportPreviewDialogVisible" width="80%" top="5vh">
<div class="report-preview" v-if="currentReport">
<div class="report-section">
<h2 class="report-title">{{ currentReport.name }}</h2>
<div class="report-content">
<p><strong>项目名称:</strong> {{ currentReport.project }}</p>
<p><strong>机组编号:</strong> {{ currentReport.turbine }}</p>
<p><strong>检查日期:</strong> {{ currentReport.date }}</p>
<p><strong>检查人员:</strong> {{ currentReport.inspector }}</p>
</div>
</div>
<div class="report-section">
<h3 class="report-title">检查概况</h3>
<div class="report-content">
<p>{{ currentReport.summary }}</p>
</div>
</div>
<div class="report-section" v-if="currentReport.defects && currentReport.defects.length > 0">
<h3 class="report-title">缺陷列表</h3>
<div class="report-content">
<div class="defect-item" v-for="(defect, index) in currentReport.defects" :key="index">
<div class="defect-title">{{ defect.code }} - {{ defect.name }} ({{ defect.severity }})</div>
<div class="defect-desc">{{ defect.description }}</div>
<div class="defect-images" v-if="defect.images && defect.images.length > 0">
<img
v-for="(img, imgIndex) in defect.images"
:key="imgIndex"
:src="img.thumbnail"
class="defect-image-preview"
@click="previewImage(img.full)"
>
</div>
</div>
</div>
</div>
<div class="report-section">
<h3 class="report-title">检查结论</h3>
<div class="report-content">
<p>{{ currentReport.conclusion }}</p>
</div>
</div>
<div class="report-section" v-if="currentReport.recommendations">
<h3 class="report-title">处理建议</h3>
<div class="report-content">
<p>{{ currentReport.recommendations }}</p>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="reportPreviewDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="downloadReport(currentReport)">下载报告</el-button>
</span>
</el-dialog>
</div>
2025-07-25 17:57:25 +08:00
<!-- 全生命周期管理模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'inspection-lifecycle'">
<h2 class="module-title">全生命周期管理</h2>
<div class="card-container">
<el-tabs v-model="lifecycleTab">
<el-tab-pane label="机组档案" name="turbine">
<div style="margin-bottom: 20px;">
<el-input
placeholder="搜索机组编号或名称"
v-model="turbineSearch"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-table :data="filteredTurbines" border style="width: 100%">
<el-table-column prop="code" label="机组编号" width="120"></el-table-column>
<el-table-column prop="name" label="机组名称" width="180"></el-table-column>
<el-table-column prop="type" label="机组类型" width="120"></el-table-column>
<el-table-column prop="location" label="地理位置"></el-table-column>
<el-table-column prop="installDate" label="投运日期" width="120"></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 === '停机' ? 'warning' : 'danger'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewTurbineLifecycle(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="叶片档案" name="blade">
<div style="margin-bottom: 20px;">
<el-input
placeholder="搜索叶片编号或所属机组"
v-model="bladeSearch"
clearable
style="width: 300px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-table :data="filteredBlades" border style="width: 100%">
<el-table-column prop="code" label="叶片编号" width="120"></el-table-column>
<el-table-column prop="turbine" label="所属机组" width="180"></el-table-column>
<el-table-column prop="position" label="叶片位置" width="120"></el-table-column>
<el-table-column prop="type" label="叶片型号" width="120"></el-table-column>
<el-table-column prop="manufacturer" label="制造商" width="120"></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 label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewBladeLifecycle(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
<!-- 机组生命周期详情对话框 -->
<el-dialog :title="currentTurbine ? currentTurbine.name + ' - 生命周期记录' : '机组生命周期'" :visible.sync="turbineLifecycleDialogVisible" width="80%">
<div v-if="currentTurbine">
<el-descriptions :column="3" border style="margin-bottom: 20px;">
<el-descriptions-item label="机组编号">{{ currentTurbine.code }}</el-descriptions-item>
<el-descriptions-item label="机组类型">{{ currentTurbine.type }}</el-descriptions-item>
<el-descriptions-item label="投运日期">{{ currentTurbine.installDate }}</el-descriptions-item>
<el-descriptions-item label="地理位置">{{ currentTurbine.location }}</el-descriptions-item>
<el-descriptions-item label="运行状态">
<el-tag :type="currentTurbine.status === '运行中' ? 'success' : currentTurbine.status === '停机' ? 'warning' : 'danger'">
{{ currentTurbine.status }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="累计发电量">{{ currentTurbine.totalPower }} kWh</el-descriptions-item>
</el-descriptions>
<h3 style="margin-bottom: 15px;">生命周期事件</h3>
<el-timeline>
<el-timeline-item
v-for="(event, index) in turbineLifecycleEvents"
:key="index"
:timestamp="event.time"
placement="top"
:type="event.type === '检查' ? 'primary' : event.type === '维修' ? 'warning' : event.type === '更换' ? 'danger' : 'info'"
>
<el-card>
<h4>{{ event.type }} - {{ event.title }}</h4>
<p>{{ event.description }}</p>
<div v-if="event.images && event.images.length > 0" style="margin-top: 10px;">
<el-image
v-for="(img, imgIndex) in event.images"
:key="imgIndex"
:src="img.thumbnail"
:preview-src-list="[img.full]"
style="width: 100px; height: 100px; margin-right: 10px;"
></el-image>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="turbineLifecycleDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
2025-07-25 17:57:25 +08:00
<!-- 可靠性评估模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'delivery-reliability'">
<h2 class="module-title">可靠性评估</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-select v-model="selectedProject" placeholder="请选择项目" style="width: 300px;">
<el-option
v-for="project in projects"
:key="project.id"
:label="project.name"
:value="project.id"
></el-option>
</el-select>
<el-button type="primary" @click="generateReliabilityReport" style="margin-left: 20px;">生成评估报告</el-button>
</div>
<div v-if="reliabilityData" class="chart-container" ref="reliabilityChart"></div>
<div v-if="reliabilityData" style="margin-top: 20px;">
<h3>评估结果</h3>
<el-table :data="reliabilityData.details" border style="width: 100%">
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="score" label="可靠性评分" width="120">
<template slot-scope="scope">
<el-rate
v-model="scope.row.score"
disabled
show-score
text-color="#ff9900"
score-template="{value} 分"
></el-rate>
</template>
</el-table-column>
<el-table-column prop="defectCount" 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 prop="recommendation" label="维护建议"></el-table-column>
</el-table>
</div>
<div v-if="reliabilityData" style="margin-top: 20px;">
<el-button type="primary" @click="exportReliabilityReport">导出评估报告</el-button>
<el-button @click="printReliabilityReport">打印</el-button>
</div>
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 数据质量评估模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'delivery-quality'">
<h2 class="module-title">数据质量评估</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-select v-model="selectedProject" placeholder="请选择项目" style="width: 300px;">
<el-option
v-for="project in projects"
:key="project.id"
:label="project.name"
:value="project.id"
></el-option>
</el-select>
<el-button type="primary" @click="generateQualityReport" style="margin-left: 20px;">生成质量报告</el-button>
</div>
<div v-if="qualityData" class="chart-container" ref="qualityChart"></div>
<div v-if="qualityData" style="margin-top: 20px;">
<h3>质量评估指标</h3>
<el-table :data="qualityData.metrics" border style="width: 100%">
<el-table-column prop="name" label="指标名称" width="180"></el-table-column>
<el-table-column prop="score" label="评分" width="120">
<template slot-scope="scope">
<el-progress :percentage="scope.row.score" :status="scope.row.score >= 90 ? 'success' : scope.row.score >= 70 ? '' : 'exception'"></el-progress>
</template>
</el-table-column>
<el-table-column prop="description" label="说明"></el-table-column>
</el-table>
</div>
<div v-if="qualityData" style="margin-top: 20px;">
<h3>改进建议</h3>
<el-card shadow="never">
<div v-html="qualityData.suggestions"></div>
</el-card>
</div>
<div v-if="qualityData" style="margin-top: 20px;">
<el-button type="primary" @click="exportQualityReport">导出质量报告</el-button>
<el-button @click="printQualityReport">打印</el-button>
</div>
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 质量入库模块 -->
2025-07-08 10:16:29 +08:00
<div v-if="activeModule === 'delivery-storage'">
<h2 class="module-title">质量入库</h2>
<div class="card-container">
<el-steps :active="storageStep" finish-status="success" style="margin-bottom: 20px;">
<el-step title="选择项目"></el-step>
<el-step title="数据验证"></el-step>
<el-step title="质量检查"></el-step>
<el-step title="入库完成"></el-step>
</el-steps>
<div v-if="storageStep === 0">
<h3>选择要入库的项目</h3>
<el-select v-model="selectedProject" placeholder="请选择项目" style="width: 300px;">
<el-option
v-for="project in projects"
:key="project.id"
:label="project.name"
:value="project.id"
></el-option>
</el-select>
<el-button type="primary" @click="checkProjectData" style="margin-left: 20px;">检查数据</el-button>
</div>
<div v-if="storageStep === 1">
<h3>数据验证结果</h3>
<el-table :data="storageCheckResults" border style="width: 100%; margin-bottom: 20px;">
<el-table-column prop="item" label="检查项" width="200"></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="message" label="说明"></el-table-column>
</el-table>
<el-button type="primary" @click="storageStep = 2" :disabled="!canProceedToNextStep">下一步</el-button>
</div>
<div v-if="storageStep === 2">
<h3>质量检查</h3>
<el-form :model="storageForm" label-width="120px">
<el-form-item label="数据质量评分">
<el-rate
v-model="storageForm.qualityScore"
show-text
:texts="['很差', '较差', '一般', '良好', '优秀']"
></el-rate>
</el-form-item>
<el-form-item label="入库备注">
<el-input type="textarea" v-model="storageForm.notes"></el-input>
</el-form-item>
<el-form-item label="入库位置">
<el-select v-model="storageForm.storageLocation" placeholder="请选择入库位置">
<el-option label="主数据库" value="main"></el-option>
<el-option label="历史数据库" value="history"></el-option>
<el-option label="测试数据库" value="test"></el-option>
</el-select>
</el-form-item>
</el-form>
2025-07-25 17:57:25 +08:00
<el-button @click="storageStep = 1">上一步</el-button>
<el-button type="primary" @click="completeStorage">完成入库</el-button>
</div>
<div v-if="storageStep === 3">
<el-result
icon="success"
title="入库成功"
subTitle="项目数据已成功入库"
>
<template slot="extra">
<el-button type="primary" size="medium" @click="viewStoredData">查看入库数据</el-button>
<el-button size="medium" @click="storageStep = 0">继续入库</el-button>
</template>
</el-result>
</div>
</div>
</div>
<!-- 净空距离模块 -->
<div v-if="activeModule === 'monitor-clearance'">
<h2 class="module-title">净空距离监测</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="monitorDate"
type="date"
placeholder="选择监测日期"
>
</el-date-picker>
<el-button type="primary" style="margin-left: 20px;" @click="searchMonitorData">查询</el-button>
<el-button @click="exportMonitorData">导出数据</el-button>
</div>
<div class="chart-container" ref="clearanceChart"></div>
<el-table :data="clearanceData" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="time" label="时间" width="180"></el-table-column>
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="distance" label="净空距离(m)" width="120"></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 label="操作" width="120">
<template slot-scope="scope">
<el-button size="mini" @click="viewClearanceDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 形变监测模块 -->
<div v-if="activeModule === 'monitor-deformation'">
<h2 class="module-title">形变监测</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="deformationDateRange"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
>
</el-date-picker>
<el-button type="primary" style="margin-left: 20px;" @click="searchDeformationData">查询</el-button>
<el-button @click="exportDeformationData">导出数据</el-button>
</div>
<div class="chart-container" ref="deformationChart"></div>
<el-table :data="deformationData" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="date" label="日期" width="150"></el-table-column>
<el-table-column prop="turbine" label="机组编号" width="120"></el-table-column>
<el-table-column prop="deformationX" label="X方向形变(mm)" width="150"></el-table-column>
<el-table-column prop="deformationY" label="Y方向形变(mm)" width="150"></el-table-column>
<el-table-column prop="deformationZ" label="Z方向形变(mm)" width="150"></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 label="操作" width="120">
<template slot-scope="scope">
<el-button size="mini" @click="viewDeformationDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 图像检测模块 -->
<div v-if="activeModule === 'monitor-image'">
<h2 class="module-title">图像检测</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-date-picker
v-model="imageDate"
type="date"
placeholder="选择监测日期"
>
</el-date-picker>
<el-button type="primary" style="margin-left: 20px;" @click="searchImageData">查询</el-button>
<el-button @click="exportImageData">导出数据</el-button>
</div>
<div style="display: flex; flex-wrap: wrap;">
<div class="monitor-card" style="width: calc(50% - 10px); margin-right: 20px; margin-bottom: 20px;" v-for="(item, index) in imageData" :key="index">
<div class="monitor-card-header">
{{ item.turbine }} - {{ item.time }}
<el-tag :type="item.status === '正常' ? 'success' : 'danger'" size="small">
{{ item.status }}
</el-tag>
</div>
<div class="monitor-card-body">
<img :src="item.image" style="width: 100%; cursor: pointer;" @click="previewImage(item.image)">
<div style="margin-top: 10px;">
<el-tag v-for="(defect, i) in item.defects" :key="i" style="margin-right: 5px; margin-bottom: 5px;">
{{ defect.type }} ({{ defect.severity }})
</el-tag>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 航线规划模块 -->
<div v-if="activeModule === 'others-route'">
<h2 class="module-title">航线规划</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="createNewRoute">新建航线</el-button>
<el-button icon="el-icon-refresh" @click="refreshRoutes">刷新</el-button>
<el-button icon="el-icon-download" @click="exportRoutes">导出航线</el-button>
</div>
<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="routeSearch"
clearable
style="margin-bottom: 15px;"
>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<el-table :data="filteredRoutes" border style="width: 100%">
<el-table-column prop="name" label="航线名称"></el-table-column>
<el-table-column label="操作" width="80">
<template slot-scope="scope">
<el-button size="mini" @click="viewRoute(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div style="flex: 1; padding-left: 20px;">
<div v-if="currentRoute" style="margin-bottom: 20px;">
<h3>{{ currentRoute.name }}</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="创建人">{{ currentRoute.creator }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ currentRoute.createTime }}</el-descriptions-item>
<el-descriptions-item label="适用机型">{{ currentRoute.droneType }}</el-descriptions-item>
<el-descriptions-item label="航线长度">{{ currentRoute.length }} km</el-descriptions-item>
<el-descriptions-item label="预计耗时">{{ currentRoute.duration }} min</el-descriptions-item>
</el-descriptions>
<div style="margin-top: 20px;">
<img :src="currentRoute.preview" style="width: 100%; border: 1px solid #ebeef5;">
</div>
</div>
<div v-else style="height: 500px; display: flex; align-items: center; justify-content: center; color: #909399;">
请从左侧选择航线查看详情
</div>
</div>
</div>
</div>
</div>
<!-- 三维模型模块 -->
<div v-if="activeModule === 'others-model'">
<h2 class="module-title">三维模型</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="uploadModel">上传模型</el-button>
<el-button icon="el-icon-refresh" @click="refreshModels">刷新</el-button>
<el-button icon="el-icon-download" @click="exportModels">导出模型</el-button>
2025-07-08 10:16:29 +08:00
</div>
2025-07-25 17:57:25 +08:00
<div style="display: flex; flex-wrap: wrap;">
<div class="monitor-card" style="width: calc(33.33% - 14px); margin-right: 20px; margin-bottom: 20px;" v-for="(model, index) in models" :key="index">
<div class="monitor-card-header">
{{ model.name }}
<el-tag size="small" style="float: right;">{{ model.type }}</el-tag>
</div>
<div class="monitor-card-body">
<img :src="model.thumbnail" style="width: 100%; height: 180px; object-fit: cover; cursor: pointer;" @click="viewModel(model)">
<div style="margin-top: 10px;">
<el-button size="mini" @click="viewModel(model)">查看</el-button>
<el-button size="mini" type="primary" @click="editModel(model)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteModel(model)">删除</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 报告模板库模块 -->
<div v-if="activeModule === 'others-template'">
<h2 class="module-title">报告模板库</h2>
<div class="card-container">
<div style="margin-bottom: 20px;">
<el-input
placeholder="搜索模板名称"
v-model="templateSearch"
clearable
style="width: 300px;"
2025-07-08 10:16:29 +08:00
>
2025-07-25 17:57:25 +08:00
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<el-button type="primary" style="margin-left: 20px;" icon="el-icon-plus" @click="createTemplate">新建模板</el-button>
2025-07-08 10:16:29 +08:00
</div>
2025-07-25 17:57:25 +08:00
<el-table :data="filteredTemplates" border style="width: 100%">
<el-table-column prop="name" label="模板名称" width="200"></el-table-column>
<el-table-column prop="type" label="模板类型" width="120"></el-table-column>
<el-table-column prop="creator" label="创建人" width="120"></el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button size="mini" @click="viewTemplate(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editTemplate(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteTemplate(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
2025-07-08 10:16:29 +08:00
</div>
</div>
2025-07-25 17:57:25 +08:00
<!-- 系统设置模块 -->
<div v-if="activeModule === 'others-settings'">
<h2 class="module-title">系统设置</h2>
2025-07-08 10:16:29 +08:00
<div class="card-container">
2025-07-25 17:57:25 +08:00
<el-tabs v-model="settingsTab">
<el-tab-pane label="个人设置" name="personal">
2025-07-08 10:16:29 +08:00
<el-form :model="settingsForm" label-width="120px">
<el-form-item label="主题颜色">
<el-color-picker v-model="settingsForm.themeColor"></el-color-picker>
</el-form-item>
<el-form-item label="通知方式">
<el-checkbox-group v-model="settingsForm.notificationMethods">
<el-checkbox label="站内消息" name="type"></el-checkbox>
<el-checkbox label="电子邮件" name="type"></el-checkbox>
<el-checkbox label="手机短信" name="type"></el-checkbox>
</el-checkbox-group>
</el-form-item>
2025-07-25 17:57:25 +08:00
<el-form-item>
<el-button type="primary" @click="saveSettings">保存设置</el-button>
<el-button @click="resetSettings">恢复默认</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="系统维护" name="system">
<el-form :model="systemForm" label-width="120px">
2025-07-08 10:16:29 +08:00
<el-form-item label="数据备份">
2025-07-25 17:57:25 +08:00
<el-switch v-model="systemForm.autoBackup"></el-switch>
<span style="margin-left: 10px; color: #909399;">{{ systemForm.autoBackup ? '已开启' : '已关闭' }}</span>
2025-07-08 10:16:29 +08:00
</el-form-item>
<el-form-item label="备份频率">
2025-07-25 17:57:25 +08:00
<el-select v-model="systemForm.backupFrequency" :disabled="!systemForm.autoBackup">
2025-07-08 10:16:29 +08:00
<el-option label="每天" value="daily"></el-option>
<el-option label="每周" value="weekly"></el-option>
<el-option label="每月" value="monthly"></el-option>
</el-select>
</el-form-item>
2025-07-25 17:57:25 +08:00
<el-form-item label="备份目录">
<el-input v-model="systemForm.backupPath"></el-input>
</el-form-item>
2025-07-08 10:16:29 +08:00
<el-form-item>
2025-07-25 17:57:25 +08:00
<el-button type="primary" @click="backupNow">立即备份</el-button>
<el-button @click="restoreBackup">恢复备份</el-button>
2025-07-08 10:16:29 +08:00
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="关于系统" name="about">
<div style="text-align: center; margin: 30px 0;">
<h3>风电叶片检查智能管理平台</h3>
<p style="margin: 20px 0; color: #606266;">版本: 2.1.0</p>
<p style="margin: 20px 0; color: #606266;">© 2023 风电科技股份有限公司 版权所有</p>
<img src="https://via.placeholder.com/200x100?text=Company+Logo" style="max-width: 200px;">
</div>
<div style="margin-top: 30px;">
<h4>系统依赖</h4>
<el-table :data="dependencies" border style="width: 100%">
<el-table-column prop="name" label="名称" width="200"></el-table-column>
<el-table-column prop="version" label="版本" width="120"></el-table-column>
<el-table-column prop="license" label="许可证"></el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<!-- 图片预览对话框 -->
<el-dialog :visible.sync="imagePreviewDialogVisible" class="image-preview-dialog">
<img :src="previewImageUrl" class="full-image">
</el-dialog>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
isCollapse: false,
activeModule: 'dashboard',
2025-07-25 17:57:25 +08:00
dashboardTab: 'projects',
projectTab: 'all',
constructionTab: 'turbines',
2025-07-08 10:16:29 +08:00
standardTab: 'defect',
lifecycleTab: 'turbine',
2025-07-25 17:57:25 +08:00
settingsTab: 'personal',
2025-07-08 10:16:29 +08:00
// 用户信息
userInfo: {
name: '张工程师',
role: '施工组长'
},
// 工作台数据
2025-07-25 17:57:25 +08:00
myProjects: [
{ id: 1, name: 'A风场2023年检查', turbineCount: 15, startDate: '2023-10-01', endDate: '2023-12-31', status: '进行中', progress: 65 },
{ id: 2, name: 'B风场维修项目', turbineCount: 8, startDate: '2023-09-15', endDate: '2023-11-30', status: '进行中', progress: 45 }
],
ongoingTasks: [
2025-07-08 10:16:29 +08:00
{ name: 'A风场1-5号机组检查', project: 'A风场2023年检查', progress: 65, status: 'success', deadline: '2023-11-15' },
2025-07-25 17:57:25 +08:00
{ name: 'B风场缺陷修复', project: 'B风场维修项目', progress: 30, status: 'exception', deadline: '2023-11-20' }
2025-07-08 10:16:29 +08:00
],
2025-07-25 17:57:25 +08:00
pendingReports: [
{ name: 'A风场2号机组检查报告', project: 'A风场2023年检查', turbine: 'A-002', submitter: '张工程师', submitTime: '2023-11-06 14:30' }
2025-07-08 10:16:29 +08:00
],
2025-07-25 17:57:25 +08:00
defectCount: 8,
2025-07-08 10:16:29 +08:00
notifications: [
{ title: '新任务分配', content: '您有新的检查任务: D风场初步检查', time: '2023-11-08 08:00', action: '查看' },
{ title: '报告审核通过', content: '您提交的A风场检查报告已通过审核', time: '2023-11-07 17:30' },
{ title: '系统更新', content: '系统将于今晚23:00进行维护更新预计耗时2小时', time: '2023-11-07 15:20' }
],
// 项目列表数据
projectSearch: '',
projects: [
{ id: 1, name: 'A风场2023年检查', turbineCount: 15, startDate: '2023-10-01', endDate: '2023-12-31', status: '进行中', progress: 65 },
{ id: 2, name: 'B风场维修项目', turbineCount: 8, startDate: '2023-09-15', endDate: '2023-11-30', status: '进行中', progress: 45 },
{ id: 3, name: 'C风场年度检查', turbineCount: 20, startDate: '2023-08-01', endDate: '2023-10-31', status: '已完成', progress: 100 },
{ id: 4, name: 'D风场初步检查', turbineCount: 12, startDate: '2023-11-10', endDate: '2023-12-15', status: '未开始', progress: 0 }
],
2025-07-25 17:57:25 +08:00
pendingProjects: [
{ id: 4, name: 'D风场初步检查', turbineCount: 12, startDate: '2023-11-10', endDate: '2023-12-15', status: '未开始', progress: 0 }
],
// 我的施工数据
currentProject: null,
turbineSearch: '',
turbines: [
{ id: 1, code: 'A-001', type: '2.5MW', location: 'A风场东区', status: '施工中', progress: 80 },
{ id: 2, code: 'A-002', type: '2.5MW', location: 'A风场东区', status: '待施工', progress: 0 },
{ id: 3, code: 'A-003', type: '2.5MW', location: 'A风场西区', status: '已完成', progress: 100 }
],
constructionReports: [
{ turbine: 'A-001', date: '2023-11-05', inspector: '张工程师', defectCount: 2, status: '已提交' },
{ turbine: 'A-003', date: '2023-11-07', inspector: '李技术员', defectCount: 0, status: '草稿' }
],
recordDateRange: '',
constructionRecords: [
{ date: '2023-11-05', turbine: 'A-001', type: '检查', content: '完成1号机组检查发现2处缺陷', operator: '张工程师' },
{ date: '2023-11-06', turbine: 'A-001', type: '维修', content: '修复1号机组前缘腐蚀', operator: '王维修' },
{ date: '2023-11-07', turbine: 'A-003', type: '检查', content: '完成3号机组检查未发现缺陷', operator: '李技术员' }
],
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
2025-07-08 10:16:29 +08:00
// 数据入库
currentBlade: {
position: '1号机组',
type: 'A型'
},
fileList: [],
uploadedData: [
{ name: 'IMG_20231105_1430.jpg', type: 'image', size: '3.2MB', time: '2023-11-05 14:32', status: '成功' },
{ name: 'VID_20231106_0915.mp4', type: 'video', size: '45.6MB', time: '2023-11-06 09:18', status: '成功' },
{ name: 'IMG_20231107_1645.jpg', type: 'image', size: '2.8MB', time: '2023-11-07 16:48', status: '成功' }
],
2025-07-25 17:57:25 +08:00
// 数据预处理
preprocessStep: 0,
selectedProject: null,
preprocessData: [
{ name: 'IMG_20231105_1430.jpg', type: 'image', size: '3.2MB', time: '2023-11-05 14:32' },
{ name: 'VID_20231106_0915.mp4', type: 'video', size: '45.6MB', time: '2023-11-06 09:18' }
],
preprocessForm: {
imageOptions: ['去噪', '增强'],
videoOptions: ['关键帧提取'],
outputFormat: 'jpg',
outputPath: '/output/processed'
},
preprocessProgress: 0,
preprocessStatus: '准备预处理...',
preprocessRunning: false,
2025-07-08 10:16:29 +08:00
// 缺陷检测算法
detectionRunning: false,
detectionProgress: 0,
detectionStatus: '正在初始化...',
defectDetectionResults: [
{
type: '前缘腐蚀',
position: '1号机组-2号叶片-叶尖',
size: '15x8mm',
severity: '高',
images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+1', full: 'https://via.placeholder.com/800x600?text=Defect+1' },
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+2', full: 'https://via.placeholder.com/800x600?text=Defect+2' }
]
},
{
type: '后缘开裂',
position: '2号机组-3号叶片-中部',
size: '32x5mm',
severity: '中',
images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+3', full: 'https://via.placeholder.com/800x600?text=Defect+3' }
]
}
],
// 树状可视化管理
treeSearch: '',
treeData: [
{
id: 1,
label: 'A风场',
type: 'farm',
children: [
{
id: 101,
label: '1号机组',
type: 'turbine',
status: '正常',
lastInspection: '2023-11-05',
defects: 2,
children: [
{ id: 1011, label: '1号叶片', type: 'blade', status: '正常', images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Blade+1', full: 'https://via.placeholder.com/800x600?text=Blade+1' }
]},
{ id: 1012, label: '2号叶片', type: 'blade', status: '缺陷', images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Blade+2', full: 'https://via.placeholder.com/800x600?text=Blade+2' },
{ thumbnail: 'https://via.placeholder.com/100x100?text=Blade+3', full: 'https://via.placeholder.com/800x600?text=Blade+3' }
]},
{ id: 1013, label: '3号叶片', type: 'blade', status: '正常', images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Blade+4', full: 'https://via.placeholder.com/800x600?text=Blade+4' }
]}
]
},
{
id: 102,
label: '2号机组',
type: 'turbine',
status: '正常',
lastInspection: '2023-11-06',
defects: 1,
children: [
{ id: 1021, label: '1号叶片', type: 'blade', status: '正常' },
{ id: 1022, label: '2号叶片', type: 'blade', status: '正常' },
{ id: 1023, label: '3号叶片', type: 'blade', status: '缺陷' }
]
}
]
}
],
treeProps: {
label: 'label',
children: 'children'
},
selectedTreeNode: null,
// 标准信息库
defectStandards: [
{ code: 'DEF001', name: '前缘腐蚀', category: '腐蚀类', severity: '高', description: '叶片前缘因风沙侵蚀导致的材料损失' },
{ code: 'DEF002', name: '后缘开裂', category: '裂纹类', severity: '中', description: '叶片后缘出现的结构性裂纹' },
{ code: 'DEF003', name: '表面划痕', category: '损伤类', severity: '低', description: '叶片表面因外力导致的划痕' }
],
inspectionStandards: [
{ code: 'INS001', name: '前缘检查标准', category: '前缘', method: '目视+无人机', description: '前缘腐蚀、损伤的检查标准' },
{ code: 'INS002', name: '后缘检查标准', category: '后缘', method: '目视+敲击', description: '后缘开裂、分层的检查标准' }
],
repairStandards: [
{ code: 'REP001', name: '前缘腐蚀修复', defectType: '前缘腐蚀', method: '打磨+补漆', description: '前缘腐蚀的标准修复流程', material: '环氧树脂,玻璃纤维布' },
{ code: 'REP002', name: '后缘开裂修复', defectType: '后缘开裂', method: '注胶+加压', description: '后缘开裂的标准修复流程', material: '结构胶,夹具' }
],
// 报告生成
reportList: [
{ name: 'A风场1号机组检查报告', project: 'A风场2023年检查', turbine: 'A-001', date: '2023-11-05', status: '已发布' },
{ name: 'A风场2号机组检查报告', project: 'A风场2023年检查', turbine: 'A-002', date: '2023-11-06', status: '待审核' },
{ name: 'B风场1号机组检查报告', project: 'B风场维修项目', turbine: 'B-001', date: '2023-10-28', status: '已发布' }
],
currentReport: null,
reportPreviewDialogVisible: false,
// 全生命周期管理
turbineSearch: '',
bladeSearch: '',
turbines: [
{ code: 'A-001', name: 'A风场1号机组', type: '2.5MW', location: 'A风场东区', installDate: '2020-05-15', status: '运行中', totalPower: '12500000' },
{ code: 'A-002', name: 'A风场2号机组', type: '2.5MW', location: 'A风场东区', installDate: '2020-05-20', status: '运行中', totalPower: '11800000' },
{ code: 'B-001', name: 'B风场1号机组', type: '3.0MW', location: 'B风场西区', installDate: '2019-08-10', status: '维修中', totalPower: '9800000' }
],
blades: [
{ code: 'A-001-1', turbine: 'A-001', position: '1号', type: 'B58', manufacturer: '风电科技', status: '正常' },
{ code: 'A-001-2', turbine: 'A-001', position: '2号', type: 'B58', manufacturer: '风电科技', status: '缺陷' },
{ code: 'A-001-3', turbine: 'A-001', position: '3号', type: 'B58', manufacturer: '风电科技', status: '正常' },
{ code: 'B-001-1', turbine: 'B-001', position: '1号', type: 'C62', manufacturer: '绿色能源', status: '维修中' }
],
currentTurbine: null,
turbineLifecycleDialogVisible: false,
turbineLifecycleEvents: [
{ time: '2023-11-05', type: '检查', title: '年度例行检查', description: '发现2号叶片前缘腐蚀需后续维修', images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Inspection+1', full: 'https://via.placeholder.com/800x600?text=Inspection+1' }
]},
{ time: '2023-08-12', type: '维修', title: '齿轮箱更换', description: '更换故障齿轮箱耗时3天', images: [] },
{ time: '2022-11-20', type: '检查', title: '年度例行检查', description: '机组运行状态良好,无重大缺陷', images: [] },
{ time: '2020-05-15', type: '投运', title: '机组投运', description: '新机组安装完成并投入运行', images: [] }
],
// 可靠性评估
reliabilityData: null,
// 数据质量评估
qualityData: null,
// 质量入库
storageCheckResults: [
{ item: '检查数据完整性', status: '通过', message: '所有必填字段均已填写' },
{ item: '图片质量检查', status: '通过', message: '所有图片清晰可辨' },
{ item: '视频质量检查', status: '通过', message: '视频完整且清晰' },
{ item: '报告一致性检查', status: '通过', message: '报告与数据一致' }
],
storageForm: {
qualityScore: 4,
notes: '',
storageLocation: 'main'
},
2025-07-25 17:57:25 +08:00
// 净空距离监测
monitorDate: '',
clearanceData: [
{ time: '2023-11-05 08:00', turbine: 'A-001', distance: '12.5', status: '正常' },
{ time: '2023-11-05 12:00', turbine: 'A-001', distance: '11.8', status: '正常' },
{ time: '2023-11-05 16:00', turbine: 'A-001', distance: '9.2', status: '警告' }
],
// 形变监测
deformationDateRange: '',
deformationData: [
{ date: '2023-11-05', turbine: 'A-001', deformationX: '2.1', deformationY: '1.5', deformationZ: '0.8', status: '正常' },
{ date: '2023-11-06', turbine: 'A-001', deformationX: '2.3', deformationY: '1.6', deformationZ: '0.9', status: '正常' },
{ date: '2023-11-07', turbine: 'A-001', deformationX: '3.5', deformationY: '2.8', deformationZ: '1.2', status: '警告' }
],
// 图像检测
imageDate: '',
imageData: [
{ turbine: 'A-001', time: '2023-11-05 08:00', image: 'https://via.placeholder.com/400x300?text=Turbine+A-001', status: '正常', defects: [] },
{ turbine: 'A-001', time: '2023-11-05 12:00', image: 'https://via.placeholder.com/400x300?text=Turbine+A-001', status: '缺陷', defects: [
{ type: '前缘腐蚀', severity: '中' }
]},
{ turbine: 'A-002', time: '2023-11-06 09:00', image: 'https://via.placeholder.com/400x300?text=Turbine+A-002', status: '正常', defects: [] }
],
// 航线规划
routeSearch: '',
routes: [
{ name: 'A风场东区航线', creator: '张工程师', createTime: '2023-10-15', droneType: 'DJI M300', length: '5.2', duration: '45', preview: 'https://via.placeholder.com/600x400?text=Route+Preview' },
{ name: 'B风场西区航线', creator: '李技术员', createTime: '2023-09-20', droneType: 'DJI M300', length: '6.8', duration: '60', preview: 'https://via.placeholder.com/600x400?text=Route+Preview' }
],
currentRoute: null,
// 三维模型
models: [
{ name: 'A型叶片模型', type: '叶片', thumbnail: 'https://via.placeholder.com/300x200?text=Blade+Model' },
{ name: '2.5MW机组模型', type: '机组', thumbnail: 'https://via.placeholder.com/300x200?text=Turbine+Model' },
{ name: 'B风场地形模型', type: '地形', thumbnail: 'https://via.placeholder.com/300x200?text=Terrain+Model' }
],
// 报告模板库
templateSearch: '',
templates: [
{ name: '标准检查报告模板', type: '检查报告', creator: '系统管理员', createTime: '2023-01-10', description: '标准的风电叶片检查报告模板' },
{ name: '维修报告模板', type: '维修报告', creator: '王维修', createTime: '2023-03-15', description: '风电叶片维修记录报告模板' },
{ name: '月度报告模板', type: '月度报告', creator: '李经理', createTime: '2023-05-20', description: '风电场月度运行报告模板' }
],
// 系统设置
2025-07-08 10:16:29 +08:00
settingsForm: {
themeColor: '#409EFF',
2025-07-25 17:57:25 +08:00
notificationMethods: ['站内消息', '电子邮件']
},
systemForm: {
2025-07-08 10:16:29 +08:00
autoBackup: true,
2025-07-25 17:57:25 +08:00
backupFrequency: 'weekly',
backupPath: '/backups'
2025-07-08 10:16:29 +08:00
},
dependencies: [
{ name: 'Vue.js', version: '2.6.14', license: 'MIT' },
{ name: 'Element UI', version: '2.15.9', license: 'MIT' },
{ name: 'ECharts', version: '5.3.2', license: 'Apache-2.0' }
],
// 图片预览
previewImageUrl: '',
imagePreviewDialogVisible: false
}
},
computed: {
filteredProjects() {
if (!this.projectSearch) return this.projects;
return this.projects.filter(project =>
project.name.toLowerCase().includes(this.projectSearch.toLowerCase())
);
},
filteredTurbines() {
if (!this.turbineSearch) return this.turbines;
return this.turbines.filter(turbine =>
2025-07-25 17:57:25 +08:00
turbine.code.toLowerCase().includes(this.turbineSearch.toLowerCase())
2025-07-08 10:16:29 +08:00
);
},
filteredBlades() {
if (!this.bladeSearch) return this.blades;
return this.blades.filter(blade =>
blade.code.toLowerCase().includes(this.bladeSearch.toLowerCase()) ||
blade.turbine.toLowerCase().includes(this.bladeSearch.toLowerCase())
);
},
2025-07-25 17:57:25 +08:00
filteredRoutes() {
if (!this.routeSearch) return this.routes;
return this.routes.filter(route =>
route.name.toLowerCase().includes(this.routeSearch.toLowerCase())
);
},
filteredTemplates() {
if (!this.templateSearch) return this.templates;
return this.templates.filter(template =>
template.name.toLowerCase().includes(this.templateSearch.toLowerCase()) ||
template.type.toLowerCase().includes(this.templateSearch.toLowerCase())
2025-07-08 10:16:29 +08:00
);
},
canProceedToNextStep() {
return this.storageCheckResults.every(item => item.status === '通过');
2025-07-25 17:57:25 +08:00
},
newProjectCount() {
return this.pendingProjects.length;
2025-07-08 10:16:29 +08:00
}
},
mounted() {
this.initCharts();
},
watch: {
treeSearch(val) {
this.$refs.tree.filter(val);
}
},
methods: {
toggleSidebar() {
this.isCollapse = !this.isCollapse;
},
2025-07-25 17:57:25 +08:00
switchModule(module) {
this.activeModule = module;
// 初始化模块数据
if (module === 'dashboard') {
this.$nextTick(() => {
this.initCharts();
});
}
},
logout() {
this.$confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '已退出登录'
});
// 实际应用中这里应该跳转到登录页面
}).catch(() => {
this.$message({
type: 'info',
message: '已取消退出'
});
});
},
// 工作台方法
initCharts() {
// 项目进度图表
if (this.$refs.projectChart) {
const projectChart = echarts.init(this.$refs.projectChart);
projectChart.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风场', 'D风场']
},
series: [
{
name: '已完成',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [320, 120, 200, 0],
itemStyle: {
color: '#67C23A'
}
},
{
name: '进行中',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [80, 180, 0, 0],
itemStyle: {
color: '#409EFF'
}
},
{
name: '未开始',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [0, 0, 0, 400],
itemStyle: {
color: '#909399'
}
}
]
});
}
// 净空距离图表
if (this.$refs.clearanceChart) {
const clearanceChart = echarts.init(this.$refs.clearanceChart);
clearanceChart.setOption({
title: {
text: '净空距离变化趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: this.clearanceData.map(item => item.time.split(' ')[1])
},
yAxis: {
type: 'value',
name: '距离(m)'
},
series: [{
data: this.clearanceData.map(item => item.distance),
type: 'line',
smooth: true,
markLine: {
silent: true,
data: [{
yAxis: 10,
name: '安全阈值',
lineStyle: {
color: '#F56C6C'
},
label: {
formatter: '安全阈值: 10m',
position: 'start'
}
}]
}
}]
});
}
// 形变监测图表
if (this.$refs.deformationChart) {
const deformationChart = echarts.init(this.$refs.deformationChart);
deformationChart.setOption({
title: {
text: '形变监测趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['X方向', 'Y方向', 'Z方向'],
bottom: 10
},
xAxis: {
type: 'category',
data: this.deformationData.map(item => item.date)
},
yAxis: {
type: 'value',
name: '形变量(mm)'
},
series: [
{
name: 'X方向',
data: this.deformationData.map(item => item.deformationX),
type: 'line',
smooth: true
},
{
name: 'Y方向',
data: this.deformationData.map(item => item.deformationY),
type: 'line',
smooth: true
},
{
name: 'Z方向',
data: this.deformationData.map(item => item.deformationZ),
type: 'line',
smooth: true
}
]
});
}
},
viewTaskDetail(task) {
this.$message(`查看任务: ${task.name}`);
},
handleNotification(notification) {
this.$message(`处理通知: ${notification.title}`);
},
// 项目列表方法
showProjectDialog() {
this.$message('显示新建项目对话框');
},
refreshProjects() {
this.$message('刷新项目列表');
},
viewProjectDetail(project) {
this.$message(`查看项目: ${project.name}`);
},
enterProject(project) {
this.currentProject = project;
this.activeModule = 'project-construction';
this.$message(`进入项目: ${project.name}`);
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
// 我的施工方法
startConstruction(turbine) {
this.$confirm(`确定要开始 ${turbine.code} 的施工吗?`, '提示', {
2025-07-08 10:16:29 +08:00
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
2025-07-25 17:57:25 +08:00
turbine.status = '施工中';
2025-07-08 10:16:29 +08:00
this.$message({
type: 'success',
2025-07-25 17:57:25 +08:00
message: '已开始施工'
});
// 记录施工台账
this.constructionRecords.unshift({
date: new Date().toLocaleDateString(),
turbine: turbine.code,
type: '施工',
content: `开始${turbine.code}的施工检查`,
operator: this.userInfo.name
2025-07-08 10:16:29 +08:00
});
}).catch(() => {
this.$message({
type: 'info',
2025-07-25 17:57:25 +08:00
message: '已取消操作'
2025-07-08 10:16:29 +08:00
});
});
},
2025-07-25 17:57:25 +08:00
completeConstruction(turbine) {
this.$confirm(`确定要完成 ${turbine.code} 的施工吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
turbine.status = '已完成';
turbine.progress = 100;
this.$message({
type: 'success',
message: '施工已完成'
});
// 记录施工台账
this.constructionRecords.unshift({
date: new Date().toLocaleDateString(),
turbine: turbine.code,
type: '施工',
content: `完成${turbine.code}的施工检查`,
operator: this.userInfo.name
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
2025-07-08 10:16:29 +08:00
});
2025-07-25 17:57:25 +08:00
},
viewTurbineDetail(turbine) {
this.$message(`查看机组详情: ${turbine.code}`);
},
createConstructionReport() {
this.$message('创建施工报告');
},
refreshReports() {
this.$message('刷新报告列表');
},
viewReport(report) {
this.currentReport = {
name: report.turbine + '施工报告',
project: this.currentProject.name,
turbine: report.turbine,
date: report.date,
inspector: report.inspector,
summary: `本次施工检查${report.defectCount > 0 ? '发现' + report.defectCount + '处缺陷' : '未发现缺陷'}`,
defects: report.defectCount > 0 ? [
2025-07-08 10:16:29 +08:00
{
2025-07-25 17:57:25 +08:00
code: 'DEF001',
name: '前缘腐蚀',
severity: '高',
description: '叶片前缘有明显腐蚀痕迹面积约15x8mm',
images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+1', full: 'https://via.placeholder.com/800x600?text=Defect+1' }
2025-07-08 10:16:29 +08:00
]
}
2025-07-25 17:57:25 +08:00
] : [],
conclusion: report.defectCount > 0 ? '机组存在缺陷,需要维修' : '机组状况良好',
recommendations: report.defectCount > 0 ? '建议在下次停机时进行维修处理' : '无需特别处理'
};
this.reportPreviewDialogVisible = true;
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
editReport(report) {
this.$message(`编辑报告: ${report.turbine}`);
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
deleteReport(report) {
this.$confirm(`确定要删除 ${report.turbine} 的报告吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.constructionReports = this.constructionReports.filter(item => item.turbine !== report.turbine);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
searchRecords() {
this.$message('查询施工台账');
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
exportRecords() {
this.$message('导出台账数据');
2025-07-08 10:16:29 +08:00
},
2025-07-25 17:57:25 +08:00
viewRecordDetail(record) {
this.$message(`查看台账详情: ${record.turbine} - ${record.type}`);
2025-07-08 10:16:29 +08:00
},
// 数据入库方法
handleFileChange(file, fileList) {
this.fileList = fileList;
},
beforeUpload(file) {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
this.$message.error('上传文件大小不能超过 10MB!');
}
return isLt10M;
},
handleRemove(file, fileList) {
this.fileList = fileList;
},
submitUpload() {
if (this.fileList.length === 0) {
this.$message.warning('请先选择要上传的文件');
return;
}
this.$message.success('开始上传文件');
// 模拟上传进度
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
clearInterval(interval);
this.$message.success('文件上传成功');
// 添加到已上传数据列表
this.fileList.forEach(file => {
this.uploadedData.unshift({
name: file.name,
type: this.activeDataType,
size: (file.size / 1024 / 1024).toFixed(1) + 'MB',
time: new Date().toLocaleString(),
status: '成功'
});
});
this.fileList = [];
}
}, 300);
},
clearFiles() {
this.fileList = [];
},
showBatchImportDialog() {
this.$message('显示批量导入对话框');
},
previewFile(file) {
2025-07-25 17:57:25 +08:00
this.previewImageUrl = 'https://via.placeholder.com/800x600?text=' + file.name.split('.')[0];
this.imagePreviewDialogVisible = true;
2025-07-08 10:16:29 +08:00
},
deleteFile(file) {
this.$confirm(`确定要删除文件 ${file.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.uploadedData = this.uploadedData.filter(item => item.name !== file.name);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
2025-07-25 17:57:25 +08:00
// 数据预处理方法
startPreprocess() {
this.preprocessRunning = true;
this.preprocessStatus = '正在预处理数据...';
const interval = setInterval(() => {
this.preprocessProgress += Math.random() * 10;
if (this.preprocessProgress >= 100) {
clearInterval(interval);
this.preprocessStatus = '预处理完成';
this.preprocessStep = 3;
this.preprocessRunning = false;
this.$message.success('数据预处理完成');
}
}, 500);
},
viewPreprocessResults() {
this.$message('查看预处理结果');
this.preprocessStep = 0;
},
2025-07-08 10:16:29 +08:00
// 缺陷检测算法方法
runDefectDetection() {
this.detectionRunning = true;
this.detectionProgress = 0;
this.detectionStatus = '正在初始化...';
const statusMessages = [
'加载图像数据...',
'预处理图像...',
'运行缺陷检测算法...',
'分析检测结果...',
'生成报告...'
];
const interval = setInterval(() => {
this.detectionProgress += Math.random() * 10;
if (this.detectionProgress >= 100) {
clearInterval(interval);
this.detectionStatus = '检测完成';
setTimeout(() => {
this.detectionRunning = false;
this.$message.success('缺陷检测完成');
}, 500);
} else {
const statusIndex = Math.min(
Math.floor(this.detectionProgress / 20),
statusMessages.length - 1
);
this.detectionStatus = statusMessages[statusIndex];
}
}, 500);
},
showDetectionSettings() {
this.$message('显示检测设置对话框');
},
exportDetectionResults() {
this.$message('导出检测结果');
},
previewDefectImages(defect) {
this.previewImageUrl = defect.images[0].full;
this.imagePreviewDialogVisible = true;
},
markAsFalseAlarm(defect) {
this.$confirm(`确定要将 ${defect.type} 标记为误报吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.defectDetectionResults = this.defectDetectionResults.filter(item => item !== defect);
this.$message({
type: 'success',
message: '已标记为误报!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
// 树状可视化管理方法
filterTreeNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
handleTreeNodeClick(data) {
this.selectedTreeNode = data;
},
previewImage(url) {
2025-07-25 17:57:25 +08:00
this.previewImageUrl = typeof url === 'string' ? url : url.full;
2025-07-08 10:16:29 +08:00
this.imagePreviewDialogVisible = true;
},
// 标准信息库方法
showAddStandardDialog(type) {
this.$message(`显示新增${type === 'defect' ? '缺陷' : type === 'inspection' ? '检查' : '维修'}标准对话框`);
},
exportStandards(type) {
this.$message(`导出${type === 'defect' ? '缺陷' : type === 'inspection' ? '检查' : '维修'}标准`);
},
editStandard(standard) {
this.$message(`编辑标准: ${standard.name}`);
},
deleteStandard(standard) {
this.$confirm(`确定要删除标准 ${standard.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.standardTab === 'defect') {
this.defectStandards = this.defectStandards.filter(item => item.code !== standard.code);
} else if (this.standardTab === 'inspection') {
this.inspectionStandards = this.inspectionStandards.filter(item => item.code !== standard.code);
} else {
this.repairStandards = this.repairStandards.filter(item => item.code !== standard.code);
}
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
// 报告生成方法
generateNewReport() {
this.$message('生成新报告');
},
refreshReportsList() {
this.$message('刷新报告列表');
},
exportAllReports() {
this.$message('导出全部报告');
},
viewReport(report) {
this.currentReport = {
name: report.name,
project: report.project,
turbine: report.turbine,
date: report.date,
inspector: '张工程师',
summary: '本次检查共发现3处缺陷其中1处为严重缺陷需要尽快处理。',
defects: [
{
code: 'DEF001',
name: '前缘腐蚀',
severity: '高',
description: '叶片前缘有明显腐蚀痕迹面积约15x8mm',
images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+1', full: 'https://via.placeholder.com/800x600?text=Defect+1' }
]
},
{
code: 'DEF002',
name: '后缘开裂',
severity: '中',
description: '叶片后缘有细小裂纹长度约32mm',
images: [
{ thumbnail: 'https://via.placeholder.com/100x100?text=Defect+2', full: 'https://via.placeholder.com/800x600?text=Defect+2' }
]
}
],
conclusion: '机组整体状况良好,但存在两处需要关注的缺陷。',
recommendations: '建议在下次停机时对1号叶片前缘腐蚀进行修复处理并持续观察2号叶片后缘开裂情况。'
};
this.reportPreviewDialogVisible = true;
},
editReport(report) {
this.$message(`编辑报告: ${report.name}`);
},
publishReport(report) {
this.$confirm(`确定要发布报告 ${report.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
report.status = '已发布';
this.$message({
type: 'success',
message: '报告已发布!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消发布'
});
});
},
deleteReport(report) {
this.$confirm(`确定要删除报告 ${report.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.reportList = this.reportList.filter(item => item.name !== report.name);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
downloadReport(report) {
this.$message(`下载报告: ${report.name}`);
this.reportPreviewDialogVisible = false;
},
// 全生命周期管理方法
viewTurbineLifecycle(turbine) {
this.currentTurbine = turbine;
this.turbineLifecycleDialogVisible = true;
},
viewBladeLifecycle(blade) {
this.$message(`查看叶片生命周期: ${blade.code}`);
},
// 可靠性评估方法
generateReliabilityReport() {
if (!this.selectedProject) {
this.$message.warning('请先选择项目');
return;
}
this.reliabilityData = {
project: this.projects.find(p => p.id === this.selectedProject).name,
score: 82,
details: [
{ turbine: 'A-001', score: 85, defectCount: 2, severity: '中', recommendation: '建议在下次停机时修复前缘腐蚀' },
{ turbine: 'A-002', score: 90, defectCount: 1, severity: '低', recommendation: '持续观察后缘开裂情况' },
{ turbine: 'A-003', score: 95, defectCount: 0, severity: '无', recommendation: '机组状况良好,无需特别处理' }
]
};
// 初始化图表
this.$nextTick(() => {
const chart = echarts.init(this.$refs.reliabilityChart);
chart.setOption({
title: {
text: '机组可靠性评分',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
max: 100,
axisLabel: {
formatter: '{value} 分'
}
},
yAxis: {
type: 'category',
data: this.reliabilityData.details.map(item => item.turbine)
},
series: [
{
name: '可靠性评分',
type: 'bar',
data: this.reliabilityData.details.map(item => ({
value: item.score,
itemStyle: {
color: item.score >= 90 ? '#67C23A' : item.score >= 70 ? '#409EFF' : '#F56C6C'
}
})),
label: {
show: true,
position: 'right',
formatter: '{c} 分'
}
}
]
});
});
},
exportReliabilityReport() {
this.$message('导出可靠性评估报告');
},
printReliabilityReport() {
this.$message('打印可靠性评估报告');
},
// 数据质量评估方法
generateQualityReport() {
if (!this.selectedProject) {
this.$message.warning('请先选择项目');
return;
}
this.qualityData = {
project: this.projects.find(p => p.id === this.selectedProject).name,
score: 88,
metrics: [
{ name: '数据完整性', score: 95, description: '所有必填字段均已填写' },
{ name: '数据准确性', score: 90, description: '数据与实际情况基本一致' },
{ name: '数据一致性', score: 85, description: '各数据源之间无明显矛盾' },
{ name: '数据及时性', score: 80, description: '数据采集和录入略有延迟' },
{ name: '数据可用性', score: 90, description: '数据可直接用于分析和决策' }
],
suggestions: `
<p><strong>改进建议:</strong></p>
<ul>
<li>加强数据采集的及时性尽量在检查完成后24小时内完成数据录入</li>
<li>对检查人员进行数据一致性培训,减少不同人员记录方式的差异</li>
<li>建立数据质量检查机制,在数据入库前进行自动校验</li>
</ul>
`
};
// 初始化图表
this.$nextTick(() => {
const chart = echarts.init(this.$refs.qualityChart);
chart.setOption({
title: {
text: '数据质量评估雷达图',
left: 'center'
},
tooltip: {},
radar: {
indicator: this.qualityData.metrics.map(item => ({
name: item.name,
max: 100
})),
radius: '65%'
},
series: [{
type: 'radar',
data: [{
value: this.qualityData.metrics.map(item => item.score),
name: '数据质量评分',
areaStyle: {
color: 'rgba(64, 158, 255, 0.5)'
}
}]
}]
});
});
},
exportQualityReport() {
this.$message('导出数据质量评估报告');
},
printQualityReport() {
this.$message('打印数据质量评估报告');
},
// 质量入库方法
checkProjectData() {
if (!this.selectedProject) {
this.$message.warning('请先选择项目');
return;
}
this.storageStep = 1;
},
completeStorage() {
this.storageStep = 3;
this.$message.success('项目数据入库成功');
},
viewStoredData() {
this.$message('查看入库数据');
this.storageStep = 0;
},
2025-07-25 17:57:25 +08:00
// 净空距离监测方法
searchMonitorData() {
this.$message('查询净空距离数据');
},
exportMonitorData() {
this.$message('导出净空距离数据');
},
viewClearanceDetail(record) {
this.$message(`查看净空距离详情: ${record.turbine} - ${record.time}`);
},
// 形变监测方法
searchDeformationData() {
this.$message('查询形变监测数据');
},
exportDeformationData() {
this.$message('导出形变监测数据');
},
viewDeformationDetail(record) {
this.$message(`查看形变监测详情: ${record.turbine} - ${record.date}`);
},
// 图像检测方法
searchImageData() {
this.$message('查询图像检测数据');
},
exportImageData() {
this.$message('导出图像检测数据');
},
// 航线规划方法
createNewRoute() {
this.$message('创建新航线');
},
refreshRoutes() {
this.$message('刷新航线列表');
},
viewRoute(route) {
this.currentRoute = route;
},
exportRoutes() {
this.$message('导出航线数据');
},
// 三维模型方法
uploadModel() {
this.$message('上传三维模型');
},
refreshModels() {
this.$message('刷新模型列表');
},
viewModel(model) {
this.$message(`查看模型: ${model.name}`);
},
editModel(model) {
this.$message(`编辑模型: ${model.name}`);
},
deleteModel(model) {
this.$confirm(`确定要删除模型 ${model.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.models = this.models.filter(item => item.name !== model.name);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
exportModels() {
this.$message('导出三维模型');
},
// 报告模板库方法
createTemplate() {
this.$message('创建新模板');
},
viewTemplate(template) {
this.$message(`查看模板: ${template.name}`);
},
editTemplate(template) {
this.$message(`编辑模板: ${template.name}`);
},
deleteTemplate(template) {
this.$confirm(`确定要删除模板 ${template.name} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.templates = this.templates.filter(item => item.name !== template.name);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
2025-07-08 10:16:29 +08:00
// 系统设置方法
saveSettings() {
2025-07-25 17:57:25 +08:00
this.$message.success('设置已保存');
2025-07-08 10:16:29 +08:00
},
resetSettings() {
this.settingsForm = {
themeColor: '#409EFF',
2025-07-25 17:57:25 +08:00
notificationMethods: ['站内消息', '电子邮件']
2025-07-08 10:16:29 +08:00
};
this.$message.success('已恢复默认设置');
2025-07-25 17:57:25 +08:00
},
backupNow() {
this.$message.success('备份已开始');
},
restoreBackup() {
this.$message('恢复备份');
2025-07-08 10:16:29 +08:00
}
}
});
</script>
</body>
2025-07-25 17:57:25 +08:00
</html>