cuiqian2004
2025-09-25 9efd4a80aae58cf36266e774d3c820cc8e115028
test
13个文件已修改
1635 ■■■■■ 已修改文件
App.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/request.js 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/vehicle.js 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/index/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/map/index.vue 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/map/infos/scene-create.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/map/js/ctx.js 966 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/map/scene.vue 297 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/map/task.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/my/help-feedback.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/my/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue
@@ -15,7 +15,8 @@
                min_y: 0,
                img_x: 1,
                img_y: 1,
            }
            },
            withLog:false
        },
        onLaunch: function() {
            console.log('App Launch')
api/request.js
@@ -9,26 +9,73 @@
        // },
        method: "GET",
        dataType: "json",
    },
    apiCount: {
        agv_state: 0,
        laser_data: 0,
    },
    addLog(item) {
        const list = session.getValue("request_log") || []
        const res = typeof item.data == 'string' ? item.data : JSON.stringify(item.data)
        if (res.length > 64 * 1024) {
            // const maxData = session.getValue("request_log_max_data") || {}
            // const key = `data${new Date().getTime()}`
            // maxData[key] = item.data
            // item.data_key = key
            // delete item.data
            // session.setValue("request_log_max_data", maxData)
             item.data  = res.length
        }
        if(item.url.indexOf("get_agv_state") > 0)
        {
        if (!getApp().globalData.withLog) {
            return
        }
        if (item.statusCode == 200) {
            const ret = JSON.parse(item.data)
            if (ret.code == 0) {
                if (item.url.indexOf("get_agv_state") > 0) {
                    if (this.apiCount.agv_state % 20 != 0) {
                        this.apiCount.agv_state++;
                        return
                    }
                    this.apiCount.agv_state = 1;
                }
                if (item.url.indexOf("laser_data") > 0) {
                    if (ret.data?.base_map?.image_base64) {
                        this.apiCount.laser_data++;
                    } else {
                        if (this.apiCount.laser_data % 20 != 0) {
                            this.apiCount.laser_data++;
                            return
                        }
                        this.apiCount.laser_data = 1;
                    }
                }
                const res = ret.data
                if (res) {
                    if (item.url.indexOf("getMapUrl") > 0) {
                        if (res.filedata) {
                            res.data_length = res.filedata.length
                            res.filedata = "..."
                            item.data = JSON.stringify(ret)
                        }
                    } else if (item.url.indexOf("laser_data") > 0) {
                        if (res?.base_map?.image_base64) {
                            res.base_map.image_length = res.base_map.image_base64.length
                            res.base_map.image_base64 = "..."
                            item.data = JSON.stringify(ret)
                        }
                    }
                    if (JSON.stringify(res).length > 100 * 1024) {
                        // const maxData = session.getValue("request_log_max_data") || {}
                        // const key = `data${new Date().getTime()}`
                        // maxData[key] = item.data
                        // item.data_key = key
                        // session.setValue("request_log_max_data", maxData)
                        ret.data = "..."
                        ret.data_length = JSON.stringify(res).length
                        item.data = JSON.stringify(ret)
                    }
                }
            }
        }
        const list = session.getValue("request_log") || []
        list.unshift(item)
        if (list.length > 512) {
            const oldItem = list.pop()
            if (oldItem.data_key) {
@@ -47,9 +94,9 @@
        };
        options.method = options.method || this.common.method;
        options.dataType = options.dataType || this.common.dataType;
        if(options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0)
        {
        if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0 && options.url.indexOf(
                "taskGroupStatus") < 0) {
            console.log("url", options.url, options.data)
        }
        return new Promise((resolve, reject) => {
@@ -61,11 +108,11 @@
                method: options.method,
                dataType: options.dataType,
                success: (result) => {
                    if(options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0)
                    {
                    if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf(
                            "laser_data") < 0) { //
                        console.log("result", result)
                    }
                    const now = new Date()
                    const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
                    if (result.statusCode != 200) {
@@ -87,14 +134,7 @@
                            msg: "访问失败,状态码:" + result.statusCode
                        })
                    }
                    this.addLog({
                        date,
                        method: `${options.method || ""}`,
                        url: options.url,
                        param: options.data,
                        statusCode: result.statusCode,
                        data: result.data
                    })
                    var ret = result.data
                    if (typeof ret == 'string') {
                        try {
@@ -116,13 +156,21 @@
                            })
                        }
                    }
                    this.addLog({
                        date,
                        method: `${options.method || ""}`,
                        url: options.url,
                        param: options.data,
                        statusCode: result.statusCode,
                        data: JSON.stringify(ret)
                    })
                    if (ret.code == 0) {
                        resolve(ret.data);
                    } else {
                        reject({
                            msg: ret.msg || ret.message|| ""
                            msg: ret.msg || ret.message || ""
                        });
                    }
                },
api/vehicle.js
@@ -893,4 +893,85 @@
        data: {data}
    })
}
}
/**
 * GET 5000/api/check_mapserver_is_start
 * 判断构图程序是否启动
 * @param
 * @returns
 */
export const checkMapServerIsStart = (ip) => {
    var header = {
        "Content-Type": "application/json;charset=UTF-8"
    };
    var url = `http://${ip}:5000/api/check_mapserver_is_start/`;
    return http.request({
        method: "GET",
        url,
        header,
    })
}
/**
 * POST 5000/api/start_or_stop_mapserver
 * 开启或者关闭构图程序: 1开启2关闭
 * @param  status 1开启2关闭
 * @returns
 */
export const startOrStopMapServer = (ip, status) => {
    var header = {
        "Content-Type": "application/json;charset=UTF-8"
    };
    var url = `http://${ip}:5000/api/start_or_stop_mapserver/`;
    return http.request({
        method: "POST",
        url,
        header,
        data: {status}
    })
}
 /**
  * GET 5000/api/check_mapserver_is_ok
  * 构图程序是否准备到位
  * @param
  * @returns
  */
 export const checkMapServerIsOk = (ip) => {
     var header = {
         "Content-Type": "application/json;charset=UTF-8"
     };
     var url = `http://${ip}:5000/api/check_mapserver_is_ok/`;
     return http.request({
         method: "GET",
         url,
         header,
     })
 }
 /**
  * GET 5000/api/check_save_map_is_ok
  * 结束构图是否保存完成
  * @param
  * @returns
  */
 export const checkSaveMapIsOk = (ip) => {
     var header = {
         "Content-Type": "application/json;charset=UTF-8"
     };
     var url = `http://${ip}:5000/api/check_save_map_is_ok/`;
     return http.request({
         method: "GET",
         url,
         header,
     })
 }
manifest.json
@@ -2,8 +2,8 @@
    "name" : "ES-GO",
    "appid" : "__UNI__C988375",
    "description" : "",
    "versionName" : "1.2.9",
    "versionCode" : 129,
    "versionName" : "1.3.2",
    "versionCode" : 132,
    "transformPx" : false,
    /* 5+App特有相关 */
    "app-plus" : {
pages/index/index.vue
@@ -877,6 +877,8 @@
            margin-top: 150rpx;
            margin-left: calc(750rpx - 330rpx);
            width: 320rpx;
            max-height: 50vh;
            overflow: auto;
            align-items: center;
            justify-content: center;
            flex-direction: column;
pages/login/index.vue
@@ -188,7 +188,7 @@
                        password: ""
                    })
                }
                getApp().globalData.withLog  =     session.getValue("write_log") ? true:false
                uni.reLaunch({
                    url: "/pages/index/index"
                })
pages/map/index.vue
@@ -33,7 +33,9 @@
                v-model:opSceneType="opSceneType" @create-ok="onCreateSceneOk"></SceneCreateInfo> -->
            <view class="map-content" v-show="mapOperationType !='scene_create'">
                <view class="content">
                    <view class="fabric" :message="ctxDataStr" :change:message="ctx.receiveMsg" id="canvasMap"></view>
                    <view ref="canvasCtx" class="fabric" id="canvasMap" :message="ctxDataStr"
                        :change:message="ctx.receiveMsg">
                    </view>
                    <view class="loading-overlay" v-if="bgLoading">
                        <view class="loading-content">
                            <view class="loading-spinner"></view>
@@ -386,9 +388,10 @@
    </view>
</template>
<script src="./js/ctx.js" module="ctx" lang="renderjs"></script>
<script>
    import {
        ref
    } from "vue";
    import {
        showToast,
        showModal,
@@ -440,6 +443,7 @@
                    list: []
                },
                sceneList: [],
                ctxDataStr: "[]",
                mapOperationType: "",
                mapOperationStatus: "",
@@ -482,7 +486,9 @@
                    img_x: 1,
                    img_y: 1
                },
                positioningAgv: false
                positioningAgv: false,
                isPageVisible: true,
                destroyFlag: false,
            }
        },
        computed: {
@@ -539,10 +545,29 @@
        },
        onHide() {
            this.isPageVisible = false
        },
        onUnload() {
        async onUnload() {
            this.isPageVisible = false
            console.log("onUnload")
        },
        onBackPress() {
                this.isPageVisible = false
            if (this.destroyFlag)
                return false
            else {
                this.ctxDataStr = JSON.stringify([{
                    method: "destroy",
                }])
                return true
            }
        },
        methods: {
            setData(obj) {
                let that = this;
@@ -583,6 +608,10 @@
            },
            clickShowMenu() {
                // const list = [...this.sceneList]
                // for(let i =0; i < 20;i++){
                //     list.push("test" + i)
                // }
                this.menuPopup = {
                    type: "scene",
                    list: this.sceneList,
@@ -786,7 +815,7 @@
                        // this.mapOperationType = 'scene_create'
                        // this.opSceneType = 'add_name'
                        uni.navigateTo({
                            url: `/pages/map/scene?ip=${this.vehicleIp}`,
                            url: `/pages/map/scene?ip=${_this.vehicleIp}`,
                            events: {
                                // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
                                create_finish: function(data) {
@@ -910,12 +939,18 @@
                        },
                        {
                            method: "public_teaching_path",
                            param: this.teachingMode.Public || []
                            param: {
                                list: this.teachingMode.Public || [],
                                show: this.showTeachingPathFlag
                            }
                        },
                        {
                            method: "station_teaching_path",
                            param: this.teachingMode.Stations || []
                            param: {
                                list: this.teachingMode.Stations || [],
                                show: this.showTeachingPathFlag
                            }
                        },
                        {
@@ -939,7 +974,14 @@
            receiveRenderData(param) {
                console.log('接收到视图层的数据:', param);
                if (param.method == "set_backgroud_progress") {
                if (param.method === "destroy_complete") {
                    if (param.param) {
                        this.destroyFlag = true
                        uni.navigateBack({
                            delta: 1
                        })
                    }
                } else if (param.method == "set_backgroud_progress") {
                    if (param.type == "start") {
                        this.setData({
                            bgProgressPercent: 50,
@@ -1129,6 +1171,7 @@
                this.mapOperationType = "edit_map"
            },
            clickExtendMap() {
                const _this = this
                uni.navigateTo({
                    url: `/pages/map/scene?ip=${this.vehicleIp}&opType=extend&sceneId=${this.sceneId}`,
                    events: {
@@ -1170,6 +1213,7 @@
            },
            async loadSceneList() {
                try {
                    uni.showLoading({
                        title: "加载场景中"
                    })
@@ -1846,26 +1890,40 @@
                    }
                }
            },
            async reloadTeachingMode() {
            async reloadTeachingMode(finish) {
                try {
                    this.teachingMode = await this.loadTeachingMode()
                    this.ctxDataStr = JSON.stringify([{
                    const list = [{
                            method: "clear_teaching_path",
                        },
                        {
                            method: "public_teaching_path",
                            param: this.teachingMode.Public || []
                            param: {
                                list: this.teachingMode.Public || [],
                                show: this.showTeachingPathFlag
                            }
                        },
                        {
                            method: "station_teaching_path",
                            param: this.teachingMode.Stations || []
                        }, {
                            method: "show_teaching_path",
                            param: {
                                list: this.teachingMode.Stations || [],
                                show: this.showTeachingPathFlag
                            }
                        }
                    ])
                        // , {
                        //     method: "show_teaching_path",
                        //     param: {
                        //         show: this.showTeachingPathFlag
                        //     }
                        // }
                    ]
                    if (finish) {
                        list.push({
                            method: "teaching_finish",
                        })
                    }
                    this.ctxDataStr = JSON.stringify(list)
                } catch (ex) {
                    showError(ex)
@@ -1939,8 +1997,8 @@
                                param: {
                                    x: agv.x,
                                    y: agv.y,
                                    width:80,
                                    height:80,
                                    width: 80,
                                    height: 80,
                                }
                            })
@@ -2049,10 +2107,8 @@
            },
            clickTeachingFinish() {
                this.mapOperationType = ""
                this.ctxDataStr = JSON.stringify([{
                    method: "teaching_finish",
                }])
                this.reloadTeachingMode()
                this.reloadTeachingMode(true)
            },
            async teachingStart(mode) {
@@ -2155,24 +2211,26 @@
                            )
                        if (res?.name)
                            this.teachingModeCur.name = res.name
                            const {
                                data
                            } = await getTeachingMode(this.vehicleIp)
                            const publicList = data.Public || []
                            const curIndex = publicList.findIndex((a)=>a.name == oldName)
                            if(curIndex > -1)
                            {
                                this.ctxDataStr = JSON.stringify([{
                                    method: "public_teaching_path",
                                    param: [publicList[curIndex]]
                                } ])
                            }
                        const {
                            data
                        } = await getTeachingMode(this.vehicleIp)
                        const publicList = data.Public || []
                        const curIndex = publicList.findIndex((a) => a.name == oldName)
                        if (curIndex > -1) {
                            this.ctxDataStr = JSON.stringify([{
                                method: "public_teaching_path",
                                param: {
                                    list: [publicList[curIndex]],
                                    show: true
                                }
                            }])
                        }
                    } else {
                        this.teachingModeCur
                            .main_road =
@@ -2705,6 +2763,8 @@
            margin-top: 140rpx;
            margin-left: 225rpx;
            width: 300rpx;
            max-height: 50vh;
            overflow: auto;
            align-items: center;
            justify-content: center;
            flex-direction: column;
pages/map/infos/scene-create.vue
@@ -102,6 +102,7 @@
        computed: {
        },
        mounted() {
            const _this = this
            uni.getSystemInfo({
pages/map/js/ctx.js
@@ -31,7 +31,6 @@
export default {
    data() {
        return {
            vehicleIp: "",
            canvasId: "",
            canvas: null,
            eleWidth: 0,
@@ -64,16 +63,17 @@
                img_y: 1
            },
            pressObjTimer: 0,
            selectable: true,
        }
    },
    mounted() {
        console.log("ctx mounted")
        this.init()
    },
    methods: {
        async init(ip) {
            try {
                this.agvObj = null
                const _this = this
                fabric.Object.prototype.setControlsVisibility({
                    mt: false, // 中间上
@@ -107,6 +107,7 @@
                canvas.setAttribute("id", this.canvasId)
                canvas.setAttribute("type", "2d")
                cantainerEl.appendChild(canvas)
                this.canvas = new fabric.Canvas(this.canvasId, {
                    allowTouchScrolling: true, // 允许触摸滚动
                    selection: true,
@@ -120,6 +121,8 @@
                    renderOnAddRemove: false,
                    imageSmoothingEnabled: true
                })
                this.canvas.clear()
                this.eleWidth = cantainerEl.clientWidth
                this.eleHeight = cantainerEl.clientHeight
@@ -144,6 +147,28 @@
            } catch (ex) {
                this.showError(ex)
            }
        },
        destroyCanvas() {
            console.log("destroyCanvas")
            if (this.canvas) {
                // 2. 移除所有事件监听器
                this.removeAllEventListeners();
                // 3. 清空所有对象(分批处理避免阻塞)
                this.clearObjects();
                // 4. 销毁Fabric.js实例
                this.canvas.dispose();
                // 6. 清除所有变量引用
                this.cleanupReferences();
                this.canvas = null
                // 通知Vue层销毁完成
                this.$ownerInstance.callMethod('receiveRenderData', {
                    method: "destroy_complete",
                    param: true
                });
            }
        },
        patchFabricForUniApp(canvas) {
@@ -425,177 +450,12 @@
            cantainerEl.addEventListener('touchstart', function(e) {
                //        console.log('touchstart:', e.touches.length);
                e.preventDefault(); // 阻止默认行为
                _this.canvas.fire('touch:start', {
                    e: e
                });
                _this.canvasTouchStart(e)
                //    _this.canvas._onMouseDown(e);
                _this.pointerSelectObject(e)
                if (!_this.canvas.getActiveObject()) {
                    // 根据触摸点数量判断交互类型
                    if (e.touches.length === 1) {
                        _this.handleSingleTouch(e.touches[0]);
                    } else if (e.touches.length >= 2) {
                        _this.handleMultiTouch(e.touches);
                    }
                } else {
                    if (e.touches.length === 1) {
                        const touch = e.touches[0]
                        _this.lastPosX = touch.clientX;
                        _this.lastPosY = touch.clientY;
                        const list = _this.canvas.getActiveObjects()
                        if (list.length === 1) {
                            if (!_this.objEditing) {
                                this.pressObjTimer = setTimeout(function() {
                                    const zoom = _this.canvas.getZoom();
                                    const vpt = _this.canvas.viewportTransform;
                                    if (list[0].eleType == "station") {
                                        _this.setAllObjectSelectable(false)
                                        list[0].set({
                                            selectable: true
                                        })
                                        let deltaX = list[0].left * zoom
                                        let deltaY = list[0].top * zoom
                                        const scale = zoom < 1 ? zoom : 1
                                        _this.$ownerInstance.callMethod('receiveRenderData', {
                                            method: "edit_station",
                                            station: list[0].data,
                                            view: {
                                                x: vpt[4] + deltaX,
                                                y: vpt[5] + deltaY,
                                                // x: e.touches[0].clientX,
                                                // y: e.touches[0].clientY,
                                                width: list[0].width * scale,
                                                height: list[0].height * scale
                                            }
                                        });
                                    } else if (list[0].eleType == "public_teaching") {
                                        const pt = _this.canvas.getPointer(touch); // ← 关键
                                        // 2. pointer 就是画布坐标
                                        _this.$ownerInstance.callMethod('receiveRenderData', {
                                            method: "select_teaching_path",
                                            data: list[0].data,
                                            type: "public",
                                            point: pt
                                        });
                                    } else if (list[0].eleType == "station_teaching") {
                                        _this.$ownerInstance.callMethod('receiveRenderData', {
                                            method: "select_teaching_path",
                                            data: list[0].data,
                                            type: "station",
                                            point: pt
                                        });
                                    }
                                }, 1000); //
                            }
                            let activeObj = list[0]
                            if (activeObj.eleType == "region_pt_add") {
                                const data = activeObj.mainObj?.data
                                data.path.push({
                                    x: _this.getActualXFromImg(activeObj.left) || 0,
                                    y: _this.getActualYFromImg(activeObj.top) || 0
                                })
                                _this.updateRegion(activeObj.mainObj, data)
                            } else if (activeObj.eleType == "cmd") {
                                let data = activeObj.mainObj.data
                                const objCmdMain = activeObj.mainObj
                                if (objCmdMain.eleType == "edit_teaching") {
                                    const left = _this.getActualXFromImg(objCmdMain.left)
                                    const top = _this.getActualYFromImg(objCmdMain.top)
                                    const right = _this.getActualXFromImg(objCmdMain.left + objCmdMain.width)
                                    const bottom = _this.getActualYFromImg(objCmdMain.top + objCmdMain.height)
                                    data = []
                                    if (Number.isNaN(left) || Number.isNaN(top) || Number.isNaN(right) || Number
                                        .isNaN(bottom)) {
                                        const now = new Date()
                                        const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
                                        _this.$ownerInstance.callMethod('receiveRenderData', {
                                            method: "show_log",
                                            data: {
                                                date,
                                                method: `POST`,
                                                url: "app/log/edit_teaching",
                                                param: objCmdMain,
                                                statusCode: 100,
                                                data: _this.mapInfo
                                            },
                                        });
                                    }
                                    data.push([left, top])
                                    data.push([left, bottom])
                                    data.push([right, bottom])
                                    data.push([right, top])
                                    console.log(data)
                                }
                                _this.$ownerInstance.callMethod('receiveRenderData', {
                                    method: "edit_finish",
                                    cmd: activeObj.id,
                                    type: objCmdMain.eleType,
                                    data: data,
                                });
                                if (activeObj.id == "ok") {
                                    if (objCmdMain.eleType == "region") {
                                        _this.addRegionFinish(activeObj.mainObj)
                                    } else if (activeObj.mainObj.eleType == "virtual_wall") {
                                        _this.addVirtualWallFinish(activeObj.mainObj)
                                    }
                                }
                                if (objCmdMain.eleType == "edit_teaching") {
                                    let list = _this.canvas.getObjects() || []
                                    for (let i2 in list) {
                                        const obj = list[i2]
                                        obj.set({
                                            opacity: 1
                                        })
                                    }
                                    if (objCmdMain?.mainObj) {
                                        objCmdMain.mainObj.set({
                                            selectable: true
                                        })
                                    }
                                    const ptObjs = objCmdMain.ptObjs || []
                                    _this.canvas.remove(objCmdMain)
                                    for (let i2 in ptObjs) {
                                        const obj = ptObjs[i2]
                                        _this.canvas.remove(obj)
                                    }
                                }
                                if (objCmdMain.eleType == "station") {
                                    _this.setAllObjectSelectable(true)
                                    objCmdMain.tipObj.set({
                                        left: objCmdMain.left,
                                        top: objCmdMain.top - objCmdMain.height / 2 -
                                            objCmdMain.tipObj.height / 2,
                                        visible: true
                                    })
                                    objCmdMain.tipObj.setCoords()
                                }
                                _this.closeOkCancelControl()
                                _this.canvas.requestRenderAll()
                            }
                            // else if (activeObj.eleType == "station") {
                            //     _this.$ownerInstance.callMethod('receiveRenderData', {
                            //         method: "select_station",
                            //         data: activeObj.data,
                            //         select: activeObj.mark ? false : true
                            //     });
                            // }
                        }
                    } else if (e.touches.length >= 2) {
                        _this.handleMultiTouch(e.touches);
                    }
                }
            });
            cantainerEl.addEventListener('touchmove', function(e) {
@@ -603,33 +463,7 @@
                //    console.log('touchmove:', e.touches.length);
                e.preventDefault(); // 阻止默认行为
                // 处理移动
                const list = _this.canvas.getActiveObjects()
                if (list.length == 0) {
                    if (e.touches.length === 1) {
                        _this.handleSingleTouchMove(e.touches[0]);
                    } else if (e.touches.length >= 2) {
                        _this.handleMultiTouchMove(e.touches);
                    }
                } else {
                    if (e.touches.length === 1) {
                        if (list.length > 1 || list[0].lockMovementX) {
                            if (list[0].lockMovementX) {
                                _this.canvas.discardActiveObject();
                            }
                            _this.handleSingleTouchMove(e.touches[0]);
                        } else {
                            const touch = e.touches[0]
                            const deltaX = touch.clientX - this.lastPosX;
                            const deltaY = touch.clientY - this.lastPosY;
                            if (Math.abs(deltaX) > 20)
                                clearTimeout(this.pressObjTimer);
                        }
                    } else if (e.touches.length >= 2) {
                        _this.canvas.discardActiveObject();
                        _this.handleMultiTouchMove(e.touches);
                    }
                }
                _this.canvasTouchMove(e)
            });
            cantainerEl.addEventListener('touchend', function(e) {
@@ -665,13 +499,210 @@
                    clearTimeout(this.pressObjTimer);
                    this.pressObjTimer = null
                }
                // const activeObj = _this.canvas.getActiveObject()
                // if (activeObj) {
                //     if (activeObj.lockMovementX) {
                //         _this.canvas.discardActiveObject();
                //     }
                // }
            })
        },
        canvasTouchStart(e) {
            const _this = this
            _this.pointerSelectObject(e)
            if (!_this.canvas.getActiveObject()) {
                // 根据触摸点数量判断交互类型
                if (e.touches.length === 1) {
                    _this.handleSingleTouch(e.touches[0]);
                } else if (e.touches.length >= 2) {
                    _this.handleMultiTouch(e.touches);
                }
            } else {
                if (e.touches.length === 1) {
                    const touch = e.touches[0]
                    _this.lastPosX = touch.clientX;
                    _this.lastPosY = touch.clientY;
                    const list = _this.canvas.getActiveObjects()
                    if (list.length === 1) {
                        if (!_this.objEditing) {
                            this.pressObjTimer = setTimeout(function() {
                                const zoom = _this.canvas.getZoom();
                                const vpt = _this.canvas.viewportTransform;
                                if (list[0].eleType == "station") {
                                    _this.setAllObjectSelectable(false)
                                    list[0].set({
                                        selectable: true
                                    })
                                    let deltaX = list[0].left * zoom
                                    let deltaY = list[0].top * zoom
                                    const scale = zoom < 1 ? zoom : 1
                                    _this.$ownerInstance.callMethod('receiveRenderData', {
                                        method: "edit_station",
                                        station: list[0].data,
                                        view: {
                                            x: vpt[4] + deltaX,
                                            y: vpt[5] + deltaY,
                                            // x: e.touches[0].clientX,
                                            // y: e.touches[0].clientY,
                                            width: list[0].width * scale,
                                            height: list[0].height * scale
                                        }
                                    });
                                } else if (list[0].eleType == "public_teaching") {
                                    const pt = _this.canvas.getPointer(touch); // ← 关键
                                    // 2. pointer 就是画布坐标
                                    _this.$ownerInstance.callMethod('receiveRenderData', {
                                        method: "select_teaching_path",
                                        data: list[0].data,
                                        type: "public",
                                        point: pt
                                    });
                                } else if (list[0].eleType == "station_teaching") {
                                    _this.$ownerInstance.callMethod('receiveRenderData', {
                                        method: "select_teaching_path",
                                        data: list[0].data,
                                        type: "station",
                                        point: pt
                                    });
                                }
                            }, 1000); //
                        }
                        let activeObj = list[0]
                        if (activeObj.eleType == "region_pt_add") {
                            const data = activeObj.mainObj?.data
                            data.path.push({
                                x: _this.getActualXFromImg(activeObj.left) || 0,
                                y: _this.getActualYFromImg(activeObj.top) || 0
                            })
                            _this.updateRegion(activeObj.mainObj, data)
                        } else if (activeObj.eleType == "cmd") {
                            let data = activeObj.mainObj.data
                            const objCmdMain = activeObj.mainObj
                            if (objCmdMain.eleType == "edit_teaching") {
                                const left = _this.getActualXFromImg(objCmdMain.left)
                                const top = _this.getActualYFromImg(objCmdMain.top)
                                const right = _this.getActualXFromImg(objCmdMain.left + objCmdMain.width)
                                const bottom = _this.getActualYFromImg(objCmdMain.top + objCmdMain.height)
                                data = []
                                if (Number.isNaN(left) || Number.isNaN(top) || Number.isNaN(right) || Number
                                    .isNaN(bottom)) {
                                    const now = new Date()
                                    const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
                                    _this.$ownerInstance.callMethod('receiveRenderData', {
                                        method: "show_log",
                                        data: {
                                            date,
                                            method: `POST`,
                                            url: "app/log/edit_teaching",
                                            param: objCmdMain,
                                            statusCode: 100,
                                            data: _this.mapInfo
                                        },
                                    });
                                }
                                data.push([left, top])
                                data.push([left, bottom])
                                data.push([right, bottom])
                                data.push([right, top])
                                console.log(data)
                            }
                            _this.$ownerInstance.callMethod('receiveRenderData', {
                                method: "edit_finish",
                                cmd: activeObj.id,
                                type: objCmdMain.eleType,
                                data: data,
                            });
                            if (activeObj.id == "ok") {
                                if (objCmdMain.eleType == "region") {
                                    _this.addRegionFinish(activeObj.mainObj)
                                } else if (activeObj.mainObj.eleType == "virtual_wall") {
                                    _this.addVirtualWallFinish(activeObj.mainObj)
                                }
                            }
                            if (objCmdMain.eleType == "edit_teaching") {
                                let list = _this.canvas.getObjects() || []
                                for (let i2 in list) {
                                    const obj = list[i2]
                                    obj.set({
                                        opacity: 1
                                    })
                                }
                                if (objCmdMain?.mainObj) {
                                    objCmdMain.mainObj.set({
                                        selectable: true
                                    })
                                }
                                const ptObjs = objCmdMain.ptObjs || []
                                _this.canvas.remove(objCmdMain)
                                for (let i2 in ptObjs) {
                                    const obj = ptObjs[i2]
                                    _this.canvas.remove(obj)
                                }
                            }
                            if (objCmdMain.eleType == "station") {
                                _this.setAllObjectSelectable(true)
                                objCmdMain.tipObj.set({
                                    left: objCmdMain.left,
                                    top: objCmdMain.top - objCmdMain.height / 2 -
                                        objCmdMain.tipObj.height / 2,
                                    visible: true
                                })
                                objCmdMain.tipObj.setCoords()
                            }
                            _this.closeOkCancelControl()
                            _this.canvas.requestRenderAll()
                        }
                        // else if (activeObj.eleType == "station") {
                        //     _this.$ownerInstance.callMethod('receiveRenderData', {
                        //         method: "select_station",
                        //         data: activeObj.data,
                        //         select: activeObj.mark ? false : true
                        //     });
                        // }
                    }
                } else if (e.touches.length >= 2) {
                    _this.handleMultiTouch(e.touches);
                }
            }
        },
        canvasTouchMove(e) {
            const _this = this
            const list = _this.canvas.getActiveObjects()
            if (list.length == 0) {
                if (e.touches.length === 1) {
                    _this.handleSingleTouchMove(e.touches[0]);
                } else if (e.touches.length >= 2) {
                    _this.handleMultiTouchMove(e.touches);
                }
            } else {
                if (e.touches.length === 1) {
                    if (list.length > 1 || list[0].lockMovementX) {
                        if (list[0].lockMovementX) {
                            _this.canvas.discardActiveObject();
                        }
                        _this.handleSingleTouchMove(e.touches[0]);
                    } else {
                        const touch = e.touches[0]
                        const deltaX = touch.clientX - this.lastPosX;
                        const deltaY = touch.clientY - this.lastPosY;
                        if (Math.abs(deltaX) > 20)
                            clearTimeout(this.pressObjTimer);
                    }
                } else if (e.touches.length >= 2) {
                    _this.canvas.discardActiveObject();
                    _this.handleMultiTouchMove(e.touches);
                }
            }
        },
        // 计算点到线段的距离
@@ -753,7 +784,6 @@
            objects.splice(0, 1);
            const objActive = this.canvas.getActiveObject()
            let pointerList = []
            let pointerList2 = []
            for (let i = objects.length - 1; i >= 0; i--) {
@@ -893,44 +923,6 @@
                }
                return
            }
            // objects.forEach(obj => {
            //     if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
            //         // 设置一个“点击容忍度”,比如 5 像素
            //         const tolerance = 5;
            //         // 临时扩大路径的点击区域
            //         const originalStrokeWidth = obj.strokeWidth;
            //         obj.strokeWidth = originalStrokeWidth + tolerance * 2;
            //         const isHit = obj.intersectsWithPointer(pointer);
            //         // 恢复原始宽度
            //         obj.strokeWidth = originalStrokeWidth;
            //         if (isHit) {
            //             pointerList.push(obj)
            //         }
            //     } else if (obj instanceof fabric.Rect || obj instanceof fabric.Ellipse) {
            //         const isHit = obj.containsPoint(pointer);
            //         if (isHit) {
            //             pointerList.push(obj)
            //         }
            //     }
            //     // else if (obj instanceof fabric.Group )
            //     // {
            //     //     const objects2 = obj.getObjects();
            //     //     const isHit = obj.containsPoint(pointer);
            //     //     if (isHit) {
            //     //         pointerList.push(obj)
            //     //     }
            //     // }
            // });
            // if (pointerList.length === 0) {
            //     return
            // }
            // const obj = pointerList.pop()
            // this.canvas.discardActiveObject()
            // this.canvas.setActiveObject(obj)
        },
        objectMoving(target) {
            const _this = this
@@ -955,7 +947,7 @@
                });
                _this.createOkCancelControl(obj)
                _this.canvas.renderAll()
                _this.canvas.requestRenderAll()
            } else if (target?.eleType == "edit_teaching") {
                _this.updateEditTeachingPath(target)
@@ -977,7 +969,7 @@
                else
                    _this.updateRegion(target, data)
                _this.canvas.renderAll()
                _this.canvas.requestRenderAll()
            } else if (target?.eleType == "wall_pt") {
                const data = target.mainObj?.data
@@ -1007,7 +999,7 @@
                _this.updateVirtualWall(target.mainObj, data)
                _this.canvas.renderAll()
                _this.canvas.requestRenderAll()
            } else if (target?.eleType == "region_pt") {
                const data = target.mainObj?.data
                if (!data)
@@ -1039,10 +1031,8 @@
                        }
                        _this.updateRegion(target.mainObj, data)
                    }
                    _this.canvas.renderAll()
                    _this.canvas.requestRenderAll()
                }
            } else if (target?.eleType == "edit_teaching_pt") {
@@ -1126,28 +1116,6 @@
        onSelectionChanage() {
        },
        safeLoadImage(url, maxSize = 2048) {
            console.log(url)
            return new Promise((resolve) => {
                const img = new Image();
                img.onload = () => {
                    // 检查尺寸是否超出限制
                    const scale = Math.min(
                        maxSize / Math.max(img.width, img.height),
                        1
                    );
                    resolve(new fabric.Image(img, {
                        scaleX: scale,
                        scaleY: scale
                    }));
                };
                img.onerror = () => {
                    console.error('图片加载失败');
                    resolve(null);
                };
                img.src = url;
            });
        },
        // 将 Base64 转为 Blob,再生成 URL
        base64ToBlob(base64, mime) {
@@ -1234,6 +1202,7 @@
                    _this.$ownerInstance.callMethod('receiveRenderData', {
                        method: "set_backgroud_progress",
                        type: "error",
                        msg:err
                    });
                    console.error("图片加载失败", err)
                    reject(new Error('图片加载失败'));
@@ -1253,14 +1222,66 @@
            }
            return null;
        },
        clearObjects() {
            const list = this.canvas.getObjects()
            list.splice(0, 1)
            for (let i in list) {
                const obj = list[i]
                this.canvas.remove(obj)
        // 移除所有事件监听器
        removeAllEventListeners() {
            if (!this.canvas) return;
            // 移除Fabric.js内置事件
            this.canvas.off();
            // 移除自定义事件监听器
            const cantainerEl = document.getElementById("canvasMap");
            if (cantainerEl) {
                cantainerEl.replaceWith(cantainerEl.cloneNode(true));
            }
        },
        clearObjects() {
            if (!this.canvas) return;
            this.canvas.discardActiveObject()
            const objects = this.canvas.getObjects()
            const batchSize = 50; // 每批删除50个对象
            for (let i = 0; i < objects.length; i += batchSize) {
                const batch = objects.slice(i, i + batchSize);
                this.canvas.remove(...batch);
            }
            this.canvas.clear();
            this.workSpace = null;
            this.agvObj = null;
            this.curTeachingObj = null;
            this.objEditing = null;
            this.editObject = null;
            this.drawingObj = null;
            if (this.pressObjTimer) {
                clearTimeout(this.pressObjTimer);
                this.pressObjTimer = null;
            }
        },
        // 清理所有引用
        cleanupReferences() {
            this.canvas = null;
            this.workSpace = null;
            this.agvObj = null;
            this.curTeachingObj = null;
            this.objEditing = null;
            this.editObject = null;
            this.drawingObj = null;
            this.mapInfo = {
                proportion: 1,
                img_proportion: 1,
                max_x: 1,
                max_y: 1,
                min_x: 0,
                min_y: 0,
                img_x: 1,
                img_y: 1
            };
            // 清除所有定时器
            if (this.pressObjTimer) {
                clearTimeout(this.pressObjTimer);
                this.pressObjTimer = null;
            }
        },
        setBackground(info) {
            const _this = this
@@ -1277,7 +1298,7 @@
            // this.canvas.selectionColor = 'rgba(100, 200, 255, 0.3)'; // 选中背景色
            // this.canvas.selectionBorderColor = '#1890ff'; // 边框颜色
            // this.canvas.selectionLineWidth = 3; // 边框宽度
            this.agvObj = null
            const cantainerEl = document.getElementById("canvasMap")
            this.eleWidth = cantainerEl.clientWidth
            this.eleHeight = cantainerEl.clientHeight
@@ -1314,25 +1335,8 @@
                                left: 0,
                                top: 0,
                            });
                            // if (_this.workSpace instanceof fabric.Group) {
                            //     const objs = _this.workSpace.getObjects()
                            //     const rect = objs[1]
                            //     _this.workSpace.remove(objs[0])
                            //     _this.workSpace.insertAt(0,img)
                            //     rect.set({
                            //         width: _this.mapInfo.img_x,
                            //         height: _this.mapInfo.img_y,
                            //     })
                            //     _this.workSpace.set({
                            //         width: _this.mapInfo.img_x,
                            //         height: _this.mapInfo.img_y,
                            //     })
                            //     resolve()
                            //     return
                            // }
                            _this.clearObjects()
                            //
                            const rect = new fabric.Rect({
                                left: 0,
                                top: 0,
@@ -1359,21 +1363,48 @@
                                height: _this.mapInfo.img_y,
                            });
                            _this.canvas.add(wsGroup)
                            if (_this.workSpace) {
                                _this.canvas.remove(_this.workSpace)
                            }
                            _this.workSpace = wsGroup
                            _this.clearObjects()
                            _this.canvas.add(wsGroup)
                            // _this.canvas.renderAll()
                            _this.workSpace = wsGroup
                        }
                        //_this.checkMemoryUsage()
                        resolve()
                    }).catch((err) => {
                        _this.$ownerInstance.callMethod('receiveRenderData', {
                            method: "set_backgroud_progress",
                            type: "error",
                        // _this.$ownerInstance.callMethod('receiveRenderData', {
                        //     method: "set_backgroud_progress",
                        //     type: "error",
                        //     msg:err
                        // });
                        const rect = new fabric.Rect({
                            left: 0,
                            top: 0,
                            width: _this.mapInfo.img_x,
                            height: _this.mapInfo.img_y,
                            stroke: "#333",
                            strokeWidth: 1,
                            strokeDashArray: [5, 5],
                            strokeLineCap: 'butt',
                            fill: "rgba(255,255,255,0)",
                        })
                        let wsGroup = new fabric.Group([ rect], {
                            id: "workspace",
                            eleType: "workspace",
                            selectable: false,
                            hasControls: false,
                            left: 0,
                            top: 0,
                            width: _this.mapInfo.img_x,
                            height: _this.mapInfo.img_y,
                        });
                        _this.clearObjects()
                        _this.canvas.add(wsGroup)
                        _this.workSpace = wsGroup
                        resolve()
                    })
@@ -1425,12 +1456,7 @@
                scale: scale
            });
            if (!this.workSpace) return;
            // this.setCenterFromObject(this.workSpace);
            // 超出画布不展示
            // _this.workSpace.clone().then((cloned) => {
            //     _this.canvas.clipPath = cloned;
            //     _this.canvas.requestRenderAll();
            // });
        },
        setDrawingType(type, svg) {
            if (svg) {
@@ -1474,7 +1500,6 @@
                y: touch.clientY
            };
            //    console.log('单点触摸开始');
            let activeObj = this.canvas.getActiveObject();
            if (!activeObj) {
                if (!this.drawType) {
@@ -1520,7 +1545,6 @@
                }
                // 移动视口
                //    console.log('relativePan', deltaX, deltaY);
                const vpt = this.canvas.viewportTransform;
                this.canvas.relativePan(new fabric.Point(deltaX, deltaY));
                if (this.objEditing) {
@@ -1543,7 +1567,7 @@
                this.lastPosX = touch.clientX;
                this.lastPosY = touch.clientY;
                this.canvas.renderAll()
                this.canvas.requestRenderAll()
            } else if (this.isDrawing) {
                const vpt = this.canvas.viewportTransform;
                //    console.log("viewportTransform", vpt[4], vpt[5])
@@ -1849,7 +1873,7 @@
            if (this.objEditing) {
                this.createOkCancelControl(this.objEditing)
            }
            this.canvas.renderAll()
            this.canvas.requestRenderAll()
        },
@@ -2186,7 +2210,7 @@
            }
            // console.log("addTeachingPath",path2)
            // path2 += " Z"
            let strokeWidth = 1
@@ -2222,8 +2246,7 @@
                    lockScalingY: true,
                    lockMovementX: true,
                    lockMovementY: true,
                    selectable: false,
                    opacity: 1,
                    mainRoad: main_road,
                    data: teachingData
                })
@@ -2271,8 +2294,8 @@
                const obj = list[i2]
                obj.set({
                    opacity: show ? 1 : 0,
                    strokeDashArray: [],
                    strokeLineCap: '',
                    // strokeDashArray: [],
                    // strokeLineCap: '',
                    hasControls: show,
                    selectable: show,
                })
@@ -2432,23 +2455,23 @@
            this.createOkCancelControl(obj)
            this.canvas.requestRenderAll();
        },
        updateAgv(info) {
        updateAgv(info, obj) {
            const _this = this
            return new Promise((resolve) => {
                //    const scale = this.getAutoScale()
                const left = _this.getXOnImg(info.x) // * scale
                const top = _this.getYOnImg(info.y) //* scale
                const angle = info.angle * 180 / Math.PI
                if (this.agvObj) {
                    this.agvObj.set({
                if (obj) {
                    obj.set({
                        left,
                        top,
                        angle,
                        data: info
                    });
                    this.agvObj.setCoords()
                    _this.canvas.requestRenderAll();
                    resolve()
                    obj.setCoords()
                    this.canvas.requestRenderAll();
                    resolve(obj)
                } else {
                    const zoom = _this.canvas.getZoom();
                    fabric.loadSVGFromURL("static/images/van.svg").then(
@@ -2489,16 +2512,13 @@
                            console.log("agv", JSON.stringify(info))
                            _this.canvas.add(obj)
                            //    _this.canvas.bringObjectToFront(obj);
                            _this.agvObj = obj
                            resolve()
                            resolve(obj)
                        }
                    )
                }
            })
        },
        addVirtualWallShow(info) {
            const path = info.path || []
            if (path.length != 2)
@@ -2526,7 +2546,6 @@
            });
            return line
        },
        addVirtualWall(info) {
            const path = info.path || []
            if (path.length != 2)
@@ -2705,18 +2724,7 @@
                    y: this.getYOnImg(pt.y)
                }
                // if (pt.x < 10) {
                //     pt.x = 10
                // }
                // if (pt.y < 10) {
                //     pt.y = 10
                // }
                // if (pt.x > this.workSpace.width - 10) {
                //     pt.x = 10
                // }
                // if (pt.y > this.workSpace.height - 10) {
                //     pt.y = 10
                // }
                if (index > 0) {
                    path2 += ` L${pt2} ${pt2.y}`
                } else {
@@ -2754,18 +2762,6 @@
            const path = info.path || []
            let path2 = ""
            path.forEach((pt, index) => {
                // if (pt.x < 10) {
                //     pt.x = 10
                // }
                // if (pt.y < 10) {
                //     pt.y = 10
                // }
                // if (pt.x > this.workSpace.width - 10) {
                //     pt.x = this.workSpace.width - 10
                // }
                // if (pt.y > this.workSpace.height - 10) {
                //     pt.y = this.workSpace.height - 10
                // }
                let pt2 = {
                    x: this.getXOnImg(pt.x),
                    y: this.getYOnImg(pt.y)
@@ -3060,7 +3056,7 @@
            this.canvas.add(ellipse)
            const offX = 20 * Math.cos(angle)
            const offY = 20 * Math.sin(angle)
            if (this.objAgvLaser) {
                this.canvas.remove(this.objAgvLaser)
            }
@@ -3083,14 +3079,30 @@
        updateLaserPoint(param) {
            let list2 = this.canvas.getObjects() || []
            list2 = list2.filter((a) => a.eleType == "laser_point")
            list2 = list2.filter((a) => a.eleType == "laser_point_cur_group")
            for (let i in list2) {
                const obj = list2[i]
                obj.set({
                    fill: "#000",
                const objGroup = list2[i]
                objGroup.set({
                    eleType: "laser_point_group",
                })
                const list3 = objGroup.getObjects()
                for (let i3 in list3) {
                    const obj = list3[i3]
                    obj.set({
                        fill: "#0000FF",
                    })
                }
            }
            const list = param.xy || []
            const list = param?.xy || []
            const objs = []
            let right = 0
            let bottom = 0
            let left = this.mapInfo.img_x
            let top = this.mapInfo.img_y
            for (let i in list) {
                const pt = list[i]
                const pt2 = {
@@ -3098,82 +3110,80 @@
                    y: this.getYOnImg(pt[1])
                }
                const point = new fabric.Rect({
                    id: "laser_point",
                    eleType: "laser_point",
                    // id: "laser_point",
                    // eleType: "laser_point",
                    left: pt2.x,
                    top: pt2.y,
                    width: 1,
                    height: 1,
                    fill: "#F5222D",
                    originX: "center",
                    originY: "center",
                    selectable: false,
                    hasControls: true,
                    lockRotation: true,
                    lockScalingX: true,
                    lockScalingY: true,
                    lockMovementX: true,
                    lockMovementY: true,
                    originX: "left",
                    originY: "top",
                    // selectable: false,
                    // hasControls: true,
                    // lockRotation: true,
                    // lockScalingX: true,
                    // lockScalingY: true,
                    // lockMovementX: true,
                    // lockMovementY: true,
                });
                // let point = new fabric.Ellipse({
                //    id: "laser_point",
                //     eleType: "laser_point",
                //     left: pt[0],
                //     top: pt[1],
                //     rx: 2,
                //     ry: 1,
                //     strokeWidth: 1,
                //     stroke: "#F5222D",
                //     fill: "#F5222D",
                //     originX: "center",
                //     originY: "center",
                //     selectable: false,
                //     hasControls: true,
                //     lockRotation: true,
                //     lockScalingX: true,
                //     lockScalingY: true,
                //     lockMovementX: true,
                //     lockMovementY: true,
                // });
                this.canvas.add(point)
                objs.push(point)
                if (left > pt2.x) {
                    left = pt2.x
                }
                if (top > pt2.y) {
                    top = pt2.y
                }
                if (right < pt2.x + 1) {
                    right = pt2.x + 1
                }
                if (bottom < pt2.y + 1) {
                    bottom = pt2.y + 1
                }
            }
            //console.log("updateLaserPoint", list.length, left, right,top,bottom)
            const groupObj = new fabric.Group(objs, {
                id: `laser_point_group`,
                eleType: "laser_point_cur_group",
                left,
                top,
                width: right - left,
                height: bottom - top,
                // originX: "left",
                // originY: "top",
                selectable: false,
                hasControls: true,
            })
            this.canvas.add(groupObj)
        },
        ensurePointVisible(pt) {
            var zoom = this.canvas.getZoom();
            var vpt = this.canvas.viewportTransform; // 当前变换矩阵
            var newPanX = -vpt[4];
            var newPanY = -vpt[5];
            const  offWidth = pt.width || 20
            const  offHeight = pt.height || 20
        //    console.log("ensurePointVisible",pt.x,pt.y,newPanX,newPanY, this.eleWidth,this.eleHeight)
            const offWidth = pt.width || 20
            const offHeight = pt.height || 20
            //    console.log("ensurePointVisible",pt.x,pt.y,newPanX,newPanY, this.eleWidth,this.eleHeight)
            if (pt.x * zoom < -vpt[4] + offWidth || pt.x * zoom > -vpt[4] + this.eleWidth - offWidth) {
                if (pt.x * zoom - this.eleWidth / 2 <offWidth) {
                if (pt.x * zoom - this.eleWidth / 2 < offWidth) {
                    newPanX = -offWidth
                }
                // else if (pt.x * zoom > this.mapInfo.img_x * zoom - 20) {
                //     newPanX = this.mapInfo.img_x * zoom - this.eleWidth + 20
                // }
                else {
                } else {
                    newPanX = pt.x * zoom - this.eleWidth / 2
                }
            }
            if (pt.y * zoom < -vpt[5] +offHeight || pt.y * zoom > -vpt[5] + this.eleHeight - (120+offHeight)) {
            if (pt.y * zoom < -vpt[5] + offHeight || pt.y * zoom > -vpt[5] + this.eleHeight - (120 +
                    offHeight)) {
                if (pt.y * zoom - this.eleHeight / 2 <offHeight) {
                if (pt.y * zoom - this.eleHeight / 2 < offHeight) {
                    newPanY = -offHeight
                }
                // else if (pt.y * zoom > this.mapInfo.img_y * zoom - 180) {
                //     newPanY = this.mapInfo.img_y * zoom - this.eleHeight + 180
                // }
                 else {
                    newPanY = pt.y * zoom -(this.eleHeight - 120) / 2
                } else {
                    newPanY = pt.y * zoom - (this.eleHeight - 120) / 2
                }
            }
        //    console.log("ensurePointVisible2",newPanX,newPanY)
            // 只有在需要时才平移
            if (newPanX !== -vpt[4] || newPanY !== -vpt[5]) {
                this.canvas.absolutePan({
@@ -3182,10 +3192,10 @@
                });
            }
        },
        ensurePointCenter(pt){
        ensurePointCenter(pt) {
            var zoom = this.canvas.getZoom();
            var newPanX = newPanX = pt.x * zoom - this.eleWidth / 2
            var newPanY =  pt.y * zoom -(this.eleHeight - 150) / 2
            var newPanY = pt.y * zoom - (this.eleHeight - 150) / 2
            this.canvas.absolutePan({
                x: newPanX,
                y: newPanY
@@ -3193,44 +3203,58 @@
        },
        setAllObjectSelectable(selectable) {
            let flag = false
            this.canvas.forEachObject(function(obj) {
                if (obj.canSelect) {
                    if (!obj.flag) {
                    if (obj.selectable != selectable) {
                        flag = true
                        obj.set({
                            selectable: selectable,
                            lockEdit: true
                        })
                    }
                    obj.set({
                        selectable: selectable,
                        lockEdit: true
                    })
                }
            });
            if (flag) {
            if (flag)
                this.canvas.requestRenderAll()
            }
        },
        receiveMsg(newValue, oldValue) {
            if (typeof newValue == "undefined")
                return;
            const _this = this
            //console.log("receiveMsg",_this.initFlag)
            setTimeout(() => {
                if (_this.initFlag) {
                    _this.handleMsg(newValue, oldValue)
                } else {
            if (!this.canvas) {
                return
            }
            if (_this.initFlag) {
                _this.handleMsg(newValue, oldValue)
            } else {
                setTimeout(() => {
                    _this.receiveMsg(newValue, oldValue)
                }
            }, 100)
                }, 100)
            }
        },
        async handleMsg(newValue, oldValue) {
            const _this = this
            if (!this.canvas) {
                return
            }
            try {
                //console.log("handleMsg", newValue)
                var data = JSON.parse(newValue);
                // console.log("handleMsg", data.length)
                const destroyCommand = data.find(item => item.method === "destroy");
                if (destroyCommand) {
                    this.destroyCanvas();
                    return;
                }
                for (var i = 0; i < data.length; i++) {
                    const item = data[i]
                    if (item.method == "init") {
                        if (item.param?.editMode) {
@@ -3243,7 +3267,20 @@
                        await _this.setBackground(item.param)
                    } else if (item.method == "update_agv_state") {
                        const info = item.param || {}
                        await _this.updateAgv(info)
                        const obj = _this.agvObj
                        const obj2 = await _this.updateAgv(info, obj)
                        let obj3 = _this.agvObj
                        _this.agvObj = obj2
                        if (obj && obj2 != obj) {
                            _this.canvas.remove(obj)
                            if (obj3 == obj)
                                obj3 = undefined
                        }
                        if (obj3 && obj2 != obj3) {
                            _this.canvas.remove(obj3)
                        }
                    } else if (item.method == "update_current_teaching") {
                        const info = item.param || []
                        await _this.updateCurrentTeaching(info)
@@ -3257,17 +3294,14 @@
                            height: 20
                        }
                        this.ensurePointVisible(pt)
                    }
                    else if (item.method == "move_pt_center") {
                    } else if (item.method == "move_pt_center") {
                        const info2 = item.param || {}
                        const pt = {
                            x: this.getXOnImg(info2.x),
                            y: this.getYOnImg(info2.y)
                        }
                        this.ensurePointCenter(pt)
                    }
                    else if (item.method == "add_station") {
                    } else if (item.method == "add_station") {
                        const stationList = item.param || []
                        let list = _this.canvas.getObjects() || []
                        for (let i2 in stationList) {
@@ -3322,7 +3356,6 @@
                                }
                            }
                        }
                    } else if (item.method == "remove_station") {
@@ -3341,9 +3374,7 @@
                                _this.canvas.remove(list[curIndex])
                                if (tipObj) {
                                    _this.canvas.remove(tipObj)
                                }
                            }
                        }
@@ -3475,14 +3506,11 @@
                        _this.setAllObjectSelectable(false)
                        let list = _this.canvas.getObjects() || []
                        list.forEach((obj) => {
                            if (obj.eleType == "public_teaching" || obj.eleType == "station_teaching") {
                            if (obj.eleType == "public_teaching" || obj.eleType ==
                                "station_teaching") {
                                obj.set({
                                    opacity: 1
                                })
                                // obj.set({
                                //     hasControls: false,
                                //     selectable: false,
                                // })
                            } else if (obj.eleType == "agv")
                                obj.set({
                                    opacity: 1
@@ -3501,38 +3529,46 @@
                        for (let i2 in list) {
                            const obj = list[i2]
                            obj.set({
                                selectable: obj?.canSelect ?true:false,
                                selectable: obj?.canSelect ? true : false,
                                opacity: 1
                            })
                        }
                        _this.showTeachingPath(_this.showTeachPathFlag ? true : false)
                    }
                    else if (item.method == "clear_teaching_path") {
                    } else if (item.method == "clear_teaching_path") {
                        let list = _this.canvas.getObjects() || []
                        list = list.filter((a) => a.eleType == "public_teaching" || a.eleType == "station_teaching")
                        list = list.filter((a) => a.eleType == "public_teaching" || a.eleType ==
                            "station_teaching")
                        for (let i2 in list) {
                            this.canvas.remove(list[i2])
                        }
                    }
                    else if (item.method == "public_teaching_path") {
                        const teachingPathList = item.param || []
                    } else if (item.method == "public_teaching_path") {
                        const teachingPathList = item.param?.list || []
                        const show = item.param?.show || false
                        for (let i2 in teachingPathList) {
                            const teachingPath = teachingPathList[i2]
                            const id = `public_teaching_${teachingPath.name}`
                            await this.addTeachingPath(teachingPath, id, "public_teaching")
                            const obj = await this.addTeachingPath(teachingPath, id, "public_teaching",
                                show)
                            obj.set({
                                selectable: show ? true : false,
                                opacity: show ? 1 : 0,
                            })
                        }
                    } else if (item.method == "station_teaching_path") {
                        const teachingPathList = item.param || []
                        const show = item.param?.show || false
                        const teachingPathList = item.param?.list || []
                        for (let i2 in teachingPathList) {
                            const teachingPath = teachingPathList[i2]
                            const id = `station_teaching_${teachingPath.src_dst}`
                            await this.addTeachingPath(teachingPath, id, "station_teaching")
                            const obj = await this.addTeachingPath(teachingPath, id, "station_teaching",
                                show)
                            obj.set({
                                selectable: show ? true : false,
                                opacity: show ? 1 : 0,
                            })
                        }
                    } else if (item.method == "show_teaching_path") {
                        _this.showTeachPathFlag = item.param.show
@@ -3559,9 +3595,7 @@
                                const obj = list[curIndex]
                                this.canvas.remove(obj)
                            }
                        }
                    } else if (item.method == "edit_teaching") {
                        const teachingMode = item.param
                        _this.showEditTeachingPath(teachingMode)
@@ -3583,7 +3617,6 @@
                        for (let i2 in wallList) {
                            const wall = wallList[i2]
                            const obj = await _this.addVirtualWall(wall)
                        }
                    } else if (item.method == "wall_list") {
                        const wallList = item.param || []
@@ -3601,8 +3634,6 @@
                        for (let i2 in regionList) {
                            const region = regionList[i2]
                            const obj = await _this.addRegion(region)
                        }
                    } else if (item.method == "region_list") {
                        const regionList = item.param || []
@@ -3615,7 +3646,6 @@
                                await _this.addRegionShow(region)
                            }
                        }
                    } else if (item.method == "remove_wall") {
                        const wallList = item.param || []
                        let list = _this.canvas.getObjects() || []
@@ -3644,7 +3674,8 @@
                        this.updateLaserPoint(item.param || {})
                    }
                }
                _this.canvas.renderAll()
                if (_this.canvas)
                    _this.canvas.renderAll()
            } catch (ex) {
                console.log(ex)
                this.showError(ex)
@@ -3685,6 +3716,5 @@
                verticalAlign: "center"
            }); // undefined, "错误"
        },
    },
    }
}
pages/map/scene.vue
@@ -1,15 +1,18 @@
<template>
    <view class="pages-scene">
        <view class="map-content" v-if="opSceneType =='' ">
            <image class="img" src="/images/image 25.png" alt=" 图片" mode="aspectFit" />
            <view class="space">没有找到符合条件的地图</view>
            <image v-if="opType != 'extend'" class="img" src="/images/image 25.png" alt=" 图片" mode="aspectFit" />
            <image v-else class="img" :src="extendBase64Img" alt=" 图片" mode="aspectFit" />
            <view v-if="opType != 'extend'" class="space">没有找到符合条件的地图</view>
            <!-- <view class="loading-view">{{mapserverIsOk?"构图程序准备就绪":"等待构图程序就绪..."}}
                <view v-if="!mapserverIsOk" class="auto-circle"></view>:disabled="!mapserverIsOk"
            </view> -->
            <view class="text-button-group">
                <a-button type="primary" class="button" @click="clickStartConstructScene">
                    开始构建
                    {{opType == 'extend'?"开始扩展":'开始构建'}}
                </a-button>
                <a-button type="primary" class="button" :disabled="loading && localSceneList.length == 0"
                    @click="clickDownloadScene" disabled>
                <a-button v-if="opType != 'extend'" type="primary" class="button"
                    :disabled="loading && localSceneList.length == 0" @click="clickDownloadScene" disabled>
                    下载场景
                </a-button>
            </view>
@@ -49,12 +52,12 @@
            </view>
            <view class="bottom-content" v-else-if="opSceneType =='finish'">
                <view class="tip">场景构建完成</view>
                <view>
                    已成功构建“{{sceneName}}”
                </view>
                <view>已成功构建“{{sceneName}}”</view>
                <!-- <view class="loading-view">{{saveMapIsOk?"构图保存完成":"等待构图保存完成..."}}
                    <view v-if="!saveMapIsOk" class="auto-circle"></view>
                </view> !saveMapIsOk ||-->
                <view class="text-button-group">
                    <a-button type="primary" class="button" :disabled="loading" @click="clickFinish">构建完成</a-button>
                    <a-button type="primary" class="button" :disabled="  loading" @click="clickFinish">构建完成</a-button>
                </view>
            </view>
        </view>
@@ -81,6 +84,11 @@
        //getAgvState,
        getMapLaserData,
        saveDBData,
        checkMapServerIsStart,
        startOrStopMapServer,
        checkMapServerIsOk,
        checkSaveMapIsOk,
        getMapUrl
    } from "@/api/vehicle.js"
    export default {
@@ -101,7 +109,11 @@
                ctxDataStr: "[]",
                localSceneList: [],
                positioningAgv: true,
                robotPos: {}
                robotPos: {},
                mapserverIsOk: false,
                extendBase64Img: "",
                saveMapIsOk: false,
                destroyFlag: false,
            }
        },
@@ -137,6 +149,7 @@
                    if (res) {
                        this.opSceneType = ""
                        if (this.opType != "") {
                            uni.navigateBack({
                                delta: 1, //返回层数,2则上上页
                            })
@@ -147,11 +160,27 @@
                return true
            } else if (this.opSceneType == "finish") {
                const eventChannel = this.getOpenerEventChannel();
                eventChannel.emit('create_finish', this.sceneName);
                return false
                if (this.destroyFlag) {
                    const eventChannel = this.getOpenerEventChannel();
                    eventChannel.emit('create_finish', this.sceneName);
                    return false
                } else {
                    this.ctxDataStr = JSON.stringify([{
                        method: "destroy",
                    }])
                    return true
                }
            } else {
                return false
                if (this.destroyFlag)
                    return false
                else {
                    this.ctxDataStr = JSON.stringify([{
                        method: "destroy",
                    }])
                    return true
                }
            }
        },
@@ -180,6 +209,7 @@
            async loadData() {
                try {
                    // this.loadMapServerState()
                    if (this.opType == "") {
                        this.localSceneList = await this.loadLocalScene()
                    } else {
@@ -190,8 +220,19 @@
                            await addMap(this.ip, this.sceneName, "extend")
                            this.opSceneType = 'scan'
                        }
                    }
                        // if (this.opType == "extend") {
                        //     // const infoMap = await this.loadMapInfo(this.sceneName)
                        //     // if (infoMap.filedata) {
                        //     //     var base64Image = infoMap.filedata
                        //     //     if (base64Image.indexOf("data:image/png;base64,") < 0) {
                        //     //         base64Image = "data:image/png;base64," + infoMap.filedata
                        //     //     }
                        //     //     this.extendBase64Img = base64Image
                        //     // }
                        // }
                    }
                    this.refreshMapLaserData()
@@ -200,9 +241,81 @@
                    showError(ex)
                }
            },
            async loadMapInfo(id) {
                try {
                    const info = await getMapUrl(this.ip, id)
                    return info
                } catch (ex) {
                    return {}
                }
            },
            async loadMapServerState() {
                try {
                    this.mapserverIsOk = false
                    const res = await checkMapServerIsStart(this.ip)
                    if (res) {
                        this.mapserverIsOk = true
                    } else {
                        this.mapserverIsOk = false
                        const res = await startOrStopMapServer(this.ip, 1)
                        if (res) {
                            this.checkMapServerState()
                        }
                    }
                } catch (ex) {
                    showError(ex)
                }
            },
            async checkMapServerState() {
                try {
                    const res = await checkMapServerIsOk(this.ip)
                    if (res) {
                        this.mapserverIsOk = true
                    } else {
                        this.mapserverIsOk = false
                        setTimeout(this.checkMapServerState, 1000);
                    }
                } catch (ex) {
                    showError(ex).then((res) => {
                        setTimeout(this.checkMapServerState, 1000);
                    })
                }
            },
            async checkSaveMapState() {
                try {
                    const res = await checkSaveMapIsOk(this.ip)
                    if (res) {
                        this.saveMapIsOk = true
                    } else {
                        this.saveMapIsOk = false
                        setTimeout(this.checkSaveMapState, 1000);
                    }
                } catch (ex) {
                    showError(ex).then((res) => {
                        setTimeout(this.checkSaveMapState, 1000);
                    })
                }
            },
            receiveRenderData(param) {
                console.log('接收到视图层的数据:', param);
                if (param.method == "set_backgroud_progress") {
                if (param.method === "destroy_complete") {
                    if (param.param) {
                        this.destroyFlag = true
                        uni.navigateBack({
                            delta: 1
                        })
                    }
                } else if (param.method == "set_backgroud_progress") {
                    if (param.type == "start") {
                        this.setData({
                            bgProgressPercent: 50,
@@ -224,14 +337,34 @@
                        }, 500)
                    } else if (param.type == "error") {
                        console.log("set_backgroud_progress error")
                        this.setData({
                            bgProgressPercent: 0,
                            bgLoading: false
                        })
                        const now = new Date()
                        const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
                        showToast("加载底图失败")
                        const log = {
                            date,
                            method: `POST`,
                            url: "app/log/load_img",
                            param: param.msg,
                            statusCode: 100,
                            data: "加载底图失败"
                        }
                        const listLog = session.getValue("request_log") || []
                        listLog.unshift(log)
                        session.setValue("request_log", listLog)
                    }
                } else if (param.method == "cancel_positioning_agv") {
                    this.positioningAgv = false
                } else if (param.method == "show_log") {
                    const listLog = session.getValue("request_log") || []
                    listLog.unshift(param.data)
                    session.setValue("request_log", listLog)
                }
            },
@@ -244,8 +377,17 @@
                    return []
                }
            },
            clickStartConstructScene() {
                this.opSceneType = "add_name"
            async clickStartConstructScene() {
                try {
                    if (this.opType == "extend") {
                        await addMap(this.ip, this.sceneName, "extend")
                        this.opSceneType = 'scan'
                    } else {
                        this.opSceneType = "add_name"
                    }
                } catch (ex) {
                    showError(ex)
                }
            },
            async clickDownloadScene() {
                try {
@@ -328,7 +470,10 @@
                    uni.setNavigationBarTitle({
                        title: this.sceneName
                    })
                    this.saveMapIsOk = false
                    await stopMap(this.ip, this.sceneName)
                    // await startOrStopMapServer(this.ip, 2)
                    // this.checkSaveMapState();
                    if (this.opType == "extend") {
                        this.opSceneType = ""
                        const eventChannel = this.getOpenerEventChannel();
@@ -339,14 +484,10 @@
                    }
                } catch (ex) {
                    console.log(ex)
                    showModal("请检查车辆连接,并重新开始构建场景", "场景构建失败", false, "确定").then((res) => {
                        this.opSceneType = ''
                        if (this.opType != "") {
                            uni.navigateBack({
                                delta: 1, //返回层数,2则上上页
                            })
                        }
                        // this.loadMapServerState()
                    })
                } finally {
                    this.loading = false
@@ -368,14 +509,7 @@
                        delta: 1, //返回层数,2则上上页
                    })
                } catch (ex) {
                    showModal("请检查车辆连接,并重新开始构建场景", "场景构建失败", false, "确定").then((res) => {
                        this.opSceneType = ''
                        if (this.opType != "") {
                            uni.navigateBack({
                                delta: 1, //返回层数,2则上上页
                            })
                        }
                    })
                    console.log(ex)
                } finally {
                    this.loading = false
                    uni.hideLoading()
@@ -397,59 +531,77 @@
                    if (this.opSceneType === "scan") {
                        const data = await this.loadMapLaserData()
                        const listCtrData = []
                        let newMap = false
                        if (data.base_map?.image_base64) {
                            this.robotPos = {}
                            newMap = true
                            const mapData = data.base_map.image_base64  //this.mapId ?"terdy":
                            this.mapId = data.base_map.map_id
                            listCtrData.push({
                                method: "background",
                                param: {
                                    proportion: 1,
                                    img_proportion: 1,
                                    max_x: data.base_map.width,
                                    max_y: data.base_map.height,
                                    max_x: data.base_map.width || 100,
                                    max_y: data.base_map.height || 100,
                                    min_x: 0,
                                    min_y: 0,
                                    img_x: data.base_map.width,
                                    img_y: data.base_map.height,
                                    filedata: data.base_map.image_base64
                                    img_x: data.base_map.width || 100,
                                    img_y: data.base_map.height || 100,
                                    filedata: mapData
                                }
                            })
                        }
                        listCtrData.push({
                            method: "agv_laser",
                            param: data.robot_pose
                        })
                        listCtrData.push({
                            method: "point_cloud",
                            param: data.point_cloud
                        })
                        if (this.positioningAgv) {
                        if (data.robot_pose) {
                            listCtrData.push({
                                method: "move_pt_visible",
                                param: {
                                    x: data.robot_pose.x,
                                    y: data.robot_pose.y,
                                    width:50,
                                    height:50,
                                }
                                method: "agv_laser",
                                param: data.robot_pose
                            })
                            if (newMap) {
                                listCtrData.push({
                                    method: "move_pt_center",
                                    param: {
                                        x: data.robot_pose.x,
                                        y: data.robot_pose.y,
                                    }
                                })
                            } else {
                                if (this.positioningAgv) {
                                    listCtrData.push({
                                        method: "move_pt_visible",
                                        param: {
                                            x: data.robot_pose.x,
                                            y: data.robot_pose.y,
                                            width: 50,
                                            height: 50,
                                        }
                                    })
                                }
                            }
                        }
                        // console.log("ctxDataStr",listCtrData.length)
                        this.ctxDataStr = JSON.stringify(listCtrData)
                        this.robotPos = {
                            x: data.robot_pose.x,
                            y: data.robot_pose.y
                        if (data.robot_pose) {
                            this.robotPos = {
                                x: data.robot_pose.x,
                                y: data.robot_pose.y
                            }
                        }
                    }
                    setTimeout(this.refreshMapLaserData, 1000);
                } catch (ex) {
                    showError(ex).then((res) => {
                        setTimeout(this.refreshMapLaserData, 1000);
                    })
                    showToast(ex)
                    setTimeout(this.refreshMapLaserData, 1000);
                }
            },
            async clickVehiclePosition() {
@@ -517,6 +669,7 @@
            justify-content: center;
            .text-button-group {
                display: flex;
                width: 100%;
@@ -533,6 +686,32 @@
        }
        .loading-view {
            display: flex;
            flex-direction: row;
            .auto-circle {
                margin-left: 20rpx;
                width: 50rpx;
                height: 50rpx;
                border-radius: 50%;
                border: 10rpx solid #1890FF;
                border-top-color: transparent;
                animation: drawCircle 1s infinite linear;
            }
            @keyframes drawCircle {
                0% {
                    transform: rotate(0deg);
                }
                100% {
                    transform: rotate(360deg);
                }
            }
        }
        .bottom {
            position: fixed;
pages/map/task.vue
@@ -320,6 +320,8 @@
            margin-top: 75px;
            margin-left: 120px;
            width: 150px;
            max-height: 50vh;
            overflow: auto;
            align-items: center;
            justify-content: center;
            flex-direction: column;
pages/my/help-feedback.vue
@@ -1,31 +1,33 @@
<template>
    <view class="pages-my">
        <view class="group">
            <view class="item line" @click="clickRemoteAssistance">
            <view class="item line">
                <view>远程协助:</view>
                <view class="right"></view>
                <a>
                <a @click="clickRemoteAssistance">
                    <uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
                </a>
            </view>
            <view class="item line" @click="clickUploadLog">
            <view class="item line">
                <view>日志上传:</view>
                <view class="right"></view>
                <a>
                <a @click="clickUploadLog">
                    <uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
                </a>
            </view>
            <view class="item line" @click="clickViewInstruction">
            <view class="item line">
                <view>查看说明书:</view>
                <view class="right"></view>
                <a>
                <a @click="clickViewInstruction">
                    <uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
                </a>
            </view>
            <view class="item line" @click="clickApiLog">
            <view class="item line">
                <view>接口日志:</view>
                <switch :checked="withLog" style="transform:scale(0.7)" @change="switchChangeLog"/>{{withLog?"记录接口日志":"不记录接口日志"}}
                <view class="right"></view>
                <a>
                <a @click="clickApiLog">
                    <uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
                </a>
            </view>
@@ -48,7 +50,7 @@
        },
        data() {
            return {
                withLog: getApp().globalData.withLog || false
            }
        },
        onLoad() {
@@ -58,6 +60,11 @@
        },
        methods: {
            switchChangeLog(e) {
                this.withLog = e.detail.value
                getApp().globalData.withLog =this.withLog
                session.setValue("write_log",this.withLog ? 1:0)
            },
            clickRemoteAssistance() {
                showToast("暂未实现")
pages/my/index.vue
@@ -32,7 +32,7 @@
            </view>
         -->
            <view class="item line" @click="clickClearCache">
                <view>清楚缓存:</view>
                <view>清除缓存:</view>
                <view class="right">{{cacheSizeStr}}</view>
                <a >
                    <uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>