<template>
|
<div>
|
<HeaderBox :title="projectName" />
|
<div class="pt--35 px--20 pb--54">
|
<!-- 返回 -->
|
<div class="flex a-center">
|
<img
|
@click="backFun"
|
src="../../assets/img/back.png"
|
class="w--22 h--20 shrink0 mr--13 cursor-pointer"
|
/>
|
<div @click="backFun" class="c-fff lh--22 fs--22 SHSBold cursor-pointer">设备列表</div>
|
</div>
|
<!-- 搜索 -->
|
<div class="flex a-center gap--16 mt--24 mb--20">
|
<!-- 项目选择(委托方才显示) -->
|
<div
|
v-if="isClientMode"
|
class="flex a-center j-between pl--18 pr--15 h--40 relative w--300 bgcolor1 searchBox"
|
>
|
<el-select
|
filterable
|
v-model="projectId"
|
placeholder="请选择项目"
|
class="flex1"
|
@change="handleProjectChange"
|
>
|
<el-option
|
v-for="item in projectList"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
>
|
</el-option>
|
</el-select>
|
<div class="flex a-center gap--5">
|
<i
|
@click.stop="clearProjectSelect"
|
class="el-icon-circle-close c-fff cursor-pointer"
|
v-show="projectId"
|
/>
|
<img
|
src="../../assets/img/down-circle@2x.png"
|
class="w--15 h--16 shrink0"
|
/>
|
</div>
|
</div>
|
|
<div
|
class="flex a-center j-between pl--18 pr--15 h--40 relative w--300 bgcolor1 searchBox"
|
>
|
<el-select
|
filterable
|
v-model="type"
|
placeholder="请选择设备类型"
|
class="flex1"
|
@change="handleSearch"
|
>
|
<el-option
|
v-for="item in deviceTypeList"
|
:key="item.code"
|
:label="item.name"
|
:value="item.code"
|
>
|
</el-option>
|
</el-select>
|
<div class="flex a-center gap--5">
|
<i
|
@click.stop="clearDeviceType"
|
class="el-icon-circle-close c-fff cursor-pointer"
|
v-show="type"
|
/>
|
<img
|
src="../../assets/img/down-circle@2x.png"
|
class="w--15 h--16 shrink0"
|
/>
|
</div>
|
</div>
|
<div
|
class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker"
|
>
|
<el-date-picker
|
v-model="dateTime"
|
ref="datetime1"
|
type="datetime"
|
placeholder=""
|
@change="handleSearch"
|
clearable
|
>
|
</el-date-picker>
|
<div
|
@click.stop="handleClick(1)"
|
class="absolute h100 flex a-center c-fff fs--14 SHS pointer-events-none"
|
style="left: 20px; right: 60px; z-index: 1"
|
v-if="startTime"
|
>
|
{{ startTime }}
|
</div>
|
<div
|
@click.stop="handleClick(1)"
|
v-else
|
class="absolute h100 flex a-center c-fff fs--14 SHSR pointer-events-none"
|
style="left: 20px; right: 60px; z-index: 1"
|
>
|
请选择日期
|
</div>
|
<div
|
class="flex a-center gap--5"
|
style="position: relative; z-index: 2"
|
>
|
<i
|
@click.stop="clearStartTime"
|
class="el-icon-circle-close c-fff cursor-pointer"
|
v-show="dateTime"
|
/>
|
<img
|
@click="handleClick(1)"
|
src="../../assets/img/home/Shape@2x (6).png"
|
class="w--13 h--13 shrink0 cursor-pointer"
|
/>
|
</div>
|
</div>
|
<div
|
class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker"
|
>
|
<el-date-picker
|
v-model="dateTime2"
|
ref="datetime2"
|
type="datetime"
|
placeholder=""
|
@change="handleSearch"
|
clearable
|
>
|
</el-date-picker>
|
<div
|
@click="handleClick(2)"
|
class="absolute h100 flex a-center c-fff fs--14 SHS pointer-events-none"
|
style="left: 20px; right: 60px; z-index: 1"
|
v-if="endTime"
|
>
|
{{ endTime }}
|
</div>
|
<div
|
@click="handleClick(2)"
|
v-else
|
class="absolute h100 flex a-center c-fff fs--14 SHSR pointer-events-none"
|
style="left: 20px; right: 60px; z-index: 1"
|
>
|
请选择日期
|
</div>
|
<div
|
class="flex a-center gap--5"
|
style="position: relative; z-index: 2"
|
>
|
<i
|
@click.stop="clearEndTime"
|
class="el-icon-circle-close c-fff cursor-pointer"
|
v-show="dateTime2"
|
/>
|
<img
|
@click="handleClick(2)"
|
src="../../assets/img/home/Shape@2x (6).png"
|
class="w--13 h--13 shrink0 cursor-pointer"
|
/>
|
</div>
|
</div>
|
<div
|
class="flex a-center j-between w--300 pl--20 h--40 relative bgcolor1 searchBox"
|
>
|
<el-input
|
v-model="content"
|
placeholder="请输入搜索内容"
|
class="flex1"
|
clearable
|
@keyup.enter.native="handleSearch"
|
></el-input>
|
<div class="btn SHSR" @click="handleSearch">搜索</div>
|
</div>
|
</div>
|
<!-- 表格 -->
|
<CustomTable
|
:data="tableList"
|
height="800"
|
style="border: 2px solid #3d7fff"
|
>
|
<el-table-column
|
v-if="isClientMode"
|
label="项目名称"
|
prop="projectName"
|
/>
|
<el-table-column label="设备类型" prop="type">
|
<template #default="{ row }">
|
<div>{{ getDeviceTypeName(row.type) }}</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="设备编号" prop="name" />
|
<el-table-column label="设备出厂日期" prop="createTime">
|
<template #default="{ row }">
|
<div>
|
{{
|
getPropertyValue(row, "设备出厂日期") || row.createTime || "-"
|
}}
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="使用单位" prop="projectName">
|
<template #default="{ row }">
|
<div class="flex flex-column gap--4">
|
<div
|
v-for="(item, index) in getPropertyValues(row, '使用单位')"
|
:key="index"
|
>
|
{{ item }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="工程起重机械生产单位">
|
<template #default="{ row }">
|
<div class="flex flex-column gap--4">
|
<div
|
v-for="(item, index) in getPropertyValues(
|
row,
|
'工程起重机械生产单位'
|
)"
|
:key="index"
|
>
|
{{ item }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="工程起重机械租赁单位">
|
<template #default="{ row }">
|
<div class="flex flex-column gap--4">
|
<div
|
v-for="(item, index) in getPropertyValues(
|
row,
|
'工程起重机械租赁单位'
|
)"
|
:key="index"
|
>
|
{{ item }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="工程起重机械维保单位">
|
<template #default="{ row }">
|
<div class="flex flex-column gap--4">
|
<div
|
v-for="(item, index) in getPropertyValues(
|
row,
|
'工程起重机械维保单位'
|
)"
|
:key="index"
|
>
|
{{ item }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="工程起重机械安拆单位">
|
<template #default="{ row }">
|
<div class="flex flex-column gap--4">
|
<div
|
v-for="(item, index) in getPropertyValues(
|
row,
|
'工程起重机械安拆单位'
|
)"
|
:key="index"
|
>
|
{{ item }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="本月检查情况" prop="checkResult">
|
<template #default="{ row }">
|
<div class="flex a-center j-center">
|
<div class="ripple">
|
<div
|
class="ripple-wave wave-1"
|
:class="{ bgColor3: row.checkResult === '已检查' }"
|
></div>
|
<div
|
class="ripple-wave wave-2"
|
:class="{ bgColor4: row.checkResult === '已检查' }"
|
></div>
|
<div
|
class="ripple-wave wave-3"
|
:class="{ bgColor5: row.checkResult === '已检查' }"
|
></div>
|
</div>
|
<div class="ml--6 SHSR">{{ row.checkResult }}</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="最近一次检查时间" prop="checkTime" />
|
<el-table-column label="操作">
|
<template #default="{ row }">
|
<el-button
|
@click="toHiddenDanger(row, 3)"
|
type="text"
|
class="btnColor"
|
>隐患</el-button
|
>
|
<el-button
|
type="text"
|
class="btnColor"
|
@click="openDetailDialog(row)"
|
>详情</el-button
|
>
|
</template>
|
</el-table-column>
|
</CustomTable>
|
<CustomPagination
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange"
|
:current-page="currentPage"
|
:page-sizes="[10, 20, 50, 100]"
|
:page-size="pageSize"
|
:total="total"
|
/>
|
</div>
|
<!-- 项目列表弹窗 -->
|
<CustomDialog
|
title="检查计划列表"
|
:visible.sync="projectVisible"
|
width="1420px"
|
>
|
<CustomTable :data="detailCheckList" height="630">
|
<el-table-column label="检查计划" prop="planName" />
|
<el-table-column label="计划类型" prop="planType" >
|
<template #default="{ row }">
|
{{ formatPlanType(row.planType) }}
|
</template>
|
</el-table-column>
|
<el-table-column label="检测时间" prop="checkTime" />
|
<el-table-column label="分数" prop="score">
|
<template #default="{ row }">
|
<div>{{ getScoreDisplay(row) }}</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="风险类型" prop="riskLevel" v-if="shouldShowRiskLevel">
|
<template #default="{ row }">
|
{{ formatRiskLevel(row.riskLevel) }}
|
</template>
|
</el-table-column>
|
<el-table-column label="操作">
|
<template #default="{ row }">
|
<el-popover
|
trigger="click"
|
popper-class="custom-popper"
|
:disabled="!hasAnyReport(row)"
|
>
|
<template #reference>
|
<el-button
|
type="text"
|
:class="hasAnyReport(row) ? 'btnColor' : 'btnDisabled'"
|
:disabled="!hasAnyReport(row)"
|
@click="checkReportAvailability(row)"
|
>
|
查看报告
|
</el-button>
|
</template>
|
<div
|
class="h100 flex flex-column a-center j-between c-fff fs--16 SHSBold"
|
>
|
<div
|
@click="openReport('safe', row)"
|
class="py--12 w100 txt-center br--10 bgcolor6 report-btn"
|
:class="{
|
'report-disabled': row.containSystemRes !== 1,
|
'report-enabled': row.containSystemRes === 1,
|
}"
|
>
|
<span>安全报告</span>
|
<span v-if="row.containSystemRes !== 1" class="no-report-tag"
|
>(暂无)</span
|
>
|
</div>
|
<div
|
@click="openReport('device', row)"
|
class="py--12 w100 txt-center br--10 bgcolor7 report-btn"
|
:class="{
|
'report-disabled': row.containDeviceRes !== 1,
|
'report-enabled': row.containDeviceRes === 1,
|
}"
|
>
|
<span>设备报告</span>
|
<span v-if="row.containDeviceRes !== 1" class="no-report-tag"
|
>(暂无)</span
|
>
|
</div>
|
</div>
|
</el-popover>
|
</template>
|
</el-table-column>
|
</CustomTable>
|
<CustomPagination
|
@size-change="handleDetailSizeChange"
|
@current-change="handleDetailCurrentChange"
|
:current-page="detailCurrentPage"
|
:page-sizes="[10, 20, 50, 100]"
|
:page-size="detailPageSize"
|
:total="detailTotal"
|
/>
|
</CustomDialog>
|
<!-- 检查报告弹窗 -->
|
<CustomDialog
|
title="检查报告"
|
:visible.sync="reportVisible"
|
width="1420px"
|
@close="closeReportDialog"
|
>
|
<div style="height: 691px" class="flex flex-column" v-loading="loading">
|
<!-- PDF预览容器 -->
|
<div
|
v-if="showPreview && pdfUrl"
|
style="width: 100%; height: 100%; overflow: auto; background: #525659"
|
>
|
<iframe
|
:src="pdfUrl"
|
style="width: 100%; height: 100%; border: none"
|
frameborder="0"
|
></iframe>
|
</div>
|
<!-- 加载提示 -->
|
<div v-else class="flex flex-column a-center j-center h100">
|
<div class="c-fff fs--18 mb--20">正在加载报告...</div>
|
</div>
|
<!-- 底部操作按钮 -->
|
<div class="flex j-center gap--20 mt--20">
|
<el-button
|
type="primary"
|
@click="downloadReport"
|
:disabled="!reportBlobUrl"
|
>下载报告</el-button
|
>
|
<el-button @click="closeReportDialog">关闭</el-button>
|
</div>
|
</div>
|
</CustomDialog>
|
</div>
|
</template>
|
|
<script>
|
import HeaderBox from '../header/index.vue'
|
import moment from 'moment'
|
import {
|
deviceTypes,
|
getProjectList,
|
getDeviceListByType,
|
getDeviceDetailCheckListByType
|
} from './service.js'
|
import {
|
getFuzzyReportWord,
|
getScoreReportWord,
|
getFuzzyReportPdfUrl,
|
getScoreReportPdfUrl
|
} from '../planOfAudit/service'
|
export default {
|
components: { HeaderBox },
|
props: {},
|
data () {
|
return {
|
dateTime: null,
|
dateTime2: null,
|
projectId: null,
|
projectName: '',
|
dateTxt: '',
|
type: '', // 设备类型
|
startTime: '', // 开始时间
|
endTime: '', // 结束时间
|
content: '', // 搜索内容
|
currentPage: 1,
|
pageSize: 10,
|
total: 0,
|
projectVisible: false,
|
reportVisible: false,
|
reportUrl: '', // 已废弃,保留兼容性
|
reportBlobUrl: '', // Blob URL用于下载
|
pdfUrl: '', // PDF预览URL
|
loading: false,
|
showPreview: false, // 是否显示预览
|
deviceTypeList: [], // 设备类型列表
|
tableList: [], // 数据列表
|
// 详情弹窗相关数据
|
detailCheckList: [], // 设备详情检查列表
|
detailCurrentPage: 1,
|
detailPageSize: 10,
|
detailTotal: 0,
|
currentDeviceId: null, // 当前查看详情的设备ID
|
currentRow: null, // 当前选中的行数据
|
// 委托方相关
|
projectList: [], // 项目列表(委托方使用)
|
isClient: false, // 是否委托方端
|
|
pdfProjectName: '', // 项目名称用于报告下载
|
commentType: sessionStorage.getItem('commentType') // 评价类型
|
}
|
},
|
computed: {
|
// 是否是项目端(路由传入projectId参数)
|
isProjectMode () {
|
return !this.isClient
|
},
|
// 是否是委托方端(使用独立标识)
|
isClientMode () {
|
return this.isClient
|
},
|
// 是否显示风险类型列(commentType 不为 2 时显示)
|
shouldShowRiskLevel () {
|
return this.commentType !== '2'
|
}
|
},
|
watch: {
|
dateTime: {
|
handler (newVal, oldVal) {
|
if (newVal) {
|
this.startTime = moment(newVal).format('YYYY年MM月DD日 HH:mm:ss')
|
} else {
|
this.startTime = ''
|
}
|
},
|
immediate: true
|
},
|
dateTime2: {
|
handler (newVal, oldVal) {
|
if (newVal) {
|
this.endTime = moment(newVal).format('YYYY年MM月DD日 HH:mm:ss')
|
} else {
|
this.endTime = ''
|
}
|
},
|
immediate: true
|
},
|
// 监听弹窗关闭,重置详情数据
|
projectVisible (newVal) {
|
if (!newVal) {
|
this.detailCheckList = []
|
this.detailCurrentPage = 1
|
this.detailPageSize = 10
|
this.detailTotal = 0
|
this.currentDeviceId = null
|
}
|
}
|
},
|
created () {
|
// 判断是项目端还是委托方端
|
if (this.$route.query.projectId) {
|
this.projectId = this.$route.query.projectId
|
this.isClient = false // 项目端
|
} else {
|
this.isClient = true // 委托方端
|
this.loadProjectList() // 加载项目列表
|
}
|
|
if (this.$route.query.projectName) {
|
this.projectName = this.$route.query.projectName
|
}
|
|
// 接收从 home 页面传递过来的时间参数并回填
|
if (this.$route.query.startTime) {
|
this.dateTime = moment(this.$route.query.startTime).format(
|
'YYYY-MM-DD HH:mm:ss'
|
)
|
}
|
if (this.$route.query.endTime) {
|
this.dateTime2 = moment(this.$route.query.endTime).format(
|
'YYYY-MM-DD HH:mm:ss'
|
)
|
}
|
|
this.getTypeList()
|
this.getList()
|
},
|
mounted () {},
|
methods: {
|
// 加载项目列表(委托方使用)
|
async loadProjectList () {
|
try {
|
// 从 sessionStorage 获取 userInfo
|
const userInfoStr = sessionStorage.getItem('userInfo')
|
if (!userInfoStr) {
|
console.error('未找到用户信息')
|
return
|
}
|
const clientId = sessionStorage.getItem('clientId')
|
|
if (!clientId) {
|
console.error('用户信息中未找到 clientId')
|
return
|
}
|
|
const activeTabIndex = Number(sessionStorage.getItem('activeTab')) - 1
|
const res = await getProjectList({
|
clientId: clientId,
|
projectCategory: ['ongoing', 'fixed'][activeTabIndex]
|
})
|
if (res && res) {
|
this.projectList = res.map((item) => ({
|
label: item.name,
|
value: item.id
|
}))
|
}
|
} catch (error) {
|
console.error('加载项目列表失败:', error)
|
}
|
},
|
// 格式化计划类型
|
formatPlanType (type) {
|
const typeMap = {
|
0: '安全检查',
|
1: '质量检查',
|
2: '综合检查',
|
3: '设备检查',
|
4: '运营检查'
|
}
|
return typeMap[type] || '-'
|
},
|
// 格式化风险类型
|
formatRiskLevel (level) {
|
const levelMap = {
|
0: '拒绝接受风险',
|
1: '不可接受风险',
|
2: '可接受风险',
|
3: '可容忍风险',
|
4: '可忽略风险'
|
}
|
return levelMap[level] || '-'
|
},
|
|
// 项目选择改变(委托方使用)
|
handleProjectChange () {
|
// 选择项目后,只作为筛选条件,不改变页面标题
|
this.currentPage = 1
|
this.getList()
|
},
|
|
// 清除项目选择(委托方使用)
|
clearProjectSelect () {
|
this.projectId = null
|
this.currentPage = 1
|
this.getList()
|
},
|
|
// 清除设备类型
|
clearDeviceType () {
|
this.type = ''
|
this.handleSearch()
|
},
|
|
// 获取设备类型
|
getTypeList () {
|
deviceTypes().then((res) => {
|
this.deviceTypeList = res
|
})
|
},
|
// 根据设备类型下标获取设备类型名称
|
getDeviceTypeName (typeIndex) {
|
if (typeIndex === null || typeIndex === undefined || typeIndex === '') {
|
return '-'
|
}
|
// 如果 typeIndex 是数字下标,从设备类型列表中获取对应的名称
|
if (typeof typeIndex === 'number' && this.deviceTypeList[typeIndex]) {
|
return this.deviceTypeList[typeIndex].name
|
}
|
// 如果 typeIndex 是 code,从设备类型列表中查找对应的名称
|
const deviceType = this.deviceTypeList.find(
|
(item) => item.code === typeIndex
|
)
|
return deviceType ? deviceType.name : typeIndex
|
},
|
toHiddenDanger (row, type) {
|
this.$router.push({
|
path: '/hiddenDanger',
|
query: {
|
projectId: this.projectId || row.projectId,
|
projectName: this.projectName || row.projectName,
|
activeTab: type
|
}
|
})
|
},
|
getList () {
|
// 将 code 转换为下标
|
let typeIndex = ''
|
if (this.type) {
|
const index = this.deviceTypeList.findIndex(
|
(item) => item.code === this.type
|
)
|
typeIndex = index !== -1 ? index : ''
|
}
|
console.log(this.dateTime, this.dateTime2)
|
|
let params = {
|
projectId: this.projectId,
|
projectName: this.projectName,
|
content: this.content,
|
type: typeIndex, // 传递下标而不是 code
|
pageNum: this.currentPage,
|
pageSize: this.pageSize
|
}
|
if (this.dateTime) {
|
params.startTime = moment(this.dateTime).format('YYYY-MM-DD HH:mm:ss')
|
}
|
if (this.dateTime2) {
|
params.endTime = moment(this.dateTime2).format('YYYY-MM-DD HH:mm:ss')
|
}
|
if (this.isClient) {
|
params.clientId = sessionStorage.getItem('clientId')
|
params.category = sessionStorage.getItem('activeTab')
|
}
|
|
// 使用统一方法,根据用户类型调用对应接口
|
getDeviceListByType({ ...params }, this.isClient).then((res) => {
|
// 处理 properties 字段
|
this.tableList = res.records.map((item) => {
|
return {
|
...item,
|
parsedProperties: this.parseProperties(item.properties)
|
}
|
})
|
this.total = res.total
|
})
|
},
|
// 解析 properties 字段
|
parseProperties (properties) {
|
if (!properties) return []
|
try {
|
// 如果是字符串,先解析为数组
|
const propertiesArray =
|
typeof properties === 'string' ? JSON.parse(properties) : properties
|
// 确保返回的是数组
|
return Array.isArray(propertiesArray)
|
? propertiesArray
|
: [propertiesArray]
|
} catch (error) {
|
console.error('解析 properties 失败:', error)
|
return []
|
}
|
},
|
// 获取 properties 中某个字段的所有值(数组形式)
|
getPropertyValues (row, fieldName) {
|
if (!row.parsedProperties || row.parsedProperties.length === 0) {
|
return ['-']
|
}
|
// 从所有 properties 对象中提取指定字段的值
|
const values = row.parsedProperties
|
.map((prop) => prop[fieldName])
|
.filter((val) => val && val.trim() !== '') // 过滤空值
|
|
return values.length > 0 ? values : ['-']
|
},
|
// 获取 properties 中某个字段的第一个值(单个值)
|
getPropertyValue (row, fieldName) {
|
if (!row.parsedProperties || row.parsedProperties.length === 0) {
|
return '-'
|
}
|
// 从第一个 properties 对象中获取指定字段的值
|
const value = row.parsedProperties[0][fieldName]
|
return value && value.trim() !== '' ? value : '-'
|
},
|
|
handleSizeChange (val) {
|
this.pageSize = val
|
this.currentPage = 1 // 改变每页条数时重置到第一页
|
this.getList()
|
},
|
|
handleCurrentChange (val) {
|
this.currentPage = val
|
this.getList()
|
},
|
|
// 搜索方法
|
handleSearch () {
|
this.currentPage = 1 // 搜索时重置到第一页
|
this.getList()
|
},
|
|
handleClick (type) {
|
this.$refs[`datetime${type}`].focus()
|
},
|
|
// 清除开始时间
|
clearStartTime () {
|
this.dateTime = null
|
this.handleSearch()
|
},
|
|
// 清除结束时间
|
clearEndTime () {
|
this.dateTime2 = null
|
this.handleSearch()
|
},
|
|
backFun () {
|
this.$router.go(-1)
|
},
|
|
// 检查是否有任何报告
|
hasAnyReport (row) {
|
return row.containSystemRes === 1 || row.containDeviceRes === 1
|
},
|
|
// 检查报告可用性(当按钮被禁用时点击)
|
checkReportAvailability (row) {
|
if (!this.hasAnyReport(row)) {
|
this.$message.warning('该设备暂无可查看的报告')
|
}
|
},
|
|
// 打开报告
|
async openReport (type, row) {
|
// 根据类型检查是否有对应报告
|
const hasReport =
|
type === 'safe'
|
? row.containSystemRes === 1
|
: row.containDeviceRes === 1
|
|
if (!hasReport) {
|
this.$message.warning(`暂无${type === 'safe' ? '安全' : '设备'}报告`)
|
return
|
}
|
|
this.currentRow = row
|
this.reportVisible = true
|
this.showPreview = false
|
this.loading = true
|
|
try {
|
// 从 sessionStorage 获取模板 ID
|
const deviceTemplateId = sessionStorage.getItem('deviceTemplateId')
|
const safetyId = sessionStorage.getItem('safetyId')
|
const safetyName =
|
this.pdfProjectName + sessionStorage.getItem('safetyName')
|
const deviceTemplateName =
|
this.pdfProjectName + sessionStorage.getItem('deviceTemplateName')
|
// console.log('row', row)
|
// return
|
|
// 检查任务 ID
|
const checkTaskId = row.id
|
if (!checkTaskId) {
|
this.$message.error('缺少检查任务ID')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
|
// 第一步:调用接口生成Word文档(返回Word文档流)
|
let wordBlob
|
try {
|
if (type === 'safe') {
|
// 安全报告(模糊评价)
|
if (!safetyId) {
|
this.$message.error('缺少安全报告模板ID')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
wordBlob = await getFuzzyReportWord({
|
checkTaskId: checkTaskId,
|
templateId: safetyId,
|
uuid: safetyName
|
})
|
} else {
|
// 设备报告(打分评价)
|
if (!deviceTemplateId) {
|
this.$message.error('缺少设备报告模板ID')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
wordBlob = await getScoreReportWord({
|
checkTaskId: checkTaskId,
|
templateId: deviceTemplateId,
|
uuid: deviceTemplateName
|
})
|
}
|
} catch (error) {
|
// 捕获生成Word文档时的错误
|
console.error('生成Word文档失败:', error)
|
this.$message.error(error.message || '生成报告失败')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
|
// 检查 Word 文档是否生成成功
|
if (!wordBlob || wordBlob.size === 0) {
|
this.$message.error('报告生成失败')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
|
// 第二步:使用uuid获取PDF链接
|
let pdfUrlResponse
|
try {
|
if (type === 'safe') {
|
pdfUrlResponse = await getFuzzyReportPdfUrl({ uuid: safetyName })
|
} else {
|
pdfUrlResponse = await getScoreReportPdfUrl({
|
uuid: deviceTemplateName
|
})
|
}
|
} catch (error) {
|
// 捕获获取PDF链接时的错误
|
console.error('获取PDF链接失败:', error)
|
this.$message.error(error.message || '获取PDF链接失败')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
|
// 根据后端返回的数据结构获取PDF链接
|
// 假设后端返回格式为 { data: "http://xxx.pdf" } 或直接返回字符串
|
const pdfUrl = pdfUrlResponse.data || pdfUrlResponse
|
|
if (!pdfUrl) {
|
this.$message.error('获取PDF链接失败')
|
this.loading = false
|
this.reportVisible = false
|
return
|
}
|
|
// 保存PDF链接用于预览和下载
|
this.pdfUrl = pdfUrl
|
this.reportBlobUrl = pdfUrl // 用于下载
|
this.showPreview = true
|
|
this.$message.success('报告加载成功')
|
} catch (error) {
|
console.error('获取报告失败:', error)
|
// 优先显示错误对象中的 message
|
if (error.message) {
|
this.$message.error(error.message)
|
} else if (error.response) {
|
const status = error.response.status
|
if (status === 404) {
|
this.$message.error('报告不存在')
|
} else if (status === 500) {
|
this.$message.error('服务器错误,请稍后重试')
|
} else {
|
this.$message.error(
|
`获取报告失败: ${error.response.statusText || '未知错误'}`
|
)
|
}
|
} else {
|
this.$message.error('获取报告失败,请检查网络连接')
|
}
|
this.showPreview = false
|
this.reportVisible = false
|
} finally {
|
this.loading = false
|
}
|
},
|
|
// 下载报告
|
downloadReport () {
|
if (!this.reportBlobUrl) {
|
this.$message.warning('报告未加载')
|
return
|
}
|
|
try {
|
// 直接打开PDF链接进行下载
|
window.open(this.reportBlobUrl, '_blank')
|
this.$message.success('报告下载成功')
|
} catch (error) {
|
console.error('下载报告失败:', error)
|
this.$message.error('下载报告失败')
|
}
|
},
|
|
// 关闭报告弹窗
|
closeReportDialog () {
|
this.reportVisible = false
|
this.showPreview = false
|
|
// 清空PDF链接(不需要释放URL,因为是服务器链接)
|
this.reportBlobUrl = ''
|
this.pdfUrl = ''
|
this.reportUrl = '' // 保留兼容性
|
},
|
|
// 打开详情弹窗
|
openDetailDialog (row) {
|
this.currentDeviceId = row.projectId
|
this.pdfProjectName = row.projectName
|
this.detailCurrentPage = 1
|
this.projectVisible = true
|
this.getDetailCheckList()
|
},
|
|
// 获取设备详情检查列表
|
getDetailCheckList () {
|
if (!this.currentDeviceId && !this.projectId) return
|
|
let params = {
|
projectId: this.currentDeviceId || this.projectId,
|
pageNum: this.detailCurrentPage,
|
pageSize: this.detailPageSize
|
}
|
|
// 使用统一方法,根据用户类型调用对应接口
|
getDeviceDetailCheckListByType(params, this.isClient)
|
.then((res) => {
|
this.detailCheckList = res.records || []
|
this.detailTotal = res.total || 0
|
})
|
.catch((err) => {
|
console.error('获取设备详情检查列表失败:', err)
|
this.detailCheckList = []
|
this.detailTotal = 0
|
})
|
},
|
|
// 详情弹窗分页 - 每页条数改变
|
handleDetailSizeChange (val) {
|
this.detailPageSize = val
|
this.detailCurrentPage = 1
|
this.getDetailCheckList()
|
},
|
|
// 详情弹窗分页 - 当前页改变
|
handleDetailCurrentChange (val) {
|
this.detailCurrentPage = val
|
this.getDetailCheckList()
|
},
|
|
// 根据评价类型显示分数
|
getScoreDisplay (row) {
|
// commentType: 1模糊评价 2打分评价
|
const commentType = sessionStorage.getItem('commentType')
|
console.log('commentType', commentType, 'row row', row)
|
|
if (commentType === '1') {
|
return row.fuzzyScore || '-'
|
} else if (commentType === '2') {
|
return row.score || '-'
|
}
|
return row.fuzzyScore
|
}
|
}
|
}
|
</script>
|
<style lang="less">
|
.custom-popper {
|
background: transparent;
|
background-image: url("../../assets/img/home/77Combined Shape@2x.png");
|
background-size: 100% 100%;
|
padding: 31px 38px;
|
border-radius: 0px;
|
border: none;
|
box-shadow: none;
|
width: 227px;
|
height: 180px;
|
|
.popper__arrow {
|
display: none;
|
}
|
}
|
</style>
|
<style scoped lang="less">
|
.bgcolor1 {
|
background: rgba(2, 0, 77, 0.8);
|
box-shadow: inset 0px 0px 34px 0px rgba(17, 40, 255, 0.66);
|
border: 2px solid #0d53b7;
|
}
|
|
.bgColor2 {
|
background: rgba(24, 69, 135, 0.9);
|
}
|
|
.bgColor3 {
|
background: rgba(67, 221, 189, 1) !important;
|
}
|
|
.bgColor4 {
|
background: rgba(67, 221, 189, 0.6) !important;
|
}
|
|
.bgColor5 {
|
background: rgba(67, 221, 189, 0.4) !important;
|
}
|
|
.bgcolor6 {
|
background: linear-gradient(270deg, #64aef0 0%, #4b70ec 100%);
|
}
|
|
.bgcolor7 {
|
background: linear-gradient(270deg, #3dabea 0%, #28a5ec 100%);
|
}
|
|
.picker {
|
::v-deep .el-input__icon {
|
display: none;
|
}
|
|
::v-deep .el-range-separator {
|
display: none;
|
}
|
|
::v-deep .el-input__inner {
|
background: transparent;
|
border: none;
|
color: transparent;
|
}
|
|
::v-deep .el-range-input {
|
background: transparent;
|
border: none;
|
color: transparent;
|
}
|
|
.el-icon-circle-close {
|
font-size: 16px;
|
transition: color 0.3s;
|
|
&:hover {
|
color: #00ebfc;
|
}
|
}
|
}
|
|
.searchBox {
|
::v-deep .el-input__inner {
|
background: transparent;
|
border: none;
|
color: #fff;
|
padding: 0;
|
}
|
|
::v-deep .el-input__suffix {
|
display: none;
|
}
|
|
::v-deep .el-select__caret {
|
display: none;
|
}
|
|
.btn {
|
position: absolute;
|
top: -2px;
|
right: -2px;
|
background: linear-gradient(
|
90deg,
|
#3467d0 0%,
|
#0d226a 50.35%,
|
#376fe0 100%
|
);
|
height: 38px;
|
display: flex;
|
align-items: center;
|
font-size: 14px;
|
color: #ffffff;
|
padding: 0 26px;
|
border: 1px solid #307ad9;
|
box-sizing: border-box;
|
cursor: pointer;
|
}
|
|
.el-icon-circle-close {
|
font-size: 16px;
|
transition: color 0.3s;
|
|
&:hover {
|
color: #00ebfc;
|
}
|
}
|
}
|
|
.btnColor {
|
color: #00ebfc;
|
}
|
|
.btnDisabled {
|
color: #666666 !important;
|
cursor: not-allowed !important;
|
}
|
|
.report-btn {
|
cursor: pointer;
|
transition: all 0.3s ease;
|
position: relative;
|
}
|
|
.report-enabled {
|
&:hover {
|
opacity: 0.8;
|
transform: translateY(-2px);
|
}
|
}
|
|
.report-disabled {
|
opacity: 0.4;
|
cursor: not-allowed !important;
|
pointer-events: none;
|
}
|
|
.no-report-tag {
|
font-size: 12px;
|
margin-left: 4px;
|
opacity: 0.7;
|
}
|
|
::v-deep .el-descriptions {
|
.el-descriptions__body {
|
background-color: transparent;
|
|
.el-descriptions-item__container {
|
color: #ffffff;
|
font-family: "Source Han Sans CN Regular";
|
}
|
}
|
}
|
|
.ripple {
|
position: relative;
|
width: 14px;
|
height: 14px;
|
}
|
|
.ripple-wave {
|
position: absolute;
|
border-radius: 50%;
|
left: 50%;
|
top: 50%;
|
transform: translate(-50%, -50%);
|
}
|
|
.wave-1 {
|
width: 14px;
|
height: 14px;
|
background-color: rgba(255, 55, 95, 1);
|
}
|
|
.wave-2 {
|
width: 20px;
|
height: 20px;
|
background-color: rgba(255, 55, 95, 0.6);
|
animation: ripple-effect 2s ease-out infinite;
|
}
|
|
.wave-3 {
|
width: 24px;
|
height: 24px;
|
background-color: rgba(255, 55, 95, 0.4);
|
animation: ripple-effect 2s ease-out infinite 1s;
|
}
|
|
@keyframes ripple-effect {
|
0% {
|
transform: translate(-50%, -50%) scale(0.7);
|
opacity: 0;
|
}
|
|
20% {
|
opacity: 1;
|
opacity: 1;
|
}
|
|
100% {
|
-webkit-transform: translate(-50%, -50%) scale(1.4);
|
opacity: 0;
|
}
|
}
|
</style>
|