Bladeren bron

feat: 完善激光添加批注

jinx 2 jaren geleden
bovenliggende
commit
de91ce424c

BIN
src/assets/img/pic_bg.png


+ 116 - 0
src/components/audio/index.vue

@@ -0,0 +1,116 @@
+<template>
+    <div class="ui-audio audio" @click="clickHandler">
+        <audio loop @play="rotation" ref="audio">
+            <source :src="src" />
+        </audio>
+        <span v-for="random in randoms" :style="{ '--percent': random }" />
+    </div>
+</template>
+
+<script setup>
+import { defineProps, ref, watchEffect, defineExpose } from 'vue'
+defineProps({
+    src: String,
+    autoplay: {
+        type: Boolean,
+        default: true,
+    },
+})
+const audio = ref()
+const randoms = ref([1, 0.5, 1, 0.5])
+const playIng = ref(false)
+
+let timeout
+const rotation = () => {
+    if (!playIng.value) return
+    for (let i = 0; i < randoms.value.length; i++) {
+        randoms.value[i] = Math.random()
+    }
+    timeout = setTimeout(rotation, 200)
+}
+
+watchEffect(() => {
+    if (audio.value) {
+        if (playIng.value) {
+            audio.value.play()
+        } else {
+            audio.value.pause()
+        }
+        clearTimeout(timeout)
+        rotation()
+    }
+})
+
+const clickHandler = () => {
+    playIng.value = !playIng.value
+}
+
+defineExpose({
+    play() {
+        playIng.value = true
+    },
+    pause() {
+        playIng.value = false
+    },
+})
+// 播放音乐
+const audioAutoPlay = () => {
+    if (window.WeixinJSBridge) {
+        WeixinJSBridge.invoke(
+            'getNetworkType',
+            {},
+            function (e) {
+                audio.value.play()
+            },
+            false
+        )
+    } else {
+        document.addEventListener(
+            'touchstart',
+            function () {
+                WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
+                    audio.value.play()
+                })
+            },
+            false
+        )
+    }
+}
+const detectWeixin = () => {
+    //微信 包括PC的微信
+    return window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'
+}
+if (detectWeixin()) {
+    console.log('detectWeixin')
+    audioAutoPlay()
+}
+// 自动播放
+clickHandler()
+</script>
+
+<script>
+export default { name: 'ui-audio' }
+</script>
+<style lang="scss" scoped>
+.audio {
+    display: inline-block;
+    cursor: pointer;
+
+    > span {
+        --height: 18px;
+        width: 3px;
+        height: calc(var(--height) * var(--percent));
+        background: var(--colors-primary-base);
+        display: inline-block;
+        transition: height 0.2s linear;
+
+        &:not(:last-child) {
+            margin-right: 2px;
+        }
+    }
+
+    audio {
+        display: none;
+    }
+}
+</style>

+ 24 - 6
src/components/files/TagEditor.vue

@@ -71,15 +71,18 @@ const data = ref({
     members: [],
 })
 const onClose = () => {
-    if (window.kankan) {
-        if (notify.value.__temp) {
+    if (notify.value.__temp) {
+        if (window.kankan) {
             kankan.TagManager.remove(notify.value.sid)
+        } else {
+            tags.value.splice(tags.value.length - 1, 1)
         }
-    } else {
     }
+
     isEdit.value = false
     notify.value = null
 }
+let pushData = null
 const onSubmit = async () => {
     if (!form.value.title) {
         return (showTips.value = '请输入资料名称')
@@ -95,6 +98,9 @@ const onSubmit = async () => {
             delete notify.value.media[item]
         }
     })
+    if (notify.value._add) {
+        pushData = notify.value
+    }
     tag = JSON.stringify(notify.value, (key, value) => {
         if (key === 'visiblePanos') {
             return value.map(item => item.id)
@@ -134,8 +140,8 @@ const handlerUpload = async data => {
 
         mediaList = []
     }
-    console.log(tag)
     let params = {
+        markingId: null,
         projectId,
         userIds: tag.members,
         markingStatus: form.value.status,
@@ -145,12 +151,23 @@ const handlerUpload = async data => {
     if (tag.id) {
         params.markingId = tag.id
     }
+
     http.post(`smart-site/marking/addOrUpdate`, params).then(response => {
         if (response.success) {
+            notify.value.status = tag.status
+            notify.value.members = tag.members
+            if (pushData && notify.value._add) {
+                delete notify.value._add
+                pushData.status = tag.status
+                pushData.content = tag.content
+                pushData.members = tag.members
+                tags.value.push(pushData)
+                pushData = null
+            }
+            isEdit.value = false
             delete notify.value.__temp
             notify.value.id = response.data
             notify.value = null
-            isEdit.value = false
         } else if (response.code == 4008) {
             showTips.value = '请先登录'
         } else {
@@ -197,7 +214,8 @@ onMounted(() => {
         })
     } else if (window.laser) {
         window.laser.then(sdk => {
-            let pos = notify.value.tag.position
+            // let pos = notify.value.tag.position
+            let pos = notify.value.position
             sdk.scene.comeToTag(new THREE.Vector3(pos.x, pos.y, pos.z))
         })
     }

+ 1 - 1
src/components/files/TagManager.vue

@@ -31,7 +31,7 @@ const init = sdk => {
 const initLaserTag = () => {
     window.laser.then(sdk => {
         sdk.scene.on('posChange', cameraPos => {
-            tags.forEach(tag => {
+            tags.value.forEach(tag => {
                 const info2d = sdk.scene.getScreenByPoint(tag.position)
                 tag.x = info2d.pos.x
                 tag.y = info2d.pos.y

+ 11 - 4
src/components/files/TagView.vue

@@ -9,7 +9,7 @@
             </header>
             <article>
                 <TagMsg @setShow="setShow"></TagMsg>
-                <Comment></Comment>
+                <Comment :slideHeigt="slideHeigt"></Comment>
             </article>
             <!-- <footer></footer> -->
         </div>
@@ -20,6 +20,7 @@ import { ref, onMounted, onBeforeUnmount, computed, inject } from 'vue'
 
 import TagMsg from './content/TagMsg.vue'
 import Comment from './content/Comment.vue'
+import { nextTick } from 'process'
 const notify = inject('notify')
 const emits = defineEmits(['action'])
 const height = ref(0)
@@ -36,8 +37,13 @@ const onClose = () => {
     }
     notify.value = null
 }
-const setShow = () => {
+const slideHeigt = ref(0)
+const setShow = data => {
     canShow.value = true
+
+    nextTick(() => {
+        slideHeigt.value = document.querySelector('.left-item').offsetHeight
+    })
 }
 onMounted(() => {
     if (window.kankan) {
@@ -267,7 +273,7 @@ onBeforeUnmount(() => {
     // width: 740px;
     // height: 400px;
     width: 741px;
-    // height: 776px;
+    max-height: 776px;
     min-height: 70px;
     background: rgba(27, 27, 28, 0.8);
     box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
@@ -296,7 +302,8 @@ onBeforeUnmount(() => {
     article {
         display: flex;
         width: 100%;
-        height: 100%;
+        // max-height: 711px;
+        overflow: hidden;
         > div {
             width: 50%;
             &:first-child {

+ 142 - 56
src/components/files/content/Comment.vue

@@ -1,37 +1,44 @@
 <!--  -->
 <template>
     <div class="aside-item right-item">
-        <div class="comment-content">
+        <div class="comment-content" ref="slider" v-if="slideHeigt" :style="`height:${slideHeigt - 84}px;`">
             <div class="comment-header"><span>评论</span></div>
             <div class="comment-msg">
-                <div class="comment-item">
-                    <div class="avatar-box"></div>
+                <div class="comment-item" v-for="(i, index) in commentList">
+                    <div class="avatar-box" :style="i.head ? `background-image:url(${i.head});` : `background-image:url(${emptyAvatar});`"></div>
                     <div class="comment-box">
-                        <div class="view-top">
-                            <span class="user-name">用户名称</span>
-                            <i class="iconfont icon-del"></i>
+                        <div class="view-box view-top">
+                            <span class="user-name">{{ i.nickName }}</span>
+                            <i class="iconfont icon-del" v-if="i.userId == userId" @click="delComment({ commentId: i.commentId, index })"></i>
                         </div>
-                        <div class="view-middle">
-                            <span class="comment-text">这个地方的油漆没有干</span>
+                        <div class="view-box view-middle">
+                            <span class="comment-text">{{ i.content }}</span>
                         </div>
-                        <div class="view-bottom">
-                            <span class="comment-time">2022-11-28</span>
-                            <span class="reply-btn" @click="handlerReply">回复</span>
+                        <div class="view-box view-bottom">
+                            <span class="comment-time">{{ i.createTime }}</span>
+                            <span class="reply-btn" @click="handlerReply({ parentId: i.commentId, nickName: i.nickName }, index)">回复</span>
                         </div>
-                        <div class="reply-content">
-                            <div class="reply-item">
-                                <div class="avatar-box"></div>
+                        <div class="reply-content" v-if="i.children">
+                            <div class="reply-item" v-for="(j, j_index) in i.children">
+                                <div class="avatar-box" :style="j.head ? `background-image:url(${j.head});` : `background-image:url(${emptyAvatar});`"></div>
                                 <div class="reply-box">
-                                    <div class="view-top">
-                                        <span class="user-name">用户名称</span>
-                                        <i class="iconfont icon-del"></i>
+                                    <div class="view-box view-top">
+                                        <span class="user-name">{{ j.nickName }}</span>
+                                        <i class="iconfont icon-del" v-if="j.userId == userId" @click="delComment({ commentId: j.commentId, index: j_index, parentIndex: index })"></i>
                                     </div>
-                                    <div class="view-middle">
-                                        <span class="reply-text">回复<span class="reply-tips">@用户A</span>这个地方的油漆没有干</span>
+                                    <div class="view-box view-middle">
+                                        <span class="reply-text"
+                                            ><span v-if="j.replyId"
+                                                >回复<span class="reply-tips">@{{ j.replyNickName }}</span></span
+                                            >
+                                            {{ j.content }}</span
+                                        >
                                     </div>
-                                    <div class="view-bottom">
-                                        <span class="reply-time">2022-11-28</span>
-                                        <span class="reply-btn">回复</span>
+                                    <div class="view-box view-bottom">
+                                        <span class="reply-time">{{ j.createTime }}</span>
+                                        <span class="reply-btn" @click="handlerReply({ parentId: i.commentId, replyId: j.commentId, parentUserId: j.userId, nickName: i.nickName }, j_index)"
+                                            >回复</span
+                                        >
                                     </div>
                                 </div>
                             </div>
@@ -39,72 +46,101 @@
                     </div>
                 </div>
             </div>
+            <div class="empty-box" v-if="!commentList.length">
+                <div class="pic"></div>
+                <div>暂无评论</div>
+            </div>
         </div>
+
         <div class="input-content">
             <div class="input-box"><input ref="input$" @input="handlerInput" v-model="inputText" :placeholder="placeholderText" type="text" /></div>
             <div class="send-btn" @click="hanlderSubmit">发布</div>
         </div>
     </div>
-    <Toast v-if="showTips" type="warn" :content="showTips" :close="() => (showTips = null)" />
+    <Toast v-if="showTips" :type="tipsType" :content="showTips" :close="() => (showTips = null)" />
 </template>
 
 <script setup>
-import { ref, onMounted, onBeforeUnmount, computed, inject, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed, inject, nextTick, defineProps } from 'vue'
 import Toast from '@/components/dialog/Toast'
 import { http } from '@/utils/request'
+import avatar from '@/assets/img/avatar@2x.png'
+const props = defineProps({
+    slideHeigt: Number,
+})
+const emptyAvatar = ref(avatar)
 const notify = inject('notify')
 console.log(notify.value)
 const emits = defineEmits(['action'])
 const input$ = ref(null)
 const inputText = ref('')
-const placeholderText = ref('')
-const replyStatus = ref(false)
-
+const placeholderText = ref('发一条评论吧')
+const replyInfo = ref(null)
+const tipsType = ref('warn')
 const showTips = ref(null)
-const handlerReply = () => {
+const slider = ref(null)
+
+const handlerReply = (data, index) => {
     inputText.value = ''
-    placeholderText.value = '@用户名称'
-    replyStatus.value = true
+    placeholderText.value = '@' + data.nickName
+    delete data.nickName
+    replyInfo.value = data
+
+    console.log(replyInfo.value)
 }
 
 const handlerInput = () => {
     // console.log(inputText.value.length)
-    if (replyStatus.value && inputText.value.length) {
-        // console.log(1)
-    }
+    // if (replyInfo.value && inputText.value.length) {
+    //     // console.log(1)
+    // }
 }
 let parentId = null
 let commentId = null
-let userId = localStorage.getItem('userId')
-
+const userId = ref(localStorage.getItem('userId') - 0)
+const commentList = ref([])
 const hanlderSubmit = () => {
     if (inputText.value == '') {
+        tipsType.value = 'warn'
         showTips.value = '请输入内容'
         return
     }
-    if (replyStatus.value) {
-    } else {
-    }
 
     let params = {
-        commentId: commentId,
         markingId: notify.value.id,
         content: inputText.value,
-        userId: userId,
+        userId: userId.value,
         parentId: parentId,
     }
 
-    // if (!params.commentId) {
-    //     delete params.commentId
-    // }
-    // if (!params.parentId) {
-    //     delete params.parentId
-    // }
+    if (replyInfo.value) {
+        for (let key in replyInfo.value) {
+            if (replyInfo.value[key]) {
+                params[key] = replyInfo.value[key]
+            }
+        }
+    }
+
     console.log(params)
     http.post(`smart-site/comment/reply`, params).then(response => {
         console.log(response)
         if (response.success) {
+            getAllComments()
+
+            // if (replyInfo.value) {
+            // } else {
+            //     // slider.value.
+            //     slider.value.scrollTo({
+            //         top: 0,
+            //         behavior: 'smooth',
+            //     })
+            // }
+
+            replyInfo.value = null
+            inputText.value = ''
+            placeholderText.value = '发一条评论吧'
         } else {
+            tipsType.value = 'error'
             showTips.value = response.message
         }
     })
@@ -118,11 +154,33 @@ const onClose = () => {
     }
     notify.value = null
 }
-onMounted(() => {
+const getAllComments = () => {
     http.post(`smart-site/comment/tree/all`, { markingId: notify.value.id }).then(response => {
-        // console.log(response)
+        if (response.success) {
+            commentList.value = response.data
+        } else {
+        }
     })
+}
 
+const delComment = data => {
+    http.post(`smart-site/comment/del`, { commentId: data.commentId }).then(response => {
+        if (response.success) {
+            if (!data.parentIndex) {
+                commentList.value.splice(data.index, 1)
+            } else {
+                commentList.value[data.parentIndex].children.splice(data.index, 1)
+            }
+            tipsType.value = 'success'
+            showTips.value = '删除成功'
+        } else {
+            tipsType.value = 'error'
+            showTips.value = response.message
+        }
+    })
+}
+onMounted(() => {
+    getAllComments()
     if (window.kankan) {
         window.kankan.TagManager.focusTag(notify.value.sid, {
             direction: 'left',
@@ -140,9 +198,9 @@ onMounted(() => {
     nextTick(() => {
         input$.value.addEventListener('keydown', function (e) {
             if (e.keyCode == 8) {
-                if (replyStatus.value && !inputText.value.length) {
-                    replyStatus.value = false
-                    placeholderText.value = ''
+                if (replyInfo.value && !inputText.value.length) {
+                    replyInfo.value = null
+                    placeholderText.value = '发一条评论吧'
                 }
             }
         })
@@ -153,10 +211,10 @@ onBeforeUnmount(() => {})
 </script>
 <style lang="scss" scoped>
 .aside-item {
-    padding: 20px;
+    padding: 20px 0 20px 20px;
     box-sizing: border-box;
     line-height: 28px;
-
+    flex: 1;
     &.right-item {
         position: relative;
         .input-content {
@@ -194,7 +252,31 @@ onBeforeUnmount(() => {})
                 cursor: pointer;
             }
         }
+        .empty-box {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            flex-flow: column;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            .pic {
+                width: 134px;
+                height: 134px;
+                background: url('~@/assets/img/pic_bg.png') no-repeat;
+                background-size: 100% 100%;
+            }
+            div {
+                margin-top: 5px;
+                color: #999;
+            }
+        }
         .comment-content {
+            // height: calc(100% - 54px);
+            overflow-y: auto;
+            padding: 0 20px 0 0;
+            position: relative;
             .comment-header {
                 font-size: 16px;
                 font-weight: bold;
@@ -210,12 +292,14 @@ onBeforeUnmount(() => {})
                         width: 24px;
                         height: 24px;
                         border-radius: 50%;
-                        background: #f2f2f2;
                         margin-right: 6px;
+                        background-size: 100% 100%;
+
+                        background-repeat: no-repeat;
                     }
                     .comment-box {
                         flex: 1;
-                        > div {
+                        > div.view-box {
                             display: flex;
                             align-items: center;
                             justify-content: space-between;
@@ -258,8 +342,10 @@ onBeforeUnmount(() => {})
                                     width: 24px;
                                     height: 24px;
                                     border-radius: 50%;
-                                    background: #f2f2f2;
                                     margin-right: 6px;
+                                    background-size: 100% 100%;
+
+                                    background-repeat: no-repeat;
                                 }
                                 .reply-box {
                                     flex: 1;

+ 92 - 5
src/components/files/content/TagMsg.vue

@@ -1,13 +1,15 @@
 <!--  -->
 <template>
     <div class="aside-item left-item">
+        <UiAudio v-if="notify.type == 'audio'" :src="notify.media?.[notify.type][0].src" />
+
         <div class="content-item">
             <div class="item-title">创建时间</div>
-            <span class="content-desc">{{notify.createTime}}</span>
+            <span class="content-desc">{{ notify.createTime }}</span>
         </div>
         <div class="content-item">
             <div class="item-title">创建人</div>
-            <span class="content-desc">陈工</span>
+            <span class="content-desc">{{ notify.lastCreateBy }}</span>
         </div>
         <div class="content-item">
             <div class="item-title">状态</div>
@@ -26,9 +28,23 @@
             <div class="item-title">描述</div>
             <span class="content-desc">{{ form.describe }}</span>
         </div>
-        <div class="media-box" v-if="notify.media?.[notify.type]?.length" :class="notify.type == 'image' ? 'zoom-in' : ''">
+        <div class="media-box" :class="{ nor: notify.type == 'link', 'zoom-in': notify.type != 'video' }" @click="openView = true" v-if="notify.media?.[notify.type]?.length && notify.type != 'audio'">
             <component :is="component"></component>
         </div>
+
+        <!-- <audio preload="true" autoplay>
+            <source :src="notify.media?.[notify.type][0].src" type="audio/mpeg" />
+        </audio> -->
+        <teleport to="body">
+            <div class="mask-layer" v-if="openView">
+                <div class="close-btn">
+                    <i class="iconfont icon-close" @click="openView = false"></i>
+                </div>
+                <div class="layer-content">
+                    <component :is="component"></component>
+                </div>
+            </div>
+        </teleport>
     </div>
 </template>
 
@@ -40,6 +56,8 @@ import Image from '@/components/form/medias/Image.vue'
 import Video from '@/components/form/medias/Video.vue'
 import Audio from '@/components/form/medias/Audio.vue'
 import Link from '@/components/form/medias/Link.vue'
+import UiAudio from '@/components/audio/index.vue'
+import { nextTick } from 'process'
 const projectId = browser.valueFromUrl('projectId') || 1
 const notify = inject('notify')
 // const props = defineProps(['notify'])
@@ -51,7 +69,7 @@ const form = ref({
     status: '',
     members: [],
 })
-
+const openView = ref(false)
 const data = ref({
     status: [
         { text: '待处理', value: 1 },
@@ -109,7 +127,9 @@ onMounted(() => {
                 })
             }
         }
-        emits('setShow')
+        nextTick(() => {
+            emits('setShow')
+        })
     })
 
     if (window.kankan) {
@@ -130,13 +150,64 @@ onMounted(() => {
 
 onBeforeUnmount(() => {})
 </script>
+<style lang="scss">
+.mask-layer {
+    .swiper-button-prev,
+    .swiper-button-next {
+        width: 48px !important;
+        height: 48px !important;
+        background: rgba(0, 0, 0, 0.5) !important;
+        color: #fff;
+        &::after {
+            font-size: 18px !important;
+        }
+    }
+}
+</style>
 <style lang="scss" scoped>
+.ui-audio {
+    position: absolute;
+    right: 20px;
+    top: 20px;
+}
+.mask-layer {
+    width: 100%;
+    height: 100%;
+    position: fixed;
+    top: 0;
+    left: 0;
+    background: #292929;
+    z-index: 1000;
+    .layer-content {
+        width: 90%;
+        height: 80%;
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+    }
+    .close-btn {
+        position: absolute;
+        top: 20px;
+        right: 20px;
+        cursor: pointer;
+        z-index: 100;
+        i {
+            color: #fff;
+            font-size: 24px;
+        }
+    }
+}
 .aside-item {
     padding: 20px;
     box-sizing: border-box;
     line-height: 28px;
+    position: relative;
     .content-item {
         margin-bottom: 20px;
+        &:last-of-type {
+            margin-bottom: 0;
+        }
     }
     &.left-item {
         width: 400px;
@@ -155,6 +226,22 @@ onBeforeUnmount(() => {})
             opacity: 1;
             border: 1px solid rgba(255, 255, 255, 0.2);
             position: relative;
+            &.zoom-in {
+                cursor: zoom-in;
+            }
+            &.nor {
+                position: relative;
+                &::after {
+                    content: '';
+                    position: absolute;
+                    width: 100%;
+                    height: 100%;
+                    z-index: 10;
+                    opacity: 0;
+                    top: 0;
+                    left: 0;
+                }
+            }
             // margin-bottom: 30px;
             // &.zoom-in {
             //     cursor: zoom-in;

+ 106 - 80
src/components/files/index.vue

@@ -1,14 +1,14 @@
 <template>
     <transition name="slide-right" mode="in-out">
-        <div class="files" v-if="showFiles">
-            <div class="info">
+        <div class="files" v-if="showFiles && !isEdit">
+            <div class="info" ref="add$">
                 <button @click="onAdd">添加标注</button>
                 <div>
                     已添加的标注(<span>{{ tags.length }}</span
                     >)
                 </div>
             </div>
-            <div class="list">
+            <div class="list" :style="`height:${listStyle};`">
                 <ul>
                     <li v-for="tag in tags" @click="onClick(tag)" :class="{ active: notify?.sid == tag.sid }">
                         <div class="title"><i></i>{{ tag.title }}</div>
@@ -22,7 +22,7 @@
                     </li>
                 </ul>
             </div>
-            <div class="exit">
+            <div class="exit" ref="exit$">
                 <button type="button" @click="emits('exit')">退出</button>
             </div>
         </div>
@@ -35,12 +35,13 @@
     </transition>
 </template>
 <script setup>
-import { ref, inject, watchEffect } from 'vue'
+import { ref, inject, watchEffect, onMounted, nextTick } from 'vue'
 import { http } from '@/utils/request'
 
 let editTag = null
 let tempTag = null
-
+const exit$ = ref(null)
+const add$ = ref(null)
 const props = defineProps(['show'])
 const emits = defineEmits(['add', 'exit'])
 
@@ -48,12 +49,16 @@ const showFiles = ref(false)
 const showToolbar = ref(false)
 const showMoreSid = ref('')
 const tags = inject('tags')
+
 const notify = inject('notify')
 const isEdit = inject('isEdit')
 const onClick = tag => {
     notify.value = tag //{ event: 'focus', sid: props.tag.sid, tag: props.tag }
 }
+let isAdd = false
 const onAdd = () => {
+    isEdit.value = true
+    isAdd = true
     if (window.kankan) {
         window.kankan.TagManager.editor.then(editor => {
             editor.enter()
@@ -61,64 +66,63 @@ const onAdd = () => {
             showToolbar.value = true
         })
     } else {
-        window.laser.then(sdk => {
-            sdk.addMouseDownEvent(e => {
-                if (showToolbar.value == false) {
-                    return
-                }
-                if (e.button == 2) {
-                    const info3d = sdk.scene.getPointByScreen({ x: e.clientX, y: e.clientY })
-                    const info2d = sdk.scene.getScreenByPoint(info3d.position)
-                    if (editTag) {
-                        tempTag = {}
-                        tempTag.x = editTag.x
-                        tempTag.y = editTag.y
-                        tempTag.visible = editTag.visible
-                        tempTag.position = new THREE.Vector3(editTag.position.x, editTag.position.y, editTag.position.z)
+        laserPosition()
+        showFiles.value = false
+        showToolbar.value = true
+    }
+}
+const laserPosition = () => {
+    window.laser.then(sdk => {
+        sdk.addMouseDownEvent(e => {
+            if (showToolbar.value == false) {
+                return
+            }
+            if (e.button == 2) {
+                const info3d = sdk.scene.getPointByScreen({ x: e.clientX, y: e.clientY })
+                const info2d = sdk.scene.getScreenByPoint(info3d.position)
+                if (editTag) {
+                    tempTag = {}
+                    tempTag.x = editTag.x
+                    tempTag.y = editTag.y
+                    tempTag.visible = editTag.visible
+                    tempTag.position = new THREE.Vector3(editTag.position.x, editTag.position.y, editTag.position.z)
 
-                        editTag.x = info2d.pos.x
-                        editTag.y = info2d.pos.y
-                        editTag.visible = info2d.trueSide
-                        editTag.position = info3d.position
-                    } else {
-                        const tag = {
-                            panoId: '0',
-                            createTime: 1658223413176,
-                            icon: 'R7PTZK233714.png',
-                            x: info2d.pos.x,
-                            y: info2d.pos.y,
-                            position: info3d.position,
-                            media: { link: [{ src: 'https://test.4dkankan.com/#/scene' }] },
-                            type: 'link',
-                            title: '链接热点',
-                            content: '',
-                            sid: Date.now().toString(),
-                            visible: info2d.trueSide,
-                            __temp: true,
-                        }
-                        let index = tags.value.findIndex(item => item.__temp == true)
-                        if (index != -1) {
-                            tags.value.splice(index, 1)
-                        }
-                        tags.value.push(tag)
+                    editTag.x = info2d.pos.x
+                    editTag.y = info2d.pos.y
+                    editTag.visible = info2d.trueSide
+                    editTag.position = info3d.position
+                } else {
+                    const tag = {
+                        panoId: null,
+                        createTime: Date.now(),
+                        icon: '',
+                        x: info2d.pos.x,
+                        y: info2d.pos.y,
+                        position: info3d.position,
+                        media: null,
+                        type: '',
+                        title: '',
+                        content: '',
+                        sid: Date.now().toString(),
+                        visible: info2d.trueSide,
+                        __temp: true,
+                    }
+                    let index = tags.value.findIndex(item => item.__temp == true)
+                    if (index != -1) {
+                        tags.value.splice(index, 1)
                     }
+                    tags.value.push(tag)
                 }
-            })
+            }
         })
-
-        showFiles.value = false
-        showToolbar.value = true
-    }
+    })
 }
 const onAddCancel = () => {
     showFiles.value = true
     showToolbar.value = false
+    isAdd = false
     if (window.kankan) {
-        if (isEdit.value) {
-            kankan.TagManager.edit.exit()
-        } else {
-            kankan.TagManager.editor.then(editor => editor.exit())
-        }
+        kankan.TagManager.editor.then(editor => editor.exit())
     } else {
         let index = tags.value.findIndex(item => item.__temp == true)
         if (index != -1) {
@@ -141,31 +145,39 @@ const onAddConfirm = () => {
     showFiles.value = true
     showToolbar.value = false
     if (window.kankan) {
-        const tag = kankan.TagManager.edit.confirm()
-        console.log(tag)
-        if (tag) {
-            if (!isEdit.value) {
+        kankan.TagManager.editor.then(editor => {
+            var tag = editor.confirm()
+            if (tag) {
                 tag.icon = ''
-                tag.__temp = true
-                tags.value.push(tag)
+                if (isAdd) {
+                    tag.__temp = true
+                    tag._add = true
+                    isAdd = false
+                }
+                // tags.value.push(tag)
+                notify.value = tag
+                isEdit.value = true
             }
-            notify.value = tag
-        }
-        // kankan.TagManager.editor.then(editor => {
-        //     var tag = editor.confirm()
-
-        //     if (tag) {
-        //         tag.icon = ''
-        //         tag.__temp = true
-        //         tags.value.push(tag)
-        //         notify.value = tag
-        //         isEdit.value = true
-        //     }
-        // })
+        })
     } else {
-        let tag = tags.value.find(item => item.__temp == true)
+        let tag
+        if (isAdd) {
+            tag = tags.value.find(item => item.__temp == true)
+        } else {
+            tag = editTag
+        }
         if (tag) {
-            delete tag.__temp
+            // delete tag.__temp
+
+            if (tag) {
+                if (isAdd) {
+                    tag.__temp = true
+                    // tag._add = true
+                    isAdd = false
+                }
+
+                notify.value = tag
+            }
         }
     }
     editTag = null
@@ -185,11 +197,16 @@ const onMoreHandler = (type, tag) => {
         showFiles.value = false
         showToolbar.value = true
         isEdit.value = true
-        // window.kankan.TagManager.editor.then(editor => {
-        //     editor.enter()
-        // })
 
-        window.kankan.TagManager.focusBeforeModify(editTag.sid)
+        if (window.kankan) {
+            window.kankan.TagManager.focusTag(editTag.sid)
+            window.kankan.TagManager.editor.then(editor => {
+                editor.enter(editTag)
+            })
+        } else {
+            laserPosition()
+        }
+        // window.kankan.TagManager.focusBeforeModify(editTag.sid)
     } else if (type == 'delete') {
         http.post(`smart-site/marking/del`, {
             markingId: tag.id,
@@ -207,9 +224,17 @@ const onMoreHandler = (type, tag) => {
 const onTagFocus = tag => {
     //tid.
 }
+const listStyle = ref('')
 watchEffect(() => {
     showFiles.value = props.show
+    if (showFiles.value && !isEdit.value) {
+        nextTick(() => {
+            listStyle.value = `calc(100vh - ${add$.value.offsetHeight + exit$.value.offsetHeight + 60}px)`
+        })
+    }
 })
+
+onMounted(() => {})
 </script>
 <style lang="scss" scoped>
 button {
@@ -267,6 +292,7 @@ button {
     }
     .list {
         flex: 1;
+        overflow-y: auto;
     }
     .exit {
         padding: 0 10px;

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

@@ -11,7 +11,7 @@
         <div class="tips">支持 mp3/wav 文件:≤ 5MB</div>
         <input ref="file" type="file" style="display: none" accept=".mp3, .wav" @change="onChange" />
     </div>
-    <div class="del-btn" v-if="notify.media?.[notify.type]?.length" @click="delMedia">
+    <div class="del-btn" v-if="isEdit && notify.media?.[notify.type]?.length" @click="delMedia">
         <i class="iconfont icon-del"></i>
     </div>
 </template>
@@ -19,6 +19,7 @@
 import { ref, onMounted, inject } from 'vue'
 import { checkSizeLimitFree, base64ToDataURL } from '@/utils/file'
 const notify = inject('notify')
+const isEdit = inject('isEdit')
 const emits = defineEmits(['tips'])
 const file = ref(null)
 const media = ref([])

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

@@ -1,8 +1,8 @@
 <template>
     <div class="media" v-show="url">
         <iframe v-if="url" :src="url" frameborder="0"></iframe>
-        <div class="delete" @click.stop="onDelete"><i class="iconfont icon-delete"></i></div>
-        <div class="link">{{ url }}</div>
+        <div v-if="isEdit" class="delete" @click.stop="onDelete"><i class="iconfont icon-delete"></i></div>
+        <div class="link" v-if="isEdit">{{ url }}</div>
     </div>
     <div class="placeholder" v-show="url == null">
         <div class="icon">
@@ -18,6 +18,7 @@
 import { ref, onMounted, inject, computed } from 'vue'
 const emits = defineEmits(['tips'])
 const notify = inject('notify')
+const isEdit = inject('isEdit')
 const url = ref(null)
 const href = ref('')
 const onDelete = () => {

+ 1 - 0
src/components/header/Login.vue

@@ -85,6 +85,7 @@ const onLogin = () => {
                     localStorage.removeItem('userId')
                 }
                 localStorage.setItem('token', response.data.token)
+                localStorage.setItem('userId', response.data.user.id)
                 emits('user', {
                     head: response.data.user.head,
                     nickName: response.data.user.nickName,

+ 16 - 11
src/components/header/index.vue

@@ -11,9 +11,7 @@
         <div class="user">
             <ul>
                 <li>
-                    <i
-                        @click="showLink = true;showCopyDone = false" class="iconfont icon-share"
-                    ></i>
+                    <i @click="openLink" class="iconfont icon-share"></i>
                 </li>
                 <li><em></em></li>
                 <li v-if="user" class="uinfo" @click="showDrop = true">
@@ -41,8 +39,8 @@
     <Toast v-if="showCopyDone" content="复制成功" />
     <Toast v-if="showTips" :content="showTips" :close="() => (showTips = null)" />
     <Login v-if="showLogin" @close="showLogin = false" @user="info => (user = info)" />
-    <Loading v-if="showLoading"/>
-    <CopyLink v-if="showLink" @close="showLink = false" @done="showCopyDone = true;showLink = false" />
+    <Loading v-if="showLoading" />
+    <CopyLink v-if="showLink" @close="showLink = false" @done="done" />
 </template>
 <script setup>
 import { ref, defineProps, onMounted, watchEffect } from 'vue'
@@ -67,7 +65,14 @@ const showLoading = ref(false)
 const showLogin = ref(false)
 const showCopyDone = ref(false)
 const showTips = ref(null)
-
+const done = () => {
+    showCopyDone.value = true
+    showLink.value = false
+}
+const openLink = () => {
+    showLink.value = true
+    showCopyDone.value = false
+}
 const getCurPosInfo = () => {
     let app = sync.sourceInst
     let id
@@ -83,8 +88,8 @@ const getCurPosInfo = () => {
 
 const onSetP1 = () => {
     let p1 = getCurPosInfo()
-    if(points.value.p2 && points.value.p2.id == p1.id){
-        return  showTips.value = '匹配失败,请选择不同点位进行同步'
+    if (points.value.p2 && points.value.p2.id == p1.id) {
+        return (showTips.value = '匹配失败,请选择不同点位进行同步')
     }
     if (points.value.p1) {
         showTips.value = '关联位置已更新'
@@ -94,8 +99,8 @@ const onSetP1 = () => {
 }
 const onSetP2 = () => {
     let p2 = getCurPosInfo()
-    if(points.value.p1 && points.value.p1.id == p2.id){
-        return  showTips.value = '匹配失败,请选择不同点位进行同步'
+    if (points.value.p1 && points.value.p1.id == p2.id) {
+        return (showTips.value = '匹配失败,请选择不同点位进行同步')
     }
     if (points.value.p2) {
         showTips.value = '关联位置已更新'
@@ -113,7 +118,6 @@ const getUserInfo = () => {
                     nickName: response.data.nickName,
                 }
                 localStorage.setItem('userId', response.data.id)
-
             } else {
                 if (response.code == 4008) {
                     // 未登录
@@ -132,6 +136,7 @@ const onLogout = () => [
                 localStorage.removeItem('remember')
                 localStorage.removeItem('username')
                 localStorage.removeItem('password')
+                localStorage.removeItem('userId')
                 user.value = null
             }
         })

+ 1 - 0
src/pages/Viewer.vue

@@ -593,6 +593,7 @@ onMounted(() => {
                 item.hotData.visible = false
                 item.hotData.id = item.markingId
                 item.hotData.createTime = item.createTime
+                item.hotData.lastCreateBy = item.lastCreateBy
                 return item.hotData
             })
         }

+ 5 - 1
src/utils/request.js

@@ -183,6 +183,7 @@ export function setup(options = {}) {
     )
     fetch.interceptors.response.use(
         response => {
+        
             // 正常的文件流
             if (!/json/gi.test(response.headers['content-type'])) {
                 return response.data
@@ -194,7 +195,10 @@ export function setup(options = {}) {
                 let res = JSON.parse(enc.decode(new Uint8Array(response.data)))
                 return res
             }
-
+            if(response.code==4008){
+              //用户未登录
+              
+            }
             return response.data
         },
         error => {