Viewer.vue 27 KB

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