Jelajahi Sumber

feat: update

rindy 2 tahun lalu
induk
melakukan
34eeb7474d

+ 1 - 0
public/smart-laser-bim.html

@@ -5,6 +5,7 @@
         <meta charset="utf-8" />
         <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
         <link rel="stylesheet" href="<%= VUE_APP_STATIC_DIR %>/lib/iconfont/iconfont.css" />
+        <link rel="stylesheet" href="//at.alicdn.com/t/c/font_3693743_uzux8act1jo.css" />
         <script src="<%= VUE_APP_STATIC_DIR %>/lib/three.js/build/three.min.js"></script>
     </head>
     <body>

+ 2 - 1
src/assets/css/_settings.scss

@@ -1,7 +1,8 @@
 /* Color system */
 $white: #ffffff;
 $black: #2f2f2f;
-$green: #40b983;
+$green1: #40b983;
+$green: #0076F6;
 $darkGreen: #2f9668;
 $darkestGreen: #217751;
 $blue: #00b9f5;

+ 75 - 0
src/components/calendar/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="calendar">
+        <span class="prev" @click="emits('prev',props.name)"><i class="iconfont icon-arrows_left"></i></span>
+        <span class="cale" @click="onPickDate()">{{ value.format('YYYY-mm-dd') }}<i class="iconfont icon-date"></i></span>
+        <span class="next" @click="emits('next',props.name)"><i class="iconfont icon-arrows_right"></i></span>
+        <div class="calendar-list" v-if="showCalendar" @click="showCalendar = false">
+            <div @click.stop>
+                <datepicker language="zh" :inline="true" :value="value" :highlighted="highlighted" @selected="(...args) => emits('selected', {name:props.name,...args})"></datepicker>
+            </div>
+        </div>
+    </div>
+</template>
+<script setup>
+import { ref, defineProps } from 'vue'
+import Datepicker from '@/components/datepicker/Datepicker'
+const props = defineProps({
+    name:String,
+    value: Date,
+    highlighted: Object,
+})
+const emits = defineEmits(['prev', 'selected', 'next'])
+
+const showCalendar = ref(false)
+const onPrevDate = () => {}
+const onNextDate = () => {}
+const onPickDate = () => {
+    showCalendar.value = !showCalendar.value
+}
+</script>
+<style lang="scss" scoped>
+.calendar {
+    height: 50px;
+    background: rgba(27, 27, 28, 0.8);
+    box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
+    border-radius: 47px 47px 47px 47px;
+    border: 1px solid #000000;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-left: 10px;
+    margin-right: 10px;
+    font-size: 16px;
+    padding: 0 16px;
+    span {
+        cursor: pointer;
+    }
+    .cale {
+        i {
+            margin-left: 4px;
+            font-size: 15px;
+        }
+    }
+    .prev {
+        margin-right: 10px;
+    }
+    .next {
+        margin-left: 10px;
+    }
+}
+.calendar-list {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: 1000;
+    display: flex;
+    align-items: flex-end;
+    justify-content: center;
+    > div {
+        position: relative;
+        margin-bottom: 80px;
+    }
+}
+</style>

+ 1 - 1
src/components/datepicker/PickerDay.vue

@@ -16,7 +16,7 @@
           >&lt;</span
         >
         <span class="day__month_btn" @click="showMonthCalendar" :class="allowedToShowView('month') ? 'up' : ''"
-          >{{ isYmd ? currYearName : currMonthName }} {{ isYmd ? currMonthName : currYearName }}</span
+          >{{ isYmd ? currMonthName : currYearName }}年 {{ isYmd ? currYearName : currMonthName }}</span
         >
         <span @click="isRtl ? previousMonth() : nextMonth()" class="next" :class="{ disabled: isRightNavDisabled }"
           >&gt;</span

+ 3 - 0
src/pages/Bim.vue

@@ -92,4 +92,7 @@ onMounted(() => {
     background-size: contain;
     background-repeat: no-repeat
 }
+.bf-toolbar.bf-toolbar-bottom{
+    bottom: 40px !important;
+}
 </style>

+ 316 - 186
src/pages/LaserBim.vue

@@ -1,40 +1,45 @@
 <template>
-    <header>
-        <div v-if="project">项目 - {{ project.name }}</div>
+    <header v-show="!fscChecked">
+        <div v-if="project">{{ project.projectName }}</div>
     </header>
     <article>
         <main>
             <div class="split" v-if="source">
-                <iframe ref="sourceFrame" :src="`smart-laser.html?m=${source.num}&dev`" frameborder="0" @load="onLoadSource"></iframe>
-                <div class="tools">
+                <iframe ref="sourceFrame" :src="sourceURL" frameborder="0" @load="onLoadSource"></iframe>
+                <!-- <div class="tools">
                     <div class="item-mode">
                         <div class="iconfont icon-show_roaming_normal" :class="{ active: mode == 0 }" @click="onModeChange(0)"></div>
                         <div class="iconfont icon-show_more" :class="{ active: mode == 1 }" @click="onModeChange(1)"></div>
                     </div>
-                    <div class="item-date" v-if="target">
-                        <span class="prev" @click="onPrevDate('source')"><i class="iconfont icon-show_back"></i></span>
-                        <span @click="onPickDate('source')">{{ source.date }}</span>
-                        <span class="next" @click="onNextDate('source')"><i class="iconfont icon-show_back"></i></span>
+                </div> -->
+                <div class="tools" v-show="!fscChecked && !bimChecked">
+                    <div class="item-date">
+                        <calendar name="source" :value="sourceDate" :highlighted="highlighted" @selected="onSelected" @prev="onPrevDate" @next="onNextDate"></calendar>
                     </div>
                 </div>
             </div>
             <div class="split" v-if="target">
-                <iframe ref="targetFrame" :src="`smart-bim.html?m=${target.num}`" frameborder="0" @load="onLoadTarget"></iframe>
+                <iframe ref="targetFrame" :src="targetURL" frameborder="0" @load="onLoadTarget"></iframe>
+                <div class="tools" v-show="!fscChecked && !bimChecked">
+                    <div class="item-date">
+                        <calendar name="target" :value="targetDate" :highlighted="highlighted" @selected="onSelected" @prev="onPrevDate" @next="onNextDate"></calendar>
+                    </div>
+                </div>
             </div>
-            <div class="tools" v-if="source">
-                <div class="item-date" v-if="!target">
-                    <span class="prev" @click="onPrevDate()"><i class="iconfont icon-show_back"></i></span>
-                    <span @click="onPickDate('all')">{{ source.date }}</span>
-                    <span class="next" @click="onNextDate()"><i class="iconfont icon-show_back"></i></span>
+            <div class="model">
+                <div class="bim" :class="{ active: bimChecked, disable: true }" v-show="!fscChecked">
+                    <div @click="onBimChecked">
+                        <i class="iconfont icon-BIM"></i>
+                        <span>BIM</span>
+                    </div>
                 </div>
-                <div class="item-split" @click="onCompare">
-                    <i class="iconfont icon-size-o"></i>
+                <div class="dbs" :class="{ active: dbsChecked }" @click="onDbsChecked" v-show="!fscChecked">
+                    <i class="iconfont icon-split_screen"></i>
+                    <span>分屏</span>
                 </div>
-            </div>
-            <div class="popup" v-if="source && datepickName">
-                <div>
-                    <span @click="onPickClose"><i class="iconfont icon-close"></i></span>
-                    <datepicker  language="zh"  :inline="true" :value="datepickValue" :highlighted="highlighted" @selected="onSelected"></datepicker>
+                <div class="fsc" :class="{ active: fscChecked }" @click="onFscChecked">
+                    <i class="iconfont" :class="[fscChecked ? 'icon-full_screen_selected' : 'icon-full_screen_normal']"></i>
+                    <span>全屏</span>
                 </div>
             </div>
         </main>
@@ -42,12 +47,18 @@
 </template>
 
 <script setup>
-import Datepicker from '@/components/datepicker/Datepicker'
-import { ref, onActivated, onDeactivated, reactive, onMounted, computed } from 'vue'
+import { ref, onMounted, computed } from 'vue'
+import { http } from '@/utils/request'
+import browser from '@/utils/browser'
+import Calendar from '@/components/calendar'
 import ConvertViews from '@/utils/ConvertViews'
- 
-let sourceApp = null, sourceSceneName
-let targetApp = null
+
+let sourceApp = null,
+    targetApp = null
+
+const bimChecked = ref(null)
+const dbsChecked = ref(null)
+const fscChecked = ref(null)
 
 const views = new ConvertViews()
 const datepickName = ref(null)
@@ -58,92 +69,135 @@ const mode = ref(0)
 const source = ref(null)
 const target = ref(null)
 const project = ref(null)
-const projects = reactive([{ pid: '001', name: '五楼办公区', num: 'SS-t-bFMqa1dqUU', date: '2022-09-01' }])
-const scenes = reactive([
-    { pid: '001', num: 'SS-t-bFMqa1dqUU', date: '2022-09-01' },
-    { pid: '001', num: 'SS-t-xp9BDKfzhR', date: '2022-09-03' },
-    { pid: '001', num: 'SS-t-lc5OWhZPaC', date: '2022-09-05' },
-    { pid: '001', num: 'SS-t-Y6gLRFwxE5', date: '2022-09-10' },
-])
+
+const scenes = computed(() => {
+    if (!project.value) {
+        return []
+    }
+    return project.value.sceneList.map(item => {
+        return {
+            num: item.num,
+            type: item.type,
+            createTime: item.createTime,
+        }
+    })
+})
+const sourceURL = computed(() => {
+    if (bimChecked.value) {
+        return `smart-bim.html?m=${source.value.num}`
+    }
+
+    if (source.value.type < 2) {
+        // 看看、看见场景
+        return `smart-kankan.html?m=${source.value.num}&dev`
+    } else {
+        // 深时场景
+        return `smart-laser.html?m=${source.value.num}&dev`
+    }
+})
+const targetURL = computed(() => {
+    if (bimChecked.value) {
+        return `smart-bim.html?m=${source.value.num}`
+    }
+
+    if (source.value.type < 2) {
+        // 看看、看见场景
+        return `smart-kankan.html?m=${target.value.num}&dev`
+    } else {
+        // 深时场景
+        return `smart-laser.html?m=${target.value.num}&dev`
+    }
+})
+const sourceDate = computed(() => {
+    if (source.value) {
+        return source.value.createTime.toDate()
+    }
+})
+
+const targetDate = computed(() => {
+    if (target.value) {
+        return target.value.createTime.toDate()
+    }
+})
 const highlighted = computed(() => {
     let dates = []
     if (datepickName.value) {
-        dates = scenes.map(item => item.date.toDate())
+        dates = scenes.map(item => item.createTime.toDate())
     }
     return {
         dates: dates,
     }
 })
 
-const getView = ()=>{
+const getView = () => {
     let camera = sourceApp.viewer.mainViewport.camera
     return {
-        position : camera.position,
-        quaternion : camera.quaternion, 
-        fov : camera.fov
+        position: camera.position,
+        quaternion: camera.quaternion,
+        fov: camera.fov,
     }
-} 
+}
 
-const initConvertView = (noNeedBindEvent) => {
-    if(sourceApp && targetApp ){
-        views.init(sourceApp, targetApp , sourceApp.viewer.inputHandler.domElement, 'laser', getView(), 
-                    sourceApp.viewer.images360.panos.slice(0,2).map(e=>e.position), )
+const initConvertView = noNeedBindEvent => {
+    if (sourceApp && targetApp) {
+        views.init(
+            sourceApp,
+            targetApp,
+            sourceApp.viewer.inputHandler.domElement,
+            'laser',
+            getView(),
+            sourceApp.viewer.images360.panos.slice(0, 2).map(e => e.position)
+        )
     }
 }
 
 const onLoadSource = () => {
-    
-    if(views.loaded){
-        views.clear({dontClearTarget:true})   
+    if (bimChecked.value) {
+        // BIM单屏模式
+        return
     }
 
+    if (views.loaded) {
+        views.clear({ dontClearTarget: true })
+    }
 
     sourceApp = sourceFrame.value.contentWindow
     sourceApp.loaded.then(sdk => {
         if (mode.value != 0) {
             sdk.scene.changeMode(mode.value)
         }
-        
-        window.viewer1 = sourceApp.viewer 
-        
-        viewer1.mainViewport.view.minPitch+=0.01//防止bim垂直视角上的闪烁(似乎是因 up 要乘以某矩阵导致微小偏差所致)
-        viewer1.mainViewport.view.minPitch-=0.01
-        
-        
+
+        window.viewer1 = sourceApp.viewer
+
+        viewer1.mainViewport.view.minPitch += 0.01 //防止bim垂直视角上的闪烁(似乎是因 up 要乘以某矩阵导致微小偏差所致)
+        viewer1.mainViewport.view.minPitch -= 0.01
+
         initConvertView()
-        sourceSceneName = sourceApp.Potree.settings.number
-        sourceApp.viewer.addEventListener('camera_changed', e => { 
-            targetApp && views.receive( getView() )
-        })  
-        
-    }) 
-    
-    sourceApp.canChangePos = ()=>{
+
+        sourceApp.viewer.addEventListener('camera_changed', e => {
+            targetApp && views.receive(getView())
+        })
+    })
+
+    sourceApp.canChangePos = () => {
         return sourceApp.Potree.settings.displayMode != 'showPanos'
     }
-    
 }
-const onLoadTarget = () => { 
- 
+const onLoadTarget = () => {
     targetApp = targetFrame.value.contentWindow
- 
-    targetApp.loaded.then(viewer=>{
+
+    targetApp.loaded.then(viewer => {
         window.viewer2 = targetApp.viewer
-        initConvertView() 
+        initConvertView()
     })
-
 }
 
-
-views.addEventListener('sendCameraData',(e)=>{ //同步右侧数据
+views.addEventListener('sendCameraData', e => {
+    //同步右侧数据
     sourceApp.viewer.mainViewport.view.position.copy(e.data.position)
     sourceApp.viewer.mainViewport.view.lookAt(e.data.target)
-    
 })
 
-
-
-
 const onModeChange = targetMode => {
     if (sourceApp) {
         sourceApp.loaded.then(sdk => sdk.scene.changeMode(targetMode))
@@ -163,6 +217,7 @@ const onPickClose = () => {
     datepickValue.value = null
 }
 const onSelected = payload => {
+    alert(payload)
     if (!payload) {
         return
     }
@@ -193,7 +248,7 @@ const onSelected = payload => {
 
     datepickName.value = null
 }
-const onCompare = () => {
+const onSplit = () => {
     if (target.value) {
         target.value = null
         targetApp = null
@@ -212,55 +267,128 @@ const onCompare = () => {
 
 const onPrevDate = name => {
     let scene = null
-    if (!name || name == 'source') {
+    if (name == 'source') {
         scene = source
     } else {
         scene = target
     }
-    let index = scenes.findIndex(item => item.num == scene.value.num)
+    let index = scenes.value.findIndex(item => item.num == scene.value.num)
     if (index == -1) {
         return
     }
     if (--index == -1) {
-        index = scenes.length - 1
+        index = scenes.value.length - 1
     }
 
-    scene.value = scenes[index]
+    scene.value = scenes.value[index]
 }
 const onNextDate = name => {
     let scene = null
-    if (!name || name == 'source') {
+    if (name == 'source') {
         scene = source
     } else {
         scene = target
     }
-    let index = scenes.findIndex(item => item.num == scene.value.num)
+    let index = scenes.value.findIndex(item => item.num == scene.value.num)
     if (index == -1) {
         return
     }
-    if (++index > scenes.length - 1) {
+    if (++index > scenes.value.length - 1) {
         index = 0
     }
-   
-    scene.value = scenes[index]
+
+    scene.value = scenes.value[index]
 }
 
-onMounted(() => {
-    project.value = projects[0]
-    if (project.value) {
-        source.value = scenes[0] //scenes.find(item => item.num == project.value.num)
+// bim点击
+const onBimChecked = () => {
+    if (bimChecked.value) {
+        bimChecked.value = false
+    } else {
+        bimChecked.value = true
+    }
+}
+
+// 分屏点击
+const onDbsChecked = () => {
+    dbsChecked.value = !dbsChecked.value
+    if (dbsChecked.value) {
+        if (bimChecked.value) {
+            // BIM分屏
+        } else {
+            // 四维看看、激光场景分屏
+            let index = scenes.value.findIndex(item => item.num == source.value.num)
+            if (index == -1) {
+                return
+            }
+            if (++index > scenes.value.length - 1) {
+                index = 0
+            }
+            target.value = scenes.value[index]
+        }
+    } else {
+        target.value = null
+        targetApp = null
+        views.clear()
+    }
+}
+
+// 全屏点击
+const onFscChecked = () => {
+    let element = document.documentElement
+
+    fscChecked.value = !fscChecked.value
+
+    if (fscChecked.value) {
+        if (element.requestFullscreen) {
+            element.requestFullscreen()
+        } else if (element.webkitRequestFullScreen) {
+            element.webkitRequestFullScreen()
+        } else if (element.mozRequestFullScreen) {
+            element.mozRequestFullScreen()
+        } else if (element.msRequestFullscreen) {
+            element.msRequestFullscreen()
+        }
+    } else {
+        if (document.exitFullscreen) {
+            document.exitFullscreen()
+        } else if (document.webkitCancelFullScreen) {
+            document.webkitCancelFullScreen()
+        } else if (document.mozCancelFullScreen) {
+            document.mozCancelFullScreen()
+        } else if (document.msExitFullscreen) {
+            document.msExitFullscreen()
+        }
     }
+}
+
+onMounted(() => {
+    const projectId = browser.valueFromUrl('projectId') || 1
+    http.get(`smart-site/project/info?projectId=${projectId}`)
+        .then(response => {
+            if (response.success) {
+                project.value = response.data
+                if (project.value.sceneList.length) {
+                    source.value = project.value.sceneList[project.value.sceneList.length - 1]
+                }
+            } else {
+                alert('获取数据失败')
+            }
+        })
+        .catch(() => {
+            alert('服务器连接失败')
+        })
 })
 </script>
 
 <style lang="scss" scoped>
 header {
     height: 60px;
-    background-color: rgba(0, 0, 0, 0.8);
+    background-color: #1a1b1d;
     display: flex;
     justify-content: center;
     align-items: center;
-    font-size: 18px;
+    font-size: 16px;
 }
 article {
     display: flex;
@@ -315,117 +443,90 @@ main {
         height: 100%;
         overflow: hidden;
         position: relative;
-        .tools {
-            width: 100%;
-            padding-left: 20px;
-            padding-right: 20px;
-            justify-content: space-between;
-            left: 0;
-            transform: none;
-            .item-mode {
-                display: flex;
-                align-items: center;
-                justify-content: space-around;
-                height: 34px;
-                border-radius: 17px;
-                background-color: rgba(0, 0, 0, 0.3);
-                > div {
-                    position: relative;
-                    margin-left: 20px;
-                    margin-right: 20px;
-                    cursor: pointer;
-                    &.active {
-                        color: #00c8af;
-                    }
-                }
-                i {
-                    font-size: 18px;
-                }
-            }
-        }
+        // .tools {
+        //     width: 100%;
+        //     padding-left: 20px;
+        //     padding-right: 20px;
+        //     justify-content: space-between;
+        //     left: 0;
+        //     transform: none;
+        //     .item-mode {
+        //         display: flex;
+        //         align-items: center;
+        //         justify-content: space-around;
+        //         height: 34px;
+        //         border-radius: 17px;
+        //         background-color: rgba(0, 0, 0, 0.3);
+        //         > div {
+        //             position: relative;
+        //             margin-left: 20px;
+        //             margin-right: 20px;
+        //             cursor: pointer;
+        //             &.active {
+        //                 color: #00c8af;
+        //             }
+        //         }
+        //         i {
+        //             font-size: 18px;
+        //         }
+        //     }
+        // }
     }
-    .tools {
+    .model {
         position: absolute;
-        left: 50%;
-        transform: translateX(-50%);
-        bottom: 10px;
+        left: 50px;
+        bottom: 40px;
         z-index: 1000;
         display: flex;
-        justify-content: center;
-        align-items: center;
-        color: #fff;
-        i {
-            font-size: 18px;
-        }
-        .item-date {
-            position: relative;
-            height: 34px;
-            border-radius: 17px;
-            background-color: rgba(0, 0, 0, 0.3);
-            padding: 0 30px;
+        flex-direction: column;
+        > div {
+            cursor: pointer;
+            width: 50px;
+            height: 50px;
+            margin-top: 16px;
+            background: rgba(27, 27, 28, 0.8);
+            box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
+            border-radius: 47px 47px 47px 47px;
+            border: 1px solid #000000;
             display: flex;
-            justify-content: center;
+            flex-direction: column;
             align-items: center;
-            margin-left: 10px;
-            margin-right: 10px;
-            span {
-                cursor: pointer;
+            justify-content: center;
+
+            &.disable {
+                opacity: 0.3;
+                cursor: default;
+                > div {
+                    pointer-events: none;
+                }
             }
-            .prev,
-            .next {
-                position: absolute;
-                left: 5px;
+            > div {
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
             }
-            .next {
-                left: auto;
-                right: 5px;
-                transform: rotate(180deg);
+            &.active {
+                color: #0076f6;
+            }
+            span {
+                font-size: 12px;
+                padding-top: 1px;
+                transform: scale(0.8);
             }
-        }
-
-        .item-split {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            width: 34px;
-            height: 34px;
-            border-radius: 50%;
-            background-color: rgba(0, 0, 0, 0.3);
-            cursor: pointer;
         }
     }
-    .popup {
+    .tools {
         position: absolute;
-        left: 0;
-        top: 0;
         width: 100%;
-        height: 100%;
+        bottom: 40px;
         z-index: 1000;
         display: flex;
-        align-items: center;
         justify-content: center;
-        > div {
-            position: relative;
-            color: #444;
-            span {
-                position: absolute;
-                right: -20px;
-                top: -20px;
-                cursor: pointer;
-                background-color: rgba(0, 0, 0, 0.3);
-                width: 24px;
-                height: 24px;
-                border-radius: 50%;
-                z-index: 1000;
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                color: #fff;
-            }
-            i {
-                font-size: 12px;
-            }
-        }
+        align-items: center;
+        color: #fff;
     }
 }
 </style>
@@ -435,13 +536,42 @@ main {
     display: flex;
     flex-direction: column;
 }
+.vuejs3-datepicker__calendar {
+    background: rgba(27, 27, 28, 0.8);
+    box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
+    border-radius: 4px 4px 4px 4px;
+    opacity: 1;
+    border: 1px solid #000000;
+    filter: blur(undefinedpx);
+    color: #fff;
+}
 .vuejs3-datepicker__calendar-topbar {
     display: none !important;
 }
+.vuejs3-datepicker__calendar header .up:not(.disabled):hover {
+    background: rgba(0, 0, 0, 0.3);
+    color: #fff;
+}
+
+.vuejs3-datepicker__calendar header .prev:after {
+    border-left: 1px solid #fff;
+    border-bottom: 1px solid #fff;
+}
+.vuejs3-datepicker__calendar header .prev:not(.disabled):hover {
+    background: rgba(0, 0, 0, 0.3);
+}
+
+.vuejs3-datepicker__calendar header .next:after {
+    border-top: 1px solid #fff;
+    border-right: 1px solid #fff;
+}
+.vuejs3-datepicker__calendar header .next:not(.disabled):hover {
+    background: rgba(0, 0, 0, 0.3);
+}
 .highlighted {
     background: #4ebde1 !important;
 }
 .selected {
-    background: #00c8af !important;
+    background: #0076f6 !important;
 }
 </style>

+ 3 - 2
src/pages/laser-bim.js

@@ -1,5 +1,6 @@
 import '../assets/index.scss'
 import { createApp } from 'vue'
+import { setup } from '../utils/request'
 import App from './LaserBim'
 
 Date.prototype.format = function(fmt = 'YYYY-mm-dd HH:MM:SS') {
@@ -34,5 +35,5 @@ String.prototype.toDate = function() {
     return new Date(this.replace(/\//g, '-').replace(' ', 'T'))
 }
 
-const app = (window.__app = createApp(App))
-app.mount('#app')
+setup()
+createApp(App).mount('#app')

+ 54 - 226
src/utils/browser.js

@@ -1,27 +1,26 @@
-function mobileVersion(e, t) {
-    //ios的版本
-    var i = window.navigator.userAgent,
-        n = i.match(e)
-    return (
-        (n = n ? n[1].split(t) : []),
-        {
-            major: parseInt(n[0]) || 0,
-            minor: parseInt(n[1]) || 0,
-            patch: parseInt(n[2]) || 0
-        }
-    )
-}
 var browser = {
-    isFullscreen: function() {
+    mobileVersion(e, t) {
+        var i = window.navigator.userAgent,
+            n = i.match(e)
+        return (
+            (n = n ? n[1].split(t) : []),
+            {
+                major: parseInt(n[0]) || 0,
+                minor: parseInt(n[1]) || 0,
+                patch: parseInt(n[2]) || 0,
+            }
+        )
+    },
+    isFullscreen() {
         return document.fullscreenElement || document.mozFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement
     },
-    supportsFullscreen: function() {
+    supportsFullscreen() {
         return document.fullscreenEnabled || document.mozFullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled
     },
-    isPointerLocked: function() {
+    isPointerLocked() {
         return document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement
     },
-    requestFullscreen: function(dom, t) {
+    requestFullscreen(dom, t) {
         dom.requestFullscreen
             ? dom.requestFullscreen()
             : dom.mozRequestFullScreen
@@ -31,7 +30,7 @@ var browser = {
             : dom.msRequestFullscreen && dom.msRequestFullscreen(),
             t && $(document).on('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', browser.requestPointerLock)
     },
-    requestPointerLock: function() {
+    requestPointerLock() {
         var e
         if (document.fullscreenElement) e = document.fullscreenElement()
         else if (document.mozFullscreenElement) e = document.mozFullscreenElement()
@@ -44,10 +43,10 @@ var browser = {
             e.requestPointerLock(),
             $(document).off('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', this)
     },
-    exitPointerLock: function() {
+    exitPointerLock() {
         ;(document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock), document.exitPointerLock()
     },
-    exitFullscreen: function() {
+    exitFullscreen() {
         document.exitFullscreen
             ? document.exitFullscreen()
             : document.msExitFullscreen
@@ -56,31 +55,20 @@ var browser = {
             ? document.mozCancelFullScreen()
             : document.webkitExitFullscreen && document.webkitExitFullscreen()
     },
-    details: function() {
-        var e = navigator.userAgent.match('(Firefox|Chrome|Safari)/([\\d]+)')
-        return e
-            ? {
-                  name: e[1],
-                  version: parseInt(e[2]),
-                  platform: navigator.platform
-              }
-            : {}
-    },
-    is: function(e) {
+    is(e) {
         return this.details() && this.details().name === e
     },
-    inIframe: function() {
+    inIframe() {
         return window.parent !== window
     },
-    aspectRatio: function($elem) {
-        $elem = $elem || $('#player')
-        var e = $elem.width() / $elem.height()
+    aspectRatio() {
+        var e = window.innerWidth / window.innerHeight
         return isFinite(e) ? e : 0
     },
-    userAgent: function() {
+    userAgent() {
         return window.navigator.userAgent
     },
-    isMobile: function() {
+    isMobile() {
         var e = navigator.userAgent || navigator.vendor || window.opera
         return (
             /(android|bb\d+|meego).+mobile|android|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
@@ -91,149 +79,91 @@ var browser = {
             )
         )
     },
-    isLandscape: function() {
+    isLandscape() {
         return this.isMobile && this.aspectRatio() > 1
     },
-    isSmallScreen: function() {
+    isSmallScreen() {
         var e = screen.width / window.devicePixelRatio
         return e < 240
     },
-    detectIE: function() {
+    detectWeixin: function () {
+        //微信 包括PC的微信
+        return window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'
+    },
+    detectWeixinMiniProgram: function () {
+        return window.navigator.userAgent.match('miniProgram')
+    },
+    detectIE() {
         var e = window.navigator.userAgent,
             t = e.indexOf('MSIE ')
         return t !== -1 || !!navigator.userAgent.match(/Trident.*rv\:11\./)
     },
-    detectSafari: function() {
+    detectSafari() {
         var e = window.navigator.userAgent,
             t = e.indexOf('Safari')
-        return t !== -1 && !this.detectOpera() && !this.detectChrome() //xzw add detectOpera
+        return t !== -1 && !this.detectChrome()
     },
-    detectFirefox: function() {
+    detectFirefox() {
         var e = window.navigator.userAgent
         return e.indexOf('Firefox') !== -1
     },
-    detectChrome: function() {
+    detectChrome() {
         var e = window.navigator.userAgent
         return e.indexOf('Chrome') !== -1 && !this.detectOpera()
     },
-    detectOpera: function() {
+    detectOpera() {
         var e = window.navigator.userAgent
         return e.indexOf('OPR') !== -1
     },
-    detectIOS: function() {
+    detectIOS() {
         return this.detectIPhone() || this.detectIPad() || this.detectIPod()
     },
-    detectIPad: function() {
+    detectIPad() {
         var e = window.navigator.userAgent,
             t = /iPad/
         return t.test(e)
     },
-    detectIPod: function() {
+    detectIPod() {
         var e = window.navigator.userAgent,
             t = /iPod/
         return t.test(e)
     },
-    detectIPhone: function() {
+    detectIPhone() {
         var e = window.navigator.userAgent,
             t = /iPhone/
         return t.test(e)
     },
-    detectAndroid: function() {
+    detectAndroid() {
         var e = window.navigator.userAgent
         return e.indexOf('Android') !== -1
     },
-    detectAndroidMobile: function() {
+    detectAndroidMobile() {
         var e = window.navigator.userAgent
         return this.detectAndroid() && e.indexOf('Mobile') !== -1
     },
-    detectSamsungNative: function() {
-        var e = window.navigator.userAgent
-        return e.indexOf('SM-G900H') !== -1 || e.indexOf('GT-I9500') !== -1 || e.indexOf('SM-N900') !== -1
-    },
-    detectSamsungS6: function() {
-        var e = window.navigator.userAgent
-        return e.indexOf('SM-G92') !== -1
-    },
-    /************************************************************徐世廷*************************************************************/
-    detectHUAWEI5X: function() {
-        return -1 !== window.navigator.userAgent.indexOf('KIW-TL00H')
-    },
-    /*******************************************************************************************************************************/
-    detectWebVR: function() {
-        return !(!window.navigator.getVRDisplays || !window.VRDisplay)
-    },
-    getVRDisplay: function() {
-        var e = $.Deferred()
-        return this.detectWebVR()
-            ? (navigator.getVRDisplays().then(function(t) {
-                  t.length >= 1 && e.resolve(t[0]), e.reject(null)
-              }),
-              e)
-            : e.reject(null)
-    },
-    iosVersion: function() {
+    iosVersion() {
         if (!this.detectIOS()) throw new DeviceMismatchException('Did not detect an iDevice')
         var e = /((?:\d+\_?){1,3}) like Mac OS/,
             t = '_'
-        return mobileVersion(e, t)
+        return this.mobileVersion(e, t)
     },
-    androidVersion: function() {
+    androidVersion() {
         if (!this.detectAndroid()) throw new DeviceMismatchException('Did not detect an Android based device')
         var e = /Android ((?:\d+\.?){1,3})/,
             t = '.'
-        return mobileVersion(e, t)
-    },
-    valueFromCookie: function(e, t) {
-        var i = new RegExp(e + '=([0-9a-f]+)(; ?|$)').exec(document.cookie)
-        if (!i) return t
-        var n = i[1]
-        return 'boolean' == typeof t ? 'true' === n || '1' === n : 'number' == typeof t ? parseFloat(n) : n
+        return this.mobileVersion(e, t)
     },
-    valueFromHash: function(e, t) {
+    valueFromHash(e, t) {
         var i = new RegExp('[#&?]' + e + '=([^#&?]*)'),
             n = i.exec(window.location.href)
         if (!n) return t
         var r = n[1]
         return 'boolean' == typeof t ? 'true' === r || '1' === r : 'number' == typeof t ? parseFloat(r) : window.decodeURIComponent(r)
     },
-    //-------许钟文:-------------------------------------------------
-
-    getProjectNum: function() {
-        //获取场景projectNum
-
-        if (window.__ProjectNum && window.__ProjectNum != '__ProjectNum__') {
-            return window.__ProjectNum
-        }
-
-        var number = window.location.href.substring(window.location.href.indexOf('=') + 1)
-        if (number.indexOf('&') != -1) {
-            number = number.substring(0, number.indexOf('&'))
-        }
-        if (number.indexOf('#') != -1) {
-            number = number.substring(0, number.indexOf('#'))
-        }
-        return number
+    valueFromUrl(key) {
+        return this.urlHasValue(key, true)
     },
-
-    urlHasValue: function(key, isGetValue) {
-        // debugger
-        // if (getValue) { //得到类似n=1 的 1
-        //     var b = window.location.href.substring(window.location.href.indexOf("?") + 1);
-        //     var a = b.indexOf('&' + t + "=");
-        //     if (a > -1) {
-        //         var s = b.substring(a + ('&' + t + "=").length);
-        //         s.indexOf("&") > -1 && (s = s.substring(0, s.indexOf("&")));
-        //         s.indexOf("#") > -1 && (s = s.substring(0, s.indexOf("#")));
-        //         return s;
-        //     } else return false;
-        // } else return window.location.search.match("&" + t + "|\\?" + t) != null; //window.location.href.substring(window.location.href.indexOf("?") + 1).indexOf('&' + t) > -1;
-
-        //const value = window.location.search.match("&" + t + "|\\?" + t)
-
-        if (key === 'm' && window.__ProjectNum && window.__ProjectNum != '__ProjectNum__') {
-            return window.__ProjectNum
-        }
-
+    urlHasValue: function (key, isGetValue) {
         let querys = window.location.search.substr(1).split('&')
         if (isGetValue) {
             for (let i = 0; i < querys.length; i++) {
@@ -244,7 +174,6 @@ var browser = {
             }
             return ''
         } else {
-            //return window.location.search.match("&" + key + "|\\?" + key) != null  有bug
             for (let i = 0; i < querys.length; i++) {
                 let keypair = querys[i].split('=')
                 if (keypair[0] == key) {
@@ -254,107 +183,6 @@ var browser = {
             return false
         }
     },
-    /**
-     * 获取查询参数的值
-     * @param {String} key
-     * @returns String
-     */
-    urlQueryValue(key) {
-        return this.urlHasValue(key, true) || ''
-    },
-    /**
-     * 获取hash参数的值
-     * @param {String} key
-     * @returns String
-     */
-    urlHashValue(key) {
-        let querys = window.location.hash
-            .substr(1)
-            .replace('/?', '')
-            .split('&')
-        for (let i = 0; i < querys.length; i++) {
-            let keypair = querys[i].split('=')
-
-            if (keypair.length === 2 && keypair[0] === key) {
-                return keypair[1]
-            }
-        }
-        return ''
-    },
-    /**
-     * 判断是否存在hash
-     * @param {String} key
-     * @returns Boolean
-     */
-    urlIsHasHash(key) {
-        let querys = window.location.hash
-            .substr(1)
-            .replace('/?', '')
-            .split('&')
-        return querys.includes(key)
-    },
-    islongPhone: function() {
-        //是否是刘海全面屏幕 仅仅根据比例判断 - -
-        //screen.height == 812 && screen.width == 375)
-        var r = screen.height / screen.width //可能横屏
-        return this.isMobile() && (r > 1.99 || r < 0.502512) //18/9=2.165   //???
-    },
-    detectWeixin: function() {
-        //微信 包括PC的微信
-        return window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'
-    },
-    detectWeixinMiniProgram: function() {
-        return window.navigator.userAgent.match('miniProgram')
-    },
-    detectAlipay: function() {
-        //微信 包括PC的微信
-        return window.navigator.userAgent.toLowerCase().match(/Alipay/i) == 'alipay'
-    },
-    detectEdge: function() {
-        return window.navigator.userAgent.indexOf('Edge') > -1
-    },
-    detectApp: function() {
-        return this.urlHasValue('app')
-    },
-
-    /**
-     * 判断标签页是否切换状态
-     */
-    isTabHidden: function() {
-        var prefixes = ['webkit', 'moz', 'ms', 'o']
-
-        if ('hidden' in document) return document.hidden
-
-        for (var i = 0; i < prefixes.length; i++) {
-            if (prefixes[i] + 'Hidden' in document) return document[prefixes[i] + 'Hidden']
-        }
-
-        return false
-    },
-    hasURLParam: function(key) {
-        let querys = window.location.search.substring(1).split('&')
-        for (let i = 0; i < querys.length; i++) {
-            let keypair = querys[i].split('=')
-            if (keypair[0] == key) {
-                return true
-            }
-        }
-        return false
-    },
-    getURLParam: function(key) {
-        let querys = window.location.search.substring(1).split('&')
-        for (let i = 0; i < querys.length; i++) {
-            let keypair = querys[i].split('=')
-            if (keypair.length === 2 && keypair[0] === key) {
-                try {
-                    return decodeURIComponent(keypair[1])
-                } catch (error) {
-                    return keypair[1]
-                }
-            }
-        }
-        return ''
-    }
 }
 
 export default browser

+ 120 - 51
src/utils/request.js

@@ -5,8 +5,45 @@
  * @LastEditTime: 2021-05-08 15:49:54
  * @Description: 注释
  */
+/*
+code	message
+-5	    系统繁忙,请稍后再试!
+-4	    跨服务请求失败
+-3	    缺少必要参数
+5005	场景为空
+5012	数据不正常
+5018	zip文件只能有一层目录或无目录
+5019	必须有且仅有一个obj和mtl文件
+5020	贴图需控制在1.5M以内,obj文件需要控制在20M以内。
+5021	场景密钥不正确
+5025	数据包不存在,请先打成zip包
+5039	画墙重建模型失败
+5043	打包zip失败
+5059	该压缩包无可用obj或者mtl文件
+5060	obj文件名应为mesh.obj
+5062	无可用jpg文件
+7004	热点数据不能为空!
+7005	查询不到热点数据!
+7006	目录不存在!
+7007	文件格式不正确
+7009	球幕视频文件不存在
+7010	相机或者相机详情不存在
+7012	上传的文件名错误
+7013	上传失败
+7015	仅支持.zip文件上传
+7016	仅支持.mp4格式文件
+7018	压缩包内请勿包含文件夹
+4001    无token,请重新登录
+4002    token不合法
+4003    用户未登录
+*/
 
 import axios from 'axios'
+import browser from './browser'
+
+let fetch = null
+Promise.prototype.done = Promise.prototype.then
+Promise.prototype.fail = Promise.prototype.catch
 
 // TextDecoder polyfills for lower browser
 if (undefined === window.TextEncoder) {
@@ -14,7 +51,7 @@ if (undefined === window.TextEncoder) {
         encode(s) {
             return unescape(encodeURIComponent(s))
                 .split('')
-                .map(function(val) {
+                .map(function (val) {
                     return val.charCodeAt()
                 })
         }
@@ -26,39 +63,6 @@ if (undefined === window.TextEncoder) {
     }
 }
 
-const fetch = axios.create()
-
-fetch.interceptors.request.use(
-    config => {
-        config.headers['token'] = 123
-
-        return config
-    },
-    error => {
-        return Promise.reject(error)
-    }
-)
-fetch.interceptors.response.use(
-    response => {
-        // 正常的文件流
-        if (!/json/gi.test(response.headers['content-type'])) {
-            return response.data
-        }
-
-        // 以文件流方式请求但是返回json,需要解析为JSON对象
-        if (response.request.responseType === 'arraybuffer') {
-            let enc = new TextDecoder('utf-8')
-            let res = JSON.parse(enc.decode(new Uint8Array(response.data)))
-            return res
-        }
-
-        return response.data
-    },
-    error => {
-        console.error(error)
-    }
-)
-
 const http = {
     retry(func, retries = 0, delay = 1000) {
         return new Promise((resolve, reject) => {
@@ -85,26 +89,31 @@ const http = {
             () =>
                 new Promise((resolve, reject) => {
                     let img = new Image()
-                    img.src = url
                     img.crossOrigin = 'anonymous'
-                    img.onload = function() {
+                    img.src = url
+                    img.onload = function () {
                         resolve(img)
                     }
-                    img.onerror = function() {
+                    img.onerror = function () {
                         reject(`[${url}] load fail`)
                     }
                 }),
             retries
         )
     },
+    getText(url) {
+        return fetch.get(url, {
+            responseType: 'text',
+        })
+    },
     getBueffer(url) {
         return fetch.get(url, {
-            responseType: 'arraybuffer'
+            responseType: 'arraybuffer',
         })
     },
     getBlob(url) {
         return fetch.get(url, {
-            responseType: 'blob'
+            responseType: 'blob',
         })
     },
     post(url, data) {
@@ -118,23 +127,83 @@ const http = {
             delete data.onUploadProgress
         }
         for (let key in data) {
-            // if (key === 'files' && data[key].length > 0) {
-            //     for (let i = 0; i < data[key].length; i++) {
-            //         form.append(key, data[key][i])
-            //     }
-            // } else {
-            //     form.append(key, data[key])
-            // }
-            form.append(key, data[key])
+            if (key === 'files' && data[key].length > 0) {
+                for (let i = 0; i < data[key].length; i++) {
+                    let file = data[key][i]
+                    if (file instanceof File) {
+                        form.append(key, file)
+                    } else if (file.file) {
+                        if (file.filename) {
+                            form.append(key, file.file, file.filename)
+                        } else {
+                            form.append(key, file.file)
+                        }
+                    } else {
+                        console.warn('file is wong !', data)
+                    }
+                }
+            } else if (key == 'file' || key === 'filename') {
+                if (key == 'file') {
+                    if (data.filename) {
+                        form.append('file', data[key], data.filename)
+                    } else {
+                        form.append('file', data[key])
+                    }
+                }
+            } else {
+                form.append(key, data[key])
+            }
         }
 
         return fetch.post(url, form, {
             headers: {
-                'Content-Type': 'multipart/form-data'
+                'Content-Type': 'multipart/form-data',
             },
-            onUploadProgress: cb
+            onUploadProgress: cb,
         })
-    }
+    },
+}
+
+export function setup(options = {}) {
+    fetch = axios.create({
+        baseURL: options.server || '',
+    })
+
+    fetch.interceptors.request.use(
+        config => {
+            let token = browser.valueFromUrl('token') || localStorage.getItem('token') || ''
+            if (token) {
+                config.headers['token'] = token
+            }
+            return config
+        },
+        error => {
+            return Promise.reject(error)
+        }
+    )
+    fetch.interceptors.response.use(
+        response => {
+            // 正常的文件流
+            if (!/json/gi.test(response.headers['content-type'])) {
+                return response.data
+            }
+
+            // 以文件流方式请求但是返回json,需要解析为JSON对象
+            if (response.request.responseType === 'arraybuffer') {
+                let enc = new TextDecoder('utf-8')
+                let res = JSON.parse(enc.decode(new Uint8Array(response.data)))
+                return res
+            }
+
+            return response.data
+        },
+        error => {
+            if (error.response && error.response.data) {
+                return Promise.reject(error.response)
+            }
+            return Promise.reject(error)
+        }
+    )
 }
 
-export { http, fetch }
+export { http }

+ 2 - 6
vue.config.js

@@ -15,12 +15,8 @@ const devServer = {
         'Cache-Control': 'no-store'
     },
     proxy: {
-        '/service': {
-            target: 'https://v4-test.4dkankan.com/',
-            changeOrigin: true
-        },
-        '/sdk': {
-            target: `http://${localIP}:3099/dist/`,
+        '/smart-site': {
+            target: 'http://192.168.0.38:8011/',
             changeOrigin: true
         }
     }