右侧菜单 电站倾斜摄像和漫游修改,打包配置修改
This commit is contained in:
parent
5b982cf9cd
commit
214a8139a0
@ -25,7 +25,6 @@ VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122'
|
||||
VITE_APP_BASE_API_URL = 'http://10.84.121.21:8093'
|
||||
## 开发环境 附件服务地址
|
||||
VITE_APP_ATTACHMENT_URL = 'https://211.99.26.225:12125'
|
||||
attachment
|
||||
|
||||
# 地图服务地址
|
||||
VITE_APP_MAP_URL = 'https://211.99.26.225:18085'
|
||||
|
||||
@ -13,7 +13,6 @@ VITE_APP_PREVIEW_URL = 'https://211.99.26.225:12125'
|
||||
|
||||
## 开发环境 附件服务地址
|
||||
VITE_APP_ATTACHMENT_URL = 'https://211.99.26.225:12125'
|
||||
attachment
|
||||
|
||||
# 地图服务地址
|
||||
VITE_APP_MAP_URL = 'https://211.99.26.225:18085'
|
||||
@ -10,23 +10,13 @@ VITE_APP_BASE_API = '/dev-api'
|
||||
VITE_APP_BASE_URL = 'http://localhost:8093'
|
||||
# 测试环境
|
||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
|
||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
|
||||
# 汤伟
|
||||
VITE_APP_BASE_URL = 'http://10.84.111.235:8093'
|
||||
# 李林
|
||||
# VITE_APP_BASE_URL = 'http://10.84.111.25:8093'
|
||||
|
||||
# 测试环境线上10.84.121.122:
|
||||
VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122'
|
||||
|
||||
|
||||
|
||||
|
||||
# 开发环境导入预览地址
|
||||
VITE_APP_BASE_API_URL = 'http://10.84.121.21:8093'
|
||||
## 开发环境 附件服务地址
|
||||
VITE_APP_ATTACHMENT_URL = 'https://211.99.26.225:12125'
|
||||
|
||||
# 地图服务地址
|
||||
VITE_APP_MAP_URL = 'https://211.99.26.225:18085'
|
||||
# ?menu=systemManageMenu&page=disposeManage
|
||||
@ -8,12 +8,7 @@ VITE_APP_BASE_API = '/prod-api'
|
||||
VITE_APP_BASE_URL = 'http://localhost:8093'
|
||||
## 生产环境导入预览地址
|
||||
VITE_APP_BASE_API_URL = 'https://211.99.26.225:12130/prod-api'
|
||||
## 生产环境预览 图片视频地址
|
||||
VITE_APP_PREVIEW_URL = 'https://211.99.26.225:12125'
|
||||
|
||||
## 开发环境 附件服务地址
|
||||
## 生产环境 附件服务地址
|
||||
VITE_APP_ATTACHMENT_URL = 'https://211.99.26.225:12125'
|
||||
attachment
|
||||
|
||||
# 地图服务地址
|
||||
VITE_APP_MAP_URL = 'https://211.99.26.225:18085'
|
||||
@ -1,3 +1,5 @@
|
||||
registry=https://registry.npmmirror.com
|
||||
fetch-retries=5
|
||||
strict-peer-dependencies=false
|
||||
# 添加 Node.js 内存配置
|
||||
node-options=--max-old-space-size=4096
|
||||
@ -320,7 +320,6 @@ const loadRvcdOptions = async () => {
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const list = Array.isArray(res?.data?.data)
|
||||
? res.data.data
|
||||
: Array.isArray(res?.data)
|
||||
|
||||
@ -1,55 +1,91 @@
|
||||
<template>
|
||||
<a-tooltip placement="left" trigger="click" color="transparent">
|
||||
<div class="calculate-wrapper" :ref="componentRef">
|
||||
<a-tooltip
|
||||
placement="left"
|
||||
color="transparent"
|
||||
:open="tooltipVisible"
|
||||
@openChange="handleTooltipVisibleChange"
|
||||
>
|
||||
<template #title>
|
||||
<div class="calculate">
|
||||
<div class="calculate" :ref="tipRef">
|
||||
<a-tooltip
|
||||
v-for="(item, index) in tools"
|
||||
:key="item.title"
|
||||
:title="item.title"
|
||||
placement="top"
|
||||
>
|
||||
<div class="map-controller-item" :class="{'active': active === index}" @click="calculateClick(index)">
|
||||
<div
|
||||
class="map-controller-item"
|
||||
:class="{ active: active === index }"
|
||||
@click="calculateClick(index)"
|
||||
>
|
||||
<i :class="`icon iconfont icon-${item.icon}`"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<div class="map-item">
|
||||
<div class="map-item" @click="handleClick">
|
||||
<i class="icon iconfont icon-measuringTool"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ref, onMounted, onUnmounted, toRef } from 'vue';
|
||||
const props = defineProps<{
|
||||
map: any;
|
||||
}>();
|
||||
const map = props.map;
|
||||
const map = toRef(props, 'map');
|
||||
const active = ref(-1);
|
||||
const tooltipVisible = ref(false);
|
||||
const componentRef = ref<any | null>(null);
|
||||
const tipRef = ref<any | null>(null);
|
||||
const tools = ref([
|
||||
{ title: "测距", icon: "ranging" },
|
||||
{ title: "测面积", icon: "measure" },
|
||||
{ title: "清除", icon: "clear" },
|
||||
{ title: '测距', icon: 'ranging' },
|
||||
{ title: '测面积', icon: 'measure' },
|
||||
{ title: '清除', icon: 'clear' }
|
||||
]);
|
||||
const calculateClick = (index: number) => {
|
||||
console.log("点击了测量工具", index);
|
||||
console.log('点击了测量工具', index);
|
||||
active.value = index;
|
||||
switch (index) {
|
||||
case 0:
|
||||
map.lengthCalculate();
|
||||
map.value.lengthCalculate();
|
||||
break;
|
||||
case 1:
|
||||
map.areCalculate();
|
||||
map.value.areCalculate();
|
||||
break;
|
||||
case 2:
|
||||
active.value = -1;
|
||||
map.removeQueryLayer();
|
||||
map.value.removeQueryLayer();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const handleClick = () => {
|
||||
tooltipVisible.value = !tooltipVisible.value;
|
||||
};
|
||||
const handleTooltipVisibleChange = (visible: boolean) => {
|
||||
tooltipVisible.value = visible;
|
||||
};
|
||||
// 点击外部关闭tooltip
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
componentRef.value &&
|
||||
!componentRef.value.contains(event.target as Node)
|
||||
) {
|
||||
if (tipRef.value && !tipRef.value.contains(event.target as Node)) {
|
||||
tooltipVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.map-item {
|
||||
|
||||
@ -22,7 +22,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<a-tooltip title="图层管理" placement="left">
|
||||
<div class="map-item" @click="handleClick">
|
||||
<div
|
||||
class="map-item"
|
||||
:class="{ 'is-active': active }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i class="icon iconfont icon-layer"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
@ -30,11 +34,21 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, onUnmounted } from 'vue';
|
||||
import { ref, onMounted, computed, onUnmounted, watch } from 'vue';
|
||||
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
|
||||
const props = defineProps<{
|
||||
map?: any;
|
||||
visible?: boolean;
|
||||
active?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', v: boolean): void;
|
||||
}>();
|
||||
|
||||
const mapOrchestrator = useMapOrchestrator();
|
||||
const mapConfigStore = useMapConfigStore();
|
||||
const mapViewStore = useMapViewStore();
|
||||
@ -62,6 +76,21 @@ const tooltipVisible = ref(false);
|
||||
const componentRef = ref<any | null>(null);
|
||||
const tipRef = ref<any | null>(null);
|
||||
|
||||
// 同步外部 visible prop 到内部 tooltipVisible
|
||||
watch(
|
||||
() => props.visible,
|
||||
newVal => {
|
||||
if (newVal !== undefined && tooltipVisible.value !== newVal) {
|
||||
tooltipVisible.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 内部 tooltipVisible 变化时通知父组件
|
||||
watch(tooltipVisible, newVal => {
|
||||
emit('update:visible', newVal);
|
||||
});
|
||||
|
||||
const onCheck = async (v: any, e: any) => {
|
||||
await mapOrchestrator.applyLayerSelection({
|
||||
checkedKeys: v,
|
||||
@ -114,6 +143,10 @@ onUnmounted(() => {
|
||||
background-color: #005292;
|
||||
color: #ffffff;
|
||||
}
|
||||
&.is-active {
|
||||
background-color: #005292;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.ant-tooltip-inner {
|
||||
height: auto !important;
|
||||
|
||||
249
frontend/src/components/mapController/OSBGController.vue
Normal file
249
frontend/src/components/mapController/OSBGController.vue
Normal file
@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<div class="osbg-controller" :ref="componentRef">
|
||||
<a-tooltip
|
||||
:placement="inTransition ? 'left' : 'leftBottom'"
|
||||
:trigger="inTransition ? 'hover' : 'click'"
|
||||
:color="inTransition ? '#008BFF' : 'transparent'"
|
||||
:open="inTransition ? true : tooltipVisible"
|
||||
@openChange="onOpenChange"
|
||||
>
|
||||
<template #title>
|
||||
<!-- 过渡中:倒计时 -->
|
||||
<div v-if="inTransition" class="countdown-tip">
|
||||
电站倾斜摄影 {{ countdown }}s
|
||||
</div>
|
||||
<!-- 正常模式:菜单面板 -->
|
||||
<div v-else class="osbg-tree" :ref="tipRef">
|
||||
<div class="osbg-tree-title">电站倾斜摄像列表</div>
|
||||
<div class="osbg-tree-content">
|
||||
<a-menu
|
||||
v-if="treeData && treeData.length > 0"
|
||||
mode="inline"
|
||||
:selectable="false"
|
||||
:defaultOpenKeys="treeData.map(g => g.baseId)"
|
||||
>
|
||||
<a-sub-menu v-for="group in treeData" :key="group.baseId">
|
||||
<template #title>
|
||||
<span class="group-title">{{ group.baseName }}</span>
|
||||
</template>
|
||||
<a-menu-item
|
||||
v-for="item in group.children"
|
||||
:key="item.stnm + '_' + group.baseId"
|
||||
class="leaf-item"
|
||||
>
|
||||
<div class="leaf-content">
|
||||
<span class="leaf-name">{{ item.stnm }}</span>
|
||||
<a-switch
|
||||
:checked="item.checked !== false"
|
||||
size="small"
|
||||
@click.stop
|
||||
@change="(checked: boolean) => handleSwitchChange(item, checked)"
|
||||
/>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
<div v-else class="empty-tip">
|
||||
<span class="leaf-name">暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tooltip title="电站倾斜摄影" placement="left">
|
||||
<div
|
||||
class="map-item"
|
||||
:class="{
|
||||
'is-active': active,
|
||||
'is-disabled': disabled || inTransition
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i class="icon iconfont icon-obliquePhotography"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
map?: any;
|
||||
visible?: boolean;
|
||||
active?: boolean;
|
||||
treeData?: any[];
|
||||
disabled?: boolean;
|
||||
countdown?: number;
|
||||
inTransition?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', v: boolean): void;
|
||||
}>();
|
||||
|
||||
const tooltipVisible = ref(false);
|
||||
const componentRef = ref<any | null>(null);
|
||||
const tipRef = ref<any | null>(null);
|
||||
|
||||
// 同步外部 visible prop 到内部 tooltipVisible
|
||||
watch(
|
||||
() => props.visible,
|
||||
newVal => {
|
||||
if (newVal !== undefined && tooltipVisible.value !== newVal) {
|
||||
tooltipVisible.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 内部 tooltipVisible 变化时通知父组件
|
||||
watch(tooltipVisible, newVal => {
|
||||
emit('update:visible', newVal);
|
||||
});
|
||||
|
||||
// 过渡结束时:如果 visible 已为 true(父组件自动选中),不做清理
|
||||
watch(
|
||||
() => props.inTransition,
|
||||
(transitioning, wasTransitioning) => {
|
||||
if (wasTransitioning && !transitioning) {
|
||||
if (!props.visible) {
|
||||
tooltipVisible.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.inTransition || props.disabled) return;
|
||||
tooltipVisible.value = !tooltipVisible.value;
|
||||
};
|
||||
|
||||
const onOpenChange = (visible: boolean) => {
|
||||
if (props.inTransition) return;
|
||||
tooltipVisible.value = visible;
|
||||
};
|
||||
|
||||
// a-switch 切换
|
||||
const handleSwitchChange = (item: any, checked: boolean) => {
|
||||
item.checked = checked;
|
||||
};
|
||||
|
||||
// 点击外部关闭tooltip
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
componentRef.value &&
|
||||
!componentRef.value.contains(event.target as Node)
|
||||
) {
|
||||
if (tipRef.value && !tipRef.value.contains(event.target as Node)) {
|
||||
tooltipVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.map-item {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
color: #000;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
.iconfont {
|
||||
font-size: 20px;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #005292;
|
||||
color: #ffffff;
|
||||
}
|
||||
&.is-active {
|
||||
background-color: #005292;
|
||||
color: #ffffff;
|
||||
}
|
||||
&.is-disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.ant-tooltip-inner {
|
||||
height: auto !important;
|
||||
max-height: 400px !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
.osbg-tree {
|
||||
width: 230px;
|
||||
max-height: 300px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 2px #00000026;
|
||||
background-color: #fff;
|
||||
cursor: initial;
|
||||
border: 1px solid #ccdae7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.osbg-tree-title {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
border-bottom: 1px dotted #ccc;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.osbg-tree-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
.countdown-tip {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
// 菜单样式
|
||||
.ant-menu-inline {
|
||||
border-right: none !important;
|
||||
}
|
||||
.group-title {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
.leaf-item {
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
padding-left: 32px !important;
|
||||
margin: 0 !important;
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
.leaf-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.leaf-name {
|
||||
font-size: 13px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.empty-tip {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
@ -1,36 +1,69 @@
|
||||
<template>
|
||||
<div class="map-controller" :style="{ right: drawerOpen ? '480px' : '12px' }">
|
||||
<div class="map-controller-group" v-for="item in controllers">
|
||||
<a-tooltip
|
||||
:title="child.name"
|
||||
:placement="child.key === 'Calculate' ? 'right' : 'left'"
|
||||
v-for="child in item.children"
|
||||
:key="child.key"
|
||||
>
|
||||
<component v-if="child.component" :is="child.component" :map="map" />
|
||||
<div
|
||||
class="map-controller-group"
|
||||
v-for="(item, index) in controllers"
|
||||
:key="index"
|
||||
>
|
||||
<template v-for="child in item.children" :key="child.key">
|
||||
<!-- 组件项:直接渲染,自身已有交互逻辑 -->
|
||||
<component
|
||||
v-if="child.component"
|
||||
:is="child.component"
|
||||
:map="map"
|
||||
:visible="activeKey === child.key"
|
||||
:active="activeKey === child.key"
|
||||
:treeData="tiltPhotoTree"
|
||||
:disabled="is3DTransition && child.key === 'OSBGController'"
|
||||
:countdown="countdown"
|
||||
:inTransition="is3DTransition && child.key === 'OSBGController'"
|
||||
@update:visible="(v: boolean) => handleComponentVisibleToggle(child.key, v)"
|
||||
/>
|
||||
<!-- 非组件项:包裹 tooltip 显示名称 -->
|
||||
<a-tooltip
|
||||
v-else
|
||||
:title="getTooltipTitle(child)"
|
||||
placement="left"
|
||||
:color="getTooltipColor(child)"
|
||||
:open="getTooltipOpen(child)"
|
||||
>
|
||||
<div
|
||||
class="map-controller-item"
|
||||
:class="{ 'is-active': child.key === 'TJ' && tjVisible }"
|
||||
:class="{
|
||||
'is-active':
|
||||
(child.key === 'TJ' && tjVisible) ||
|
||||
(activeKey === child.key && child.key !== 'threedRoam'),
|
||||
'is-disabled': isItemDisabled(child)
|
||||
}"
|
||||
@click="handleControllerClick(child)"
|
||||
>
|
||||
<i class="icon iconfont" :class="'icon-' + child.icon"></i>
|
||||
<i
|
||||
class="icon iconfont"
|
||||
:class="
|
||||
child.key === 'threedRoam' && activeKey === 'threedRoam'
|
||||
? 'icon-closeCircle'
|
||||
: 'icon-' + child.icon
|
||||
"
|
||||
></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { ref, watch, toRef, computed, onUnmounted } from 'vue';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
import Calculate from './Calculate.vue';
|
||||
import LayerController from './LayerController.vue';
|
||||
import OSBGController from './OSBGController.vue';
|
||||
import { getAllTiltPhotoTree } from '@/api/system/map/TiltPhotoManagement';
|
||||
|
||||
const props = defineProps<{
|
||||
map: any;
|
||||
onClick: (key: any, mapType: any) => void;
|
||||
}>();
|
||||
const map = props.map;
|
||||
const map = toRef(props, 'map');
|
||||
|
||||
// 使用 Pinia store
|
||||
const uiStore = useUiStore();
|
||||
@ -56,6 +89,141 @@ watch(
|
||||
|
||||
const isFullScreen = ref(false);
|
||||
|
||||
// 倾斜摄影数据
|
||||
const tiltPhotoTree = ref<any[]>([]);
|
||||
|
||||
// 将 API 返回的扁平 list 转换为树结构
|
||||
const buildTiltPhotoTree = (list: any[]) => {
|
||||
if (!list || list.length === 0) return [];
|
||||
const baseMap = new Map<string, { baseName: string; children: any[] }>();
|
||||
for (const item of list) {
|
||||
const id = String(item.baseId ?? '');
|
||||
if (!baseMap.has(id)) {
|
||||
baseMap.set(id, { baseName: item.baseName || '', children: [] });
|
||||
}
|
||||
}
|
||||
for (const item of list) {
|
||||
const id = String(item.baseId ?? '');
|
||||
const group = baseMap.get(id);
|
||||
if (group) {
|
||||
group.children.push({ ...item, checked: true });
|
||||
}
|
||||
}
|
||||
return Array.from(baseMap.entries())
|
||||
.map(([baseId, group]) => ({
|
||||
baseId,
|
||||
baseName: group.baseName,
|
||||
children: group.children
|
||||
}))
|
||||
.sort((a, b) => String(a.baseId).localeCompare(String(b.baseId)));
|
||||
};
|
||||
|
||||
// 请求倾斜摄影树数据
|
||||
const fetchTiltPhotoTree = async () => {
|
||||
try {
|
||||
const res = await getAllTiltPhotoTree({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'url',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'http'
|
||||
},
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'OSGB'
|
||||
}
|
||||
]
|
||||
},
|
||||
sort: [
|
||||
{ field: 'rstcdStepSort', dir: 'asc' },
|
||||
{ field: 'ttpwr', dir: 'desc' }
|
||||
]
|
||||
});
|
||||
const list = res.data.data;
|
||||
tiltPhotoTree.value = buildTiltPhotoTree(list);
|
||||
} catch (err) {
|
||||
console.error('获取倾斜摄影数据失败:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// 3D过渡动画状态
|
||||
// layerController / OSBGController / threedRoam 三者互斥,最多一个被选中
|
||||
const activeKey = ref<string | null>(null);
|
||||
const is3DTransition = ref(false);
|
||||
const countdown = ref(0);
|
||||
let transitionTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const start3DTransition = () => {
|
||||
cleanup3DTransition();
|
||||
is3DTransition.value = true;
|
||||
countdown.value = 5;
|
||||
|
||||
const tick = () => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
// 过渡完成
|
||||
is3DTransition.value = false;
|
||||
countdown.value = 0;
|
||||
// 自动选中OSBGController
|
||||
activeKey.value = 'OSBGController';
|
||||
transitionTimer = null;
|
||||
} else {
|
||||
transitionTimer = setTimeout(tick, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
transitionTimer = setTimeout(tick, 1000);
|
||||
};
|
||||
|
||||
const cleanup3DTransition = () => {
|
||||
if (transitionTimer) {
|
||||
clearTimeout(transitionTimer);
|
||||
transitionTimer = null;
|
||||
}
|
||||
is3DTransition.value = false;
|
||||
countdown.value = 0;
|
||||
};
|
||||
|
||||
// Tooltip 辅助函数(仅用于非组件项,如 threedRoam)
|
||||
const getTooltipTitle = (child: any) => {
|
||||
if (is3DTransition.value && child.key === 'threedRoam') {
|
||||
return child.name + ' ' + countdown.value + 's';
|
||||
}
|
||||
return child.name;
|
||||
};
|
||||
|
||||
const getTooltipColor = (child: any) => {
|
||||
if (is3DTransition.value && child.key === 'threedRoam') {
|
||||
return '#008BFF';
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getTooltipOpen = (child: any) => {
|
||||
if (is3DTransition.value && child.key === 'threedRoam') {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const isItemDisabled = (child: any) => {
|
||||
return is3DTransition.value && child.key === 'threedRoam';
|
||||
};
|
||||
|
||||
// 组件切换回调(插槽面板 open/close,互斥)
|
||||
const handleComponentVisibleToggle = (key: string, visible: boolean) => {
|
||||
activeKey.value = visible ? key : null;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanup3DTransition();
|
||||
});
|
||||
|
||||
// 响应式的控制器配置
|
||||
const controllers: any = computed(() => [
|
||||
{
|
||||
@ -68,7 +236,8 @@ const controllers: any = computed(() => [
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
!uiStore.isRoaming
|
||||
? {
|
||||
children: [
|
||||
// {
|
||||
// name: "定位",
|
||||
@ -86,7 +255,8 @@ const controllers: any = computed(() => [
|
||||
icon: 'zoomOut'
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
: {},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
@ -133,7 +303,7 @@ const controllers: any = computed(() => [
|
||||
{
|
||||
name: '电站倾斜摄影',
|
||||
key: 'OSBGController',
|
||||
icon: 'obliquePhotography'
|
||||
component: OSBGController
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -187,46 +357,46 @@ const handleControllerClick = (item: any) => {
|
||||
switch (item.key) {
|
||||
case 'fullScreen':
|
||||
isFullScreen.value = !isFullScreen.value;
|
||||
map.flyTopanto([92.39728, 29.24587], 14);
|
||||
// console.log(mapClass.hasLayer('eng_point'));
|
||||
// map.jdPanelControlShowAndHidden("01", false);
|
||||
// map.mdLayerTreeShowOrHidden('eng_point', false);
|
||||
// map.mdLayerTreeShowOrHidden('wq_countryWq_point', true);
|
||||
// map.controlBaseLayerTreeShowAndHidden(
|
||||
// 'eng_point',
|
||||
// 'eng_point',
|
||||
// !isFullScreen.value
|
||||
// );
|
||||
// map.controlBaseLayerTreeShowAndHidden('lcj_bhq','lcj_bhq',!isFullScreen.value);
|
||||
// toggleFullScreen();
|
||||
toggleFullScreen();
|
||||
break;
|
||||
case 'zoomIn':
|
||||
map.zoomToggle('in');
|
||||
map.value.zoomToggle('in');
|
||||
break;
|
||||
case 'zoomOut':
|
||||
map.zoomToggle('out');
|
||||
map.value.zoomToggle('out');
|
||||
break;
|
||||
case 'dim':
|
||||
uiStore.mapType = uiStore.mapType === '2D' ? '3D' : '2D';
|
||||
if (uiStore.mapType === '2D') {
|
||||
uiStore.drawerOpen = true;
|
||||
} else {
|
||||
case 'dim': // 切换3D/2D
|
||||
{
|
||||
const switchingTo3D = uiStore.mapType === '2D';
|
||||
uiStore.mapType = switchingTo3D ? '3D' : '2D';
|
||||
if (switchingTo3D) {
|
||||
// 2D→3D:关闭抽屉,请求倾斜摄影数据,开始过渡
|
||||
uiStore.drawerOpen = false;
|
||||
fetchTiltPhotoTree();
|
||||
start3DTransition();
|
||||
} else {
|
||||
// 3D→2D:打开抽屉,清理过渡
|
||||
uiStore.drawerOpen = true;
|
||||
uiStore.isRoaming = false;
|
||||
cleanup3DTransition();
|
||||
activeKey.value = null;
|
||||
}
|
||||
props.onClick('dim', uiStore.mapType);
|
||||
}
|
||||
break;
|
||||
case 'rightDrawer':
|
||||
// 使用全局事件切换右侧面板
|
||||
// GlobalEvents.get('rightDrawerState').set(!drawerOpen);
|
||||
case 'threedRoam': // 流域三维漫游
|
||||
if (is3DTransition.value) break;
|
||||
// 互斥:选中threedRoam时取消其他选中
|
||||
activeKey.value = activeKey.value === 'threedRoam' ? null : 'threedRoam';
|
||||
uiStore.isRoaming = !uiStore.isRoaming;
|
||||
break;
|
||||
// 可以在这里添加其他控制器的处理逻辑
|
||||
case 'screenShot':
|
||||
map.mapOutPut();
|
||||
map.value.mapOutPut();
|
||||
break;
|
||||
case 'TJ':
|
||||
tjVisible.value = !tjVisible.value;
|
||||
props.onClick(4, null);
|
||||
// map.addTertiarybasinLayer(servers.Tertiarybasin, '#4DFFDD', '#92A0A5')
|
||||
break;
|
||||
default:
|
||||
console.log(`点击了控制器: ${item.name}`);
|
||||
@ -264,6 +434,10 @@ const handleControllerClick = (item: any) => {
|
||||
background-color: #005292;
|
||||
color: #ffffff;
|
||||
}
|
||||
&.is-disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
.map-controller-group:not(:first-child) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="map-filter-container">
|
||||
<div class="map-filter-container" v-show="!uiStore.isRoaming">
|
||||
<div class="toolbar">
|
||||
<a-form :model="formModel">
|
||||
<a-row :gutter="10">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mapLegendView">
|
||||
<div class="mapLegendView" v-show="!uiStore.isRoaming">
|
||||
<div class="legendTitle">
|
||||
图例
|
||||
<span class="legendBtn" @click="isOpen = !isOpen">
|
||||
@ -70,9 +70,11 @@ import { ref, computed } from 'vue';
|
||||
import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
||||
import { useMapStore } from '@/store/modules/map';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
import LegendItem from '@/components/mapLegend/LegendItem.vue';
|
||||
|
||||
const mapStore = useMapStore();
|
||||
const uiStore = useUiStore();
|
||||
const mapConfigStore = useMapConfigStore();
|
||||
const mapOrchestrator = useMapOrchestrator();
|
||||
const isOpen = ref(true);
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
const isOpen = ref(true);
|
||||
const uiStore = useUiStore();
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
|
||||
const jidiDataNum = ref(9);
|
||||
@ -12,7 +14,7 @@ onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="jidiSelectorMod">
|
||||
<div class="jidiSelectorMod" v-show="!uiStore.isRoaming">
|
||||
<a-spin :spinning="JidiSelectEventStore.loading">
|
||||
<div
|
||||
class="qgc-dropdown-select"
|
||||
@ -35,6 +37,7 @@ onMounted(() => {});
|
||||
0,
|
||||
jidiDataNum
|
||||
)"
|
||||
:key="index"
|
||||
:class="{ selected: i.selected }"
|
||||
@click="itemClick(index)"
|
||||
>
|
||||
|
||||
@ -6,6 +6,8 @@ export const useUiStore = defineStore('ui', () => {
|
||||
const drawerOpen = ref(true);
|
||||
const mapType = ref('2D');
|
||||
const mapSwitchCompletedTick = ref(0);
|
||||
// 3d 流域三维漫游
|
||||
const isRoaming = ref(false);
|
||||
|
||||
// 切换抽屉状态
|
||||
const toggleDrawer = () => {
|
||||
@ -23,6 +25,7 @@ export const useUiStore = defineStore('ui', () => {
|
||||
|
||||
return {
|
||||
drawerOpen,
|
||||
isRoaming,
|
||||
toggleDrawer,
|
||||
setDrawerOpen,
|
||||
mapType,
|
||||
|
||||
@ -253,7 +253,6 @@ const ruleColumnsMap: any = {
|
||||
},
|
||||
{
|
||||
key: 'avqSfdb',
|
||||
title: '多年平均流量*10%是否达标',
|
||||
dataIndex: 'avqSfdbName',
|
||||
visible: true,
|
||||
sort: true,
|
||||
|
||||
@ -11,52 +11,39 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineAsyncComponent } from "vue";
|
||||
import { ref, defineAsyncComponent } from 'vue';
|
||||
|
||||
// Tab 配置
|
||||
const tabs = [
|
||||
{
|
||||
name: "图层管理",
|
||||
value: "layer",
|
||||
name: '图层管理',
|
||||
value: 'layer'
|
||||
},
|
||||
{
|
||||
name: "图例结构管理",
|
||||
value: "legend",
|
||||
name: '图例结构管理',
|
||||
value: 'legend'
|
||||
},
|
||||
{
|
||||
name: "图例数据管理",
|
||||
value: "legendData",
|
||||
},
|
||||
// {
|
||||
// name: "电站专题管理",
|
||||
// value: "station",
|
||||
// },
|
||||
// {
|
||||
// name: "沿程配置管理",
|
||||
// value: "config",
|
||||
// },
|
||||
// {
|
||||
// name: "倾斜摄影管理",
|
||||
// value: "tilt",
|
||||
// },
|
||||
name: '图例数据管理',
|
||||
value: 'legendData'
|
||||
}
|
||||
];
|
||||
|
||||
// 当前激活的 tab
|
||||
const activeTab = ref("layer");
|
||||
const activeTab = ref('layer');
|
||||
|
||||
// 获取对应组件的方法
|
||||
const getComponent = (value: string) => {
|
||||
const componentMap: Record<string, any> = {
|
||||
layer: defineAsyncComponent(() => import("./components/LayerManagement/index.vue")),
|
||||
legend: defineAsyncComponent(() => import("./components/LegendStructure/index.vue")),
|
||||
legendData: defineAsyncComponent(() => import("./components/LegendData/index.vue")),
|
||||
station: defineAsyncComponent(
|
||||
() => import("./components/StationManagement/index.vue")
|
||||
layer: defineAsyncComponent(
|
||||
() => import('./components/LayerManagement/index.vue')
|
||||
),
|
||||
config: defineAsyncComponent(() => import("./components/ConfigManagement/index.vue")),
|
||||
tilt: defineAsyncComponent(
|
||||
() => import("./components/TiltPhotoManagement/index.vue")
|
||||
legend: defineAsyncComponent(
|
||||
() => import('./components/LegendStructure/index.vue')
|
||||
),
|
||||
legendData: defineAsyncComponent(
|
||||
() => import('./components/LegendData/index.vue')
|
||||
)
|
||||
};
|
||||
|
||||
return componentMap[value] || null;
|
||||
|
||||
@ -120,6 +120,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
||||
if (id.includes('node_modules')) {
|
||||
// 将 element-plus, lodash 等大型库单独拆分
|
||||
if (id.includes('element-plus')) return 'element-plus';
|
||||
if (id.includes('ant-design-vue')) return 'ant-design-vue';
|
||||
if (id.includes('lodash')) return 'lodash';
|
||||
if (id.includes('cesium')) return 'cesium';
|
||||
// 其他 node_modules 归为 vendor
|
||||
|
||||
Loading…
Reference in New Issue
Block a user