| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110 |
- <template>
- <el-container class="container">
- <!-- 侧边栏 --start -->
- <el-aside :class="asideWidth" id="el-aside">
- <el-menu
- ref="menu"
- :collapse="isCollapse"
- :uniqueOpened="true"
- default-active=""
- class="el-menu-vertical-demo"
- text-color="#303133"
- background-color="#f8f9fa"
- active-text-color="#409eff"
- :collapse-transition="false"
- >
- <!-- 展开收缩控制按钮 -->
- <el-menu-item index="0" @click="isCollapse=!isCollapse" id="fold">
- <i
- class="iconfont iconshouqi iconfont2"
- style="display:block;margin:0;text-align:center"
- :class="isCollapse?'rotate':'rotate2'"
- ></i>
- </el-menu-item>
- <!-- 加载数据 -->
- <el-sub-menu index="1">
- <template #title>
- <i class="iconfont iconshuju iconfont2"></i>
- <span>数据服务</span>
- </template>
- <el-sub-menu v-for="(data,index) in server_config" :key="data.id" :index="data.id" popper-append-to-body="false">
- <template #title>{{data.name}}</template>
- <div class="box" :index="index">
- <div
- class="imgbox"
- v-for="data2 in data.children"
- :key="data2.name"
- @click="addDatas(data.id,data2)"
- >
- <img :src="data2.thumbnail" alt class="img" />
- <img src="/img/common/cross.png" alt class="cross" v-show="data2.state != 0" />
- <span>{{data2.name}}</span>
- <el-popconfirm
- title="确定删除吗?"
- confirmButtonText="确认"
- cancelButtonText="取消"
- @confirm="DeleteDates(data.id,data2)"
- >
- <template #reference>
- <CircleClose
- v-show="data2.state != 0 && data.id !='baseLayer'"
- title="删除"
- class="circle-close-icon"
- />
- </template>
- </el-popconfirm>
- </div>
- </div>
- </el-sub-menu>
-
- <!-- 本地服务二级菜单 -->
- <el-sub-menu index="1-4">
- <template #title>自定义服务</template>
- <el-menu-item
- v-for="(service, index) in addCustomServices"
- :key="service.id || index"
- :index="'1-4-' + (service.id || index)"
- @dblclick="loadCustomService(service)"
- class="custom-service-item"
- >
- <span class="service-name" :class="{ 'service-loaded': isServiceLoaded(service) }">{{ service.name }}</span>
- <el-popconfirm
- v-if="isServiceLoaded(service)"
- title="确定卸载吗?"
- confirmButtonText="确认"
- cancelButtonText="取消"
- @confirm="unloadCustomService(service)"
- >
- <template #reference>
- <el-icon class="delete-service-icon" title="卸载">
- <CircleClose />
- </el-icon>
- </template>
- </el-popconfirm>
- </el-menu-item>
- </el-sub-menu>
-
- <el-menu-item index="1-5" @click="addCustomService" class="no-active-style">
- <el-icon @click.stop="addCustomService" class="square-plus-icon"><Plus /></el-icon>
- </el-menu-item>
- </el-sub-menu>
- <!-- 加载组件 -->
- <el-sub-menu v-for="(data,index) in config" :key="index" :index="data.id">
- <template #title>
- <i :class="data.icon" class="iconfont2"></i>
- <span>{{data.name}}</span>
- </template>
- <div class="box">
- <div
- class="imgbox"
- v-for="data2 in data.children"
- :key="data2.component"
- @click="data2.component && change(data2.component,data2.destroy)"
- >
- <img :src="data2.imgSrc" alt class="img" />
- <img
- src="/img/common/cross.png"
- alt
- class="cross"
- v-show="data2.component && (data2.component === view || data2.component === view2)"
- />
- <span>{{data2.name}}</span>
- </div>
- </div>
- </el-sub-menu>
- <!-- 模型菜单 -->
- <el-sub-menu index="model" v-if="modelConfig.id">
- <template #title>
- <i :class="modelConfig.icon" class="iconfont2"></i>
- <span>{{modelConfig.name}}</span>
- </template>
- <el-sub-menu
- v-for="(secondLevel, firstLevelKey) in groupedModels"
- :key="firstLevelKey"
- :index="'model-' + firstLevelKey"
- popper-append-to-body="false"
- >
- <template #title>
- <span>{{getFirstLevelName(firstLevelKey)}}</span>
- <el-tag size="small" type="info" style="margin-left: 8px;">{{getTotalCount(secondLevel)}}</el-tag>
- </template>
- <el-sub-menu
- v-for="(models, secondLevelKey) in secondLevel"
- :key="secondLevelKey"
- :index="'model-' + firstLevelKey + '-' + secondLevelKey"
- popper-append-to-body="false"
- >
- <template #title>
- <span>{{getSecondLevelName(secondLevelKey)}}</span>
- </template>
- <el-menu-item
- v-for="model in models"
- :key="model.id"
- :index="'model-' + firstLevelKey + '-' + secondLevelKey + '-' + model.id"
- @dblclick="loadModel(model)"
- class="model-menu-item"
- >
- <span class="model-name" :class="{ 'model-loaded': isModelLoaded(model.id) }">{{ model.name }}</span>
- <el-popconfirm
- v-if="isModelLoaded(model.id)"
- title="确定删除吗?"
- confirmButtonText="确认"
- cancelButtonText="取消"
- @confirm="removeModel(model)"
- >
- <template #reference>
- <el-icon
- class="delete-model-icon"
- title="删除模型"
- >
- <CircleClose />
- </el-icon>
- </template>
- </el-popconfirm>
- </el-menu-item>
- </el-sub-menu>
- </el-sub-menu>
- </el-sub-menu>
- <!-- 台风菜单 -->
- <el-sub-menu index="typhoon">
- <template #title>
- <img src="/img/typhoon.png" alt="" class="typhoon-icon" />
- <span>台风</span>
- </template>
- </el-sub-menu>
- </el-menu>
- </el-aside>
- <!-- 侧边栏部分--end -->
- <!-- 右侧内容展示部分 -->
- <el-main>
- <loading-bar></loading-bar>
- <!-- 添加自定义服务组件 -->
- <component
- v-if="addService"
- :is="addService"
- :clear-callback="addCustomService"
- :add-callback="addCallback"
- :get-layer-name="getName"
- ></component>
- <!-- viewer组件 -->
- <component v-if="initViewer" :is="initViewer" :after-initviewer="removeLoad" :opening-animation="true"></component>
- <!-- 分析功能组件 -->
- <keep-alive>
- <component
- v-if="view"
- :is="view"
- :key="view"
- :delete-callback="deleteCallback"
- data-url="/data/netcdf/result_100_200_9_40.nc"
- skylineSpatialAnalysisUrl="http://www.supermapol.com/realspace/services/spatialAnalysis-data_all/restjsr/spatialanalyst/geometry/3d/skylinesectorbody.json"
- ></component>
- </keep-alive>
- <!-- 地质体组件,单独展示,需要销毁 -->
- <component v-if="view2" :is="view2" :key="view2"></component>
- </el-main>
- </el-container>
- </template>
- <script>
- import config from "../../config/views_config.js"; //组件配置
- import server_config from "../../config/server_config.js"; //服务配置
- import server_flyTo_config from "../../config/server_position_config.js"; //服务坐标配置
- import layerManagement from "../../js/common/layerManagement.js"; //图层管理封装方法
- import camera from "../../js/common/camera.js"; //相机操作
- import loadingBar from "../../components/loading.vue"; //加载动画
- import { CircleClose, Plus, CirclePlus } from '@element-plus/icons-vue'; //删除图标
- import { listModel } from '@/api/watershed/model'; //模型API
- import { getDefaultMapConfig, saveMapConfig } from '@/api/cesium/mapConfig'; //地图配置API
- import serviceApi from '@/api/watershed/service'; //自定义服务API
- import { ElMessage } from 'element-plus';
- import { getToken } from '@/utils/auth'
- import axios from 'axios'
- export default {
- name: "layout-aside",
- components: {
- loadingBar,
- CircleClose
- },
- data() {
- return {
- isCollapse: false,
- asideWidth: "asideWidth1",
- view: "",
- view2: "",
- initViewer: "",
- addService: "",
- sceneURL:
- "http://www.supermapol.com/realspace/services/3D-ZF_normal/rest/realspace",
- config: config,
- server_config: server_config,
- baseLayerObj: server_config[1].children[0],
- terrainLayerObj: null,
- addCustomServices: [],
- getNames: [], //记录自定义服务名称
- varName: null, //临时保存当前自定义名称
- loadedCustomServiceIds: [], //已加载的自定义服务ID
- modelConfig: {}, //模型菜单配置
- modelData: [], //模型数据
- loadedModelEntities: [], //已加载的模型实体
- saveConfigTimer: null, //保存配置的防抖定时器
- lastSaveTime: 0, //上次保存时间戳
- };
- },
- computed: {
- groupedModels() {
- const grouped = {}
- this.modelData.forEach(model => {
- const type = model.type || '5/5-1'
- const parts = type.split('/')
- const firstLevel = parts[0] || '5'
- const secondLevel = parts[1] || '5-1'
-
- if (!grouped[firstLevel]) {
- grouped[firstLevel] = {}
- }
- if (!grouped[firstLevel][secondLevel]) {
- grouped[firstLevel][secondLevel] = []
- }
- grouped[firstLevel][secondLevel].push(model)
- })
- return grouped
- }
- },
- methods: {
- async fetchCustomServices() {
- try {
- console.log('开始从数据库加载自定义服务...');
- const response = await serviceApi.getServiceList({});
- console.log('获取到的自定义服务:', response);
-
- if (response && response.rows) {
- this.addCustomServices = response.rows.map(service => ({
- id: service.id || service.serviceId,
- name: service.name || service.serviceName,
- type: service.type || service.serviceType,
- url: service.url || service.serviceUrl,
- tokenRequired: service.tokenRequired || service.token_required,
- token: service.token || service.serviceToken,
- status: service.status,
- layers: [{
- type: service.type || service.serviceType,
- layerName: service.name || service.serviceName
- }]
- }));
- console.log('处理后的自定义服务列表:', this.addCustomServices);
- }
- } catch (error) {
- console.error('加载自定义服务失败:', error);
- }
- },
- getModelTypeOptions() {
- return [
- {
- value: '1',
- label: '水利工程实体',
- children: [
- { value: '1-1', label: '水库工程' },
- { value: '1-2', label: '水闸工程' },
- { value: '1-3', label: '泵站工程' },
- { value: '1-4', label: '灌区工程' },
- { value: '1-5', label: '堤防与护岸工程' }
- ]
- },
- {
- value: '2',
- label: '水系水利设施',
- children: [
- { value: '2-1', label: '河流' },
- { value: '2-2', label: '湖泊与水库水面' },
- { value: '2-3', label: '渠道与输水管道' },
- { value: '2-4', label: '河口与海岸带' }
- ]
- },
- {
- value: '3',
- label: '地理环境要素',
- children: [
- { value: '3-1', label: '地形地貌' },
- { value: '3-2', label: '行政区划' },
- { value: '3-3', label: '重要地物' }
- ]
- },
- {
- value: '4',
- label: '自然生态景观',
- children: [
- { value: '4-1', label: '湖泊湿地' },
- { value: '4-2', label: '森林公园' },
- { value: '4-3', label: '地质公园' },
- { value: '4-4', label: '海岸带景观' }
- ]
- },
- {
- value: '5',
- label: '模型集与项目',
- children: [
- { value: '5-1', label: '待分类模型' },
- { value: '5-2', label: 'XX市防洪排涝工程' }
- ]
- }
- ]
- },
- getFirstLevelName(firstLevelKey) {
- const options = this.getModelTypeOptions()
- const option = options.find(opt => opt.value === firstLevelKey)
- return option ? option.label : firstLevelKey
- },
- getSecondLevelName(secondLevelKey) {
- const options = this.getModelTypeOptions()
- for (const opt of options) {
- if (opt.children) {
- const child = opt.children.find(c => c.value === secondLevelKey)
- if (child) return child.label
- }
- }
- return secondLevelKey
- },
- getTotalCount(secondLevel) {
- let count = 0
- for (const key in secondLevel) {
- count += secondLevel[key].length
- }
- return count
- },
- getTypeDisplayName(type) {
- const typeMapping = {
- 'RESERVOIR': '水库',
- 'HYDROPOWER': '水电站',
- 'IRRIGATION': '灌溉',
- 'FLOOD_CONTROL': '防洪',
- 'CANAL': '渠道',
- 'PUMPING_STATION': '泵站',
- 'OTHER': '其他'
- }
-
- if (typeMapping[type]) {
- return typeMapping[type]
- }
-
- return type
- },
- async fetchModels() {
- try {
- const data = await listModel({})
- const modelList = data.rows || []
- console.log('获取到的模型数据:', modelList)
-
- this.modelData = modelList.map(model => {
- console.log('模型详情:', model)
- return {
- id: model.id,
- name: model.name,
- type: model.type,
- format: model.format,
- uploadUnit: model.uploadUnit || '未知单位',
- filePath: model.filePath || model.file_path || model.url || '',
- coordinates: model.coordinates,
- originalData: model
- }
- })
-
- console.log('处理后的模型数据:', this.modelData)
-
- if (this.modelData.length > 0) {
- this.modelConfig = {
- id: 'model',
- icon: 'iconfont iconmoxing',
- name: '模型'
- }
- }
- } catch (error) {
- console.error('获取模型数据错误:', error)
- }
- },
- async loadModel(model, shouldFlyTo = true) {
- console.log('========== 开始加载模型 ==========')
- console.log('模型名称:', model.name)
- console.log('模型路径:', model.filePath)
- console.log('经纬度:', model.coordinates)
- console.log('模型格式:', model.format)
- console.log('viewer状态:', viewer)
- if (!viewer) {
- ElMessage.error('Cesium viewer 未初始化')
- return
- }
- if (!viewer.scene) {
- ElMessage.error('Cesium scene 未初始化')
- return
- }
- console.log('检查模型是否已加载:', model.id)
- console.log('已加载的模型实体:', this.loadedModelEntities)
- console.log('isModelLoaded结果:', this.isModelLoaded(model.id))
- if (this.isModelLoaded(model.id)) {
- console.log('模型已加载,先卸载再重新加载')
- this.removeModel(model)
- }
- try {
- if (!model.filePath) {
- ElMessage.warning('模型文件路径不存在,请检查数据库中的filePath字段')
- console.error('模型路径为空,模型数据:', model)
- return
- }
- let modelUrl = model.filePath
-
- console.log('原始模型路径:', modelUrl)
-
- // 修复路径中的双斜杠问题
- modelUrl = modelUrl.replace(/\/+/g, '/')
- console.log('修复后的模型路径:', modelUrl)
-
- // 检查是否是本地路径或相对路径
- const isLocalPath = modelUrl.startsWith('/') || modelUrl.startsWith('./') || modelUrl.startsWith('../') || !modelUrl.startsWith('http')
-
- if (isLocalPath) {
- console.log('检测到本地路径,通过后端API获取模型文件...')
- const baseURL = import.meta.env.VITE_APP_BASE_API || ''
- const apiUrl = baseURL + "/common/download/resource?resource=" + encodeURIComponent(modelUrl)
- console.log('API URL:', apiUrl)
-
- try {
- const response = await axios({
- method: 'get',
- url: apiUrl,
- responseType: 'blob',
- headers: { 'Authorization': 'Bearer ' + getToken() }
- })
-
- console.log('API响应状态:', response.status)
- console.log('Content-Type:', response.headers['content-type'])
- console.log('响应数据大小:', response.data.size, 'bytes')
-
- if (response.data.size === 0) {
- console.error('下载的模型文件为空')
- ElMessage.error('模型文件为空,请检查文件是否有效')
- return
- }
-
- let mimeType = 'model/gltf-binary'
- const format = model.format ? model.format.toUpperCase() : 'GLB'
- const fileExtension = model.filePath ? model.filePath.split('.').pop().toLowerCase() : ''
-
- console.log('模型格式:', format)
- console.log('文件扩展名:', fileExtension)
-
- if (format === 'GLTF' || fileExtension === 'gltf') {
- mimeType = 'model/gltf+json'
- } else if (format === 'GLB' || fileExtension === 'glb') {
- mimeType = 'model/gltf-binary'
- } else if (format === 'OBJ' || fileExtension === 'obj') {
- mimeType = 'model/obj'
- }
-
- console.log('使用的MIME类型:', mimeType)
-
- const blob = new Blob([response.data], { type: mimeType })
- modelUrl = URL.createObjectURL(blob)
- console.log('创建的Blob URL:', modelUrl)
-
- // ElMessage.success('模型文件下载成功,开始加载...')
- } catch (apiError) {
- console.error('通过API获取模型文件失败:', apiError)
- console.error('错误详情:', apiError.response)
- ElMessage.error('无法获取模型文件,请检查文件路径或权限')
- return
- }
- } else {
- console.log('使用远程URL直接加载模型:', modelUrl)
- }
- let position
- if (model.coordinates && model.coordinates.trim() !== '' && model.coordinates !== '未设置') {
- try {
- const coords = model.coordinates.split(',').map(c => parseFloat(c.trim()))
- console.log('解析后的坐标数组:', coords)
-
- if (coords.length >= 2 && !isNaN(coords[0]) && !isNaN(coords[1])) {
- const lon = coords[0]
- const lat = coords[1]
- const height = coords[2] || 0
-
- // 获取地面高度
- const cartographic = Cesium.Cartographic.fromDegrees(lon, lat)
- const terrainHeight = viewer.scene.globe.getHeight(cartographic)
- const groundHeight = terrainHeight || 0
-
- // 模型高度 = 地面高度 + 用户指定的高度
- position = Cesium.Cartesian3.fromDegrees(lon, lat, groundHeight + height)
- console.log('使用经纬度定位 - 经度:', lon, '纬度:', lat, '地面高度:', groundHeight, '模型高度:', height, '总高度:', groundHeight + height)
- } else {
- console.warn('经纬度格式不正确或为NaN,使用默认位置')
- position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
- }
- } catch (e) {
- console.error('解析经纬度失败:', e)
- position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
- }
- } else {
- console.warn('经纬度为空或未设置,使用默认位置')
- position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
- }
- console.log('最终位置:', position)
- if (!position) {
- ElMessage.error('位置计算失败')
- return
- }
- const entity = viewer.entities.add({
- name: model.id,
- position: position,
- model: {
- uri: modelUrl,
- minimumPixelSize: 0,
- maximumScale: 1,
- scale: 1.0,
- show: true,
- distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 50000000.0),
- heightReference: Cesium.HeightReference.NONE
- }
- })
- console.log('实体已添加:', entity)
- console.log('模型URL:', modelUrl)
- console.log('模型URI:', entity.model.uri)
- this.loadedModelEntities.push(entity)
- if (shouldFlyTo) {
- setTimeout(() => {
- try {
- viewer.flyTo(entity, {
- duration: 2,
- offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), 5)
- })
- console.log('开始飞行到模型位置')
- } catch (flyError) {
- console.error('飞行失败:', flyError)
- ElMessage.warning('模型已加载,但跳转失败')
- }
- }, 100)
- }
- this.saveLoadedServices()
- // ElMessage.success(`模型 "${model.name}" 加载成功`)
- console.log('========== 模型加载完成 ==========')
- } catch (error) {
- console.error('========== 加载模型错误 ==========')
- console.error('错误信息:', error)
- console.error('错误堆栈:', error.stack)
- ElMessage.error('加载模型失败: ' + (error.message || '未知错误'))
- }
- },
- isModelLoaded(modelId) {
- return this.loadedModelEntities.some(entity => entity.name === modelId)
- },
- removeModel(model) {
- const index = this.loadedModelEntities.findIndex(entity => entity.name === model.id)
- if (index !== -1) {
- const entity = this.loadedModelEntities[index]
- viewer.entities.remove(entity)
- this.loadedModelEntities.splice(index, 1)
- this.saveLoadedServices()
- // ElMessage.success(`模型 "${model.name}" 已删除`)
- }
- },
- // 区分地质体组件(需销毁)和其他组件
- change(val, val2) {
- if (val2) {
- this.view = "";
- if (val === this.view2) {
- this.view2 = "";
- return;
- }
- this.view2 = val;
- } else {
- this.view2 = "";
- if (val === this.view) {
- this.view = "";
- return;
- }
- this.view = val;
- }
- },
- //添加公共服务
- addWebServe(obj, shouldFlyTo = true) {
- if (obj.state === 0) {
- // add mvt
- if (obj.type === "MVT") {
- layerManagement.addMvtLayer(obj.proxiedUrl, obj.name, () => {
- obj.state = 1;
- window.store.actions.setChangeLayers();
- this.saveLoadedServices();
- });
- return;
- }
- // add scene
- layerManagement.addScene(
- obj.proxiedUrl,
- { autoSetView: false },
- layers => {
- obj.state = 1;
- window.store.actions.setChangeLayers();
- // 白膜添加线框
- if (obj.style && obj.style.fillStyle) {
- layers[0].style3D.lineColor = Cesium.Color.fromCssColorString(
- "rgb(67,67,67)"
- );
- layers[0].style3D.fillStyle =
- Cesium.FillStyle[obj.style.fillStyle];
- }
- this.saveLoadedServices();
- if (shouldFlyTo) {
- this.flyTo(obj.name, server_flyTo_config);
- }
- }
- );
- } else {
- if (shouldFlyTo) {
- this.flyTo(obj.name, server_flyTo_config);
- }
- }
- },
- // 添加底图
- addBaseLayer(obj) {
- if (this.baseLayerObj.type === obj.type) return;
- this.baseLayerObj.state = 0;
- obj.state = 1;
- this.baseLayerObj = obj;
- let type = obj.type;
- let url = obj.proxiedUrl;
- let imageryLayerCollection = viewer.scene.globe._imageryLayerCollection;
- let layer = imageryLayerCollection.get(0);
- let imageryProvider;
- if (imageryLayerCollection.get(2)) {
- imageryLayerCollection.remove(imageryLayerCollection.get(2));
- }
- if (imageryLayerCollection.get(1)) {
- imageryLayerCollection.remove(imageryLayerCollection.get(1));
- }
- switch (type) {
- case "BINGMAP":
- imageryProvider = new Cesium.BingMapsImageryProvider({
- url: url,
- key:
- "Aq0D7MCY5ErORA9vrwFtfE9aancUq5J6uNjw0GieF0ostaIrVuJZ8ScXxNHHvEwS"
- });
- break;
- case "TIANDITU":
- imageryProvider = new Cesium.TiandituImageryProvider({
- url: url,
- token: "3fb1e9fda20ee995dc815c8243553ce8"
- });
- break;
- case "IMAGE":
- imageryProvider = new Cesium.SingleTileImageryProvider({
- url: url
- });
- break;
- case "OSM":
- imageryProvider = new Cesium.createOpenStreetMapImageryProvider({
- url: url
- });
- break;
- case "MAPBOX":
- imageryProvider = new Cesium.MapboxImageryProvider({
- mapId: "mapbox.dark"
- });
- break;
- case "SUPERMAPDARK":
- imageryProvider = new Cesium.SuperMapImageryProvider({
- url: url
- });
- break;
- case "SUPERMAPLIGHT":
- imageryProvider = new Cesium.SuperMapImageryProvider({
- url: url
- });
- break;
- case "GRIDIMAGERY":
- imageryProvider = imageryProvider;
- break;
- default:
- imageryProvider = new Cesium.SingleTileImageryProvider({
- url: url
- });
- break;
- }
- if (type != "GRIDIMAGERY") {
- imageryLayerCollection.addImageryProvider(imageryProvider, 0);
- imageryLayerCollection.remove(layer);
- }
- if (type == "GRIDIMAGERY") {
- imageryLayerCollection.addImageryProvider(
- new Cesium.TileCoordinatesImageryProvider(),
- 1
- );
- imageryLayerCollection.addImageryProvider(
- new Cesium.GridImageryProvider(),
- 2
- );
- }
- this.saveLoadedServices();
- },
- // 添加在线地形
- addOnlineTerrain(obj) {
- if (this.terrainLayerObj) this.terrainLayerObj.state = 0;
- obj.state = 1;
- this.terrainLayerObj = obj;
- let type = obj.type;
- let url = obj.proxiedUrl;
- switch (type) {
- case "STKTerrain":
- viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
- url: url,
- isSct: false
- });
- break;
- case "tianDiTuTerrain":
- var t_Provider = new Cesium.TiandituTerrainProvider({
- token: "3fb1e9fda20ee995dc815c8243553ce8"
- });
- viewer.terrainProvider = t_Provider;
- break;
- case "supermapOnlineTerrain":
- viewer.terrainProvider = new Cesium.SCTTerrainProvider({
- urls: [url]
- });
- break;
- default:
- break;
- }
- this.saveLoadedServices();
- },
- // 移除地形
- removeTerrainLayer() {
- viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider({
- ellipsoid: viewer.scene.globe.ellipsoid
- });
- this.terrainLayerObj = null;
- },
- addDatas(id, obj, shouldFlyTo = true) {
- switch (id) {
- case "webServe":
- this.addWebServe(obj, shouldFlyTo);
- break;
- case "baseLayer":
- this.addBaseLayer(obj);
- break;
- case "onlineTerrain":
- this.addOnlineTerrain(obj);
- break;
- }
- },
-
- // 初始化viewer后的传入回调:添加底图和清除加载动画
- async removeLoad() {
- this.isRestoring = true; // 标记正在恢复,禁止保存
-
- setTimeout(() => {
- let loading = document.getElementById("loadingbar");
- if (loading) loading.remove(); //移除加载动画
- document.getElementById("el-aside").classList.remove("disable"); //解除禁止点击
- }, 1000);
-
- // 等待自定义服务加载完成后再恢复
- setTimeout(async () => {
- await this.fetchCustomServices();
- await this.restoreLoadedServices();
- this.isRestoring = false; // 恢复完成,允许保存
- }, 1500);
- },
- // 删除场景公共服务
- DeleteDates(datatype, obj) {
- switch (datatype) {
- case "webServe":
- obj.layers.forEach(layer => {
- layerManagement.layersDelete(layer.type, layer.layerName, () => {
- obj.state = 0;
- window.store.actions.setChangeLayers();
- this.saveLoadedServices();
- });
- });
- break;
- case "baseLayer":
- break;
- case "onlineTerrain":
- this.removeTerrainLayer();
- obj.state = 0;
- this.saveLoadedServices();
- break;
- }
- },
- isServiceLoaded(service) {
- if (!service) {
- return false;
- }
- const serviceId = service.id || service.serviceId;
- return this.loadedCustomServiceIds.includes(serviceId);
- },
- async loadCustomService(service, flyTo = true, shouldSave = true) {
- console.log('========== 开始加载自定义服务 ==========');
- console.log('服务名称:', service.name);
- console.log('服务类型:', service.type);
- console.log('服务URL:', service.url);
- console.log('是否跳转视角:', flyTo);
- console.log('viewer状态:', viewer);
- if (!viewer) {
- ElMessage.error('Cesium viewer 未初始化');
- return;
- }
- if (this.isServiceLoaded(service)) {
- console.log('服务已加载,执行定位');
- if (flyTo) {
- const type = service.type || (service.layers && service.layers[0] ? service.layers[0].type : null);
- if (type === 'SCENE' || type === 'S3M') {
- const layerNames = service.loadedLayerNames || [service.name];
- let foundLayer = null;
- for (const name of layerNames) {
- foundLayer = viewer.scene.layers.find(name);
- if (foundLayer) break;
- }
- if (foundLayer) {
- console.log('跳转到图层:', foundLayer.name);
- viewer.flyTo(foundLayer, { duration: 2 });
- } else if (viewer.scene.layers.layerQueue && viewer.scene.layers.layerQueue.length > 0) {
- viewer.flyTo(viewer.scene.layers.layerQueue[0], { duration: 2 });
- }
- } else {
- const layerName = service.name;
- camera.flyByLayerName(type, layerName);
- }
- }
- return;
- }
- try {
- const type = service.type || (service.layers && service.layers[0] ? service.layers[0].type : null);
- const url = service.url;
- const layerName = service.name;
- const token = service.token;
- const tokenRequired = service.tokenRequired;
- const serviceId = service.id || service.serviceId;
- const markAsLoaded = () => {
- if (!this.loadedCustomServiceIds.includes(serviceId)) {
- this.loadedCustomServiceIds.push(serviceId);
- }
- };
- switch (type) {
- case 'SCENE':
- let sceneOptions = {
- autoSetView: flyTo
- };
- if (tokenRequired && token) {
- sceneOptions.SceneToken = token;
- }
- const beforeLayersCount = viewer.scene.layers ? viewer.scene.layers.layerQueue.length : 0;
- console.log('加载前图层数量:', beforeLayersCount);
- console.log('是否自动跳转视角:', flyTo);
- layerManagement.addScene(url, sceneOptions, (layers) => {
- console.log('SCENE加载成功:', layers);
- markAsLoaded();
- const afterLayers = viewer.scene.layers ? viewer.scene.layers.layerQueue : [];
- const newLayers = afterLayers.slice(beforeLayersCount);
- console.log('新添加的图层:', newLayers);
- const newLayerNames = newLayers.map(l => l.name);
- console.log('新图层名称:', newLayerNames);
- service.loadedLayerNames = newLayerNames;
- const currentPosition = viewer.camera.positionCartographic;
- service.defaultCameraPosition = {
- longitude: currentPosition.longitude,
- latitude: currentPosition.latitude,
- height: currentPosition.height,
- heading: viewer.camera.heading,
- pitch: viewer.camera.pitch,
- roll: viewer.camera.roll
- };
- console.log('记录默认视口位置:', service.defaultCameraPosition);
- if (flyTo && newLayers.length > 0) {
- camera.flyByLayerName('SCENE', newLayers[0].name);
- }
- });
- break;
- case 'S3M':
- let scps = [{ url: url, options: { name: layerName } }];
- layerManagement.addS3mLayers(scps, (layers) => {
- console.log('S3M加载成功:', layers);
- markAsLoaded();
- if (flyTo) {
- camera.flyByLayerName('S3M', layerName);
- }
- });
- break;
- case 'IMG':
- let imgLayer = layerManagement.addImageLayer(url);
- console.log('IMG加载成功:', imgLayer);
- markAsLoaded();
- if (flyTo) {
- camera.flyByLayerName('IMG', layerName);
- }
- break;
- case 'TERRAIN':
- let terrainProvider = layerManagement.addTerrainLayer(url, false);
- console.log('TERRAIN加载成功:', terrainProvider);
- markAsLoaded();
- break;
- case 'MVT':
- layerManagement.addMvtLayer(url, layerName, (mvtlayer) => {
- console.log('MVT加载成功:', mvtlayer);
- markAsLoaded();
- });
- break;
- default:
- ElMessage.warning('不支持的服务类型: ' + type);
- return;
- }
- if (shouldSave) {
- ElMessage.success(`服务 "${service.name}" 加载成功`);
- }
- console.log('========== 自定义服务加载完成 ==========');
-
- if (shouldSave) {
- this.saveLoadedServices();
- }
- } catch (error) {
- console.error('加载自定义服务失败:', error);
- ElMessage.error('加载服务失败: ' + (error.message || '未知错误'));
- }
- },
- unloadCustomService(service) {
- console.log('开始卸载服务:', service);
- if (!service) {
- console.warn('服务对象为空');
- return;
- }
-
- const serviceId = service.id || service.serviceId;
- const serviceType = service.type || 'SCENE';
-
- if (serviceType === 'SCENE' && service.loadedLayerNames && service.loadedLayerNames.length > 0) {
- console.log('使用loadedLayerNames卸载SCENE图层:', service.loadedLayerNames);
- service.loadedLayerNames.forEach((layerName, index) => {
- console.log('卸载图层:', layerName);
- const isLast = index === service.loadedLayerNames.length - 1;
- layerManagement.layersDelete('S3M', layerName, () => {
- console.log('图层已卸载:', layerName);
- if (isLast) {
- const idx = this.loadedCustomServiceIds.indexOf(serviceId);
- if (idx > -1) {
- this.loadedCustomServiceIds.splice(idx, 1);
- console.log('已从loadedCustomServiceIds中移除服务:', serviceId);
- }
- window.store.actions.setChangeLayers();
- this.saveLoadedServices();
- }
- });
- });
- } else if (service.layers && service.layers.length > 0) {
- service.layers.forEach((layer, index) => {
- console.log('卸载图层:', layer.type, layer.layerName);
- const isLast = index === service.layers.length - 1;
- layerManagement.layersDelete(layer.type, layer.layerName, () => {
- console.log('图层已卸载:', layer.layerName);
- if (isLast) {
- const idx = this.loadedCustomServiceIds.indexOf(serviceId);
- if (idx > -1) {
- this.loadedCustomServiceIds.splice(idx, 1);
- console.log('已从loadedCustomServiceIds中移除服务:', serviceId);
- }
- window.store.actions.setChangeLayers();
- this.saveLoadedServices();
- }
- });
- });
- } else {
- const type = service.type || 'SCENE';
- const layerName = service.name;
- console.log('卸载参数 - serviceId:', serviceId, 'type:', type, 'layerName:', layerName);
- layerManagement.layersDelete(type, layerName, () => {
- console.log('服务已卸载:', service.name);
- const idx = this.loadedCustomServiceIds.indexOf(serviceId);
- if (idx > -1) {
- this.loadedCustomServiceIds.splice(idx, 1);
- }
- window.store.actions.setChangeLayers();
- this.saveLoadedServices();
- });
- }
- },
- // 控制自定义服务组件显隐
- addCustomService() {
- if (this.addService === "") {
- this.addService = "Sm3dCustomService";
- // 重置面板位置到默认值,使用setTimeout确保组件完全渲染
- setTimeout(() => {
- const panel = document.getElementById('CustomService-panel');
- if (panel) {
- panel.style.left = '35%';
- panel.style.top = '25%';
- }
- }, 100);
- } else {
- this.addService = "";
- }
- // 移除自定义服务菜单项的选中状态
- this.$nextTick(() => {
- const menu = this.$refs.menu;
- if (menu) {
- menu.activeIndex = "";
- }
- });
- },
- // 自定义服务添加回调函数
- addCallback(layers, add_type, serviceInfo) {
- console.log('接收到addCallback调用:', layers, add_type, serviceInfo);
-
- if (!Array.isArray(layers)) {
- layers = [layers];
- }
- let serviceOption = [];
- let serviceUrl = serviceInfo ? serviceInfo.url : "";
- let serviceToken = serviceInfo ? serviceInfo.token : "";
- let isAddToken = serviceInfo ? serviceInfo.tokenRequired : false;
-
- try {
- for (let i = 0; i < layers.length; i++) {
- if (!layers[i]) {
- console.log('layers[' + i + '] 是 undefined 或 null,跳过');
- continue;
- }
- if (layers[i].hasOwnProperty("_isS3MB")) {
- let option = {
- type: "S3M",
- layerName: layers[i].name
- ? layers[i].name
- : "s3m_" + new Date().getTime().toFixed(6)
- };
- serviceOption.push(option);
- if (!layers[i].maxVisibleAltitude)
- layers[i].maxVisibleAltitude = 8000;
- } else if (viewer.imageryLayers.contains(layers[i])) {
- let option = {
- type: "IMG",
- layerName: layers[i].imageryProvider.tablename
- ? layers[i].imageryProvider.tablename
- : "image" + new Date().getTime().toFixed(6)
- };
- serviceOption.push(option);
- } else if (
- layers[i].imageryLayer &&
- layers[i].imageryLayer._imageryProvider instanceof
- Cesium.MvtProviderGL
- ) {
- let option = {
- type: "MVT",
- layerName: layers[i].name
- ? layers[i].name
- : "mvt" + new Date().getTime().toFixed(6)
- };
- serviceOption.push(option);
- let index = this.addCustomServices.length;
- let obj = {
- name: layers[i].name ? layers[i].name : "自定义服务" + index,
- layers: serviceOption
- };
- this.addCustomServices.push(obj);
- this.addService = "";
-
- // 存储服务到数据库
- console.log('准备调用saveServiceToDatabase (MVT):', serviceInfo, add_type, obj.name);
- if (serviceInfo && serviceUrl) {
- console.log('调用saveServiceToDatabase (MVT)');
- this.saveServiceToDatabase(serviceInfo, add_type, obj.name);
- } else {
- console.log('serviceInfo或serviceUrl为空 (MVT):', serviceInfo, serviceUrl);
- }
-
- return;
- } else {
- let option = {
- type: "TERRAIN",
- layerName: layers[i].tablename ? layers[i].tablename : "terrain"
- };
- serviceOption.push(option);
- }
- }
- } catch (error) {
- console.error('处理layers时发生错误:', error);
- }
-
- if (this.varName) this.getNames.push(this.varName);
- let index = this.addCustomServices.length;
- let len = this.getNames.length - 1;
- let obj = {
- name: this.varName ? this.varName : "自定义服务" + index,
- layers: serviceOption
- };
- this.addCustomServices.push(obj);
- this.addService = "";
- this.varName = undefined;
-
- // 存储服务到数据库
- console.log('准备调用saveServiceToDatabase (非MVT):', serviceInfo, add_type, obj.name);
- if (serviceInfo && serviceUrl) {
- console.log('调用saveServiceToDatabase (非MVT)');
- this.saveServiceToDatabase(serviceInfo, add_type, obj.name);
- } else {
- console.log('serviceInfo或serviceUrl为空 (非MVT):', serviceInfo, serviceUrl);
- }
-
- if ((add_type && add_type === "SCENE") || add_type === "TERRAIN") return;
- camera.flyByLayerName(add_type, obj.layers[0].layerName); //定位
- },
-
- // 保存服务到数据库
- saveServiceToDatabase(serviceInfo, serviceType, serviceName) {
- console.log('开始保存服务到数据库:', serviceInfo, serviceType, serviceName);
-
- const serviceData = {
- name: serviceName,
- type: serviceType,
- url: serviceInfo.url,
- tokenRequired: serviceInfo.tokenRequired ? 1 : 0,
- token: serviceInfo.token,
- status: 'NORMAL'
- };
-
- console.log('准备保存服务:', serviceData);
-
- serviceApi.addService(serviceData)
- .then(response => {
- console.log('服务保存成功:', response);
- const newServiceId = response.data || response.serviceId;
- if (newServiceId) {
- const service = this.addCustomServices.find(s => s.name === serviceName);
- if (service) {
- service.id = newServiceId;
- service.url = serviceInfo.url;
- service.token = serviceInfo.token;
- service.tokenRequired = serviceInfo.tokenRequired;
- service.type = serviceType;
- }
- if (!this.loadedCustomServiceIds.includes(newServiceId)) {
- this.loadedCustomServiceIds.push(newServiceId);
- }
- }
- })
- .catch(error => {
- console.error('服务保存失败:', error);
- if (error.response) {
- console.error('错误响应状态:', error.response.status);
- console.error('错误响应数据:', error.response.data);
- } else if (error.request) {
- console.error('错误请求:', error.request);
- } else {
- console.error('错误信息:', error.message);
- }
- });
- },
- // 自定义服务获取名称回调
- getName(name) {
- this.varName = name;
- },
- // 添加服务飞行函数(公共服务配置好的定位)
- flyTo(webName, obj) {
- let cameraParam = obj[webName];
- if (cameraParam) {
- viewer.scene.camera.flyTo({
- destination: new Cesium.Cartesian3(
- cameraParam.Cartesian3.x,
- cameraParam.Cartesian3.y,
- cameraParam.Cartesian3.z
- ),
- orientation: {
- heading: cameraParam.heading,
- pitch: cameraParam.pitch,
- roll: cameraParam.roll
- },
- duration: 0
- });
- return;
- } else {
- }
- },
- //图层管理右键删除回调
- deleteCallback(node_rightClick) {
- this.server_config[0].children.forEach(sceneObj => {
- sceneObj.layers.forEach(layerObj => {
- if (layerObj.layerName === node_rightClick.label) {
- sceneObj.state = 0;
- return;
- }
- });
- });
- },
- // 保存已加载的服务到数据库 (带防抖)
- saveLoadedServices() {
- if (this.isRestoring) {
- console.log('正在恢复配置,跳过保存');
- return;
- }
-
- if (this.saveConfigTimer) {
- clearTimeout(this.saveConfigTimer);
- }
-
- this.saveConfigTimer = setTimeout(async () => {
- const now = Date.now();
- if (now - this.lastSaveTime < 2000) {
- console.log('距离上次保存不足2秒,跳过保存');
- return;
- }
-
- try {
- const loadedServices = {
- baseLayer: this.baseLayerObj ? {
- type: this.baseLayerObj.type,
- name: this.baseLayerObj.name,
- proxiedUrl: this.baseLayerObj.proxiedUrl
- } : null,
- terrainLayer: this.terrainLayerObj ? {
- type: this.terrainLayerObj.type,
- name: this.terrainLayerObj.name,
- proxiedUrl: this.terrainLayerObj.proxiedUrl
- } : null,
- webServices: this.server_config[0].children
- .filter(service => service.state === 1)
- .map(service => ({
- type: service.type,
- name: service.name,
- proxiedUrl: service.proxiedUrl,
- layers: service.layers,
- style: service.style
- }))
- };
-
- const loadedModels = this.loadedModelEntities.map(entity => {
- const model = this.modelData.find(m => m.id === entity.name);
- return model ? {
- id: model.id,
- name: model.name,
- type: model.type,
- filePath: model.filePath,
- coordinates: model.coordinates,
- format: model.format
- } : null;
- }).filter(m => m !== null);
- const loadedCustomServices = this.addCustomServices
- .filter(service => this.loadedCustomServiceIds.includes(service.id || service.serviceId))
- .map(service => ({
- id: service.id || service.serviceId,
- name: service.name,
- type: service.type,
- url: service.url,
- tokenRequired: service.tokenRequired,
- token: service.token,
- loadedLayerNames: service.loadedLayerNames || []
- }));
- const configData = {
- configName: '默认配置',
- baseLayerType: loadedServices.baseLayer ? loadedServices.baseLayer.type : '',
- baseLayerName: loadedServices.baseLayer ? loadedServices.baseLayer.name : '',
- baseLayerUrl: loadedServices.baseLayer ? loadedServices.baseLayer.proxiedUrl : '',
- terrainLayerType: loadedServices.terrainLayer ? loadedServices.terrainLayer.type : '',
- terrainLayerName: loadedServices.terrainLayer ? loadedServices.terrainLayer.name : '',
- terrainLayerUrl: loadedServices.terrainLayer ? loadedServices.terrainLayer.proxiedUrl : '',
- webServices: JSON.stringify(loadedServices.webServices),
- loadedModels: JSON.stringify(loadedModels),
- loadedCustomServices: JSON.stringify(loadedCustomServices)
- };
- console.log('========== 保存地图配置 ==========');
- await saveMapConfig(configData);
- this.lastSaveTime = Date.now();
- console.log('地图配置已保存到数据库');
- } catch (error) {
- console.error('保存地图配置失败:', error);
- console.error('错误详情:', error.message);
- if (error.response) {
- console.error('响应状态:', error.response.status);
- console.error('响应数据:', error.response.data);
- }
- }
- }, 500);
- },
- // 从数据库恢复已加载的服务和模型
- async restoreLoadedServices() {
- try {
- console.log('========== 开始恢复地图配置 ==========');
- const response = await getDefaultMapConfig();
- console.log('API响应:', response);
-
- if (!response || !response.data) {
- console.log('没有找到保存的地图配置,使用默认配置');
- return;
- }
- const config = response.data;
- console.log('保存的配置:', config);
-
- let loadedServices = null;
- let loadedModels = null;
- // 保存需要恢复的自定义服务信息(不立即设置loadedCustomServiceIds,避免跳过加载步骤)
- let customServicesToLoad = [];
- if (config.loadedCustomServices) {
- try {
- const savedCustomServices = JSON.parse(config.loadedCustomServices);
- console.log('解析的自定义服务配置:', savedCustomServices);
- if (savedCustomServices && savedCustomServices.length > 0) {
- customServicesToLoad = savedCustomServices.map(s => ({
- id: s.id,
- loadedLayerNames: s.loadedLayerNames || []
- }));
- console.log('需要恢复的自定义服务:', customServicesToLoad);
- }
- } catch (e) {
- console.error('解析自定义服务配置失败:', e);
- }
- }
- // 解析服务配置
- if (config.webServices) {
- try {
- loadedServices = JSON.parse(config.webServices);
- console.log('解析的服务配置:', loadedServices);
- } catch (e) {
- console.error('解析服务配置失败:', e);
- }
- }
- // 解析模型配置
- if (config.loadedModels) {
- try {
- loadedModels = JSON.parse(config.loadedModels);
- console.log('解析的模型配置:', loadedModels);
- } catch (e) {
- console.error('解析模型配置失败:', e);
- }
- }
- // 恢复底图
- if (config.baseLayerType) {
- const baseLayer = this.server_config[1].children.find(
- layer => layer.type === config.baseLayerType
- );
- if (baseLayer) {
- console.log('恢复底图:', baseLayer.name);
- await new Promise(resolve => {
- this.addBaseLayer(baseLayer);
- setTimeout(resolve, 500);
- });
- }
- }
- // 恢复地形
- if (config.terrainLayerType) {
- const terrainLayer = this.server_config[2].children.find(
- layer => layer.type === config.terrainLayerType
- );
- if (terrainLayer) {
- await new Promise(resolve => {
- this.addOnlineTerrain(terrainLayer);
- setTimeout(resolve, 500);
- });
- }
- }
- // 恢复公共服务
- if (loadedServices && loadedServices.length > 0) {
- for (const webService of loadedServices) {
- const service = this.server_config[0].children.find(
- s => s.name === webService.name
- );
- if (service) {
- console.log('恢复公共服务:', service.name);
- await new Promise(resolve => {
- service.state = 0;
- this.addWebServe(service, false);
- setTimeout(resolve, 500);
- });
- }
- }
- }
- // 恢复模型
- if (loadedModels && loadedModels.length > 0) {
- for (const modelData of loadedModels) {
- const model = this.modelData.find(m => m.id === modelData.id);
- if (model && !this.isModelLoaded(model.id)) {
- await new Promise(resolve => {
- this.loadModel(model, false);
- setTimeout(resolve, 1000);
- });
- }
- }
- }
- // 恢复自定义服务 (初始化时不跳转视角,不保存)
- if (customServicesToLoad && customServicesToLoad.length > 0) {
- for (const serviceInfo of customServicesToLoad) {
- const customService = this.addCustomServices.find(
- s => (s.id || s.serviceId) === serviceInfo.id
- );
- if (customService && !this.isServiceLoaded(customService)) {
- // 恢复 loadedLayerNames
- if (serviceInfo.loadedLayerNames && serviceInfo.loadedLayerNames.length > 0) {
- customService.loadedLayerNames = serviceInfo.loadedLayerNames;
- console.log('恢复服务loadedLayerNames:', customService.name, serviceInfo.loadedLayerNames);
- }
- console.log('恢复自定义服务:', customService.name);
- await new Promise(resolve => {
- this.loadCustomService(customService, false, false);
- setTimeout(resolve, 1000);
- });
- }
- }
- }
- console.log('地图配置恢复完成');
- } catch (error) {
- console.error('恢复地图配置失败:', error);
- console.error('错误详情:', error.message);
- console.error('错误堆栈:', error.stack);
- if (error.response) {
- console.error('响应状态:', error.response.status);
- console.error('响应数据:', error.response.data);
- }
- }
- },
- // 清除所有持久化数据
- clearPersistence() {
- localStorage.removeItem('cesium_loaded_services');
- localStorage.removeItem('cesium_loaded_models');
- console.log('持久化数据已清除');
- }
- },
-
- watch: {
- isCollapse(val) {
- if (val) {
- this.asideWidth = "asideWidth2";
- } else {
- this.asideWidth = "asideWidth1";
- }
- }
- },
- mounted() {
- document.getElementById("el-aside").classList.add("disable"); //禁止点击
- // 直接初始化 viewer,不使用 window.onload
- this.$nextTick(() => {
- this.initViewer = "Sm3dViewer";
- // 获取模型数据
- this.fetchModels();
- // 从数据库加载自定义服务
- this.fetchCustomServices();
- });
- }
- };
- </script>
- <style lang="scss" scoped>
- .container {
- position: relative;
- z-index: 1;
- width: 100%;
- height: 100%;
- pointer-events: auto;
- }
- /* 侧边栏宽度控制 */
- .asideWidth1 {
- width: 280px !important;
- transition-duration: 0.3s;
- height: 100% !important;
- }
- .asideWidth2 {
- transition-duration: 0.5s;
- width: 40px !important;
- height: 100% !important;
- display: flex !important;
- justify-content: center !important;
- }
- /* 确保el-container能够铺满整个容器 */
- .el-container {
- height: 100% !important;
- width: 100% !important;
- pointer-events: auto;
- }
- /* 确保el-aside能够正确接收点击事件 */
- .el-aside {
- position: relative;
- z-index: 1000;
- pointer-events: auto;
- }
- /* 确保el-main能够铺满整个容器 */
- .el-main {
- position: relative;
- z-index: 1;
- height: 100% !important;
- width: 100% !important;
- padding: 0 !important;
- margin: 0 !important;
- pointer-events: auto;
- }
- /* 确保cesiumContainer能够铺满整个el-main */
- #cesiumContainer {
- position: relative;
- z-index: 10;
- height: 100% !important;
- width: 100% !important;
- pointer-events: auto;
- }
- /* 确保cesium-viewer能够铺满整个cesiumContainer */
- .cesium-viewer {
- height: 100% !important;
- width: 100% !important;
- pointer-events: auto;
- }
- /* 确保cesium-widget能够铺满整个cesium-viewer */
- .cesium-widget {
- height: 100% !important;
- width: 100% !important;
- pointer-events: auto;
- }
- /* 确保canvas能够铺满整个cesium-widget */
- .cesium-widget canvas {
- position: relative;
- z-index: 5;
- height: 100% !important;
- width: 100% !important;
- pointer-events: auto;
- }
- /* 确保侧边栏菜单能够正确接收点击事件 */
- .el-menu-vertical-demo {
- position: relative;
- z-index: 1001;
- pointer-events: auto;
- }
- /* 确保加载动画不影响点击事件 */
- #loadingbar {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- z-index: 9999;
- pointer-events: none;
- }
- /* 侧边栏样式增强 */
- .el-aside {
- background-color: #f8f9fa;
- overflow-y: scroll;
- overflow-x: hidden;
- scrollbar-width: none;
- /* firefox */
- -ms-overflow-style: none;
- height: 100% !important;
- box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
- .el-menu--collapse {
- width: 40px;
- height: 100% !important;
- }
-
- :deep(.el-menu--collapse.el-menu) {
- width: 40px !important;
- margin: 0 auto !important;
- }
-
- :deep(.el-menu--collapse .el-menu-item),
- :deep(.el-menu--collapse .el-submenu__title),
- :deep(.el-menu--collapse .el-sub-menu__title) {
- padding: 0 !important;
- text-align: center !important;
- display: flex !important;
- align-items: center !important;
- justify-content: center !important;
- position: relative !important;
- left: 0 !important;
- right: 0 !important;
- width: 40px !important;
- height: 45px !important;
- margin: 0 auto !important;
- /* 强制开启hover触发,解决折叠后无法识别子项问题 */
- pointer-events: auto !important;
- cursor: pointer !important;
- }
-
- :deep(.el-menu--collapse .el-sub-menu) {
- width: 40px !important;
- /* 二级弹框容器相对定位,让三级能基于它定位 */
- position: relative !important;
- }
-
- :deep(.el-menu--collapse .el-menu-item > *),
- :deep(.el-menu--collapse .el-submenu__title > *),
- :deep(.el-menu--collapse .el-sub-menu__title > *) {
- margin: 0 !important;
- padding: 0 !important;
- position: static !important;
- left: auto !important;
- right: auto !important;
- top: auto !important;
- bottom: auto !important;
- transform: none !important;
- float: none !important;
- display: inline-block !important;
- width: auto !important;
- height: auto !important;
- }
-
- :deep(.el-menu--collapse .el-menu-item i.rotate),
- :deep(.el-menu--collapse .el-submenu__title i.rotate),
- :deep(.el-menu--collapse .el-sub-menu__title i.rotate) {
- margin: 0 !important;
- padding: 0 !important;
- display: inline-block !important;
- text-align: center !important;
- width: auto !important;
- height: auto !important;
- position: static !important;
- left: auto !important;
- right: auto !important;
- transform: rotate(-90deg) !important;
- line-height: normal !important;
- float: none !important;
- }
-
- :deep(.el-menu--collapse .el-submenu__title .iconfont2),
- :deep(.el-menu--collapse .el-menu-item .iconfont2),
- :deep(.el-menu--collapse .el-sub-menu__title .iconfont2) {
- margin: 0 !important;
- padding: 0 !important;
- display: inline-block !important;
- text-align: center !important;
- width: auto !important;
- height: auto !important;
- position: static !important;
- left: auto !important;
- right: auto !important;
- transform: none !important;
- float: none !important;
- }
-
- :deep(.el-menu--collapse .el-sub-menu__title .typhoon-icon) {
- margin: 0 !important;
- padding: 0 !important;
- display: inline-block !important;
- text-align: center !important;
- width: 20px !important;
- height: 20px !important;
- position: static !important;
- left: auto !important;
- right: auto !important;
- transform: none !important;
- float: none !important;
- }
-
- :deep(.el-menu--collapse .el-submenu__title span),
- :deep(.el-menu--collapse .el-menu-item span),
- :deep(.el-menu--collapse .el-sub-menu__title span) {
- display: none !important;
- }
-
- :deep(.el-menu--collapse .el-submenu__icon-arrow),
- :deep(.el-menu--collapse .el-sub-menu__icon-arrow) {
- display: none !important;
- }
- .el-menu {
- border-right: none;
- height: 100% !important;
- .el-menu-item,
- .el-submenu__title {
- height: 45px;
- line-height: 45px;
- }
-
- :deep(.el-menu--collapse .el-menu-item),
- :deep(.el-menu--collapse .el-submenu__title),
- :deep(.el-menu--collapse .el-sub-menu__title) {
- padding: 0 !important;
- text-align: center !important;
- display: flex !important;
- align-items: center !important;
- justify-content: center !important;
- position: relative !important;
- left: 0 !important;
- right: 0 !important;
- width: 40px !important;
- height: 45px !important;
- line-height: normal !important;
- }
- #fold {
- height: 35px;
- transition-duration: 1s;
- color: #303133 !important;
- padding: 0 !important;
- text-align: center !important;
- line-height: 35px !important;
- display: flex !important;
- align-items: center !important;
- justify-content: center !important;
- background-color: transparent !important;
- }
-
- #fold.is-active {
- background-color: transparent !important;
- color: #303133 !important;
- }
-
- #fold:hover {
- background-color: rgba(64, 158, 255, 0.1) !important;
- }
- }
- }
- /* 修复Element Plus 2.x 子菜单标题样式 */
- .el-submenu__title {
- padding: 0 12px !important;
- }
- .el-menu--vertical > .el-menu-item {
- padding: 0 12px !important;
- }
- /* 确保标题框尺寸正确 */
- .el-submenu__title .el-submenu__icon-arrow {
- margin-top: -4px !important;
- }
- /* 修复图标和文字对齐 */
- .iconfont2 {
- font-size: 20px !important;
- margin-right: 10px;
- vertical-align: middle;
- color: #303133;
- }
- /* 台风图标样式 */
- .typhoon-icon {
- width: 20px;
- height: 20px;
- margin-right: 10px;
- vertical-align: middle;
- }
- /* 子菜单项文字颜色 */
- .el-menu--vertical .el-sub-menu .el-menu-item {
- color: #303133 !important;
- font-size: 0.875rem !important;
- line-height: 1.5;
- }
- /* 本地服务菜单项文字向左移动 */
- .custom-service-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-left: 50px !important;
- }
- .custom-service-item.is-active {
- background-color: transparent !important;
- }
- .custom-service-item .service-name {
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .custom-service-item .service-name.service-loaded {
- color: #409eff;
- font-weight: 500;
- }
- .custom-service-item .delete-service-icon {
- margin-left: 8px;
- cursor: pointer;
- color: #909399;
- transition: color 0.3s;
- }
- .custom-service-item .delete-service-icon:hover {
- color: #f56c6c;
- }
- /* 自定义服务菜单项移除选中样式 */
- .no-active-style {
- background-color: transparent !important;
- }
- .no-active-style.is-active {
- background-color: transparent !important;
- }
- /* 模型菜单项样式 */
- .model-menu-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-left: 50px !important;
- }
- .model-menu-item.is-active {
- background-color: transparent !important;
- }
- .model-menu-item .model-name {
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- padding-left: 10px;
- }
- .model-menu-item .model-name.model-loaded {
- color: #409eff;
- font-weight: 500;
- }
- .model-menu-item .delete-model-icon {
- margin-left: 8px;
- cursor: pointer;
- color: #909399;
- transition: color 0.3s;
- }
- .model-menu-item .delete-model-icon:hover {
- color: #f56c6c;
- }
- /* 滚动条隐藏 */
- .el-aside::-webkit-scrollbar {
- display: none;
- }
- /* 功能模块网格布局 */
- .box {
- font-family: "Microsoft Yahei";
- display: flex;
- width: 100%;
- padding: 0 10px 0 28px;
- justify-content: space-between;
- box-sizing: border-box;
- flex-wrap: wrap;
- .imgbox {
- position: relative;
- width: 100px;
- height: 100px;
- display: flex;
- box-sizing: border-box;
- margin-bottom: 5px;
- flex-wrap: wrap;
- justify-content: center;
- span {
- font-size: 12px;
- color: #303133;
- }
- .img {
- width: 100%;
- height: 72px;
- border-radius: 4px;
- background-color: #585858;
- border: 1px solid #e4e7ed;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- transition: all 0.3s ease;
- }
- .img:hover {
- border: 2px solid #409eff;
- box-shadow: 0 4px 8px rgba(64, 158, 255, 0.2);
- transform: translateY(-2px);
- }
- .cross {
- width: 25px;
- height: 25px;
- position: absolute;
- top: 0;
- left: 0;
- }
- }
- }
- /* 删除图标样式 */
- .circle-close-icon {
- position: absolute;
- right: 0px;
- top: 3px;
- color: #ffffff;
- cursor: pointer;
- width: 16px;
- height: 16px;
- font-size: 16px;
- outline: none;
- }
- .circle-close-icon:focus,
- .circle-close-icon:active {
- outline: none;
- box-shadow: none;
- }
- /* el-menu-item 内的删除图标样式 */
- .el-menu-item .circle-close-icon {
- top: 50%;
- transform: translateY(-50%);
- color: #000000;
- right: 25px;
- }
- /* 展开/收缩按钮动画 */
- .rotate2 {
- transform: rotate(0deg);
- -ms-transform: rotate(0); /* IE 9 */
- -moz-transform: rotate(0); /* Firefox */
- -webkit-transform: rotate(0); /* Safari 和 Chrome */
- -o-transform: rotate(0); /* Opera */
- transition-duration: 1s;
- }
- .rotate {
- margin-left: 8px !important;
- line-height: 52px !important;
- transform: rotate(-90deg);
- -ms-transform: rotate(-90deg); /* IE 9 */
- -moz-transform: rotate(-90deg); /* Firefox */
- -webkit-transform: rotate(-90deg); /* Safari 和 Chrome */
- -o-transform: rotate(-90deg); /* Opera */
- transition-duration: 1s;
- }
- /* 禁用状态 */
- .disable {
- pointer-events: none;
- }
- /* 图标样式 */
- .iconfont2 {
- font-size: 20px !important;
- margin-right: 10px;
- }
- /* 方形加号图标 */
- .square-plus-icon {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 24px;
- height: 24px;
- border: 1.5px solid #909399;
- border-radius: 4px;
- font-size: 16px;
- margin-left: 10px;
- }
- // ########### 关键修复:折叠状态下 二级弹框保留原位置 + 三级强制弹出 ###########
- // 1. 折叠状态二级弹框:强制保留原有正确位置,不做任何改动
- :deep(.el-menu--collapse .el-sub-menu > .el-sub-menu__popper) {
- position: fixed !important;
- margin-left: 0 !important;
- transform: none !important;
- z-index: 9998 !important;
- }
- // 2. 折叠状态二级弹框容器:hover时强制显示三级内容,核心触发逻辑
- :deep(.el-menu--collapse .el-sub-menu__popper) {
- pointer-events: auto !important;
- &:hover .box {
- display: flex !important;
- }
- }
- // 3. 折叠状态三级内容(box):强制定位在二级弹框右侧,顶对齐
- :deep(.el-menu--collapse .el-sub-menu__popper .box) {
- position: absolute !important;
- left: 100% !important; // 紧贴二级弹框右侧
- top: 0 !important; // 和二级弹框顶对齐
- z-index: 9999 !important;
- width: 300px !important; // 适配你的imgbox布局
- background: #f8f9fa !important;
- border: 1px solid #e4e7ed !important;
- border-left: none !important;
- box-shadow: 2px 2px 10px rgba(0,0,0,0.05) !important;
- padding: 10px !important;
- margin: 0 !important;
- // 强制显示,解决折叠后被隐藏问题
- display: flex !important;
- pointer-events: auto !important;
- }
- // 4. 折叠状态三级imgbox布局适配:清除多余间距,正常显示
- :deep(.el-menu--collapse .el-sub-menu__popper .box .imgbox) {
- margin: 0 10px 10px 0 !important;
- }
- // 5. 展开状态:恢复原有布局,不影响任何效果
- :deep(.el-menu--not-collapse .el-sub-menu__popper .box) {
- position: static !important;
- width: 100% !important;
- box-shadow: none !important;
- border: none !important;
- }
- </style>
|