bill 2 rokov pred
rodič
commit
d883faeaeb

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "@kankan/smart-bim",
   "private": true,
-  "version": "4.9.1",
+  "version": "4.9.1-apl",
   "scripts": {
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",

+ 118 - 20
public/static/lib/potree/potree.js

@@ -71013,7 +71013,8 @@ void main()
 	        
 	        
 	        let update = (e)=>{
-	            this.update(e); 
+	            //this.update(e)
+	            this.needsUpdate = true;
 	        };
 	        viewer.mapViewer && viewer.mapViewer.addEventListener("camera_changed",  update); 
 	        viewer.addEventListener("camera_changed",  update); 
@@ -71023,7 +71024,8 @@ void main()
 	         
 	        
 	        let applyMatrix = (e)=>{
-	            this.applyMatrix(e);
+	            if(this.needsUpdate) this.update(e);
+	            else this.applyMatrix(e);
 	        };
 	        viewer.addEventListener("raycaster", applyMatrix);        //before render
 	        viewer.addEventListener("render.begin", applyMatrix); //before render  //magnifier时要禁止吗
@@ -71066,15 +71068,15 @@ void main()
 	            v = false;
 	        }            
 	         
-	        if(!this.latestRealVisi && v){//变为可见后先update 
-	            this.latestRealVisi = true;
+	        /* if(!this.latestRealVisi && v){//变为可见后先update 
+	            this.latestRealVisi = true
 	            setTimeout(()=>{
-	                this.update();
-	            },1);//延迟 防止无限调用
+	                this.update()
+	            },1)//延迟 防止无限调用
 	            return false
 	        }
 	        
-	        this.latestRealVisi = v;
+	        this.latestRealVisi = v */
 	        return v;
 	    }
 	    
@@ -71146,6 +71148,7 @@ void main()
 	        if(!matrix){
 	            this.update(e);
 	            matrix = this.matrixMap.get(e.viewport);
+	            if(!matrix)return                
 	        }
 	        
 	        if(e.viewport == this.useViewport){
@@ -71168,6 +71171,82 @@ void main()
 	    }
 	}
 
+
+
+	/* 
+
+	    let orient2d
+	        
+	    if(this.lineDir){
+	        this.root.updateMatrix();//先更新,getWorldPosition才能得到正确的
+	        this.root.updateMatrixWorld(true)
+	        let center = this.root.getWorldPosition(new THREE.Vector3())
+	        //由于两个端点容易在屏幕外,所以使用center和center加dir
+	        let lineDir = this.lineDir.clone();
+	        
+	        
+	        let r1 = Potree.Utils.getPos2d(center, camera, viewer.renderArea, e.viewport); 
+	        if(!r1.trueSide)return  Potree.Utils.updateVisible(this, 'unableCompute', false);// 但这句会使realVisible为false从而无法更新//console.error('!r1.trueSide') //中心点如果在背面直接不渲染了
+	            
+	        let r2, point2
+	        
+	        let p2State = '', len=1,  p2StateHistory = []
+	        while(p2State != 'got' && p2StateHistory.length<10){ 
+	            point2 = center.clone().add(lineDir.multiplyScalar(len));
+	             
+	            r2 = Potree.Utils.getPos2d(point2, camera, viewer.renderArea, e.viewport);  
+	            if(!r2.trueSide){ //很少遇到点2在背面的
+	                if(!p2StateHistory.includes('tooLong-reverse')){
+	                    p2State = 'tooLong-reverse'  //先尝试反向
+	                    len = -len
+	                }else{
+	                    p2State = 'tooLong'
+	                    len = len / 2
+	                }
+	            }else{
+	                let dis = r2.pos.distanceTo(r1.pos)
+	                if(dis == 0){
+	                    //console.log('dis == 0') 
+	                    Potree.Utils.updateVisible(this, 'unableCompute', false)
+	                    return
+	                    break
+	                } 
+	                if(dis<10 && !p2StateHistory.includes('tooLong')){//和r1的屏幕距离太近,要加长,否则精度过低
+	                    p2State = 'tooShort'
+	                    len = 100/dis * len  
+	                }else{ 
+	                    p2State = 'got'; break;
+	                }
+	            } 
+	            p2StateHistory.push(p2State) 
+	        }
+	        //console.log(p2StateHistory,len)
+	        
+	        if(!r2.trueSide){
+	            return  Potree.Utils.updateVisible(this, 'unableCompute', false)//, console.log('  !r2.trueSide', )
+	        }
+	         
+	        Potree.Utils.updateVisible(this, 'unableCompute', true)
+	        let p1 = r1.pos,  p2 = r2.pos
+	        let vec = new THREE.Vector2().subVectors(p1,p2);
+	        let angle = -vec.angle() //根据测量线在屏幕上的角度在旋转label,使之和屏幕上的二维线平行。
+	        if(p1.x < p2.x) angle += Math.PI  //避免字是倒着的情况
+	         
+	        orient2d = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1),  angle)
+	        //console.log(this.parent.text, THREE.Math.radToDeg(angle), p1.x < p2.x   )
+	    }
+
+	    let parentQua = this.root.parent.getWorldQuaternion(new THREE.Quaternion)
+	    this.root.quaternion.multiplyQuaternions(parentQua.invert(),camera.quaternion)    //乘上parentQua.invert()是为了中和掉父结点的qua,使只剩下camera.quaternion
+
+	    if(this.lineDir){ 
+	        this.root.quaternion.multiply(orient2d) 
+	    }
+
+
+
+	 */
+
 	//可能还是要用html写,因为要加按钮和图片
 
 	class TextSprite$2 extends Object3D{ 
@@ -71198,6 +71277,7 @@ void main()
 			this.textColor = options.textColor || {r: 0, g: 0, b: 0, a: 1.0};
 	        this.borderColor = options.borderColor || { r: 0, g: 0, b: 0, a: 0.0 };
 			this.borderRadius = options.borderRadius || 6;
+	        this.margin = options.margin;
 	        if(options.text != void 0)this.setText(options.text);
 	        this.name = options.name; 
 	         
@@ -71257,7 +71337,7 @@ void main()
 	        
 			let metrics = context.measureText(this.text );
 			let textWidth = metrics.width;
-			let margin = new Vector2(this.fontsize, Math.max(  this.fontsize*0.4, 10)  );
+			let margin = this.margin || new Vector2(this.fontsize, Math.max(  this.fontsize*0.4, 10)  );
 			let spriteWidth = 2 * margin.x + textWidth + 2 * this.rectBorderThick;
 			let spriteHeight = 2 * margin.y + this.fontsize + 2 * this.rectBorderThick; 
 			context.canvas.width = spriteWidth;
@@ -71265,10 +71345,22 @@ void main()
 			context.font = this.fontWeight + ' ' + this.fontsize + 'px ' + this.fontface; 
 
 	         
-	        let diff = 2;//针对英文大部分在baseLine之上所以降低一点(metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent) / 2
+	        /* let diff = 2//针对英文大部分在baseLine之上所以降低一点(metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent) / 2
 
-	        context.textBaseline = "middle";
+	        context.textBaseline = "middle"
+	         */
+	        let expand = Math.max(1, Math.pow(this.fontsize / 16, 1.3)); // 针对英文大部分在baseLine之上所以降低一点,或者可以识别当不包含jgqp时才加这个值  
+	         
+	        //canvas原点在左上角
+	        context.textBaseline = 'alphabetic'; //  "middle"  //设置文字基线。当起点y设置为0时,只有该线以下的部分被绘制出来。middle时文字显示一半(但是对该字体所有字的一半,有的字是不一定显示一半的,尤其汉字),alphabetic时是英文字母的那条基线。
 	        
+	        let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; // 当前文本字符串在这个字体下用的实际高度
+	        
+	        //文字y向距离从textBaseline向上算
+	        let y = metrics.actualBoundingBoxAscent + margin.y + expand; 
+	        //console.log(this.text, 'y' , y, 'actualBoundingBoxAscent', metrics.actualBoundingBoxAscent,'expand',expand )
+	        
+	                                                  
 	        // border color
 	        context.strokeStyle = 'rgba(' + this.borderColor.r + ',' + this.borderColor.g + ',' +
 	            this.borderColor.b + ',' + this.borderColor.a + ')';
@@ -71285,12 +71377,12 @@ void main()
 	            context.strokeStyle = 'rgba(' + this.textBorderColor.r + ',' + this.textBorderColor.g + ',' +
 	                this.textBorderColor.b + ',' + this.textBorderColor.a + ')';
 	            context.lineWidth = this.textBorderThick;
-	            context.strokeText(this.text , this.rectBorderThick + margin.x,spriteHeight/2  + diff );
+	            context.strokeText(this.text , this.rectBorderThick + margin.x,  y /* spriteHeight/2  + diff */ );
 	        }
 	        
 			context.fillStyle = 'rgba(' + this.textColor.r + ',' + this.textColor.g + ',' +
 				this.textColor.b + ',' + this.textColor.a + ')';
-			context.fillText(this.text , this.rectBorderThick + margin.x, spriteHeight/2  + diff );//x,y
+			context.fillText(this.text , this.rectBorderThick + margin.x,  y/* spriteHeight/2  + diff */ );//x,y
 
 			let texture = new Texture(canvas);
 			texture.minFilter = LinearFilter;
@@ -88933,7 +89025,7 @@ void main()
 	        
 	        let target = params.target || null;
 	        
-			const resolution = rtEDL ? new Vector2(rtEDL.width,rtEDL.height) : params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new Vector2()); 
+	        const resolution = (rtEDL && Potree.settings.useRTPoint) ? new Vector2(rtEDL.width,rtEDL.height) : params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new Vector2());//突然发现mobile用resolution2点云会放大
 	        
 	        
 	        
@@ -98231,7 +98323,7 @@ void main()
 	            }else { 
 	            }
 	           
-	            this.dispatchEvent({type:'flyToPanoDone', makeIt});
+	            this.dispatchEvent({type:'flyToPanoDone', makeIt, disturb});
 	             
 	            toPano.deferred && toPano.deferred.resolve(makeIt);  //测量线截图时发现,resolve需要写在flying=false 后才行。
 	        };
@@ -151292,7 +151384,7 @@ ENDSEC
 	                        
 	                        this.setPointLevels();
 	                        
-	                        
+	                        this.dispatchEvent('pointDensityChanged');
 	                    }
 	                }
 	            });
@@ -151308,6 +151400,7 @@ ENDSEC
 	                            Potree.settings.pointDensity = density; 
 	                        }
 	                        UserPointDensity = density;
+	                        this.dispatchEvent('UserPointDensityChanged');
 	                    }
 	                }
 	            });
@@ -154313,11 +154406,16 @@ ENDSEC
 	            if(o.dontChangeCamDir){
 	                var inv = camera.matrixWorldInverse;
 	            }else {
-	                var cameraTemp = camera.clone();
-	                cameraTemp.position.copy(cameraPos);  
-	                cameraTemp.lookAt(target);
-	                cameraTemp.updateMatrix();
-	                cameraTemp.updateMatrixWorld();
+	                var cameraTemp = camera.clone(); 
+	                let view = viewer.mainViewport.view.clone();
+	                view.position.copy(cameraPos);
+	                view.lookAt(target);
+	                if(o.endPitch != void 0){
+	                    view.pitch = o.endPitch;
+	                    view.yaw = o.endYaw;
+	                }
+	                view.applyToCamera(cameraTemp);
+	                 
 	                //对镜头的bound
 	                var inv = cameraTemp.matrixWorldInverse;
 	            } 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 5 - 1
public/static/lib/potree/potree.js.map


+ 10 - 4
src/components/files/TagEditor.vue

@@ -36,7 +36,7 @@
     </div>
 </template>
 <script setup>
-import { ref, inject, onMounted, onBeforeUnmount, watch } from 'vue'
+import { ref, inject, onMounted, onBeforeUnmount, watch, defineEmits } from 'vue'
 import { convertBlob2File } from '@/utils/file'
 import { http } from '@/utils/request'
 import browser from '@/utils/browser'
@@ -48,6 +48,7 @@ import UiMedias from '../form/medias'
 import UiSelectList from '../form/SelectList.vue'
 import { from } from 'readable-stream'
 import i18n from '@/i18n'
+const emits = defineEmits(['action'])
 const { t } = i18n.global
 const showLoading = ref(false)
 const showTips = ref(null)
@@ -55,6 +56,8 @@ const projectId = browser.valueFromUrl('projectId') || 1
 const notify = inject('notify')
 const tags = inject('tags')
 const isEdit = inject('isEdit')
+const source = inject('source')
+
 const height = ref(0)
 const form = ref({
     title: '',
@@ -88,7 +91,7 @@ const onClose = () => {
 }
 let pushData = null
 const onSubmit = async () => {
-    if (!form.value.title) {
+    if (!form.value.title.trim()) {
         return (showTips.value = t('tag.inputTagName'))
     }
     if (!form.value.status) {
@@ -152,7 +155,7 @@ const handlerUpload = async data => {
         markingStatus: form.value.status,
         markingTitle: form.value.title,
         hotData: tag,
-        num: browser.getURLParam('m'),
+        num: source.value.num,
     }
     if (tag.id) {
         params.markingId = tag.id
@@ -160,6 +163,7 @@ const handlerUpload = async data => {
 
     http.post(`smart-site/marking/addOrUpdate`, params).then(response => {
         showLoading.value = false
+
         if (response.success) {
             notify.value.status = tag.status
             notify.value.members = tag.members
@@ -169,7 +173,8 @@ const handlerUpload = async data => {
                 pushData.content = tag.content
                 pushData.members = tag.members
                 pushData.createTime = response.data.createTime
-              
+                pushData.createBy = response.data.createBy
+
                 tags.value.push(pushData)
                 pushData = null
             }
@@ -177,6 +182,7 @@ const handlerUpload = async data => {
             delete notify.value.__temp
             notify.value.id = response.data.markingId
             notify.value = null
+            emits('action', '提交成功')
         } else if (response.code == 4008) {
             showTips.value = t('code.4008')
         } else {

+ 10 - 2
src/components/files/TagItem.vue

@@ -6,8 +6,8 @@
         :style="{ transform: `translate(${props.tag.x}px,${props.tag.y}px)`, display: props.tag.visible ? 'block' : 'none' }"
         class="tag-item"
     >
-        <div class="tag-icon">
-            <span>{{ props.index }}</span>
+        <div class="tag-icon" :class="{ active: editTagId == props.tag.sid }">
+            <span>{{ props.index }} </span>
         </div>
     </div>
 </template>
@@ -25,7 +25,12 @@ const props = defineProps({
 })
 
 const notify = inject('notify')
+const isEdit = inject('isEdit')
+const editTagId = inject('editTagId')
 const onClick = () => {
+    if (isEdit.value) {
+        return
+    }
     notify.value = props.tag //{ event: 'focus', sid: props.tag.sid, tag: props.tag }
 }
 </script>
@@ -69,6 +74,9 @@ const onClick = () => {
     border: 1px solid #fff;
     transform: rotate(-135deg);
     background-color: #0076f6;
+    &.active {
+        background-color: green;
+    }
     span {
         display: block;
         transform: rotate(135deg);

+ 19 - 12
src/components/files/TagManager.vue

@@ -4,13 +4,14 @@
         <TagEditor v-if="notify && isEdit" :notify="notify" @action="onAction" />
         <TagView v-if="notify && !isEdit" :notify="notify" />
     </div>
+    <Toast v-if="showTips" v-bind="toastOps" :content="showTips" :close="() => (showTips = null)" />
 </template>
 <script setup>
-import { inject, watch, computed } from 'vue'
+import { inject, watch, computed, ref } from 'vue'
 import TagItem from './TagItem.vue'
 import TagEditor from './TagEditor.vue'
 import TagView from './TagView.vue'
-
+import Toast from '@/components/dialog/Toast'
 let timer = setInterval(() => {
     if (window.kankan) {
         init(window.kankan)
@@ -20,24 +21,30 @@ let timer = setInterval(() => {
         clearInterval(timer)
     }
 }, 50)
-
+const showTips = ref(null)
 const notify = inject('notify')
 const tags = inject('tags')
 const isEdit = inject('isEdit')
 const init = sdk => {
     // sdk.TagManager.load(tags)
 }
-
+const toastOps = ref({
+    type: 'success',
+})
+const onAction = text => {
+    showTips.value = text
+}
 const initLaserTag = () => {
+    // consoler.error('******************initLaserTag')
     window.laser.then(sdk => {
-        sdk.scene.on('posChange', cameraPos => {
-            tags.value.forEach(tag => {
-                const info2d = sdk.scene.getScreenByPoint(tag.position)
-                tag.x = info2d.pos.x
-                tag.y = info2d.pos.y
-                tag.visible = info2d.trueSide
-            })
-        })
+        // sdk.scene.on('posChange', cameraPos => {
+        //     tags.value.forEach(tag => {
+        //         const info2d = sdk.scene.getScreenByPoint(tag.position)
+        //         tag.x = info2d.pos.x
+        //         tag.y = info2d.pos.y
+        //         tag.visible = info2d.trueSide
+        //     })
+        // })
     })
 }
 </script>

+ 22 - 5
src/components/files/TagView.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="tag-view" v-show="canShow">
         <!-- <div class="tag-view-content" :style="{ height: height + 'px' }"> -->
-        <div class="tag-view-content">
+        <div class="tag-view-content" :class="!isLogin || !isAuth ? 'no-comment' : ''">
             <header>
                 <span>{{ notify.title }}</span>
                 <!-- <i class="iconfont icon-close" @click="emits('action', null)"></i> -->
@@ -9,18 +9,20 @@
             </header>
             <article>
                 <TagMsg @setShow="setShow"></TagMsg>
-                <Comment :slideHeigt="slideHeigt"></Comment>
+                <Comment v-if="isLogin && isAuth" :slideHeigt="slideHeigt"></Comment>
             </article>
             <!-- <footer></footer> -->
         </div>
     </div>
 </template>
 <script setup>
-import { ref, onMounted, onBeforeUnmount, computed, inject } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue'
 
 import TagMsg from './content/TagMsg.vue'
 import Comment from './content/Comment.vue'
 import { nextTick } from 'process'
+const isAuth = inject('isAuth')
+const isLogin = inject('isLogin')
 const notify = inject('notify')
 const emits = defineEmits(['action'])
 const height = ref(0)
@@ -45,7 +47,16 @@ const setShow = data => {
         slideHeigt.value = document.querySelector('.left-item').offsetHeight
     })
 }
-onMounted(() => {
+
+watch(
+    () => notify.value,
+    (val, old) => {
+        if (val) {
+            goFocusTag()
+        }
+    }
+)
+const goFocusTag = () => {
     if (window.kankan) {
         window.kankan.TagManager.focusTag(notify.value.sid, {
             direction: 'left',
@@ -60,6 +71,9 @@ onMounted(() => {
             sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
         })
     }
+}
+onMounted(() => {
+  goFocusTag()
     onResize()
     window.addEventListener('resize', onResize)
 })
@@ -281,6 +295,9 @@ onBeforeUnmount(() => {
     border: 1px solid #000000;
     backdrop-filter: blur(4px);
     color: #fff;
+    &.no-comment {
+        width: 400px;
+    }
 
     header {
         padding: 0 20px;
@@ -306,7 +323,7 @@ onBeforeUnmount(() => {
         overflow: hidden;
         > div {
             width: 50%;
-            
+
             // &.left-item {
             //     border-right: solid 1px rgba(255, 255, 255, 0.16);
             // }

+ 22 - 20
src/components/files/content/Comment.vue

@@ -11,7 +11,7 @@
                     <div class="comment-box">
                         <div class="view-box view-top">
                             <span class="user-name">{{ i.nickName || $t('tag.unkownUser') }}</span>
-                            <i class="iconfont icon-del" v-if="i.userId == userId" @click="delComment({ commentId: i.commentId, index })"></i>
+                            <i class="iconfont icon-comment_delete" v-if="i.userId == userId" @click="delComment({ commentId: i.commentId, index })"></i>
                         </div>
                         <div class="view-box view-middle">
                             <span class="comment-text">{{ i.content }}</span>
@@ -26,7 +26,7 @@
                                 <div class="reply-box">
                                     <div class="view-box view-top">
                                         <span class="user-name">{{ j.nickName || $t('tag.unkownUser') }}</span>
-                                        <i class="iconfont icon-del" v-if="j.userId == userId" @click="delComment({ commentId: j.commentId, index: j_index, parentIndex: index })"></i>
+                                        <i class="iconfont icon-comment_delete" v-if="j.userId == userId" @click="delComment({ commentId: j.commentId, index: j_index, parentIndex: index })"></i>
                                     </div>
                                     <div class="view-box view-middle">
                                         <span class="reply-text"
@@ -81,6 +81,7 @@ import { http } from '@/utils/request'
 import avatar from '@/assets/img/avatar@2x.png'
 import UiConfirm from '@/components/dialog/Confirm.vue'
 import i18n from '@/i18n'
+import browser from '@/utils/browser'
 import UiInput from '../../form/Input.vue'
 const { t } = i18n.global
 const props = defineProps({
@@ -99,7 +100,7 @@ const replyInfo = ref(null)
 const tipsType = ref('warn')
 const showTips = ref(null)
 const slider = ref(null)
-
+const projectId = browser.valueFromUrl('projectId') || 1
 const handlerReply = (data, index) => {
     inputText.value = ''
     let name = data.nickName ? data.nickName : t('tag.unkownUser')
@@ -142,7 +143,7 @@ const hanlderSubmit = () => {
 
     if (canPut) {
         canPut = false
-        http.post(`smart-site/comment/reply`, params)
+        http.post(`smart-site/comment/reply/${projectId}`, params)
             .then(response => {
                 if (response.success) {
                     getAllComments()
@@ -189,7 +190,7 @@ const getAllComments = () => {
 }
 const handlerDel = status => {
     if (status == 'ok') {
-        http.post(`smart-site/comment/del`, { commentId: delComfirm.value.commentId }).then(response => {
+        http.post(`smart-site/comment/del${projectId}`, { commentId: delComfirm.value.commentId }).then(response => {
             if (response.success) {
                 // if (!delComfirm.value.parentIndex) {
                 //     commentList.value.splice(delComfirm.value.index, 1)
@@ -219,20 +220,20 @@ const delComment = data => {
 }
 onMounted(() => {
     getAllComments()
-    if (window.kankan) {
-        window.kankan.TagManager.focusTag(notify.value.sid, {
-            direction: 'left',
-            attrs: {
-                width: 0,
-                // height: 400,
-            },
-        })
-    } else if (window.laser) {
-        window.laser.then(sdk => {
-            let pos = notify.value.position
-            sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
-        })
-    }
+    // if (window.kankan) {
+    //     window.kankan.TagManager.focusTag(notify.value.sid, {
+    //         direction: 'left',
+    //         attrs: {
+    //             width: 0,
+    //             // height: 400,
+    //         },
+    //     })
+    // } else if (window.laser) {
+    //     window.laser.then(sdk => {
+    //         let pos = notify.value.position
+    //         sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
+    //     })
+    // }
     nextTick(() => {
         input$.value.addEventListener('keydown', function (e) {
             if (e.keyCode == 8) {
@@ -275,7 +276,8 @@ onBeforeUnmount(() => {})
                 border: 1px solid rgba(255, 255, 255, 0.2);
                 position: relative;
                 input {
-                    width: 75%;
+                    // width: 75%;
+                    width: 73%;
                     line-height: 34px;
                     padding: 0 5px;
                     color: #fff;

+ 22 - 17
src/components/files/content/TagMsg.vue

@@ -1,7 +1,7 @@
 <!--  -->
 <template>
-    <div class="aside-item left-item">
-        <UiAudio v-if="notify.type == 'audio'" :src="notify.media?.[notify.type][0].src" />
+    <div class="aside-item left-item" :class="!isLogin ? 'no-comment' : ''">
+        <UiAudio v-if="notify.type == 'audio' && notify.media?.[notify.type]" :src="notify.media?.[notify.type][0].src" />
 
         <div class="content-item">
             <div class="item-title">{{ $t('tag.createTime') }}</div>
@@ -9,7 +9,7 @@
         </div>
         <div class="content-item">
             <div class="item-title">{{ $t('tag.creater') }}</div>
-            <span class="content-desc">{{ notify.lastCreateBy || $t('tag.unkownUser') }}</span>
+            <span class="content-desc">{{ notify.createBy || $t('tag.unkownUser') }}</span>
         </div>
         <div class="content-item">
             <div class="item-title">{{ $t('tag.status') }}</div>
@@ -64,6 +64,7 @@ import i18n from '@/i18n'
 const { t } = i18n.global
 const projectId = browser.valueFromUrl('projectId') || 1
 const notify = inject('notify')
+const isLogin = inject('isLogin')
 // const props = defineProps(['notify'])
 const emits = defineEmits(['action', 'setShow'])
 const height = ref(0)
@@ -155,20 +156,20 @@ onMounted(() => {
         }
     })
 
-    if (window.kankan) {
-        window.kankan.TagManager.focusTag(notify.value.sid, {
-            direction: 'left',
-            attrs: {
-                width: 0,
-                // height: 400,
-            },
-        })
-    } else if (window.laser) {
-        window.laser.then(sdk => {
-            let pos = notify.value.position
-            sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
-        })
-    }
+    // if (window.kankan) {
+    //     window.kankan.TagManager.focusTag(notify.value.sid, {
+    //         direction: 'left',
+    //         attrs: {
+    //             width: 0,
+    //             // height: 400,
+    //         },
+    //     })
+    // } else if (window.laser) {
+    //     window.laser.then(sdk => {
+    //         let pos = notify.value.position
+    //         sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
+    //     })
+    // }
 })
 
 onBeforeUnmount(() => {})
@@ -228,6 +229,7 @@ onBeforeUnmount(() => {})
     position: relative;
     .content-item {
         margin-bottom: 20px;
+        word-break: break-all;
         &:last-of-type {
             margin-bottom: 0;
         }
@@ -235,6 +237,9 @@ onBeforeUnmount(() => {})
     &.left-item {
         width: 400px;
         border-right: solid 1px rgba(255, 255, 255, 0.16);
+        &.no-comment {
+            border-right: none;
+        }
         .item-title {
             font-size: 14px;
             color: #999;

+ 69 - 21
src/components/files/index.vue

@@ -8,7 +8,7 @@
                     >)
                 </div>
             </div>
-            <div class="list" :style="`height:${listStyle};`">
+            <div class="list" :style="`height:${listStyle};`" :class="{ disabled: isFlying }">
                 <ul>
                     <li v-for="tag in tags" @click="onClick(tag)" :class="{ active: notify?.sid == tag.sid }">
                         <div class="title"><i></i>{{ tag.title }}</div>
@@ -38,10 +38,10 @@
             <div>{{ $t('tag.deleteTagText') }}</div>
         </template>
     </ui-confirm>
-    <Toast v-if="showTips" type="warn" :content="showTips" :close="() => (showTips = null)" />
+    <Toast v-if="showTips" :type="toastOps" :content="showTips" :close="() => (showTips = null)" />
 </template>
 <script setup>
-import { ref, inject, watchEffect, onMounted, nextTick } from 'vue'
+import { ref, inject, watchEffect, onMounted, nextTick, watch } from 'vue'
 import browser from '@/utils/browser'
 import { http } from '@/utils/request'
 import UiConfirm from '@/components/dialog/Confirm.vue'
@@ -52,13 +52,19 @@ const exit$ = ref(null)
 const add$ = ref(null)
 const props = defineProps(['show'])
 const emits = defineEmits(['add', 'exit'])
+// const source = inject('source')
 const showTips = ref(null)
+const toastOps = ref({
+    type: 'success',
+})
 const handlerDel = status => {
     if (status == 'ok') {
         http.post(`smart-site/marking/del`, {
             markingId: delComfirm.value.id,
         }).then(response => {
             if (response.success) {
+                toastOps.value.type = 'success'
+                showTips.value = '删除成功'
                 let index = tags.value.findIndex(item => item.sid == delComfirm.value.sid)
                 if (index != -1) {
                     tags.value.splice(index, 1)
@@ -67,6 +73,7 @@ const handlerDel = status => {
                     notify.value = null
                 }
             } else {
+                toastOps.value.type = 'warm'
                 showTips.value = response.message
             }
             delComfirm.value = null
@@ -80,14 +87,29 @@ const showFiles = ref(false)
 const showToolbar = ref(false)
 const showMoreSid = ref('')
 const tags = inject('tags')
-
+const editTagId = inject('editTagId')
 const notify = inject('notify')
 const isEdit = inject('isEdit')
+const isFlying = inject('isFlying')
+watch(
+    () => isEdit.value,
+    (val, old) => {
+        if (!val) {
+            editTagId.value = null
+        }
+    }
+)
 const onClick = tag => {
-    notify.value = tag //{ event: 'focus', sid: props.tag.sid, tag: props.tag }
+    notify.value = null
+    nextTick(() => {
+        notify.value = tag //{ event: 'focus', sid: props.tag.sid, tag: props.tag }
+    })
 }
 let isAdd = false
 const onAdd = () => {
+    if (notify.value) {
+        notify.value = null
+    }
     isEdit.value = true
     isAdd = true
     if (window.kankan) {
@@ -102,8 +124,13 @@ const onAdd = () => {
         showToolbar.value = true
     }
 }
-const laserPosition = () => {
+const laserPosition = tag => {
     window.laser.then(sdk => {
+        if (!isAdd) {
+            let pos = tag.position
+            sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
+        }
+
         sdk.addMouseDownEvent(e => {
             if (showToolbar.value == false) {
                 return
@@ -188,6 +215,8 @@ const onAddConfirm = () => {
                 // tags.value.push(tag)
                 notify.value = tag
                 isEdit.value = true
+            } else {
+                onAddCancel()
             }
         })
     } else {
@@ -198,17 +227,15 @@ const onAddConfirm = () => {
             tag = editTag
         }
         if (tag) {
-            // delete tag.__temp
-
-            if (tag) {
-                if (isAdd) {
-                    tag.__temp = true
-                    // tag._add = true
-                    isAdd = false
-                }
-
-                notify.value = tag
+            if (isAdd) {
+                tag.__temp = true
+                // tag._add = true
+                isAdd = false
             }
+
+            notify.value = tag
+        } else {
+            onAddCancel()
         }
     }
     editTag = null
@@ -238,7 +265,8 @@ const onMoreHandler = (type, tag) => {
                 editor.enter(editTag)
             })
         } else {
-            laserPosition()
+            editTagId.value = editTag.sid
+            laserPosition(editTag)
         }
         // window.kankan.TagManager.focusBeforeModify(editTag.sid)
     } else if (type == 'delete') {
@@ -259,7 +287,27 @@ watchEffect(() => {
     }
 })
 
-onMounted(() => {})
+onMounted(() => {
+    // app.Camera.on('flying.started', pano => {
+    //     store.commit('setFlying', true)
+    // })
+    // app.Camera.on('flying.ended', ({ targetPano }) => {
+    //     store.commit('setFlying', false)
+    // })
+    // if (window.kankan) {
+    //     console.error(window.kankan)
+    // } else if (window.laser) {
+    //     window.laser.then(res => {
+    //         console.error(res)
+    //     })
+    // }
+    // window[0].Scene.on('loaded', sdk => {
+    //     console.error(e)
+    // })
+    // window[0].loaded.then(sdk => {
+    //     console.error(sdk)
+    // })
+})
 </script>
 <style lang="scss" scoped>
 button {
@@ -375,7 +423,7 @@ li {
     justify-content: space-between;
     font-size: 14px;
     &.active {
-        background-color: rgba(0, 118, 246, 0.1);
+        background-color: rgba(0, 118, 246, 0.3);
     }
     .title {
     }
@@ -410,13 +458,13 @@ li {
                 height: 32px;
                 line-height: 32px;
                 &:hover {
-                    background-color: rgb(0, 118, 246, 0.1);
+                    background-color: rgb(0, 118, 246, 0.3);
                 }
             }
         }
     }
     &:hover {
-        background-color: rgb(0, 118, 246, 0.1);
+        background-color: rgb(0, 118, 246, 0.3);
     }
 }
 

+ 17 - 6
src/components/form/SelectList.vue

@@ -16,7 +16,9 @@
             <div class="panel" v-show="selecterShow">
                 <ul>
                     <li v-for="item in data" @click.stop="onselecterChange(item)">
-                        <div><span class="checkbox" :class="{ checked: modelValue.includes(item) }"></span>{{ item.text || $t('tag.unkownUser') }}</div>
+                        <div>
+                            <span class="checkbox" :class="{ checked: modelValue.includes(item) }"></span><span class="name">{{ item.text || $t('tag.unkownUser') }}</span>
+                        </div>
                     </li>
                 </ul>
             </div>
@@ -114,11 +116,12 @@ li {
             left: -1px;
             right: -1px;
             top: calc(100% + 4px);
-            background: rgba(27, 27, 28, 0.8);
+            background: rgba(27, 27, 28, 0.9);
+            // background: rgb(57, 57, 60, 0.9);
             box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
             border-radius: 4px 4px 4px 4px;
             border: 1px solid #000000;
-            max-height: 200px;
+            max-height: 250px;
             overflow: hidden;
             overflow-y: auto;
             z-index: 1000;
@@ -133,6 +136,14 @@ li {
                 > div {
                     display: flex;
                     align-items: center;
+                    color: rgba(255, 255, 255, 0.6);
+                    width: 100%;
+                    .name {
+                        width: 80%;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                        white-space: nowrap;
+                    }
                 }
             }
         }
@@ -160,8 +171,8 @@ li {
         content: '';
         border: 1px solid #666;
         border-radius: 2px;
-        width: 16px;
-        height: 16px;
+        width: 14px;
+        height: 14px;
         position: absolute;
         left: 0px;
         top: 0;
@@ -173,7 +184,7 @@ li {
             background-color: #0076f6;
         }
         &::after {
-            left: 4px;
+            left: 3px;
             top: 7px;
             position: absolute;
             display: table;

+ 1 - 1
src/components/form/medias/Audio.vue

@@ -12,7 +12,7 @@
         <input ref="file" type="file" style="display: none" accept=".mp3, .wav" @change="onChange" />
     </div>
     <div class="del-btn" v-if="isEdit && notify.media?.[notify.type]?.length" @click="delMedia">
-        <i class="iconfont icon-del"></i>
+        <i class="iconfont icon-delete"></i>
     </div>
 </template>
 <script setup>

+ 1 - 1
src/components/form/medias/Image.vue

@@ -23,7 +23,7 @@
     </div>
 
     <div class="del-btn" v-if="isEdit" v-show="notify.media?.[notify.type]?.length" @click="delPic">
-        <i class="iconfont icon-del"></i>
+        <i class="iconfont icon-delete"></i>
     </div>
 </template>
 <script setup>

+ 2 - 2
src/components/form/medias/Link.vue

@@ -6,11 +6,11 @@
     </div>
     <div class="placeholder" v-show="url == null">
         <div class="icon">
-            <span>{{$t('components.linkView')}}</span>
+            <span>{{ $t('components.linkView') }}</span>
         </div>
         <div class="link">
             <input type="text" placeholder="https://" v-model.trim="href" />
-            <div class="save" @click="onConfirm"><i class="iconfont icon-checkbox1"></i></div>
+            <div class="save" :class="{ disabled: !href }" @click="onConfirm"><i class="iconfont icon-checkbox1"></i></div>
         </div>
     </div>
 </template>

+ 1 - 1
src/components/form/medias/Video.vue

@@ -21,7 +21,7 @@
         <input ref="file" type="file" style="display: none" accept=".mp4, .mov" @change="onChange" />
     </div>
     <div class="del-btn" v-if="notify.media?.[notify.type]?.length && isEdit" @click="delMedia">
-        <i class="iconfont icon-del"></i>
+        <i class="iconfont icon-delete"></i>
     </div>
 </template>
 <script setup>

+ 25 - 4
src/components/header/Login.vue

@@ -9,14 +9,14 @@
                         <span class="icon">
                             <i class="iconfont icon-user"></i>
                         </span>
-                        <input type="text" v-model.trim="username" />
+                        <input type="text" v-model.trim="username" :placeholder="$t('header.inputPhoneNum')" />
                         <div class="tips" v-show="errors.username">{{ errors.username }}</div>
                     </div>
                     <div class="input">
                         <span class="icon">
                             <i class="iconfont icon-password"></i>
                         </span>
-                        <input :type="showpass ? 'text' : 'password'" v-model.trim="password" />
+                        <input :type="showpass ? 'text' : 'password'" v-model.trim="password" :placeholder="$t('header.inputPassword')" />
                         <div class="tips" v-show="errors.password">{{ errors.password }}</div>
                         <span class="showpass" @click="showpass = !showpass">
                             <i class="iconfont" :class="[showpass ? 'icon-log_eye_selected' : 'icon-log_eye_normal']"></i>
@@ -42,17 +42,32 @@
     </Teleport>
 </template>
 <script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject } from 'vue'
 import { http } from '@/utils/request'
 import common from '@/utils/common'
+import browser from '@/utils/browser'
 import { useI18n, getLocale } from '@/i18n'
+const projectId = browser.valueFromUrl('projectId') || ''
 const { t } = useI18n({ useScope: 'global' })
 const emits = defineEmits(['close', 'user'])
+const isAuth = inject('isAuth')
 const showpass = ref(false)
 const remember = ref(false)
 const username = ref('')
 const password = ref('')
 const errors = ref({})
+const getAuth = () => {
+    http.post(`smart-site/validatedProject/${projectId}`)
+        .then(res => {
+            if (res.code == 0) {
+                isAuth.value = true
+            } else if (res.code == 4002) {
+                //没有权限
+                isAuth.value = false
+            }
+        })
+        .catch(() => {})
+}
 const onLogin = () => {
     errors.value = {}
     if (!username.value) {
@@ -73,9 +88,11 @@ const onLogin = () => {
         phoneNum: username.value,
         rememberMe: remember.value,
         randomcode: '1234',
+        projectId,
     })
         .then(response => {
             if (response.success) {
+                getAuth()
                 if (remember.value) {
                     localStorage.setItem('remember', true)
                     localStorage.setItem('username', username.value)
@@ -94,7 +111,11 @@ const onLogin = () => {
                 })
                 emits('close')
             } else {
-                errors.value.message = response.message
+                if (response.code == 4002) {
+                    errors.value.message = '该账号下未检测到当前场景,请更换账号重新登录'
+                } else {
+                    errors.value.message = response.message
+                }
             }
         })
         .catch(() => {

+ 26 - 2
src/components/header/index.vue

@@ -15,7 +15,7 @@
                 </li>
                 <li><em></em></li>
                 <li v-if="user" class="uinfo" @click="showDrop = true">
-                    <img :src="user.head + '&x-oss-process=image/resize,m_fill,w_64,h_64/quality,q_70'" alt="" />
+                    <img :src="creatAvatar(user.head)" alt="" />
                     <div class="menu">
                         <ul>
                             <li>
@@ -63,8 +63,10 @@ const props = defineProps({
     project: Object,
     showAdjust: Boolean,
 })
+const projectId = browser.valueFromUrl('projectId') || ''
 const emits = defineEmits(['update', 'getUserId'])
 const isLogin = inject('isLogin')
+const isAuth = inject('isAuth')
 const user = ref(null)
 const points = ref({ p1: null, p2: null })
 const showLink = ref(false)
@@ -85,6 +87,16 @@ const openLink = () => {
     showLink.value = true
     showCopyDone.value = false
 }
+const creatAvatar = head => {
+    let url
+    if (head.indexOf('?') != -1) {
+        url = head + '&x-oss-process=image/resize,m_fill,w_64,h_64/quality,q_70'
+    } else {
+        url = head + '?x-oss-process=image/resize,m_fill,w_64,h_64/quality,q_70'
+    }
+
+    return url
+}
 const getCurPosInfo = () => {
     let app = sync.sourceInst
     let id
@@ -120,7 +132,18 @@ const onSetP2 = () => {
     points.value.p2 = p2
     emits('update', 'p2', points.value.p2)
 }
-
+const getAuth = () => {
+    http.post(`smart-site/validatedProject/${projectId}`)
+        .then(res => {
+            if (res.code == 0) {
+                isAuth.value = true
+            } else if (res.code == 4002) {
+                //没有权限
+                isAuth.value = false
+            }
+        })
+        .catch(() => {})
+}
 const getUserInfo = () => {
     http.post(`smart-site/getUserInfo`)
         .then(response => {
@@ -131,6 +154,7 @@ const getUserInfo = () => {
                 }
                 localStorage.setItem('userId', response.data.id)
                 isLogin.value = true
+                getAuth()
             } else {
                 if (response.code == 4008) {
                     // 未登录

+ 2 - 0
src/locales/zh.json

@@ -12,6 +12,8 @@
   },
   "home.name": "首页",
   "header": {
+    "inputPhoneNum": "请输入手机号码",
+    "inputPassword": "请输入密码",
     "passwordText1": "密码不能为空",
     "phoneText1": "手机号码不能为空",
     "phoneText2": "请输入正确手机号",

+ 7 - 0
src/pages/SViewer.vue

@@ -163,6 +163,10 @@ const sourceDays = computed(() => {
         }
     })
 
+    outDays.year.sort((a,b)=>b-a)
+    outDays.month.sort()
+    outDays.day.sort()
+
     return outDays
 })
 
@@ -197,6 +201,9 @@ const onSelected = data => {
         if (find.num != source.value.num) {
             source.value = find
         }
+    } else {
+        showTips.value = t('home.dateScene')
+        return
     }
 }
 

+ 115 - 35
src/pages/Viewer.vue

@@ -6,7 +6,7 @@
         <main>
             <div class="split">
                 <iframe ref="sourceFrame" v-if="sourceURL" :src="sourceURL" frameborder="0" @load="onLoadSource"></iframe>
-                <div class="tools" v-if="source && !showRules" v-show="showWidget && !showAdjust && !fscChecked && (dbsChecked || (!target && !bimChecked))">
+                <div class="tools" v-if="source && !showRules && !ruleChecked" v-show="showWidget && !showAdjust && !fscChecked && (dbsChecked || (!target && !bimChecked))">
                     <div class="item-date">
                         <calendar
                             name="source"
@@ -72,25 +72,26 @@
                 </div>
             </div>
             <div class="model" v-show="showWidget && !showAdjust && !showRules">
-                <div v-if="isLogin" class="file" :class="{ active: fileChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked">
-                    <div @click="onFileChecked">
-                        <i class="iconfont icon-note1"></i>
-                        <span>{{ $t('home.tag') }}</span>
-                    </div>
-                </div>
-                <div v-if="source?.type < 2" class="rule" :class="{ active: fileChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked">
+                <div class="rule" :class="{ active: ruleChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked">
                     <div @click="onRuleChecked">
                         <i class="iconfont icon-measurement"></i>
                         <span>{{ $t('common.measure') }}</span>
                     </div>
                 </div>
-                <div class="bim" :class="{ active: bimChecked, disable: bimDisable }" v-show="!fscChecked && !showBim">
+                <div v-if="isLogin && isAuth" class="file" :class="{ active: fileChecked, disable: fileDisable }" v-show="!fscChecked && !showBim && !dbsChecked && !bimChecked && !ruleChecked">
+                    <div @click="onFileChecked">
+                        <i class="iconfont icon-note1"></i>
+                        <span>{{ $t('home.tag') }}</span>
+                    </div>
+                </div>
+
+                <div class="bim" :class="{ active: bimChecked, disable: bimDisable }" v-show="!fscChecked && !showBim && !ruleChecked">
                     <div @click="onBimChecked">
                         <i class="iconfont icon-BIM"></i>
                         <span>BIM</span>
                     </div>
                 </div>
-                <div class="dbs" :class="{ active: dbsChecked, disable: dbsDisable }" v-show="!fscChecked && !showBim">
+                <div class="dbs" :class="{ active: dbsChecked, disable: dbsDisable }" v-show="!fscChecked && !showBim && !ruleChecked">
                     <div @click="onDbsChecked">
                         <i class="iconfont icon-split_screen"></i>
                         <span>{{ $t('home.splitScreen') }}</span>
@@ -123,15 +124,24 @@ import i18n from '@/i18n'
 import Rules from './Rules'
 const { t } = i18n.global
 const isDev = process.env.VUE_APP_TEST == 1
-
+const rules = []
 const tags = ref([])
 const notify = ref(null)
 const isEdit = ref(false)
 const isLogin = ref(false)
+const isFlying = ref(false)
+const editTagId = ref(null)
+const isAuth = ref(false)
+const source = ref(null)
+
 provide('tags', tags)
 provide('notify', notify)
 provide('isEdit', isEdit)
+provide('editTagId', editTagId)
 provide('isLogin', isLogin)
+provide('isFlying', isFlying)
+provide('isAuth', isAuth)
+provide('source', source)
 const userId = ref(localStorage.getItem('userId') || null)
 
 const getUserId = id => {
@@ -153,6 +163,7 @@ const bimChecked = ref()
 const dbsChecked = ref(null)
 const fscChecked = ref(null)
 const fileChecked = ref(false)
+const ruleChecked = ref(false)
 
 const datepickName = ref(null)
 
@@ -161,7 +172,6 @@ const targetFrame = ref(null)
 
 const mode = ref(0)
 
-const source = ref(null)
 const target = ref(null)
 const project = ref(null)
 const points = ref({ p1: null, p2: null })
@@ -213,7 +223,8 @@ const sourceURL = computed(() => {
     if (!source.value) {
         return
     }
-
+    // console.log(1111)
+    getTagList(source.value.num)
     if (source.value.type < 2) {
         // 看看、看见场景
         return `smart-kankan.html?m=${source.value.num}${isDev ? '&dev' : ''}`
@@ -226,6 +237,7 @@ const targetURL = computed(() => {
     if (bimChecked.value) {
         return `smart-bim.html?m=${project.value.bimData.bimOssFilePath}`
     }
+    getTagList(target.value.num)
     if (target.value.type < 2) {
         // 看看、看见场景
         return `smart-kankan.html?m=${target.value.num}${isDev ? '&dev' : ''}`
@@ -304,11 +316,38 @@ const onLoadSource = () => {
         // BIM单屏模式
         return
     }
+
     if (source.value.type < 2) {
+        window['laser'] = null
         window['kankan'] = sourceFrame.value.contentWindow.app
         window['kankan'].TagManager.load(tags.value)
+
+        window['kankan'].Camera.on('flying.started', pano => {
+            isFlying.value = true
+        })
+        window['kankan'].Camera.on('flying.ended', pano => {
+            isFlying.value = false
+        })
     } else {
+        window['kankan'] = null
         window['laser'] = sourceFrame.value.contentWindow.loaded
+        window.laser.then(sdk => {
+            sdk.scene.on('posChange', cameraPos => {
+                tags.value.forEach(tag => {
+                    const info2d = sdk.scene.getScreenByPoint(tag.position)
+                    tag.x = info2d.pos.x
+                    tag.y = info2d.pos.y
+                    tag.visible = info2d.trueSide
+                })
+            })
+
+            window[0].viewer.images360.addEventListener('flyToPano', () => {
+                isFlying.value = true
+            })
+            window[0].viewer.images360.addEventListener('flyToPanoDone', () => {
+                isFlying.value = false
+            })
+        })
     }
     loadSourceScene(sourceFrame, source.value.type < 2 ? 'kankan' : 'laser', mode.value)
 }
@@ -332,8 +371,10 @@ const onModeChange = targetMode => {
 const onDensityChange = density => {
     if (sync.sourceInst) {
         sync.sourceInst.loaded.then(sdk => {
+            //console.log('onDensityChange',sync.sourceInst.sceneName, density.type)
             let data = sdk.scene.changePointDensity(density.type)
             sdk.scene.changeDensityPercent(data.percent)
+            sync.sourceInst.viewer.dispatchEvent('densityChange')
         })
         densityType.value = density
     }
@@ -553,17 +594,54 @@ const onP2Click = type => {
 
     flyToP1P2(points.value.p2)
 }
-const onRuleChecked = () => {
-    showRules.value = true
-    window.kankan.TagManager.startMeasure()
 
-    // store.commit('SetPlayerOptions', { showRulesWidgets: true })
-    // emits('close')
+const onRuleHandler = sdk => {
+    let rule = sdk.startMeasure()
+    rule.bus.on('end', () => {
+        setTimeout(() => {
+            onRuleHandler(sdk)
+        }, 1)
+    })
+    rules.push(rule)
 }
 
-onMounted(() => {
-    const num = browser.valueFromUrl('m') || ''
-    const projectId = browser.valueFromUrl('projectId') || 1
+const onRuleChecked = () => {
+    if (ruleChecked.value) {
+        ruleChecked.value = false
+        rules.forEach(rule => {
+            rule.clear()
+        })
+        return
+    }
+    if (source.value.type < 2) {
+        showRules.value = true
+        window.kankan.TagManager.startMeasure()
+    } else {
+        sync.sourceInst.loaded.then(sdk => {
+            onRuleHandler(sdk)
+        })
+        ruleChecked.value = true
+    }
+}
+const getTagList = num => {
+    http.post(`smart-site/marking/list`, {
+        projectId: projectId,
+        pageNum: 1,
+        pageSize: 200,
+        num: num ? num : browser.getURLParam('m'),
+    }).then(response => {
+        if (response.data && response.data.list) {
+            tags.value = response.data.list.map(item => {
+                item.hotData.visible = false
+                item.hotData.id = item.markingId
+                item.hotData.createTime = item.createTime
+                item.hotData.createBy = item.createBy
+                return item.hotData
+            })
+        }
+    })
+}
+const getInfo = () => {
     http.get(`smart-site/project/info?projectId=${projectId}&sceneOrder=asc`)
         .then(response => {
             if (response.success) {
@@ -611,20 +689,22 @@ onMounted(() => {
         .catch(() => {
             showTips.value = t('code.failed')
         })
-    http.post(`smart-site/marking/list`, {
-        projectId: projectId,
-        pageNum: 1,
-        pageSize: 200,
-        num: browser.getURLParam('m'),
-    }).then(response => {
-        if (response.data && response.data.list) {
-            tags.value = response.data.list.map(item => {
-                item.hotData.visible = false
-                item.hotData.id = item.markingId
-                item.hotData.createTime = item.createTime
-                item.hotData.lastCreateBy = item.lastCreateBy
-                return item.hotData
-            })
+}
+const num = browser.valueFromUrl('m') || ''
+const projectId = browser.valueFromUrl('projectId') || 1
+
+onMounted(() => {
+    getInfo()
+    getTagList()
+    document.addEventListener('fullscreenchange', () => {
+        if (document.fullscreenElement) {
+            if (!fscChecked.value) {
+                fscChecked.value = true
+            }
+        } else {
+            if (fscChecked.value) {
+                fscChecked.value = false
+            }
         }
     })
 })

+ 35 - 10
src/utils/ConvertViews.js

@@ -102,8 +102,15 @@ export default class ConvertViews extends THREE.EventDispatcher{
                 }
                 master.viewer.images360.addEventListener('flyToPano',flyToPano)
               
+              
+                var cancelFlyToPano = (e)=>{//防止点云模式下飞到pano途中停止后另一边还在飞
+                    e.disturb && this.laserCancelFly(customer) 
+                }
+                master.viewer.images360.addEventListener('flyToPanoDone',cancelFlyToPano)
+                
+              
                 var cameraMove = (e)=>{ 
-                    if(master != this.masterApp || !customer.viewer )return
+                    if(master != this.masterApp || !customer.viewer  )return
                     
                     this.fakeAppUpdateInfo(master)
                     
@@ -120,6 +127,18 @@ export default class ConvertViews extends THREE.EventDispatcher{
                 }
                 master.addEventListener('mouseup',dragEnd)
                     
+                    
+                var pointDensityChanged = ()=>{
+                    if(customer.Potree.settings.UserDensityPercent != master.Potree.settings.UserDensityPercent){
+                        customer.Potree.settings.UserDensityPercent = master.Potree.settings.UserDensityPercent  //在sdk里初始化了UserDensityPercent所以不能只用UserPointDensity了
+                        customer.viewer.setPointLevels()
+                        console.log('UserPointDensity', master.sceneName,  master.Potree.settings.UserDensityPercent)
+                    }
+                     
+                }
+                master.viewer.addEventListener('densityChange',pointDensityChanged)
+                   
+                    
             }else if(sourceApp.sceneType == 'kankan'){
                 
                 
@@ -136,8 +155,10 @@ export default class ConvertViews extends THREE.EventDispatcher{
                 
                 
                 var cameraMove = (e)=>{//暂时只有漫游模式
-                    if(!e.hasChanged.cameraChanged || !customer.app || !customer.app.core)return
-                    
+                    if(!e.hasChanged.cameraChanged || !customer.app || !customer.app.core|| 
+                        master != this_.masterApp 
+                    )return
+                    //console.log('cameraMove', master.sceneName)
                     this.fakeAppUpdateInfo(master)
                     this.syncView(master, customer) 
                 }
@@ -159,14 +180,17 @@ export default class ConvertViews extends THREE.EventDispatcher{
                 if(master.sceneType == 'laser'){
                     if(!master.viewer )return //master已替换,不用处理
                     master.viewer.images360.removeEventListener('flyToPano',flyToPano)
+                    master.viewer.images360.removeEventListener('flyToPanoDone',cancelFlyToPano)
                     master.viewer.removeEventListener('camera_changed',cameraMove)
+                    master.viewer.removeEventListener('densityChange',pointDensityChanged)
+                    
                 }else if(master.sceneType == 'kankan'){
                     player1.off("flying.started",flyToPano)
                     player1.off("update",cameraMove)
                 }
                 
-                master.removeEventListener('mousedown',changeMaster)
-                master.removeEventListener('mousewheel',changeMaster)
+                dom.removeEventListener('pointerdown',changeMaster)
+                dom.removeEventListener('mousewheel',changeMaster)
                 master.removeEventListener('mouseup',dragEnd)
                 this.removeEventListener('clearBind-sameType',dispose)
             } 
@@ -177,7 +201,7 @@ export default class ConvertViews extends THREE.EventDispatcher{
         
         bind(sourceApp, targetApp) 
         bind(targetApp, sourceApp) 
-        
+        master.viewer.dispatchEvent('densityChange')//同步点云质量
          
         
         this.loaded = true 
@@ -626,7 +650,7 @@ export default class ConvertViews extends THREE.EventDispatcher{
         let {position,target} = this.getTranPosData(data, convertInfo, customer && customer.fakeApp == convertInfo.sourceFakeApp  ) 
  
 
-        if(customer && customer.sceneType == 'laser'){
+        if(customer && customer.sceneType == 'laser'){ 
             this.laserSyncView(customer, {position,target})
         }else if(customer && customer.sceneType == 'kankan'){
             this.syncView(sourceApp, targetApp, convertInfo)
@@ -795,6 +819,7 @@ export default class ConvertViews extends THREE.EventDispatcher{
                 currentPano :  images360.currentPano && images360.currentPano.id,
                 isAtPano : images360.isAtPano(),
                 quaternionChanged : true,
+                bumping: images360.bumping,
                 
             } 
         }else if(app.sceneType == 'kankan'){
@@ -829,7 +854,7 @@ export default class ConvertViews extends THREE.EventDispatcher{
         convertInfo = convertInfo || this.convertInfo
         if(fakeApp.sceneType == 'laser'){
             //customer.Potree.settings.displayMode = fakeApp.viewInfo.displayMode
-            if(fakeApp.viewInfo.isAtPano || fakeApp.viewInfo.displayMode == 'showPanos'){ //不改变漫游点,仅转换朝向 
+            if(fakeApp.viewInfo.isAtPano || fakeApp.viewInfo.bumping || fakeApp.viewInfo.displayMode == 'showPanos'){ //不改变漫游点,仅转换朝向 
                 if( fakeApp.viewInfo.quaternionChanged){
                     let diffQua = customer.fakeApp == convertInfo.targetFakeApp ? convertInfo.diffQua : convertInfo.diffQuaInvert
                     //let diffQua = customer == this.targetApp ? convertInfo.diffQua : convertInfo.diffQuaInvert
@@ -894,7 +919,7 @@ export default class ConvertViews extends THREE.EventDispatcher{
             pano.dispatchEvent({type:'changeMarkerTex',name:'ring'})
         })  */
         //app.Potree.settings.pointDensity = 'high'
-        app.Potree.settings.UserDensityPercent = this.isMobile ? 0.8 : 1 ; //因为nodeMaxLevel不同,感觉只有最高质量能看起来一样
+        app.Potree.settings.UserDensityPercent = 1;  //在sdk里初始化了UserDensityPercent所以不能只用UserPointDensity了
         app.viewer.setPointLevels()
         app.Potree.settings.rotAroundPoint = false   //去除原因:比较好同步,尤其当左边在当前点位,右边同步后却离开当前点位的话拖拽就会绕点旋转了
         setTimeout(()=>{//laser的代码中莫名会请求showPointCloud,所以尽量晚一点覆盖它,再确保一次
@@ -1070,7 +1095,7 @@ function getId(){
 /* 
 
 note:
-
+还不支持laser和4dkk同屏
 
 访问:
 window[0]   window[1]