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

287 lines
10 KiB
Vue
Raw Normal View History

2025-08-25 20:27:25 +08:00
<template>
<div class="go-iframe-container" :style="{ width: w + 'px', height: h + 'px' }">
2025-08-28 17:12:22 +08:00
<!-- 按钮容器绝对定位在左上角 -->
<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>
2025-08-27 10:01:26 +08:00
<iframe ref="iframeRef" class="iframe-content" src="/static/index.html"></iframe>
2025-08-25 20:27:25 +08:00
</div>
</template>
<script setup lang="ts">
2025-08-28 17:12:22 +08:00
import axios from 'axios';
2025-08-25 20:27:25 +08:00
import config, { BarOption } from './config';
2025-08-28 17:12:22 +08:00
import { PropType, toRefs, ref, onMounted, computed, watch } from 'vue';
2025-08-25 20:27:25 +08:00
import dataJson from './data.json'
import axiosInstance from '@/api/axios';
2025-08-25 20:27:25 +08:00
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)
2025-08-28 17:12:22 +08:00
const buttons = ref([
{ id: 1, label: '场所分布情况' },
{ id: 2, label: '当日作业情况' }
]);
const activeButtonId = ref(buttons.value[0].id);
2025-08-25 20:27:25 +08:00
2025-08-28 17:12:22 +08:00
// 按钮点击事件
const selectButton = (id: number) => {
activeButtonId.value = id; // 更新激活状态的 ID
console.log(`按钮 ${id} 被选中`);
updateChartData(id);
};
/**
* 根据按钮ID更新图表数据
* @param buttonId 当前激活的按钮ID
*/
const updateChartData = async (buttonId: number) => {
2025-09-03 14:50:26 +08:00
let dataToRender: any = [];
2025-08-28 17:12:22 +08:00
if (buttonId === 1) {
2025-08-28 19:45:19 +08:00
dataToRender = await fetchData('/spaceMapMock');
2025-08-28 17:12:22 +08:00
} else if (buttonId === 2) {
2025-08-28 19:45:19 +08:00
dataToRender = await fetchData('/dayMapMock');
2025-08-28 17:12:22 +08:00
} else {
2025-09-03 14:50:26 +08:00
console.warn(`未知的按钮ID: ${buttonId}`);
2025-08-28 17:12:22 +08:00
}
props.chartConfig.option.dataset.barOptions = dataToRender;
console.log(`已为按钮 ${buttonId} 加载 ${dataToRender.length} 条数据。`);
};
const fetchData = async (url: string) => {
2025-08-28 19:45:19 +08:00
if (url == '/spaceMapMock') {
return dataJson.barOptions;
2025-09-03 14:50:26 +08:00
} else {
2025-08-28 19:45:19 +08:00
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" }] }]
}
2025-09-03 14:50:26 +08:00
// 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;
// }
2025-08-28 17:12:22 +08:00
};
2025-08-25 20:27:25 +08:00
const BarOptionsData = computed(() => {
2025-08-28 17:12:22 +08:00
return props.chartConfig.option.dataset.barOptions.map((item: unknown) =>
2025-08-25 20:27:25 +08:00
new BarOption(item as unknown as Partial<BarOption>)
);
});
onMounted(() => {
const iframe = iframeRef.value;
if (iframe) {
2025-08-28 17:12:22 +08:00
iframe.onload = async () => {
2025-08-25 20:27:25 +08:00
console.log(" Iframe content has loaded!");
if (iframe.contentWindow && (iframe.contentWindow as any).g) {
iframeApi = (iframe.contentWindow as any).g.active3d;
isIframeReady.value = true;
2025-08-28 17:12:22 +08:00
2025-09-03 14:50:26 +08:00
const data = await fetchData('/spaceMapMock');
2025-08-28 17:12:22 +08:00
props.chartConfig.option.dataset.barOptions = data;
2025-08-25 20:27:25 +08:00
} else {
console.error("无法访问 iframe 内的 g.active3d 对象。");
}
2025-08-28 17:12:22 +08:00
setTimeout(() => {
BarOptionsData.value.forEach((barConfig: any) => {
callIframeMethod("setProvinceFocusable", false)
// console.log(" Drawing bar with config:", barConfig);
2025-08-28 17:12:22 +08:00
callIframeMethod("createBar", barConfig);
});
}, 500); // 500ms延迟可根据实际情况调整
2025-08-25 20:27:25 +08:00
};
}
});
2025-08-28 17:12:22 +08:00
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);
2025-08-25 20:27:25 +08:00
setTimeout(() => {
2025-08-28 17:12:22 +08:00
callIframeMethod("clearAllBars");
newBarOptionsData.forEach((barConfig: BarOption) => {
console.log(" Re-drawing bar with config:", barConfig);
2025-08-25 20:27:25 +08:00
callIframeMethod("createBar", barConfig);
});
2025-08-28 17:12:22 +08:00
}, 500);
},
{
immediate: true,
deep: false
2025-08-25 20:27:25 +08:00
}
2025-08-28 17:12:22 +08:00
);
2025-08-25 20:27:25 +08:00
/**
* 调用 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;
2025-08-28 17:12:22 +08:00
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);
/* 略微提高亮度 */
2025-08-25 20:27:25 +08:00
}
.iframe-content {
width: 100%;
height: 100%;
border: none;
}
2025-08-28 17:12:22 +08:00
</style>