go-viee-fetch-Demo/src/packages/components/Charts/ConfinedSpace/Map/index.vue

289 lines
10 KiB
Vue
Raw 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 class="go-iframe-container" :style="{ width: w + 'px', height: h + 'px' }">
<!-- 按钮容器绝对定位在左上角 -->
<div class="button-container-wrapper">
<div class="button-container">
<div v-for="btn in buttons" :key="btn.id" :class="['button', { active: btn.id === activeButtonId }]"
@click="selectButton(btn.id)">
<span>{{ btn.label }}</span>
</div>
</div>
</div>
<iframe ref="iframeRef" class="iframe-content" src="/static/index.html"></iframe>
</div>
</template>
<script setup lang="ts">
import axios from 'axios';
import config, { BarOption } from './config';
import { PropType, toRefs, ref, onMounted, computed, watch } from 'vue';
import dataJson from './data.json'
import axiosInstance from '@/api/axios';
const iframeRef = ref<HTMLIFrameElement | null>(null);
const isIframeReady = ref(false); // 2. 创建一个状态来跟踪 iframe 是否已加载
let iframeApi: any = null;
const props = defineProps({
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const buttons = ref([
{ id: 1, label: '场所分布情况' },
{ id: 2, label: '当日作业情况' }
]);
const activeButtonId = ref(buttons.value[0].id);
// 按钮点击事件
const selectButton = (id: number) => {
activeButtonId.value = id; // 更新激活状态的 ID
console.log(`按钮 ${id} 被选中`);
updateChartData(id);
};
/**
* 根据按钮ID更新图表数据
* @param buttonId 当前激活的按钮ID
*/
const updateChartData = async (buttonId: number) => {
let dataToRender: BarOption[] = [];
if (buttonId === 1) {
dataToRender = await fetchData('/spaceMapMock');
} else if (buttonId === 2) {
dataToRender = await fetchData('/dayMapMock');
} else {
console.warn(`未知的按钮ID: ${buttonId},将使用默认数据。`);
}
// 更新 chartConfig 中的数据,这将触发 BarOptionsData computed 和 watch
props.chartConfig.option.dataset.barOptions = dataToRender;
console.log(`已为按钮 ${buttonId} 加载 ${dataToRender.length} 条数据。`);
};
const fetchData = async (url: string) => {
if (url == '/spaceMapMock') {
return dataJson.barOptions;
} else if (url == '/dayMapMock') {
return [{ "location": "四川省.成都市", "items": [{ "tall": 120, "color": "#ff4757" }, { "tall": 50, "color": "#3A86FF" }] }, { "location": "广东省.深圳市", "items": [{ "tall": 90, "color": "#5352ed" }, { "tall": 40, "color": "#5352ed" }] }, { "location": "浙江省.杭州市", "items": [{ "tall": 150, "color": "#ff4757" }, { "tall": 70, "color": "#4ECDC4" }] }, { "location": "江苏省.南京市", "items": [{ "tall": 100, "color": "#ff4757" }, { "tall": 45, "color": "#FFD166" }] }, { "location": "湖北省.武汉市", "items": [{ "tall": 110, "color": "#ff4757" }, { "tall": 52, "color": "#6A0572" }] }, { "location": "陕西省.西安市", "items": [{ "tall": 85, "color": "#ff4757" }, { "tall": 38, "color": "#1A535C" }] }, { "location": "山东省.青岛市", "items": [{ "tall": 130, "color": "#ff4757" }, { "tall": 60, "color": "#FF8C42" }] }, { "location": "河南省.郑州市", "items": [{ "tall": 95, "color": "#ff4757" }, { "tall": 43, "color": "#7209B7" }] }, { "location": "福建省.厦门市", "items": [{ "tall": 140, "color": "#ff4757" }, { "tall": 65, "color": "#06D6A0" }] }, { "location": "辽宁省.大连市", "items": [{ "tall": 88, "color": "#ff4757" }, { "tall": 41, "color": "#118AB2" }] }]
}
try {
// 无后端接口, 此为预留
const response: any = await axiosInstance.get(`/dev${url}`, { baseURL: '' });
if (response.state === true && response.value && response.value.length > 0) {
return response.value; // 返回原始数据
} else {
console.warn('API调用失败或返回空数据将使用静态数据。');
return dataJson.barOptions;
}
} catch (error) {
console.error('获取图表数据失败将使用静态数据:', error);
return dataJson.barOptions;
}
};
const BarOptionsData = computed(() => {
return props.chartConfig.option.dataset.barOptions.map((item: unknown) =>
new BarOption(item as unknown as Partial<BarOption>)
);
});
onMounted(() => {
const iframe = iframeRef.value;
if (iframe) {
iframe.onload = async () => {
console.log(" Iframe content has loaded!");
if (iframe.contentWindow && (iframe.contentWindow as any).g) {
iframeApi = (iframe.contentWindow as any).g.active3d;
isIframeReady.value = true;
const data = await fetchData('/map');
props.chartConfig.option.dataset.barOptions = data;
} else {
console.error("无法访问 iframe 内的 g.active3d 对象。");
}
setTimeout(() => {
BarOptionsData.value.forEach((barConfig: any) => {
callIframeMethod("setProvinceFocusable", false)
// console.log(" Drawing bar with config:", barConfig);
callIframeMethod("createBar", barConfig);
});
}, 500); // 500ms延迟可根据实际情况调整
};
}
});
watch(
BarOptionsData,
(newBarOptionsData) => {
if (!isIframeReady.value) {
console.warn("Iframe API not ready, skipping bar rendering in watch.");
return;
}
console.log("Watch triggered with newBarOptionsData:", newBarOptionsData);
setTimeout(() => {
callIframeMethod("clearAllBars");
newBarOptionsData.forEach((barConfig: BarOption) => {
console.log(" Re-drawing bar with config:", barConfig);
callIframeMethod("createBar", barConfig);
});
}, 500);
},
{
immediate: true,
deep: false
}
);
/**
* 调用 iframe 内部方法的通用函数
*/
function callIframeMethod(methodName: string, ...args: any[]) {
if (iframeApi && typeof iframeApi[methodName] === 'function') {
iframeApi[methodName].apply(iframeApi, args);
} else if (isIframeReady.value) {
console.error(`方法 ${methodName} 在 iframe API 上不存在。`);
}
}
</script>
<style lang="scss" scoped>
.go-iframe-container {
overflow: hidden;
position: relative; // 为内部的绝对定位元素提供定位上下文
--inactive-border-color: #337ab7;
--inactive-text-color: #a0c0e0;
--inactive-bg-color: rgba(10, 25, 40, 0.7);
--active-border-color: #f7b731;
--active-text-color: #f7b731;
--active-text-shadow: 0 0 8px rgba(247, 183, 49, 0.7);
/* 边框L型括号的尺寸参数 */
--bracket-thickness: 3px;
/* 括号线条粗细 */
--bracket-arm-length: 8px;
/* 括号横向臂长 */
--vertical-segment-height: 8px;
/* 括号垂直部分顶部和底部的长度,中间留空 */
}
.button-container-wrapper {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
}
.button-container {
display: flex;
flex-direction: column;
gap: 10px;
width: fit-content;
padding: 0;
border-radius: 5px;
}
.button {
position: relative;
padding: 12px 30px;
font-size: 1.1em;
font-weight: bold;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
/* 平滑过渡效果 */
overflow: hidden;
--_current-border-color: var(--inactive-border-color);
--_current-text-color: var(--inactive-text-color);
--_current-text-shadow: none;
background-color: var(--inactive-bg-color);
/* 按钮背景色 */
color: var(--_current-text-color);
text-shadow: var(--_current-text-shadow);
/* 按钮本体的细线边框:顶部、底部、右侧 */
border-top: 1px solid var(--_current-border-color);
border-bottom: 1px solid var(--_current-border-color);
border-right: 1px solid var(--_current-border-color);
border-left: none;
/* 左侧边框由伪元素绘制 */
}
/* 激活状态的样式 */
.button.active {
--_current-border-color: var(--active-border-color);
--_current-text-color: var(--active-text-color);
--_current-text-shadow: var(--active-text-shadow);
color: var(--_current-text-color);
text-shadow: var(--_current-text-shadow);
border-top-color: var(--_current-border-color);
border-bottom-color: var(--_current-border-color);
border-right-color: var(--_current-border-color);
}
.button::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
width: var(--bracket-arm-length);
height: calc(100% + 2px);
border-color: var(--_current-border-color);
border-style: solid;
/* border-width: top right bottom left */
border-width: var(--bracket-thickness) 0 var(--bracket-thickness) var(--bracket-thickness);
/* 使用线性渐变背景来创建左侧垂直部分的断开效果 */
background: linear-gradient(to bottom,
var(--_current-border-color) 0%,
var(--_current-border-color) var(--vertical-segment-height),
transparent var(--vertical-segment-height),
transparent calc(100% - var(--vertical-segment-height)),
var(--_current-border-color) calc(100% - var(--vertical-segment-height)),
var(--_current-border-color) 100%) no-repeat left center / var(--bracket-thickness) 100%;
transition: border-color 0.3s ease, background 0.3s ease;
/* 过渡颜色变化 */
}
.button::after {
content: '';
position: absolute;
top: -1px;
right: -1px;
/* 向右偏移1px与父元素的右边缘对齐 */
width: var(--bracket-arm-length);
height: calc(100% + 2px);
border-color: var(--_current-border-color);
border-style: solid;
/* 伪元素的边框用于绘制 L 型括号的水平部分和右侧垂直部分的厚度 */
/* border-width: top right bottom left */
border-width: var(--bracket-thickness) var(--bracket-thickness) var(--bracket-thickness) 0;
/* 使用线性渐变背景来创建右侧垂直部分的断开效果 */
background: linear-gradient(to bottom,
var(--_current-border-color) 0%,
var(--_current-border-color) var(--vertical-segment-height),
transparent var(--vertical-segment-height),
transparent calc(100% - var(--vertical-segment-height)),
var(--_current-border-color) calc(100% - var(--vertical-segment-height)),
var(--_current-border-color) 100%) no-repeat right center / var(--bracket-thickness) 100%;
transition: border-color 0.3s ease, background 0.3s ease;
}
/* 非激活状态按钮的悬停效果 */
.button:not(.active):hover {
filter: brightness(1.1);
/* 略微提高亮度 */
}
.iframe-content {
width: 100%;
height: 100%;
border: none;
}
</style>