Files
LinyeZhanshi/src/components/TreePopup.vue

276 lines
6.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="visible" class="tree-popup" :style="{ left: position.x + 'px', top: position.y + 'px' }">
<el-card shadow="always" class="popup-card" :body-style="{ padding: '0', position: 'relative' }">
<span class="close-btn" @click="$emit('close')">×</span>
<div class="popup-header-wrapper">
<div class="popup-header">
<span class="title">样木详情</span>
<div class="pagination" v-if="total > 1">
<el-button size="small" circle :disabled="currentIndex === 0" @click="prevItem" class="nav-btn">&lt;</el-button>
<span class="page-info">{{ currentIndex + 1 }} / {{ total }}</span>
<el-button size="small" circle :disabled="currentIndex === total - 1" @click="nextItem" class="nav-btn">&gt;</el-button>
</div>
<el-tag v-else size="small" type="success">样木</el-tag>
</div>
</div>
<div class="scroll-content">
<div class="content-padding">
<div class="sub-header">
ID: {{ currentData.id || '-' }}
</div>
<div class="info-grid">
<div class="info-item" v-for="(label, key) in fieldMap" :key="key">
<span class="label">{{ label }}</span>
<span class="value">{{ getDisplayValue(key) }}</span>
</div>
</div>
<div class="media-container" v-if="mediaItems.length > 0">
<el-carousel :key="currentIndex" height="160px" indicator-position="none" :interval="4000" arrow="hover">
<el-carousel-item v-for="(item, index) in mediaItems" :key="index">
<el-image
:src="item.src"
class="media-content"
fit="contain"
:preview-src-list="allImages"
:initial-index="item.imgIndex"
preview-teleported
@click.stop
/>
</el-carousel-item>
</el-carousel>
</div>
<div v-else class="no-media">暂无样木照片</div>
</div>
</div>
</el-card>
</div>
</template>
<script setup>
import { computed, ref, watch } from "vue";
import { ElCard, ElCarousel, ElCarouselItem, ElTag, ElImage, ElButton } from "element-plus";
const props = defineProps({
visible: Boolean,
data: { type: [Array, Object], default: () => [] },
position: { type: Object, default: () => ({ x: 0, y: 0 }) }
});
defineEmits(['close']);
const MEDIA_BASE_URL = '/photos'; // 确保和之前一致
const currentIndex = ref(0);
watch(() => props.visible, (val) => {
if (val) currentIndex.value = 0;
});
// 数据标准化:转数组
const dataList = computed(() => {
if (Array.isArray(props.data)) return props.data;
if (props.data && Object.keys(props.data).length > 0) return [props.data];
return [];
});
const total = computed(() => dataList.value.length);
const currentData = computed(() => dataList.value[currentIndex.value] || {});
const prevItem = () => { if (currentIndex.value > 0) currentIndex.value--; };
const nextItem = () => { if (currentIndex.value < total.value - 1) currentIndex.value++; };
// ✅ 样木字段映射
const fieldMap = {
sz: "树种",
xj: "胸径(cm)",
cjl: "材积率", // 或 蓄积
ymh: "样木号",
parentId: "所属样地ID",
grandparentid: "所属小班ID",
databaseName: "数据库",
version: "版本",
type: "类型"
};
function getDisplayValue(key) {
const val = currentData.value[key];
if (val === null || val === undefined || val === '') return '-';
if (key === 'xj' || key === 'cjl') return Number(val).toFixed(2);
return val;
}
// ✅ 媒体处理:样木接口返回的是 photo 字符串,不是 list
const mediaItems = computed(() => {
const photoPath = currentData.value.photo;
if (!photoPath) return [];
// 构造完整的图片路径
const fullSrc = `${MEDIA_BASE_URL}${photoPath}`;
return [{ type: "image", src: fullSrc, imgIndex: 0 }];
});
const allImages = computed(() => mediaItems.value.map(i => i.src));
</script>
<style scoped>
.tree-popup {
position: relative; /* 配合 Flex 布局 */
z-index: 2002;
width: 260px;
}
/* 箭头指向左侧 */
.tree-popup::after {
content: '';
position: absolute;
bottom: 20px;
left: -6px;
border-width: 6px 6px 6px 0;
border-style: solid;
border-color: transparent #fff transparent transparent;
filter: drop-shadow(-2px 0 2px rgba(0,0,0,0.1));
}
.popup-card {
border-radius: 8px;
border: 1px solid #dcdfe6;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
/* 关闭按钮 */
.close-btn {
position: absolute;
top: 8px;
right: 8px; /* 稍微靠右一点 */
font-size: 20px;
font-weight: bold;
color: #fff;
cursor: pointer;
z-index: 10; /* 确保在 header 内容之上 */
line-height: 1;
opacity: 0.8;
}
.close-btn:hover {
opacity: 1;
color: #ffe6e6;
}
.popup-header-wrapper {
background-color: #67c23a; /* 绿色头部 */
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
/* ✅ 关键修改:右边距避让关闭按钮 */
padding-right: 35px;
color: white;
height: 32px;
}
.title {
font-weight: bold;
font-size: 14px;
white-space: nowrap; /* 防止标题换行 */
}
/* 翻页器 */
.pagination {
display: flex;
align-items: center;
gap: 4px;
}
.page-info {
font-size: 12px;
font-weight: bold;
white-space: nowrap;
}
.nav-btn {
width: 18px;
height: 18px;
min-height: 18px;
font-size: 12px;
padding: 0;
background: rgba(255,255,255,0.2);
border: none;
color: white;
}
.nav-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* 内容区域 */
.scroll-content {
max-height: 350px;
overflow-y: auto;
background: #fff;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
.content-padding {
padding: 12px;
}
.sub-header {
font-size: 12px;
color: #909399;
margin-bottom: 8px;
border-bottom: 1px solid #eee;
padding-bottom: 4px;
}
.info-grid {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 12px;
}
.info-item {
display: flex;
justify-content: space-between;
border-bottom: 1px dashed #eee;
padding-bottom: 4px;
font-size: 12px;
}
.info-item .label { color: #909399; flex-shrink: 0; }
.info-item .value { color: #303133; font-weight: bold; text-align: right; word-break: break-all; }
.media-container {
position: relative;
background: #000;
border-radius: 4px;
overflow: hidden;
}
.media-content {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
.no-media {
height: 50px;
background: #f5f7fa;
color: #c0c4cc;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
border-radius: 4px;
}
/* 滚动条美化 */
.scroll-content::-webkit-scrollbar { width: 6px; }
.scroll-content::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 3px; }
</style>