SViewer.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. <template>
  2. <main>
  3. <iframe ref="sourceFrame" v-if="sourceURL" :src="sourceURL" frameborder="0" @load="onLoadSource"></iframe>
  4. <div class="model" v-show="!showAdjust">
  5. <div class="bim" :class="{ active: bimChecked, disable: project && !project.bimData }">
  6. <div @click="onBimChecked">
  7. <i class="iconfont icon-BIM"></i>
  8. </div>
  9. <span v-show="showBimTips">BIM</span>
  10. </div>
  11. </div>
  12. <div class="tools" v-if="source" v-show="!bimChecked">
  13. <div class="item-date">
  14. <Calendar :value="sourceDate" :highlighted="sourceDays" @selected="onSelected" @prev="onPrevDate" @next="onNextDate" />
  15. </div>
  16. <div class="item-mode" v-if="source.type == 2">
  17. <div class="iconfont icon-show_roaming" :class="{ active: mode == 0 }" @click="onModeChange(0)"></div>
  18. <div class="iconfont icon-show_plane" :class="{ active: mode == 1 }" @click="onModeChange(1)"></div>
  19. </div>
  20. <div class="item-density" v-if="source.type == 2 && mode == 1" @click="showDensity = true">
  21. <span>{{ densityType.text }}</span>
  22. <div @click.stop="showDensity = false" v-if="showDensity">
  23. <ul>
  24. <li class="title">{{ $t('home.thickness') }}</li>
  25. <li v-for="density in densityTypes" @click="onDensityChange(density)">
  26. {{ density.text }}<i class="iconfont" :class="{ 'icon-check': density.type == densityType.type }"></i>
  27. </li>
  28. </ul>
  29. </div>
  30. </div>
  31. </div>
  32. </main>
  33. <Toast v-if="showTips" type="warn" :content="showTips" :close="() => (showTips = null)" />
  34. </template>
  35. <script setup>
  36. import { ref, onMounted, computed, nextTick } from 'vue'
  37. import { http } from '@/utils/request'
  38. import Toast from '@/components/dialog/Toast'
  39. import browser from '@/utils/browser'
  40. import Calendar from '@/components/calendar/mobile.vue'
  41. import sync, { loadSourceScene, loadTargetScene, setPanoWithBim } from '@/utils/sync'
  42. import i18n from '@/i18n'
  43. const { t } = i18n.global
  44. const isDev = process.env.VUE_APP_TEST == 1
  45. // 点位信息
  46. let lastFakeApp = null
  47. let panoData
  48. // 是否BIM模式
  49. const showBim = ref(browser.urlHasValue('bim'))
  50. const showBimTips = ref(false)
  51. const showTips = ref(null)
  52. const showDensity = ref(false)
  53. const bimChecked = ref(null)
  54. const sourceFrame = ref(null)
  55. const mode = ref(0)
  56. const source = ref(null)
  57. const target = ref(null)
  58. const project = ref(null)
  59. const densityTypes = ref([
  60. { type: 'high', text: t('common.high') },
  61. { type: 'middle', text: t('common.middle') },
  62. { type: 'low', text: t('common.low') },
  63. ])
  64. const densityType = ref(densityTypes.value[0])
  65. const scenes = computed(() => {
  66. if (!project.value) {
  67. return []
  68. }
  69. return project.value.sceneList.map(item => {
  70. return {
  71. num: item.num,
  72. type: item.type,
  73. createTime: item.createTime,
  74. }
  75. })
  76. })
  77. const sourceURL = computed(() => {
  78. if (lastFakeApp && sourceFrame.value.contentWindow.fakeApp /* && sourceFrame.value && ( sourceFrame.value.contentWindow.app || sourceFrame.value.contentWindow.viewer)*/) {
  79. //fakeApp代表已经初始化完毕
  80. sync.views.fakeAppUpdateInfo(sourceFrame.value.contentWindow)
  81. }
  82. if (bimChecked.value) {
  83. return `smart-bim.html?m=${project.value.bimData.bimOssFilePath}`
  84. }
  85. if (!source.value) {
  86. return
  87. }
  88. if (source.value.type < 2) {
  89. return `smart-kankan.html?m=${source.value.num}`
  90. } else {
  91. return `smart-laser.html?m=${source.value.num}${isDev ? '&dev' : ''}`
  92. }
  93. })
  94. const onLoadSource = () => {
  95. let win = sourceFrame.value.contentWindow
  96. window.app = win
  97. let loaded = () => {
  98. if (lastFakeApp) {
  99. if (bimChecked.value || lastFakeApp.sceneType == 'bim') {
  100. //->bim 也可能是bim和bim转,当快速点击按钮,其他类型的没加载好就跳回bim
  101. sync.views.bindFakeWithBim(lastFakeApp, win, panoData)
  102. } else {
  103. sync.views.bindWithSameFakeType(lastFakeApp, win)
  104. }
  105. }
  106. if (project.value.sceneList.length) {
  107. lastFakeApp = sync.views.createFakeApp(sourceFrame.value.contentWindow)
  108. }
  109. }
  110. if (bimChecked.value) {
  111. //bim
  112. win.sceneType = 'bim'
  113. win.loaded.then(sdk => {
  114. loaded()
  115. })
  116. } else if (source.value.type < 2) {
  117. win.sceneType = 'kankan'
  118. let sdk = win.app
  119. sdk.Scene.on('loaded', () => {
  120. loaded()
  121. })
  122. } else {
  123. win.sceneType = 'laser'
  124. win.loaded.then(sdk => {
  125. sync.views.laserInit(win, mode.value)
  126. loaded()
  127. })
  128. }
  129. }
  130. const sourceDate = computed(() => {
  131. if (source.value) {
  132. return source.value.createTime.toDate()
  133. }
  134. })
  135. const sourceDays = computed(() => {
  136. const outDays = { year: [], month: [], day: [] }
  137. if (!scenes.value) {
  138. return outDays
  139. }
  140. const inDays = scenes.value.map(item => item.createTime.split(' ')[0].split('-'))
  141. inDays.forEach(item => {
  142. const [year, month, day] = item
  143. if (outDays.year.indexOf(year) == -1) {
  144. outDays.year.push(year)
  145. }
  146. if (outDays.month.indexOf(month) == -1) {
  147. outDays.month.push(month)
  148. }
  149. if (outDays.day.indexOf(day) == -1) {
  150. outDays.day.push(day)
  151. }
  152. })
  153. return outDays
  154. })
  155. const onModeChange = targetMode => {
  156. if (sourceFrame.value && sourceFrame.value.contentWindow.loaded) {
  157. sourceFrame.value.contentWindow.loaded.then(sdk => sdk.scene.changeMode(targetMode))
  158. mode.value = targetMode
  159. sync.views.laserChangeMode(mode.value)
  160. }
  161. }
  162. const onDensityChange = density => {
  163. if (sourceFrame.value && sourceFrame.value.contentWindow.loaded) {
  164. sourceFrame.value &&
  165. sourceFrame.value.contentWindow.loaded.then(sdk => {
  166. let data = sdk.scene.changePointDensity(density.type)
  167. sdk.scene.changeDensityPercent(data.percent)
  168. })
  169. densityType.value = density
  170. }
  171. showDensity.value = false
  172. }
  173. const onSelected = data => {
  174. if (!data.payload) {
  175. return
  176. }
  177. let { payload } = data
  178. let time = payload.format('YYYY-mm-dd')
  179. let find = scenes.value.find(c => c.createTime.indexOf(time) != -1)
  180. if (find) {
  181. if (find.num != source.value.num) {
  182. source.value = find
  183. }
  184. }
  185. }
  186. const onPrevDate = name => {
  187. let index = scenes.value.findIndex(item => item.num == source.value.num)
  188. if (index == -1) {
  189. return
  190. }
  191. if (--index == -1) {
  192. index = scenes.value.length - 1
  193. }
  194. source.value = scenes.value[index]
  195. }
  196. const onNextDate = name => {
  197. let index = scenes.value.findIndex(item => item.num == source.value.num)
  198. if (index == -1) {
  199. return
  200. }
  201. if (++index > scenes.value.length - 1) {
  202. index = 0
  203. }
  204. source.value = scenes.value[index]
  205. }
  206. // bim点击
  207. const onBimChecked = () => {
  208. showBimTips.value = true
  209. setTimeout(() => {
  210. showBimTips.value = false
  211. }, 2000)
  212. if (!project.value || !project.value.bimData) {
  213. showTips.value = t('home.notFindFile')
  214. return
  215. }
  216. if (bimChecked.value) {
  217. bimChecked.value = false
  218. showBimTips.value = false
  219. } else {
  220. bimChecked.value = true
  221. }
  222. }
  223. onMounted(() => {
  224. const num = browser.valueFromUrl('m') || ''
  225. const projectId = browser.valueFromUrl('projectId') || 1
  226. http.get(`smart-site/project/info?projectId=${projectId}&sceneOrder=asc`)
  227. .then(response => {
  228. if (response.success) {
  229. project.value = response.data
  230. if (showBim.value) {
  231. onBimChecked()
  232. }
  233. if (project.value.sceneList.length) {
  234. if (num) {
  235. source.value = project.value.sceneList.find(c => c.num == num)
  236. } else {
  237. source.value = project.value.sceneList[project.value.sceneList.length - 1]
  238. }
  239. if (!source.value) {
  240. return (showTips.value = t('home.sceneDelete'))
  241. }
  242. } else {
  243. return (showTips.value = t('home.sceneDelete'))
  244. }
  245. if (response.data.panos) {
  246. response.data.panos = JSON.parse(response.data.panos)
  247. panoData = response.data.panos //convert with bim
  248. }
  249. } else {
  250. showTips.value = response.message
  251. }
  252. })
  253. .catch(err => {
  254. showTips.value = t('code.failed')
  255. })
  256. })
  257. </script>
  258. <style lang="scss" scoped>
  259. main {
  260. width: 100%;
  261. height: 100%;
  262. iframe {
  263. width: 100%;
  264. height: 100%;
  265. }
  266. .tools {
  267. position: absolute;
  268. width: 100%;
  269. bottom: 40px;
  270. z-index: 2000;
  271. display: flex;
  272. justify-content: center;
  273. align-items: center;
  274. color: #fff;
  275. pointer-events: none;
  276. > div {
  277. pointer-events: all;
  278. }
  279. .item-mode {
  280. height: 50px;
  281. background: rgba(27, 27, 28, 0.8);
  282. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  283. border-radius: 47px 47px 47px 47px;
  284. border: 1px solid #000000;
  285. display: flex;
  286. justify-content: center;
  287. align-items: center;
  288. font-size: 16px;
  289. padding: 0 16px;
  290. div {
  291. cursor: pointer;
  292. font-size: 18px;
  293. }
  294. div:last-child {
  295. margin-left: 20px;
  296. }
  297. div.active {
  298. color: #0076f6;
  299. }
  300. }
  301. .item-density {
  302. position: relative;
  303. cursor: pointer;
  304. height: 50px;
  305. width: 50px;
  306. background: rgba(27, 27, 28, 0.8);
  307. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  308. border-radius: 50%;
  309. border: 1px solid #000000;
  310. display: flex;
  311. justify-content: center;
  312. align-items: center;
  313. margin-left: 10px;
  314. margin-right: 10px;
  315. font-size: 16px;
  316. padding: 0 16px;
  317. > div {
  318. position: fixed;
  319. left: 0;
  320. right: 0;
  321. bottom: 0;
  322. height: 100vh;
  323. z-index: 1000;
  324. background: rgba(0, 0, 0, 0.5);
  325. display: flex;
  326. align-items: flex-end;
  327. justify-content: center;
  328. }
  329. ul {
  330. width: 100%;
  331. padding-bottom: 20px;
  332. border-radius: 12px;
  333. background: rgba(27, 27, 28, 0.8);
  334. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  335. border: 1px solid #000000;
  336. list-style: none;
  337. .title {
  338. justify-content: center;
  339. font-weight: 500;
  340. padding: 20px 0;
  341. border-bottom: solid 1px rgba(255, 255, 255, 0.1);
  342. }
  343. li {
  344. display: flex;
  345. align-items: center;
  346. justify-content: space-between;
  347. padding: 25px 20px;
  348. font-size: 18px;
  349. i {
  350. color: #0076f6;
  351. font-size: 20px;
  352. }
  353. }
  354. }
  355. }
  356. }
  357. .model {
  358. position: absolute;
  359. left: 15px;
  360. top: 20px;
  361. z-index: 1000;
  362. display: flex;
  363. flex-direction: column;
  364. span {
  365. position: absolute;
  366. left: 130%;
  367. top: 48%;
  368. background: rgba(27, 27, 28, 0.8);
  369. padding: 4px;
  370. border-radius: 4px;
  371. color: #fff;
  372. &::before {
  373. content: '';
  374. position: absolute;
  375. right: 100%;
  376. top: 50%;
  377. margin-top: -7px;
  378. width: 0;
  379. height: 0;
  380. border-top: 7px solid transparent;
  381. border-right: 14px solid rgba(27, 27, 28, 0.8);
  382. border-bottom: 7px solid transparent;
  383. }
  384. }
  385. > div {
  386. cursor: pointer;
  387. width: 50px;
  388. height: 50px;
  389. margin-top: 16px;
  390. background: rgba(27, 27, 28, 0.8);
  391. box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  392. border-radius: 47px 47px 47px 47px;
  393. border: 1px solid #000000;
  394. display: flex;
  395. flex-direction: column;
  396. align-items: center;
  397. justify-content: center;
  398. > div {
  399. width: 100%;
  400. height: 100%;
  401. display: flex;
  402. flex-direction: column;
  403. align-items: center;
  404. justify-content: center;
  405. }
  406. &.active {
  407. color: #0076f6;
  408. }
  409. &.disable {
  410. opacity: 0.5;
  411. }
  412. i {
  413. font-size: 20px;
  414. }
  415. }
  416. }
  417. }
  418. </style>
  419. <style lang="scss">
  420. #app {
  421. background-color: rgba(0, 0, 0, 0.8);
  422. display: flex;
  423. flex-direction: column;
  424. }
  425. </style>