Viewer.vue 32 KB

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