877 lines
28 KiB
Vue
877 lines
28 KiB
Vue
<template>
|
||
<div class="h-full flex flex-col overflow-hidden p-4">
|
||
<!-- 头部区域 (固定) -->
|
||
<div class="flex-none pb-2 border-b border-cyan-500/10 mb-2">
|
||
<div class="flex justify-between items-center">
|
||
<div class="flex items-center gap-2">
|
||
<div class="w-1 h-5 bg-cyan-500 shadow-[0_0_8px_#06b6d4]"></div>
|
||
<h3
|
||
class="text-lg font-bold text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-blue-500 font-mono tracking-wide"
|
||
>
|
||
属性面板 <span class="text-xs text-cyan-500/50 font-normal">/// PROPERTIES</span>
|
||
</h3>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<el-tag v-if="store.currentNodeId" class="tech-tag font-mono">{{
|
||
store.currentNodeId
|
||
}}</el-tag>
|
||
<span v-else class="text-gray-500 text-sm font-mono">NO SELECTION</span>
|
||
<el-tooltip :content="isMaximized ? '还原' : '最大化'" placement="left" effect="dark">
|
||
<button class="tech-icon-btn" @click="emit('toggleMaximize')">
|
||
<el-icon><FullScreen /></el-icon>
|
||
</button>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容区域 (可滚动) -->
|
||
<div
|
||
class="flex-1 overflow-y-auto overflow-x-hidden min-h-0 custom-scrollbar"
|
||
v-if="store.currentNodeId"
|
||
>
|
||
<div class="flex flex-wrap">
|
||
<div class="w-[450px]">
|
||
<!-- 基本信息 -->
|
||
<div class="tech-divider mb-2">
|
||
<span class="text-cyan-400 font-mono text-sm">基本信息</span>
|
||
<div class="h-[1px] bg-gradient-to-r from-cyan-500/50 to-transparent flex-1 ml-4"></div>
|
||
</div>
|
||
<el-descriptions :column="1" border size="small" class="tech-descriptions mb-2">
|
||
<el-descriptions-item
|
||
v-for="(item, index) in properties.basic"
|
||
:key="index"
|
||
:label="item.label"
|
||
>
|
||
{{ item.value }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
<div class="w-[450px]">
|
||
<!-- 技术参数 -->
|
||
<div class="tech-divider mb-2">
|
||
<span class="text-cyan-400 font-mono text-sm">技术参数</span>
|
||
<div class="h-[1px] bg-gradient-to-r from-cyan-500/50 to-transparent flex-1 ml-4"></div>
|
||
</div>
|
||
<el-descriptions :column="1" border size="small" class="tech-descriptions mb-2">
|
||
<el-descriptions-item
|
||
v-for="(item, index) in properties.technical"
|
||
:key="index"
|
||
:label="item.label"
|
||
>
|
||
{{ item.value }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</div>
|
||
<!-- 运行状态 -->
|
||
<div class="tech-divider mb-2">
|
||
<span class="text-cyan-400 font-mono text-sm">状态</span>
|
||
<div class="h-[1px] bg-gradient-to-r from-cyan-500/50 to-transparent flex-1 ml-4"></div>
|
||
</div>
|
||
<div class="flex flex-wrap gap-3">
|
||
<div
|
||
v-for="(item, index) in properties.status"
|
||
:key="index"
|
||
class="sm:w-[50%] lg:w-[210px] flex justify-between p-2 bg-[#0f172a] border border-cyan-500/10 rounded cursor-pointer hover:border-cyan-500/50 hover:bg-[#0f172a]/80 transition-all group"
|
||
@click="openTableDialog(item)"
|
||
>
|
||
<span
|
||
class="text-sm text-gray-400 font-mono group-hover:text-cyan-400 transition-colors"
|
||
>{{ item.label }}</span
|
||
>
|
||
<div class="flex items-center gap-2">
|
||
<el-tag v-if="item.status" :type="item.status" size="small" class="tech-status-tag">{{
|
||
item.value
|
||
}}</el-tag>
|
||
<span v-else class="font-medium text-cyan-300 font-mono">{{ item.value }}</span>
|
||
<el-icon
|
||
class="text-gray-600 group-hover:text-cyan-400 transition-colors text-xs opacity-0 group-hover:opacity-100"
|
||
><List
|
||
/></el-icon>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 图库功能模块 -->
|
||
<div v-if="showGallery" class="mb-6">
|
||
<el-divider content-position="left">CAD图库</el-divider>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="flex justify-center items-center h-32">
|
||
<el-icon class="is-loading text-2xl text-blue-500">
|
||
<Loading />
|
||
</el-icon>
|
||
<span class="ml-2 text-gray-600">正在加载图片...</span>
|
||
</div>
|
||
|
||
<div v-else>
|
||
<!-- 轮播组件 -->
|
||
<div class="mb-4"></div>
|
||
|
||
<!-- 视图模式切换 -->
|
||
<div class="flex justify-between items-center mb-3">
|
||
<span class="text-sm text-gray-600">共 {{ imageList.length }} 张图片</span>
|
||
<el-radio-group v-model="viewMode" size="small">
|
||
<el-radio-button value="grid">
|
||
<el-icon><Grid /></el-icon>
|
||
网格
|
||
</el-radio-button>
|
||
<el-radio-button value="carousel">
|
||
<el-icon><PictureRounded /></el-icon>
|
||
走马灯
|
||
</el-radio-button>
|
||
</el-radio-group>
|
||
</div>
|
||
|
||
<!-- 缩略图区域 -->
|
||
<div class="transition-all duration-300">
|
||
<!-- 网格视图 -->
|
||
<div
|
||
v-if="viewMode === 'grid'"
|
||
class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3"
|
||
>
|
||
<div
|
||
v-for="(image, index) in imageList"
|
||
:key="image.id"
|
||
class="relative group cursor-pointer rounded-lg overflow-hidden border-2 transition-all duration-200 hover:border-blue-400"
|
||
:class="{ 'border-blue-500 ring-2 ring-blue-200': currentIndex === index }"
|
||
@click="openPreview(index)"
|
||
>
|
||
<el-image
|
||
:src="image.thumb"
|
||
:alt="image.meta.title"
|
||
fit="contain"
|
||
class="w-full h-18"
|
||
lazy
|
||
>
|
||
<template #placeholder>
|
||
<div class="w-full h-18 flex items-center justify-center bg-gray-200">
|
||
<el-icon class="text-xl text-gray-400"><Picture /></el-icon>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
|
||
<!-- 查看详情按钮 -->
|
||
<div
|
||
class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition-all duration-200 flex items-center justify-center"
|
||
>
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
circle
|
||
class="opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||
>
|
||
<el-icon><ZoomIn /></el-icon>
|
||
</el-button>
|
||
</div>
|
||
|
||
<div
|
||
class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-2"
|
||
>
|
||
<p class="text-white text-xs truncate">{{ image.meta.title }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 走马灯视图 -->
|
||
<div v-else>
|
||
<el-carousel
|
||
v-model="currentIndex"
|
||
:interval="3000"
|
||
arrow="always"
|
||
indicator-position="outside"
|
||
height="150px"
|
||
class="rounded-lg overflow-hidden"
|
||
>
|
||
<el-carousel-item v-for="(image, index) in imageList" :key="image.id">
|
||
<div
|
||
class="w-full h-full bg-gray-100/0 flex items-center justify-center cursor-pointer"
|
||
@click="openPreview(index)"
|
||
@dblclick="showImageInfo(index)"
|
||
>
|
||
<el-image
|
||
:src="image.thumb"
|
||
:alt="image.meta.title"
|
||
fit="contain"
|
||
class="w-full h-full"
|
||
lazy
|
||
>
|
||
<template #placeholder>
|
||
<div class="w-full h-full flex items-center justify-center bg-gray-200">
|
||
<el-icon class="text-3xl text-gray-400"><Picture /></el-icon>
|
||
</div>
|
||
</template>
|
||
<template #error>
|
||
<div class="w-full h-full flex items-center justify-center bg-gray-200">
|
||
<el-icon class="text-3xl text-red-400"><Warning /></el-icon>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
</div>
|
||
</el-carousel-item>
|
||
</el-carousel>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
v-else
|
||
class="h-full flex flex-col items-center justify-center text-gray-400 min-h-[300px]"
|
||
>
|
||
<el-icon class="text-4xl mb-2"><InfoFilled /></el-icon>
|
||
<p>请在左侧模型树或3D视图中选择一个部件查看详情</p>
|
||
</div>
|
||
|
||
<!-- 大图预览组件 -->
|
||
<el-image-viewer
|
||
v-if="showPreview"
|
||
:url-list="previewList"
|
||
:initial-index="previewIndex"
|
||
@close="showPreview = false"
|
||
>
|
||
<template #toolbar="{ index }">
|
||
<div class="flex items-center gap-2">
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
circle
|
||
title="下载图片"
|
||
@click="downloadImage(index)"
|
||
>
|
||
<el-icon><Download /></el-icon>
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
circle
|
||
title="复制链接"
|
||
@click="copyImageUrl(index)"
|
||
>
|
||
<el-icon><CopyDocument /></el-icon>
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
circle
|
||
title="查看信息"
|
||
@click="showImageInfo(index)"
|
||
>
|
||
<el-icon><InfoFilled /></el-icon>
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-image-viewer>
|
||
|
||
<!-- 图片信息对话框 -->
|
||
<el-dialog v-model="showInfoDialog" title="图片信息" width="400px">
|
||
<div v-if="currentImage" class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">标题:</span>
|
||
<span>{{ currentImage.meta.title }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">标签:</span>
|
||
<el-tag size="small">{{ currentImage.meta.tag }}</el-tag>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">尺寸:</span>
|
||
<span>{{ currentImage.meta.dimensions }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">创建时间:</span>
|
||
<span>{{ currentImage.meta.createdAt }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">文件大小:</span>
|
||
<span>{{ currentImage.meta.size }}</span>
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 数据表格弹窗 -->
|
||
<el-dialog
|
||
v-model="showTableDialog"
|
||
:title="currentParamItem ? `${currentParamItem.label} - 历史记录` : '运行状态记录'"
|
||
width="900px"
|
||
append-to-body
|
||
class="tech-dialog-modal"
|
||
>
|
||
<div class="mb-4 flex flex-wrap gap-4 items-center">
|
||
<div class="text-sm text-cyan-400 font-mono">
|
||
<span class="mr-2">当前节点:</span>
|
||
<el-tag size="small" class="tech-tag">{{ store.currentNodeId || 'Unknown' }}</el-tag>
|
||
</div>
|
||
<div class="flex-1"></div>
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
class="tech-button"
|
||
@click="() => generateMockData()"
|
||
>
|
||
<el-icon class="mr-1"><Refresh /></el-icon> 刷新数据
|
||
</el-button>
|
||
</div>
|
||
|
||
<el-table :data="tableData" height="500" stripe style="width: 100%">
|
||
<el-table-column prop="time" label="记录时间" width="200" />
|
||
<el-table-column prop="parameter" label="监测参数" width="180" />
|
||
<el-table-column prop="value" label="监测数值" />
|
||
<el-table-column prop="status" label="状态判定" width="120">
|
||
<template #default="scope">
|
||
<el-tag :type="scope.row.statusType" size="small">{{ scope.row.status }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="operator" label="记录人" width="120" />
|
||
</el-table>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
// 导入必要的Vue API
|
||
import { computed, onMounted, ref, watch } from 'vue';
|
||
// 导入必要的图标组件
|
||
import {
|
||
CopyDocument,
|
||
Download,
|
||
FullScreen,
|
||
Grid,
|
||
InfoFilled,
|
||
List,
|
||
Loading,
|
||
Picture,
|
||
PictureRounded,
|
||
Refresh,
|
||
Warning,
|
||
ZoomIn,
|
||
} from '@element-plus/icons-vue';
|
||
// 导入Element Plus组件
|
||
import { ElMessage } from 'element-plus';
|
||
// 导入电站数据状态管理
|
||
import { usePowerStationStore } from '~/stores/powerStation';
|
||
|
||
// 定义组件属性
|
||
const props = defineProps<{
|
||
isMaximized?: boolean // 是否最大化显示
|
||
}>()
|
||
|
||
// 定义组件事件
|
||
const emit = defineEmits(['toggleMaximize'])
|
||
|
||
// 获取电站状态管理实例
|
||
const store = usePowerStationStore()
|
||
|
||
// 图片数据类型定义
|
||
interface ImageMeta {
|
||
title: string
|
||
tag: string
|
||
dimensions: string
|
||
createdAt: string
|
||
size: string
|
||
}
|
||
|
||
interface ImageItem {
|
||
id: string
|
||
thumb: string
|
||
src: string
|
||
meta: ImageMeta
|
||
}
|
||
|
||
interface PropertyItem {
|
||
label: string
|
||
value: string
|
||
unit?: string
|
||
status?: string
|
||
}
|
||
|
||
interface PropertiesData {
|
||
basic: PropertyItem[]
|
||
technical: PropertyItem[]
|
||
status: PropertyItem[]
|
||
}
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const imageList = ref<ImageItem[]>([])
|
||
const currentIndex = ref(0)
|
||
const viewMode = ref<'grid' | 'carousel'>('carousel')
|
||
const showPreview = ref(false)
|
||
const previewIndex = ref(0)
|
||
const showInfoDialog = ref(false)
|
||
const currentImage = ref<ImageItem | null>(null)
|
||
const properties = ref<PropertiesData>({ basic: [], technical: [], status: [] })
|
||
|
||
// 表格弹窗相关
|
||
const showTableDialog = ref(false)
|
||
const tableData = ref<any[]>([])
|
||
|
||
// 表格弹窗相关
|
||
const currentParamItem = ref<PropertyItem | null>(null)
|
||
|
||
const openTableDialog = (item: PropertyItem) => {
|
||
currentParamItem.value = item
|
||
showTableDialog.value = true
|
||
generateMockData(item)
|
||
}
|
||
|
||
const generateMockData = (item?: PropertyItem) => {
|
||
const targetItem = item || currentParamItem.value
|
||
if (!targetItem) return
|
||
|
||
const label = targetItem.label
|
||
const operators = ['系统自动', '操作员A', '操作员B']
|
||
|
||
// 根据不同的标签生成不同的模拟数据
|
||
tableData.value = Array.from({ length: 20 }).map((_, i) => {
|
||
let value = ''
|
||
let statusObj = { label: '正常', type: 'success' }
|
||
|
||
// 基于标签的简单的模拟逻辑
|
||
if (label.includes('温度')) {
|
||
const val = 50 + Math.random() * 20
|
||
value = val.toFixed(1) + ' °C'
|
||
if (val > 68) statusObj = { label: '高温警告', type: 'warning' }
|
||
} else if (label.includes('压力')) {
|
||
const val = 10 + Math.random() * 5
|
||
value = val.toFixed(2) + ' MPa'
|
||
if (val > 14.5) statusObj = { label: '高压', type: 'warning' }
|
||
} else if (label.includes('健康')) {
|
||
// 健康度
|
||
const val = 90 + Math.random() * 10
|
||
value = Math.min(100, val).toFixed(1) + '%'
|
||
if (val < 92) statusObj = { label: '需关注', type: 'warning' }
|
||
} else if (label.includes('状态') || label.includes('运行')) {
|
||
const states = ['正常运行', '以正常运行', '低负荷运行']
|
||
value = states[Math.floor(Math.random() * states.length)]
|
||
// 偶尔出个异常
|
||
if (Math.random() > 0.9) {
|
||
value = '停机维护'
|
||
statusObj = { label: '停机', type: 'info' }
|
||
}
|
||
} else if (label.includes('报警')) {
|
||
const subStatus = Math.random()
|
||
if (subStatus > 0.8) {
|
||
value = '轻微报警'
|
||
statusObj = { label: '报警', type: 'warning' }
|
||
} else {
|
||
value = '无报警'
|
||
}
|
||
} else {
|
||
// 默认回退
|
||
value = (Math.random() * 100).toFixed(2)
|
||
}
|
||
|
||
// 随机插入一些异常
|
||
if (Math.random() > 0.95 && statusObj.type === 'success') {
|
||
statusObj = { label: '异常', type: 'danger' }
|
||
}
|
||
|
||
return {
|
||
time: new Date(Date.now() - i * 1000 * 60 * 30).toLocaleString(),
|
||
parameter: label,
|
||
value: value,
|
||
status: statusObj.label,
|
||
// @ts-ignore - Element Plus tag type
|
||
statusType: statusObj.type || 'success',
|
||
operator: operators[i % operators.length],
|
||
}
|
||
})
|
||
}
|
||
|
||
// 计算属性
|
||
|
||
// 计算属性
|
||
const showGallery = computed(() => {
|
||
// 开发环境下总是显示图库用于测试
|
||
if (process.dev) return true
|
||
// 检查currentNodeId是否包含CAD相关标识
|
||
return store.currentNodeId?.includes('CAD') || false
|
||
})
|
||
|
||
const previewList = computed(() => {
|
||
return imageList.value.map(item => item.src)
|
||
})
|
||
|
||
// 异步加载属性数据
|
||
const fetchProperties = async (nodeId: string) => {
|
||
if (!nodeId) {
|
||
console.error('获取属性数据失败: nodeId不能为空')
|
||
// 清空属性数据
|
||
properties.value = {
|
||
basic: [],
|
||
technical: [],
|
||
status: [],
|
||
}
|
||
return
|
||
}
|
||
|
||
try {
|
||
const response = await $fetch('/api/power-station/properties', {
|
||
query: { nodeId },
|
||
})
|
||
properties.value = response as PropertiesData
|
||
} catch (error) {
|
||
console.error('获取属性数据失败:', error)
|
||
// 如果API调用失败,使用模拟数据
|
||
properties.value = {
|
||
basic: [
|
||
{ label: 'ID', value: nodeId },
|
||
{ label: '名称', value: '一级再热器进口连接管' },
|
||
{ label: '节点类型', value: '设备' },
|
||
{ label: '所属系统', value: '主系统' },
|
||
{ label: '材质', value: 'SA213-T12' },
|
||
{ label: '规格', value: '∅610*38' },
|
||
],
|
||
technical: [
|
||
{ label: '设计压力', value: '16.5 MPa', unit: 'MPa' },
|
||
{ label: '设计温度', value: '545', unit: '°C' },
|
||
{ label: '材料', value: 'SA-516 Gr.70' },
|
||
{ label: '厚度', value: '25', unit: 'mm' },
|
||
],
|
||
status: [
|
||
{ label: '运行状态', value: '正常运行', status: 'success' },
|
||
{ label: '健康度', value: '95%', status: 'success' },
|
||
{ label: '维护状态', value: '正常', status: 'success' },
|
||
{ label: '报警状态', value: '无报警', status: 'success' },
|
||
],
|
||
}
|
||
}
|
||
}
|
||
|
||
// 异步加载图片数据
|
||
const loadImages = async () => {
|
||
loading.value = true
|
||
try {
|
||
// 模拟异步加载
|
||
await new Promise(resolve => setTimeout(resolve, 500))
|
||
|
||
// 构建图片数据
|
||
imageList.value = [
|
||
{
|
||
id: 'cad1',
|
||
thumb: '/Cad_Preview/一级过热器出口连接管.png',
|
||
src: '/Cad/一级过热器出口连接管.png',
|
||
meta: {
|
||
title: '一级过热器出口连接管',
|
||
tag: '主视图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '2.3MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad2',
|
||
thumb: '/Cad_Preview/一级再热器出口连接管.png',
|
||
src: '/Cad/一级再热器出口连接管.png',
|
||
meta: {
|
||
title: '一级再热器出口连接管',
|
||
tag: '侧视图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '1.8MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad3',
|
||
thumb: '/Cad_Preview/二级过热器出口连接管.png',
|
||
src: '/Cad/二级过热器出口连接管.png',
|
||
meta: {
|
||
title: '二级过热器出口连接管',
|
||
tag: '俯视图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '3.1MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad4',
|
||
thumb: '/Cad_Preview/一级再热器管排.png',
|
||
src: '/Cad/一级再热器管排.png',
|
||
meta: {
|
||
title: '一级再热器管排',
|
||
tag: '管排图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '2.1MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad5',
|
||
thumb: '/Cad_Preview/二级再热器管排.png',
|
||
src: '/Cad/二级再热器管排.png',
|
||
meta: {
|
||
title: '二级再热器管排',
|
||
tag: '管排图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '2.5MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad6',
|
||
thumb: '/Cad_Preview/二级过热器管排.png',
|
||
src: '/Cad/二级过热器管排.png',
|
||
meta: {
|
||
title: '二级过热器管排',
|
||
tag: '管排图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '2.7MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad7',
|
||
thumb: '/Cad_Preview/一级再热器进口连接管.png',
|
||
src: '/Cad/一级再热器进口连接管.png',
|
||
meta: {
|
||
title: '一级再热器进口连接管',
|
||
tag: '进口图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '1.9MB',
|
||
},
|
||
},
|
||
{
|
||
id: 'cad8',
|
||
thumb: '/Cad_Preview/二级再热器进口连接管.png',
|
||
src: '/Cad/二级再热器进口连接管.png',
|
||
meta: {
|
||
title: '二级再热器进口连接管',
|
||
tag: '进口图',
|
||
dimensions: '1920x1080',
|
||
createdAt: '2024-12-04',
|
||
size: '2.2MB',
|
||
},
|
||
},
|
||
]
|
||
} catch (error) {
|
||
console.error('加载图片失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 选择图片(当前未使用,保留备用)
|
||
// const selectImage = (index: number) => {
|
||
// currentIndex.value = index
|
||
// }
|
||
|
||
// 打开预览
|
||
const openPreview = (index: number) => {
|
||
currentIndex.value = index // 同步更新当前索引
|
||
previewIndex.value = index
|
||
showPreview.value = true
|
||
}
|
||
|
||
// 下载图片
|
||
const downloadImage = (index: number) => {
|
||
const image = imageList.value[index]
|
||
if (image) {
|
||
const link = document.createElement('a')
|
||
link.href = image.src
|
||
link.download = `${image.meta.title}.png`
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
document.body.removeChild(link)
|
||
}
|
||
}
|
||
|
||
// 复制图片链接
|
||
const copyImageUrl = async (index: number) => {
|
||
const image = imageList.value[index]
|
||
if (image) {
|
||
try {
|
||
await navigator.clipboard.writeText(window.location.origin + image.src)
|
||
ElMessage.success('图片链接已复制到剪贴板')
|
||
} catch (error) {
|
||
ElMessage.error('复制失败,请手动复制')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示图片信息
|
||
const showImageInfo = (index: number) => {
|
||
currentImage.value = imageList.value[index]
|
||
showInfoDialog.value = true
|
||
}
|
||
|
||
// 监听currentNodeId变化,重新加载数据
|
||
watch(
|
||
() => store.currentNodeId,
|
||
newId => {
|
||
if (newId) {
|
||
// 当选择的节点ID变化时,重新获取属性数据
|
||
fetchProperties(newId)
|
||
|
||
// 如果需要显示图库,加载图片
|
||
if (showGallery.value) {
|
||
loadImages()
|
||
}
|
||
}
|
||
}
|
||
)
|
||
|
||
// 组件挂载时初始化数据
|
||
onMounted(() => {
|
||
if (store.currentNodeId) {
|
||
// 获取初始属性数据
|
||
fetchProperties(store.currentNodeId)
|
||
|
||
// 如果需要显示图库,加载图片
|
||
if (showGallery.value) {
|
||
loadImages()
|
||
}
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* Tech Icon Button */
|
||
.tech-icon-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
background: rgba(15, 23, 42, 0.5);
|
||
border: 1px solid rgba(34, 211, 238, 0.2);
|
||
color: #94a3b8;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.tech-icon-btn:hover {
|
||
background: rgba(6, 182, 212, 0.1);
|
||
border-color: #22d3ee;
|
||
color: #22d3ee;
|
||
box-shadow: 0 0 10px rgba(6, 182, 212, 0.4);
|
||
}
|
||
|
||
/* Tech Tag */
|
||
:deep(.tech-tag) {
|
||
background-color: rgba(6, 182, 212, 0.1);
|
||
border-color: rgba(6, 182, 212, 0.3);
|
||
color: #22d3ee;
|
||
}
|
||
|
||
/* Tech Descriptions */
|
||
:deep(.tech-descriptions .el-descriptions__body) {
|
||
background-color: transparent;
|
||
}
|
||
|
||
:deep(.tech-descriptions .el-descriptions__label) {
|
||
background-color: rgba(15, 23, 42, 0.8) !important;
|
||
color: #94a3b8;
|
||
font-family: monospace;
|
||
border-color: rgba(34, 211, 238, 0.1) !important;
|
||
}
|
||
|
||
:deep(.tech-descriptions .el-descriptions__content) {
|
||
background-color: transparent !important;
|
||
color: #e2e8f0;
|
||
font-family: monospace;
|
||
border-color: rgba(34, 211, 238, 0.1) !important;
|
||
}
|
||
|
||
/* Tech Status Tag */
|
||
:deep(.tech-status-tag.el-tag--success) {
|
||
background-color: rgba(16, 185, 129, 0.1);
|
||
border-color: rgba(16, 185, 129, 0.2);
|
||
color: #34d399;
|
||
}
|
||
|
||
/* Tech Button */
|
||
:deep(.tech-button) {
|
||
background-color: rgba(6, 182, 212, 0.1);
|
||
border-color: #06b6d4;
|
||
color: #22d3ee;
|
||
font-family: monospace;
|
||
}
|
||
|
||
:deep(.tech-button:hover) {
|
||
background-color: rgba(6, 182, 212, 0.2);
|
||
box-shadow: 0 0 15px rgba(6, 182, 212, 0.3);
|
||
}
|
||
|
||
/* Custom Scrollbar */
|
||
.custom-scrollbar::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.custom-scrollbar::-webkit-scrollbar-track {
|
||
background: rgba(15, 23, 42, 0.5);
|
||
}
|
||
|
||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||
background: rgba(34, 211, 238, 0.2);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(34, 211, 238, 0.4);
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* Styling for the tech dialog modal - Global style to affect element-plus dialog projected to body */
|
||
.tech-dialog-modal {
|
||
background-color: #0b1120 !important;
|
||
border: 1px solid #06b6d4 !important;
|
||
box-shadow: 0 0 20px rgba(6, 182, 212, 0.2) !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-dialog__header {
|
||
border-bottom: 1px solid rgba(34, 211, 238, 0.1);
|
||
margin-right: 0 !important;
|
||
padding: 15px 20px !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-dialog__title {
|
||
color: #22d3ee !important;
|
||
font-family: monospace;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.tech-dialog-modal .el-dialog__body {
|
||
background-color: #0b1120 !important;
|
||
color: #94a3b8 !important;
|
||
padding: 20px !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-dialog__close {
|
||
color: #22d3ee !important;
|
||
}
|
||
|
||
/* Table overrides inside dialog */
|
||
.tech-dialog-modal .el-table {
|
||
background-color: transparent !important;
|
||
--el-table-tr-bg-color: transparent !important;
|
||
--el-table-header-bg-color: rgba(15, 23, 42, 0.8) !important;
|
||
--el-table-row-hover-bg-color: rgba(6, 182, 212, 0.1) !important;
|
||
--el-table-border-color: rgba(34, 211, 238, 0.1) !important;
|
||
color: #94a3b8 !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-table th.el-table__cell {
|
||
background-color: rgba(15, 23, 42, 0.8) !important;
|
||
color: #22d3ee !important;
|
||
border-bottom: 1px solid rgba(34, 211, 238, 0.2) !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-table td.el-table__cell {
|
||
border-bottom: 1px solid rgba(34, 211, 238, 0.1) !important;
|
||
}
|
||
|
||
.tech-dialog-modal .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||
background-color: rgba(30, 41, 59, 0.3) !important;
|
||
}
|
||
|
||
/* Tag overrides within dialog */
|
||
.tech-dialog-modal .el-tag {
|
||
background-color: rgba(15, 23, 42, 0.8) !important;
|
||
border-color: rgba(34, 211, 238, 0.3) !important;
|
||
}
|
||
</style>
|