Viewer.vue 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. <template>
  2. <Rules v-if="showRules" @close="closeRules" />
  3. <AppHeader v-show="!fscChecked" :project="project" @getUserId="getUserId" :show-adjust="showAdjust" @update="onPointsUpdate" />
  4. <article>
  5. <main>
  6. <div class="split">
  7. <iframe ref="sourceFrame" v-if="sourceURL" :src="sourceURL" frameborder="0" @load="onLoadSource"></iframe>
  8. <div class="tools" v-if="source && !showRules && !ruleChecked" v-show="showWidget && !showAdjust && !fscChecked && (dbsChecked || (!target && !bimChecked))">
  9. <div class="item-date">
  10. <calendar
  11. name="source"
  12. :count="scenes.length"
  13. :controls="controls"
  14. :value="sourceDate"
  15. :highlighted="sourceDays"
  16. @selected="onSelected"
  17. @pick="onPickDate"
  18. @prev="onPrevDate"
  19. @next="onNextDate"
  20. ></calendar>
  21. </div>
  22. <div class="item-mode" v-if="source.type == 2 || source.type == 3">
  23. <div class="iconfont icon-show_roaming" :class="{ active: mode == 0 }" @click="onModeChange(0)"></div>
  24. <div class="iconfont icon-show_plane" :class="{ active: mode == 1 }" @click="onModeChange(1)"></div>
  25. </div>
  26. <div class="item-density" v-if="(source.type == 2 && mode == 1) || (source.type == 3 && mode == 1)" :class="{ active: showDensity }" @click="showDensity = !showDensity">
  27. <span>{{ densityType.text }}</span>
  28. <i class="iconfont icon-arrows_down"></i>
  29. <ul>
  30. <li v-for="density in densityTypes" @click="onDensityChange(density)">{{ density.text }}</li>
  31. </ul>
  32. </div>
  33. </div>
  34. <div class="points" v-if="showAdjust">
  35. <div :class="{ active: points.p1 }" @click="onP1Click('left')">
  36. <i class="iconfont" :class="[points.p1 ? 'icon-positioning01' : 'icon-positioning02']"></i>
  37. <span>P1</span>
  38. </div>
  39. <div :class="{ active: points.p2 }" @click="onP2Click('left')">
  40. <i class="iconfont" :class="[points.p2 ? 'icon-positioning01' : 'icon-positioning02']"></i>
  41. <span>P2</span>
  42. </div>
  43. </div>
  44. </div>
  45. <div class="split" v-if="target">
  46. <iframe ref="targetFrame" :src="targetURL" frameborder="0" @load="onLoadTarget"></iframe>
  47. <div class="tools" v-show="!fscChecked && !bimChecked">
  48. <div class="item-date target">
  49. <calendar
  50. name="target"
  51. :count="scenes.length"
  52. :controls="controls"
  53. :value="targetDate"
  54. :highlighted="targetDays"
  55. @selected="onSelected"
  56. @pick="onPickDate"
  57. @prev="onPrevDate"
  58. @next="onNextDate"
  59. ></calendar>
  60. </div>
  61. </div>
  62. <div class="points" v-if="showAdjust">
  63. <div :class="{ active: points.p1 }" @click="onP1Click('right')">
  64. <i class="iconfont" :class="[points.p1 ? 'icon-positioning01' : 'icon-positioning02']"></i>
  65. <span>P1</span>
  66. </div>
  67. <div :class="{ active: points.p2 }" @click="onP2Click('right')">
  68. <i class="iconfont" :class="[points.p2 ? 'icon-positioning01' : 'icon-positioning02']"></i>
  69. <span>P2</span>
  70. </div>
  71. </div>
  72. </div>
  73. <div class="model" v-show="showWidget && !showAdjust && !showRules">
  74. <div class="rule" :class="{ active: ruleChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked">
  75. <div @click="onRuleChecked">
  76. <i class="iconfont icon-measurement"></i>
  77. <span>{{ $t('common.measure') }}</span>
  78. </div>
  79. </div>
  80. <div v-if="isLogin && isAuth" class="file" :class="{ active: fileChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked && !ruleChecked">
  81. <div @click="onFileChecked">
  82. <i class="iconfont icon-note1"></i>
  83. <span>{{ $t('home.tag') }}</span>
  84. </div>
  85. </div>
  86. <div class="bim" :class="{ active: bimChecked, disable: bimDisable }" v-show="!fscChecked && !showBim && !ruleChecked">
  87. <div @click="onBimChecked">
  88. <i class="iconfont icon-BIM"></i>
  89. <span>BIM</span>
  90. </div>
  91. </div>
  92. <div class="dbs" :class="{ active: dbsChecked, disable: dbsDisable }" v-show="!fscChecked && !showBim && !ruleChecked">
  93. <div @click="onDbsChecked">
  94. <i class="iconfont icon-split_screen"></i>
  95. <span>{{ $t('home.splitScreen') }}</span>
  96. </div>
  97. </div>
  98. <div class="fsc" :class="{ active: fscChecked }" @click="onFscChecked">
  99. <i class="iconfont" :class="[fscChecked ? 'icon-full_screen_selected' : 'icon-full_screen_normal']"></i>
  100. <span>{{ $t('home.fullScreen') }}</span>
  101. </div>
  102. </div>
  103. <TagManager ref="tagManager" />
  104. </main>
  105. </article>
  106. <Toast v-if="showTips" type="warn" :content="showTips" :close="() => (showTips = null)" />
  107. <Files :show="fileChecked" @exit="() => (fileChecked = false)" />
  108. </template>
  109. <script setup>
  110. import { ref, onMounted, computed, nextTick, provide, watch } from 'vue'
  111. import { http } from '@/utils/request'
  112. import axios from 'axios'
  113. import browser from '@/utils/browser'
  114. import Toast from '@/components/dialog/Toast'
  115. import AppHeader from '@/components/header'
  116. import Calendar from '@/components/calendar'
  117. import Files from '@/components/files'
  118. import TagManager from '@/components/files/TagManager'
  119. import sync, { beforeChangeURL, loadSourceScene, loadTargetScene, setPanoWithBim, flyToP1P2 } from '@/utils/sync'
  120. import i18n, { getLocale, useI18n, useScope, setI18nLanguage, loadLocaleMessages } from '@/i18n'
  121. import Rules from './Rules'
  122. let tagsNum = null
  123. const { t } = useI18n({ useScope: 'global' })
  124. const isDev = process.env.VUE_APP_TEST == 1
  125. const rules = []
  126. const tags = ref([])
  127. const notify = ref(null)
  128. const isEdit = ref(false)
  129. const isLogin = ref(false)
  130. const isFlying = ref(false)
  131. const editTagId = ref(null)
  132. const isAuth = ref(false)
  133. const source = ref(null)
  134. const tagManager = ref(null)
  135. provide('tags', tags)
  136. provide('notify', notify)
  137. provide('isEdit', isEdit)
  138. provide('editTagId', editTagId)
  139. provide('isLogin', isLogin)
  140. provide('isFlying', isFlying)
  141. provide('isAuth', isAuth)
  142. provide('source', source)
  143. const userId = ref(localStorage.getItem('userId') || null)
  144. const getUserId = id => {
  145. userId.value = id
  146. }
  147. const showRules = ref(false)
  148. const closeRules = () => {
  149. showRules.value = false
  150. window.kankan.TagManager.cancelMeasure()
  151. }
  152. // 是否BIM模式
  153. const showBim = ref(browser.urlHasValue('bim'))
  154. // 是否校准模式
  155. const showSplit = ref(browser.urlHasValue('split'))
  156. const showAdjust = ref(browser.urlHasValue('adjust'))
  157. const bimChecked = ref()
  158. const dbsChecked = ref(null)
  159. const fscChecked = ref(null)
  160. const fileChecked = ref(false)
  161. const ruleChecked = ref(false)
  162. const datepickName = ref(null)
  163. const sourceFrame = ref(null)
  164. const targetFrame = ref(null)
  165. const mode = ref(0)
  166. const target = ref(null)
  167. const project = ref(null)
  168. const points = ref({ p1: null, p2: null })
  169. watch(
  170. () => dbsChecked.value,
  171. (val, old) => {
  172. let tagsVieww = document.querySelector(`div[xui_tags_view]`)
  173. if (val) {
  174. // tagsVieww.style.visibility = 'hidden'
  175. // tagsVieww.style.width = '50%'
  176. // tagsVieww.style.overflow = 'hidden'
  177. } else {
  178. // tagsVieww.style.visibility = 'visible'
  179. // tagsVieww.style.width = '100%'
  180. // tagsVieww.style.overflow = 'visible'
  181. if (bimChecked.value) {
  182. tagsVieww.style.visibility = 'hidden'
  183. }
  184. }
  185. }
  186. )
  187. watch(
  188. () => bimChecked.value,
  189. (val, old) => {
  190. let tagsVieww = document.querySelector(`div[xui_tags_view]`)
  191. if (val) {
  192. if (!dbsChecked.value) {
  193. tagsVieww.style.visibility = 'hidden'
  194. }
  195. } else {
  196. if (!dbsChecked.value) {
  197. tagsVieww.style.visibility = 'visible'
  198. }
  199. }
  200. }
  201. )
  202. const densityTypes = ref([
  203. { type: 'high', text: t('common.high') },
  204. { type: 'middle', text: t('common.middle') },
  205. { type: 'low', text: t('common.low') },
  206. ])
  207. const densityType = ref(densityTypes.value[0])
  208. const showTips = ref(null)
  209. const showDensity = ref(false)
  210. const showWidget = computed(() => {
  211. if (fileChecked.value) {
  212. return false
  213. }
  214. return true
  215. })
  216. const scenes = computed(() => {
  217. if (!project.value) {
  218. return []
  219. }
  220. return project.value.sceneList.map(item => {
  221. return {
  222. num: item.num,
  223. type: item.type,
  224. createTime: item.createTime,
  225. }
  226. })
  227. })
  228. const controls = computed(() => {
  229. if (bimChecked.value) {
  230. return scenes.value.length > 1
  231. }
  232. return dbsChecked.value ? scenes.value.length > 2 : scenes.value.length > 1
  233. })
  234. const sourceURL = computed(() => {
  235. beforeChangeURL('source')
  236. if (bimChecked.value && !dbsChecked.value) {
  237. return `smart-bim.html?m=${project.value.bimData.bimOssFilePath}&lang=${getLocale()}`
  238. }
  239. if (!source.value) {
  240. return
  241. }
  242. getTagList(source.value.num)
  243. if (source.value.type < 2) {
  244. // 看看、看见场景
  245. return `smart-kankan.html?m=${source.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}`
  246. } else {
  247. getLaserInfo(source.value.num)
  248. // 深时场景
  249. let url = ''
  250. if (process.env.VUE_APP_ENV == 'aws') {
  251. url = `smart-laser.html?m=${source.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}&panoResourceBasePath=${
  252. process.env.VUE_APP_RESOURCE_URL
  253. }&resourceBasePath=${process.env.VUE_APP_LASER_RESOURCE_URL}&region=${process.env.VUE_APP_ENV}`
  254. } else {
  255. url = `smart-laser.html?m=${source.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}&panoResourceBasePath=${
  256. process.env.VUE_APP_RESOURCE_URL
  257. }&resourceBasePath=${process.env.VUE_APP_LASER_RESOURCE_URL}`
  258. }
  259. return url
  260. }
  261. })
  262. const noPanorama = ref(true)
  263. const getLaserInfo = num => {
  264. axios
  265. .get(process.env.VUE_APP_LASER_URL + `laser/dataset/${num}/getDataSet`)
  266. .then(res => {
  267. if (res.data.code == 200) {
  268. let list = res.data.data
  269. list.forEach(item => {
  270. if (item.pointCount) {
  271. noPanorama.value = false
  272. }
  273. })
  274. if (noPanorama.value) {
  275. // onModeChange(1)
  276. mode.value = 1
  277. }
  278. } else {
  279. }
  280. })
  281. .catch(() => {
  282. // showTips.value = t('code.failed')
  283. })
  284. }
  285. const targetURL = computed(() => {
  286. if (bimChecked.value) {
  287. return `smart-bim.html?m=${project.value.bimData.bimOssFilePath}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}`
  288. }
  289. // getTagList(target.value.num)
  290. if (target.value.type < 2) {
  291. // 看看、看见场景
  292. return `smart-kankan.html?m=${target.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}`
  293. } else {
  294. // 深时场景
  295. let url = ''
  296. if (process.env.VUE_APP_ENV == 'aws') {
  297. url = `smart-laser.html?m=${target.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}&panoResourceBasePath=${
  298. process.env.VUE_APP_RESOURCE_URL
  299. }&resourceBasePath=${process.env.VUE_APP_LASER_RESOURCE_URL}&region=${process.env.VUE_APP_ENV}`
  300. } else {
  301. url = `smart-laser.html?m=${target.value.num}${isDev ? '&dev' : ''}&lang=${getLocale()}&serve_link=${process.env.VUE_APP_LASER_URL}&panoResourceBasePath=${
  302. process.env.VUE_APP_RESOURCE_URL
  303. }&resourceBasePath=${process.env.VUE_APP_LASER_RESOURCE_URL}`
  304. }
  305. return url
  306. }
  307. })
  308. const sourceDate = computed(() => {
  309. if (source.value) {
  310. return source.value.createTime.toDate()
  311. }
  312. })
  313. const targetDate = computed(() => {
  314. if (target.value) {
  315. return target.value.createTime.toDate()
  316. }
  317. })
  318. const sourceDays = computed(() => {
  319. let dates = []
  320. if (datepickName.value == 'source') {
  321. if (dbsChecked.value) {
  322. // 分屏模式
  323. if (bimChecked.value) {
  324. // BIM模式
  325. dates = scenes.value.map(item => item.createTime.toDate())
  326. } else {
  327. // 非BIM模式
  328. dates = scenes.value.filter(item => item.createTime != target.value.createTime).map(item => item.createTime.toDate())
  329. }
  330. } else {
  331. // 非分屏模式
  332. dates = scenes.value.map(item => item.createTime.toDate())
  333. }
  334. }
  335. return {
  336. dates: dates,
  337. }
  338. })
  339. const targetDays = computed(() => {
  340. let dates = []
  341. if (datepickName.value == 'target') {
  342. dates = scenes.value.filter(item => item.createTime != source.value.createTime).map(item => item.createTime.toDate())
  343. }
  344. return {
  345. dates: dates,
  346. }
  347. })
  348. const fileDisable = computed(() => {
  349. return false
  350. })
  351. const bimDisable = computed(() => {
  352. if (!project.value || !project.value.bimData) {
  353. return true
  354. }
  355. })
  356. const dbsDisable = computed(() => {
  357. if (scenes.value.length == 0) {
  358. // 没有场景的情况
  359. return 1
  360. }
  361. if (!bimChecked.value && scenes.value.length == 1) {
  362. // 只有1个场景的情况
  363. return 2
  364. }
  365. })
  366. const onLoadSource = () => {
  367. if (bimChecked.value && !dbsChecked.value) {
  368. // BIM单屏模式
  369. return
  370. }
  371. if (source.value.type < 2) {
  372. window['laser'] = null
  373. window['kankan'] = sourceFrame.value.contentWindow.app
  374. window['kankan'].TagManager.load(tags.value)
  375. window['kankan'].Camera.on('flying.started', pano => {
  376. isFlying.value = true
  377. })
  378. window['kankan'].Camera.on('flying.ended', pano => {
  379. isFlying.value = false
  380. })
  381. } else {
  382. window['kankan'] = null
  383. window['laser'] = sourceFrame.value.contentWindow.loaded
  384. window.laser.then(sdk => {
  385. sdk.scene.on('posChange', cameraPos => {
  386. tags.value.forEach(tag => {
  387. const info2d = sdk.scene.getScreenByPoint(tag.position, true)
  388. // tag.x = info2d.pos.x
  389. // tag.y = info2d.pos.y
  390. // tag.visible = info2d.trueSide && info2d.inSight
  391. tag.visible = info2d.trueSide && info2d.inSight
  392. if (tag.visible) {
  393. tag.x = info2d.pos.x
  394. tag.y = info2d.pos.y
  395. }
  396. })
  397. })
  398. window[0].viewer.images360.addEventListener('flyToPano', () => {
  399. isFlying.value = true
  400. })
  401. window[0].viewer.images360.addEventListener('flyToPanoDone', () => {
  402. isFlying.value = false
  403. })
  404. })
  405. }
  406. loadSourceScene(sourceFrame, source.value.type < 2 ? 'kankan' : 'laser', mode.value)
  407. }
  408. const onLoadTarget = () => {
  409. if (bimChecked.value) {
  410. loadTargetScene(targetFrame, 'bim')
  411. } else {
  412. loadTargetScene(targetFrame, target.value.type < 2 ? 'kankan' : 'laser', mode.value)
  413. }
  414. }
  415. const onModeChange = targetMode => {
  416. if (targetMode == 0 && noPanorama.value) {
  417. showTips.value = t('home.noPanorama')
  418. return
  419. }
  420. window.Log('changeMode:' + targetMode, '#3cffff')
  421. if (sync.sourceInst) {
  422. sync.sourceInst.loaded.then(sdk => sdk.scene.changeMode(targetMode))
  423. mode.value = targetMode
  424. sync.views.laserChangeMode(mode.value)
  425. }
  426. }
  427. const onDensityChange = density => {
  428. if (sync.sourceInst) {
  429. sync.sourceInst.loaded.then(sdk => {
  430. //console.log('onDensityChange',sync.sourceInst.sceneName, density.type)
  431. let data = sdk.scene.changePointDensity(density.type)
  432. sdk.scene.changeDensityPercent(data.percent)
  433. sync.sourceInst.viewer.dispatchEvent('densityChange')
  434. })
  435. densityType.value = density
  436. }
  437. }
  438. const onPickDate = name => {
  439. datepickName.value = name
  440. tagManager.value.onClose()
  441. }
  442. const onSelected = data => {
  443. if (!data.payload) {
  444. return
  445. }
  446. let { name, payload } = data
  447. let date = payload.format('YYYY-mm-dd')
  448. let dates = (name == 'source' ? sourceDays : targetDays).value.dates.map(item => item.format('YYYY-mm-dd'))
  449. if (dates.indexOf(date) != -1) {
  450. let time = payload.format('YYYY-mm-dd HH:MM')
  451. let find = scenes.value.find(c => c.createTime.indexOf(time) != -1)
  452. if (find) {
  453. if (name == 'source') {
  454. if (find.num != source.value.num) {
  455. source.value = find
  456. }
  457. } else {
  458. if (find.num != target.value.num) {
  459. target.value = find
  460. }
  461. }
  462. }
  463. } else {
  464. showTips.value = t('home.dateScene')
  465. }
  466. datepickName.value = null
  467. }
  468. const onPrevDate = name => {
  469. tagManager.value.onClose()
  470. let scene = null
  471. if (name == 'source') {
  472. scene = source
  473. } else {
  474. scene = target
  475. }
  476. let index = scenes.value.findIndex(item => item.num == scene.value.num)
  477. if (index == -1) {
  478. return
  479. }
  480. if (--index == -1) {
  481. index = scenes.value.length - 1
  482. }
  483. if (target.value && !bimChecked.value) {
  484. // 分屏模式判断
  485. if (name == 'source') {
  486. if (scenes.value[index].createTime == target.value.createTime) {
  487. index--
  488. }
  489. } else {
  490. if (scenes.value[index].createTime == source.value.createTime) {
  491. index--
  492. }
  493. }
  494. if (index == -1) {
  495. index = scenes.value.length - 1
  496. }
  497. }
  498. scene.value = scenes.value[index]
  499. }
  500. const onNextDate = name => {
  501. tagManager.value.onClose()
  502. let scene = null
  503. if (name == 'source') {
  504. scene = source
  505. } else {
  506. scene = target
  507. }
  508. let index = scenes.value.findIndex(item => item.num == scene.value.num)
  509. if (index == -1) {
  510. return
  511. }
  512. if (++index > scenes.value.length - 1) {
  513. index = 0
  514. }
  515. if (target.value && !bimChecked.value) {
  516. // 分屏模式判断
  517. if (name == 'source') {
  518. if (scenes.value[index].createTime == target.value.createTime) {
  519. index++
  520. }
  521. } else {
  522. if (scenes.value[index].createTime == source.value.createTime) {
  523. index++
  524. }
  525. }
  526. if (index > scenes.value.length - 1) {
  527. index = 0
  528. }
  529. }
  530. scene.value = scenes.value[index]
  531. }
  532. const onFileChecked = () => {
  533. fileChecked.value = !fileChecked.value
  534. }
  535. // bim点击
  536. const onBimChecked = () => {
  537. if (bimDisable.value) {
  538. return (showTips.value = t('home.notFindFile'))
  539. }
  540. if (bimChecked.value) {
  541. bimChecked.value = false
  542. if (dbsChecked.value) {
  543. // 如果没有多场景数据,取消分屏
  544. if (scenes.value.length < 2) {
  545. onDbsChecked()
  546. return
  547. }
  548. // 判断是否分屏状态
  549. let index = scenes.value.findIndex(item => item.num == source.value.num)
  550. if (index == -1) {
  551. return
  552. }
  553. if (++index > scenes.value.length - 1) {
  554. index = 0
  555. }
  556. target.value = scenes.value[index]
  557. }
  558. } else {
  559. bimChecked.value = true
  560. }
  561. }
  562. // 分屏点击
  563. const onDbsChecked = () => {
  564. if (dbsDisable.value && !dbsChecked.value) {
  565. return (showTips.value = t('home.notFindScene'))
  566. }
  567. dbsChecked.value = !dbsChecked.value
  568. if (dbsChecked.value) {
  569. if (bimChecked.value) {
  570. // BIM分屏
  571. source.value = scenes.value[scenes.value.length - 1]
  572. target.value = project.value.bimData
  573. } else {
  574. // 四维看看、激光场景分屏
  575. let index = scenes.value.findIndex(item => item.num == source.value.num)
  576. if (index == -1) {
  577. return
  578. }
  579. if (++index > scenes.value.length - 1) {
  580. index = 0
  581. }
  582. target.value = scenes.value[index]
  583. }
  584. } else {
  585. target.value = null
  586. }
  587. }
  588. // 全屏点击
  589. const onFscChecked = () => {
  590. let element = document.documentElement
  591. fscChecked.value = !fscChecked.value
  592. if (fscChecked.value) {
  593. if (element.requestFullscreen) {
  594. element.requestFullscreen()
  595. } else if (element.webkitRequestFullScreen) {
  596. element.webkitRequestFullScreen()
  597. } else if (element.mozRequestFullScreen) {
  598. element.mozRequestFullScreen()
  599. } else if (element.msRequestFullscreen) {
  600. element.msRequestFullscreen()
  601. }
  602. } else {
  603. if (document.exitFullscreen) {
  604. document.exitFullscreen()
  605. } else if (document.webkitCancelFullScreen) {
  606. document.webkitCancelFullScreen()
  607. } else if (document.mozCancelFullScreen) {
  608. document.mozCancelFullScreen()
  609. } else if (document.msExitFullscreen) {
  610. document.msExitFullscreen()
  611. }
  612. }
  613. }
  614. const onPointsUpdate = (type, data) => {
  615. points.value[type] = data
  616. }
  617. const onP1Click = type => {
  618. if (!points.value.p1) {
  619. showTips.value = t('home.notChoosePoint')
  620. return
  621. }
  622. console.log(points.value.p1)
  623. // todo 定位
  624. flyToP1P2(points.value.p1)
  625. }
  626. const onP2Click = type => {
  627. if (!points.value.p2) {
  628. showTips.value = t('home.notChoosePoint')
  629. return
  630. }
  631. // todo 定位
  632. flyToP1P2(points.value.p2)
  633. }
  634. const onRuleHandler = sdk => {
  635. let rule = sdk.startMeasure()
  636. rule.bus.on('end', () => {
  637. setTimeout(() => {
  638. onRuleHandler(sdk)
  639. }, 1)
  640. })
  641. rules.push(rule)
  642. }
  643. const onRuleChecked = () => {
  644. if (ruleChecked.value) {
  645. ruleChecked.value = false
  646. rules.forEach(rule => {
  647. rule.clear()
  648. })
  649. return
  650. }
  651. if (source.value.type < 2) {
  652. showRules.value = true
  653. window.kankan.TagManager.startMeasure()
  654. } else {
  655. sync.sourceInst.loaded.then(sdk => {
  656. onRuleHandler(sdk)
  657. })
  658. ruleChecked.value = true
  659. }
  660. }
  661. const getTagList = num => {
  662. const pnum = num ? num : browser.getURLParam('m')
  663. http.post(`smart-site/marking/list`, {
  664. projectId: projectId,
  665. pageNum: 1,
  666. pageSize: 200,
  667. num: pnum,
  668. }).then(response => {
  669. if (response.data && response.data.list) {
  670. tags.value.length = 0
  671. response.data.list
  672. .map(item => {
  673. item.hotData.visible = false
  674. item.hotData.id = item.markingId
  675. item.hotData.createTime = item.createTime
  676. item.hotData.createBy = item.createBy
  677. return item.hotData
  678. })
  679. .forEach(item => {
  680. tags.value.push(item)
  681. })
  682. if (window['kankan']) {
  683. window['kankan'].TagManager.load(tags.value)
  684. }
  685. }
  686. })
  687. }
  688. const getInfo = () => {
  689. http.get(`smart-site/project/info?projectId=${projectId}&sceneOrder=asc`)
  690. .then(response => {
  691. if (response.success) {
  692. if (response.data) {
  693. document.title = response.data.projectName
  694. if (response.data.panos) {
  695. try {
  696. response.data.panos = JSON.parse(response.data.panos)
  697. points.value.p1 = response.data.panos.p1
  698. points.value.p2 = response.data.panos.p2
  699. setPanoWithBim(response.data.panos)
  700. } catch (error) {
  701. console.error(error)
  702. }
  703. }
  704. project.value = response.data
  705. if (showBim.value) {
  706. onBimChecked()
  707. } else if (project.value.sceneList.length) {
  708. if (num) {
  709. source.value = project.value.sceneList.find(c => c.num == num)
  710. } else {
  711. source.value = project.value.sceneList[project.value.sceneList.length - 1]
  712. }
  713. if (!source.value) {
  714. return (showTips.value = t('home.sceneDelete'))
  715. }
  716. // mode.value = source.value?.location == 6 ? 1 : 0
  717. if (showAdjust.value || showSplit.value) {
  718. onBimChecked()
  719. nextTick(() => onDbsChecked())
  720. }
  721. } else {
  722. return (showTips.value = t('home.sceneDelete'))
  723. }
  724. }
  725. } else {
  726. if (response.code == 4008) {
  727. // 未登录
  728. localStorage.removeItem('token')
  729. } else {
  730. showTips.value = t(`code.${response.code}`)
  731. }
  732. }
  733. })
  734. .catch(() => {
  735. showTips.value = t('code.failed')
  736. })
  737. }
  738. const num = browser.valueFromUrl('m') || ''
  739. const projectId = browser.valueFromUrl('projectId') || 1
  740. onMounted(() => {
  741. getInfo()
  742. // getTagList()
  743. document.addEventListener('fullscreenchange', () => {
  744. if (document.fullscreenElement) {
  745. if (!fscChecked.value) {
  746. fscChecked.value = true
  747. }
  748. } else {
  749. if (fscChecked.value) {
  750. fscChecked.value = false
  751. }
  752. }
  753. })
  754. })
  755. </script>
  756. <style lang="scss" scoped>
  757. article {
  758. display: flex;
  759. width: 100%;
  760. height: 100%;
  761. overflow: hidden;
  762. }
  763. aside {
  764. width: 160px;
  765. height: 100%;
  766. background-color: rgba(0, 0, 0, 0.8);
  767. h4 {
  768. font-size: 16px;
  769. text-align: center;
  770. }
  771. ul {
  772. margin-top: 20px;
  773. }
  774. li {
  775. margin: 0;
  776. padding: 0;
  777. font-size: 16px;
  778. margin-left: 20px;
  779. cursor: pointer;
  780. &:hover,
  781. &.active {
  782. color: #00c8af;
  783. }
  784. }
  785. }
  786. main {
  787. flex: 1;
  788. width: 100%;
  789. height: 100%;
  790. position: relative;
  791. display: flex;
  792. &.full {
  793. .split {
  794. width: 50%;
  795. }
  796. }
  797. iframe {
  798. position: absolute;
  799. left: 0;
  800. top: 0;
  801. z-index: 1000;
  802. width: 100%;
  803. height: 100%;
  804. border: none;
  805. outline: none;
  806. }
  807. .split {
  808. margin-left: 2px;
  809. width: 100%;
  810. height: 100%;
  811. overflow: hidden;
  812. position: relative;
  813. &:first-child,
  814. &:last-child {
  815. margin-left: 0;
  816. }
  817. .points {
  818. position: absolute;
  819. left: 50%;
  820. top: 64px;
  821. z-index: 9999;
  822. display: flex;
  823. transform: translateX(-50%);
  824. div {
  825. cursor: pointer;
  826. margin-left: 20px;
  827. width: 70px;
  828. height: 88px;
  829. background: rgba(27, 27, 28, 0.8);
  830. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25), inset 0px 0px 0px 2px rgba(255, 255, 255, 0.1);
  831. border-radius: 8px 8px 8px 8px;
  832. opacity: 1;
  833. border: 1px solid #000000;
  834. display: flex;
  835. flex-direction: column;
  836. align-items: center;
  837. justify-content: center;
  838. &.active {
  839. color: #0076f6;
  840. }
  841. i {
  842. font-size: 24px;
  843. }
  844. span {
  845. font-size: 16px;
  846. margin-top: 10px;
  847. }
  848. }
  849. }
  850. }
  851. .model {
  852. position: absolute;
  853. left: 50px;
  854. bottom: 40px;
  855. z-index: 1000;
  856. display: flex;
  857. flex-direction: column;
  858. > div {
  859. cursor: pointer;
  860. width: 50px;
  861. height: 50px;
  862. margin-top: 16px;
  863. background: rgba(27, 27, 28, 0.8);
  864. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  865. border-radius: 47px 47px 47px 47px;
  866. border: 1px solid #000000;
  867. display: flex;
  868. flex-direction: column;
  869. align-items: center;
  870. justify-content: center;
  871. > div {
  872. width: 100%;
  873. height: 100%;
  874. display: flex;
  875. flex-direction: column;
  876. align-items: center;
  877. justify-content: center;
  878. }
  879. &.active {
  880. color: #0076f6;
  881. }
  882. &.disable {
  883. opacity: 0.5;
  884. }
  885. span {
  886. font-size: 12px;
  887. padding-top: 1px;
  888. transform: scale(0.8);
  889. text-align: center;
  890. }
  891. }
  892. }
  893. .tools {
  894. position: absolute;
  895. width: 100%;
  896. bottom: 40px;
  897. z-index: 2000;
  898. display: flex;
  899. justify-content: center;
  900. align-items: center;
  901. color: #fff;
  902. pointer-events: none;
  903. > div {
  904. pointer-events: all;
  905. }
  906. .item-mode {
  907. height: 50px;
  908. background: rgba(27, 27, 28, 0.8);
  909. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  910. border-radius: 47px 47px 47px 47px;
  911. border: 1px solid #000000;
  912. display: flex;
  913. justify-content: center;
  914. align-items: center;
  915. margin-left: 10px;
  916. margin-right: 10px;
  917. font-size: 16px;
  918. padding: 0 16px;
  919. div {
  920. cursor: pointer;
  921. font-size: 18px;
  922. }
  923. div:last-child {
  924. margin-left: 20px;
  925. }
  926. div.active {
  927. color: #0076f6;
  928. }
  929. }
  930. .item-density {
  931. position: relative;
  932. cursor: pointer;
  933. height: 50px;
  934. width: 68px;
  935. background: rgba(27, 27, 28, 0.8);
  936. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  937. border-radius: 47px 47px 47px 47px;
  938. border: 1px solid #000000;
  939. display: flex;
  940. justify-content: center;
  941. align-items: center;
  942. margin-left: 10px;
  943. margin-right: 10px;
  944. font-size: 16px;
  945. padding: 0 16px;
  946. &.active {
  947. ul {
  948. display: block;
  949. }
  950. i {
  951. transform: scale(0.8) rotate(180deg);
  952. }
  953. }
  954. span {
  955. margin-right: 4px;
  956. }
  957. i {
  958. transform: scale(0.8);
  959. font-size: 14px;
  960. }
  961. ul {
  962. display: none;
  963. position: absolute;
  964. left: 0;
  965. bottom: calc(100% + 10px);
  966. width: 68px;
  967. padding: 10px 0;
  968. border-radius: 12px;
  969. text-align: center;
  970. background: rgba(27, 27, 28, 0.8);
  971. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  972. border: 1px solid #000000;
  973. list-style: none;
  974. li {
  975. padding: 5px 0;
  976. &:hover {
  977. color: #0076f6;
  978. }
  979. }
  980. }
  981. }
  982. }
  983. }
  984. </style>
  985. <style lang="scss">
  986. #app {
  987. background-color: rgba(0, 0, 0, 0.8);
  988. display: flex;
  989. flex-direction: column;
  990. }
  991. .vuejs3-datepicker__calendar {
  992. background: rgba(27, 27, 28, 0.8);
  993. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  994. border-radius: 4px 4px 4px 4px;
  995. opacity: 1;
  996. border: 1px solid #000000;
  997. filter: blur(undefinedpx);
  998. color: #fff;
  999. }
  1000. .vuejs3-datepicker__calendar-topbar {
  1001. display: none !important;
  1002. }
  1003. .vuejs3-datepicker__calendar header .up:not(.disabled):hover {
  1004. background: rgba(0, 0, 0, 0.3);
  1005. color: #fff;
  1006. }
  1007. .vuejs3-datepicker__calendar header .prev:after {
  1008. border-left: 1px solid #fff;
  1009. border-bottom: 1px solid #fff;
  1010. }
  1011. .vuejs3-datepicker__calendar header .prev:not(.disabled):hover {
  1012. background: rgba(0, 0, 0, 0.3);
  1013. }
  1014. .vuejs3-datepicker__calendar header .next:after {
  1015. border-top: 1px solid #fff;
  1016. border-right: 1px solid #fff;
  1017. }
  1018. .vuejs3-datepicker__calendar header .next:not(.disabled):hover {
  1019. background: rgba(0, 0, 0, 0.3);
  1020. }
  1021. .vuejs3-datepicker__calendar .cell {
  1022. font-size: 16px !important;
  1023. border-radius: 4px;
  1024. }
  1025. .highlighted {
  1026. color: #076ede !important;
  1027. background: transparent !important;
  1028. }
  1029. .selected {
  1030. color: #fff !important;
  1031. background: #0076f6 !important;
  1032. }
  1033. </style>