diff --git a/README.md b/README.md index a9bafc2..d37fa1d 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,6 @@ npm run lint ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). + +### 项目须知 +1.屏幕视窗大小1281px * 752px diff --git a/package.json b/package.json index f682c1c..695002a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "vue": "^2.6.14", "vue-i18n": "8.0.0", "vue-router": "3.5.2", - "vue-touch-keyboard": "^0.3.2", "vuex": "3.6.2" }, "devDependencies": { diff --git a/src/config/getData.js b/src/config/getData.js index 06a0a0f..d6df314 100644 --- a/src/config/getData.js +++ b/src/config/getData.js @@ -44,4 +44,6 @@ export const relocate = (x, y, angle) => post('teaching/relocate?x=' + x + '&y=' // 关机重启 export const rebootVehicle = () => get('vehicle/rebootVehicle', {}) // 同步地图 -export const synchronizedMap = () => post('mapInfo/synchronizedMap', {}) \ No newline at end of file +export const synchronizedMap = () => post('mapInfo/synchronizedMap', {}) +// 错误 +export const queryErrorDataByCode = (code) => post('anomalyInfo/queryErrorDataByCode?code=' + code, {}) \ No newline at end of file diff --git a/src/config/mork.js b/src/config/mork.js index b1410e3..1792e8e 100644 --- a/src/config/mork.js +++ b/src/config/mork.js @@ -3,8 +3,8 @@ export const queryStation = () => { let res = { message: 'ok', data: [ - {station_code: 'B1', station_name: 'B1', x: '10', Y: '10', angle: '90'}, - {station_code: 'B2', station_name: 'B2', x: '10', Y: '10', angle: '90'}, + {station_code: 'B1', station_name: 'Starting position on the first floor Starting position on the first floor', x: '10', Y: '10', angle: '90'}, + {station_code: 'B2', station_name: '一楼起点位置一楼起点位置一楼起点位置', x: '10', Y: '10', angle: '90'}, {station_code: 'B3', station_name: 'B3', x: '10', Y: '10', angle: '90'}, {station_code: 'B4', station_name: 'B4', x: '10', Y: '10', angle: '90'}, {station_code: 'B5', station_name: 'B5', x: '10', Y: '10', angle: '90'}, @@ -134,3 +134,62 @@ export const getMappingStatus = () => { } return res } +export const queryErrorDataByCode = (code) => { + let res = { + "error_id": "1", + "error_name": "导航定位失败", + "zh_error_name": '导航定位失败', + "en_error_name": '导航定位error', + "error_code": "1", + "error_category": 1, + "error_description": "出现了定位切换失败", + "zh_error_description": null, + "en_error_description": null, + "error_type": 1, + "graphicDescription": [ + { + "error_handling_id": "1", + "error_code": "1", + "error_handling_type": 1, + "error_handling_title": "出现了定位切换失败", + "zh_error_handling_title": '出现了定位切换失败', + "en_error_handling_title": '出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error', + "error_handling_seq": 1, + "error_handling_addre": null + }, + { + "error_handling_id": "2", + "error_code": "1", + "error_handling_type": 1, + "error_handling_title": "解决定位", + "zh_error_handling_title": null, + "en_error_handling_title": '出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error', + "error_handling_seq": 2, + "error_handling_addre": null + }, + { + "error_handling_id": "3", + "error_code": "1", + "error_handling_type": 1, + "error_handling_title": "解决定位", + "zh_error_handling_title": null, + "en_error_handling_title": '出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error出现了定位切换失败error', + "error_handling_seq": 2, + "error_handling_addre": null + } + ], + "videoDescription": [ + { + "error_handling_id": "3", + "error_code": "1", + "error_handling_type": 2, + "error_handling_title": "大苏打", + "zh_error_handling_title": null, + "en_error_handling_title": null, + "error_handling_seq": 1, + "error_handling_addre": null + } + ] + } + return res +} \ No newline at end of file diff --git a/src/i18n/langs/en.js b/src/i18n/langs/en.js index 9bf5363..1a433d5 100644 --- a/src/i18n/langs/en.js +++ b/src/i18n/langs/en.js @@ -2,6 +2,7 @@ module.exports = { index: 'Index', Administratorauthentication: 'Administrator authentication', Notloggedinyet: 'Not logged in yet', + Loginsuccessful:'Login successful', Logout: 'Log out', Login: 'Log in', Configuration: 'Configuration', @@ -13,17 +14,90 @@ module.exports = { Pleasepassword: 'Please enter the administrator password', Language: 'Language', Pleaseselect: 'Please select', + ServiceIP: 'Service IP', SchedulingIP: 'Scheduling IP', + PleaseServiceIP: 'Please enter the service IP', PleaseIP: 'Please enter the scheduling IP', PleaseWIFI: 'Please enter the WIFI', + ServiceIPnotempty: 'Service IP cannot be empty', Passwordnotempty: 'WIFI cannot be empty.', Schedulingnotempty: 'Scheduling IP cannot be empty.', WIFInotempty: 'WIFI cannot be empty.', Time: 'Time', Name: 'Name', Category: 'Category', + Type: 'Type', Description: 'Description', + Operations: 'Operations', + Detail: 'Detail', Empty: 'Empty', + Deletetaskchain: 'Delete task chain', + Canceltask: 'Cancel task', + Savetask: 'Save task', SendTask: 'Send Task', - HistoricalTasks: 'Historical Tasks' + HistoricalTasks: 'Historical Tasks', + Basicconfiguration: 'Basic config', + Shutdownrestart: 'Shut down & restart', + Synchronizemap: 'Sync map', + Customize: 'Customize', + Taskchain: 'Task chain', + Modifyname: 'Modify name', + Pickup: 'Pickup', + Drop: 'Drop', + Move: 'Move', + Return: 'Return', + PickAbb: 'Pick', + DropAbb: 'Drop', + MoveAbb: 'Move', + ReturnAbb: 'Return', + Prompt: 'Prompt', + Confirm: 'Confirm', + Operationcancelled: 'Operation has been cancelled', + Isrelocatepoint: 'Is it certain to re - locate the position?', + Twostationsnotsame: 'Two adjacent stations cannot be the same.', + Select5stations: 'Select a maximum of 5 stations.', + Returnpointselected: 'The return point has been selected.', + Number: 'Number', + Alias: 'Alias', + XCoordinate: 'X Coordinate', + YCoordinate: 'Y Coordinate', + AngleValue: 'Angle Value', + PathID: 'Path ID', + StartCode: 'Start Code', + EndCode: 'End Code', + Mapisload: 'Map is loading...', + Donewmap: 'Do you want to start creating a new map? The existing map will be overwritten. Do you wish to continue?', + Mapbuildingcancel: 'Map - building has been cancelled', + MarkPoint: 'Mark Point', + AbandonMapbuild: 'Abandon Map - building', + FinishMapbuild: 'Finish Map - building', + SetUpStation: 'Set Up Station', + StationName: 'Station Name', + Mapdeployed: 'Map is being deployed...', + Mapgenerated: 'Map is being generated...', + Doabandonmapattempt: 'Do you want to abandon this map - building attempt?', + next: 'Next', + previous: 'Previous', + close: 'Done', + driverTxt1: 'This shows the surrounding environment scanned by the current vehicle. Drag the vehicle to start recording the travel route.', + driverTxt2: 'When the vehicle moves to a work point, click the "Mark Point" button to record the current position of the vehicle as a work point.', + driverTxt3: 'To complete map - building, click the "Finish Map - building" button and wait for the map to be generated automatically.', + carbuildingmap: 'The cart is building the map...', + errorbuildingredone: 'An error occurred during map - building. Map - building needs to be redone.', + sureendbuilding: 'Are you sure to end the map - building?', + endbuildingcancel: 'The ending of map - building has been cancelled', + errorendbuilding: 'An error occurred while ending the map - building.', + Newfailedagain: 'New map creation failed. Do you want to create a new map again?', + Loading: 'Loading...', + AutomaticMode: 'Automatic', + ManualMode: 'Manual', + CommonFault: 'Common Fault', + SeriousFault: 'Serious Fault', + Appliances: 'Appliances', + Navigation: 'Navigation', + WebSocketerror: 'WebSocket connection error', + AttreconnectWebSocket: 'Attempting to reconnect to WebSocket...', + PicturesTexts: 'Illustration with Pictures and Texts', + VideoInstructions: 'Video Instructions', + Handmethod: 'Handling method' } diff --git a/src/i18n/langs/zh.js b/src/i18n/langs/zh.js index 1b16fe8..d237937 100644 --- a/src/i18n/langs/zh.js +++ b/src/i18n/langs/zh.js @@ -2,6 +2,7 @@ module.exports = { index: '主页', Administratorauthentication: '管理员认证', Notloggedinyet: '暂未登录', + Loginsuccessful:'登录成功', Logout: '退出账户', Login: '登录', Configuration: '配置', @@ -13,17 +14,90 @@ module.exports = { Pleasepassword: '请输入管理员密码', Language: '语言', Pleaseselect: '请选择', + ServiceIP: '服务IP', SchedulingIP: '调度IP', + PleaseServiceIP: '请输入服务IP', PleaseIP: '请输入调度IP', PleaseWIFI: '请输入WIFI', + ServiceIPnotempty: '服务IP不能为空', Passwordnotempty: '密码不能为空', Schedulingnotempty: '调度IP不能为空', WIFInotempty: 'WIFI不能为空', Time: '时间', Name: '名称', Category: '类别', + Type: '类型', Description: '说明', + Operations: '操作', + Detail: '查看', Empty: '清空', + Deletetaskchain: '删除任务链', + Canceltask: '取消任务', + Savetask: '保存任务', SendTask: '发送任务', - HistoricalTasks: '历史任务' + HistoricalTasks: '历史任务', + Basicconfiguration: '基础配置', + Shutdownrestart: '关机重启', + Synchronizemap: '同步地图', + Customize: '自定义', + Taskchain: '任务链', + Modifyname: '修改名称', + Pickup: '取货', + Drop: '放货', + Move: '移动', + Return: '返回点', + PickAbb: '取', + DropAbb: '放', + MoveAbb: '移', + ReturnAbb: '返', + Prompt: '提示', + Confirm: '确定', + Operationcancelled: '已取消操作', + Isrelocatepoint: '是否确定点位重定位?', + Twostationsnotsame: '相邻的两个站点不能相同', + Select5stations: '最多选取5个站点', + Returnpointselected: '返回点已选取', + Number: '编号', + Alias: '别名', + XCoordinate: 'X坐标', + YCoordinate: 'Y坐标', + AngleValue: '角度值', + PathID: '路径ID', + StartCode: '起点编码', + EndCode: '终点编码', + Mapisload: '地图加载中...', + Donewmap: '是否开始新建地图,地图会进行覆盖, 是否继续?', + Mapbuildingcancel: '已取消建图', + MarkPoint: '打点', + AbandonMapbuild: '放弃建图', + FinishMapbuild: '结束建图', + SetUpStation: '设置站点', + StationName: '站点名称', + Mapdeployed: '正在部署地图中...', + Mapgenerated: '正在生成地图中...', + Doabandonmapattempt: '是否放弃本次建图?', + next: '下一步', + previous: '上一步', + close: '完成', + driverTxt1: '当前车辆扫描的周围环境,拉动车辆将开始记录行走路线。', + driverTxt2: '移动到工位点时,点击打点按钮记录当前车辆所在位置为工位点。', + driverTxt3: '完成建图,点击结束建图按钮,等待地图自动生成。', + carbuildingmap: '小车正在建图中', + errorbuildingredone: '建图出现错误,需要重新建图', + sureendbuilding: '确定是否结束建图?', + endbuildingcancel: '已取消结束建图', + errorendbuilding: '结束建图出现错误', + Newfailedagain: '新建地图失败, 是否重新建图?', + Loading: '加载中...', + AutomaticMode: '自动模式', + ManualMode: '手动模式', + CommonFault: '普通故障', + SeriousFault: '严重故障', + Appliances: '电器', + Navigation: '导航', + WebSocketerror: 'WebSocket 连接错误', + AttreconnectWebSocket: '尝试重新连接 WebSocket...', + PicturesTexts: '图文说明', + VideoInstructions: '视频说明', + Handmethod: '处理方法' } diff --git a/src/images/new/agv.png b/src/images/new/agv.png index b9e632c..acfb94b 100644 Binary files a/src/images/new/agv.png and b/src/images/new/agv.png differ diff --git a/src/main.js b/src/main.js index ccc1531..aa7b919 100644 --- a/src/main.js +++ b/src/main.js @@ -3,8 +3,6 @@ import App from './App.vue' import router from './router' import store from './vuex/store' import './style/reset.css' -import VueTouchKeyboard from 'vue-touch-keyboard' -import 'vue-touch-keyboard/dist/vue-touch-keyboard.css' import { Row, Col, Button, Icon, Dialog, Form, FormItem, Input, Select, Option, Table, TableColumn, Tabs, TabPane, Popover, Loading, MessageBox, Message, Progress } from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import './style/common.styl' @@ -33,7 +31,6 @@ Vue.use(Loading) Vue.use(Progress) Vue.prototype.$confirm = MessageBox.confirm Vue.prototype.$message = Message -Vue.use(VueTouchKeyboard) Vue.prototype.$post = post Vue.config.productionTip = false @@ -57,7 +54,7 @@ if (savedUserRole) { Vue.filter('findByValue', (array, value) => { if (!Array.isArray(array)) return '' const item = array.find(item => item.value === value) - return item ? item.text : '' + return item ? item[i18n.locale.slice(0, 2) + '_' + 'text'] : '' }) new Vue({ diff --git a/src/pages/modules/building.vue b/src/pages/modules/building.vue index 97be9aa..4fbfe15 100644 --- a/src/pages/modules/building.vue +++ b/src/pages/modules/building.vue @@ -1,31 +1,30 @@ @@ -15,15 +15,24 @@ import { mapGetters } from 'vuex' // import { points } from '../../config/point.js' // import { points1 } from '../../config/point1.js' // import { points2 } from '../../config/point2.js' + +// 常量定义 +const UPDATE_INTERVAL = 800 // 限制更新频率,毫秒 +const DEFAULT_VIEW_SIZE = 20 // 无点云时的默认可视范围 +const BOUND_MARGIN = 0.1 // 边界边距(避免点贴屏幕边缘,值为10%的范围) +const MIN_ZOOM_RATIO = 0.5 +const MAX_ZOOM_RATIO = 3 + export default { - /* eslint-disable */ - data () { + name: 'PointCloudMap', + data() { return { // Three.js核心对象 scene: null, camera: null, renderer: null, controls: null, + // 点云相关 pointCloudGeometry: null, pointCloudMaterial: null, @@ -31,19 +40,23 @@ export default { allPoints: [], // 存储所有累加的点云数据 pointCount: 0, pointScale: 1.0, + // 小车相关 vehicleImage: require('../../images/new/agv.png'), carMesh: null, carTexture: null, + // WebSocket相关 socket: null, isLoading: true, reconnectTimer: null, // 重连计时器 + // 动画与性能 - viewSize: 20, + viewSize: DEFAULT_VIEW_SIZE, animationId: null, lastUpdateTime: 0, - updateInterval: 800, // 限制更新频率,毫秒 + updateInterval: UPDATE_INTERVAL, + // 点云边界:记录所有点的最大/最小x、y pointBounds: { minX: Infinity, // 初始设为无穷大,方便后续比较 @@ -51,14 +64,14 @@ export default { minY: Infinity, maxY: -Infinity }, - boundMargin: 0.1, // 边界边距(避免点贴屏幕边缘,值为10%的范围) - defaultViewSize: 20, // 无点云时的默认可视范围 + // 标记组件是否已销毁 - isDestroyed: false + isDestroyed: false, + baseViewSize: 0 } }, computed: { - ...mapGetters(['serverUrl', , 'userRole', 'carPosition']), + ...mapGetters(['serverUrl', 'userRole', 'carPosition']), }, watch: { carPosition: { @@ -68,98 +81,39 @@ export default { deep: true } }, - mounted () { - // 初始化Three.js场景 - this.initThreeJs(); - // 初始化控制器(拖动和缩放) - this.initControls(); - // 初始化点云 - this.initPointCloud(); - // 初始化小车模型 - this.initCar(); - // 启动动画循环 - this.startAnimationLoop(); - // 监听窗口大小变化 - window.addEventListener('resize', this.handleResize); - // 初始加载完成 - setTimeout(() => { - this.isLoading = false; - }, 1000); + mounted() { + this.initializeComponent() }, - beforeDestroy () { - // 先标记组件已销毁,阻止新消息处理 - this.isDestroyed = true; - // 1. 清理WebSocket(先移除事件监听,再关闭连接) - this.closeWebSocket() - // 2. 停止动画循环并清理 - if (this.animationId) { - cancelAnimationFrame(this.animationId); - this.animationId = null; - } - // 3. 清理OrbitControls(关键!控制器有内部事件监听) - if (this.controls) { - this.controls.dispose(); // 控制器自带的清理方法,移除事件监听 - this.controls = null; - } - // 4. 清理Three.js场景对象(先从场景移除,再释放资源) - if (this.scene) { - // 移除点云网格 - if (this.pointCloudMesh) { - this.scene.remove(this.pointCloudMesh); - } - // 移除小车模型 - if (this.carMesh) { - this.scene.remove(this.carMesh); - } - // 清空场景(彻底释放所有子对象引用) - this.scene.clear(); - this.scene = null; - } - // 5. 释放Three.js渲染器及DOM - if (this.renderer) { - // 从DOM中移除canvas元素(避免残留DOM节点) - const container = this.$refs.canvasContainer; - if (container && this.renderer.domElement) { - container.removeChild(this.renderer.domElement); - } - // 释放渲染器资源 - this.renderer.dispose(); - this.renderer.forceContextLoss(); // 强制释放WebGL上下文(关键!) - this.renderer = null; - } - // 6. 释放几何体和材质(确保先移除引用) - if (this.pointCloudGeometry) { - this.pointCloudGeometry.dispose(); - this.pointCloudGeometry = null; - } - if (this.pointCloudMaterial) { - // 先清除材质的纹理引用(避免纹理无法释放) - this.pointCloudMaterial.map = null; - this.pointCloudMaterial.dispose(); - this.pointCloudMaterial = null; - } - // 7. 释放小车纹理(确保材质不再引用) - if (this.carTexture) { - this.carTexture.dispose(); - this.carTexture = null; - } - // 8. 移除窗口大小变化监听(避免重复监听) - window.removeEventListener('resize', this.handleResize); - // 9. 清空其他引用(帮助GC回收) - this.camera = null; - this.carMesh = null; - this.pointCloudMesh = null; + beforeDestroy() { + this.cleanup() }, methods: { + /** + * 初始化组件 + */ + initializeComponent() { + this.initThreeJs() + this.initControls() + this.initPointCloud() + this.initCar() + this.startAnimationLoop() + window.addEventListener('resize', this.handleResize) + + // 初始加载完成 + setTimeout(() => { + this.isLoading = false + }, 1000) + }, + /** * 初始化Three.js场景、相机和渲染器 */ - initThreeJs() { - const container = this.$refs.canvasContainer; + initThreeJs() { + const container = this.$refs.canvasContainer const aspect = container.clientWidth / container.clientHeight // 创建场景 - this.scene = new THREE.Scene(); + this.scene = new THREE.Scene() // 创建正交相机(适合2D场景) this.camera = new THREE.OrthographicCamera( @@ -169,43 +123,42 @@ export default { -this.viewSize / 2, // bottom 0.1, // near 1000 // far - ); - this.camera.position.z = 100; // 保持在Z轴上方,不影响2D视图 + ) + this.camera.position.z = 100 // 保持在Z轴上方,不影响2D视图 // 创建渲染器 - 启用alpha通道实现透明背景 this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, // 关键:启用alpha通道 transparent: true // 关键:允许透明 - }); - this.renderer.setSize(container.clientWidth, container.clientHeight); - this.renderer.setPixelRatio(window.devicePixelRatio); + }) + this.renderer.setSize(container.clientWidth, container.clientHeight) + this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // 限制最大像素比 // 可选:设置clearAlpha确保完全透明 - this.renderer.setClearAlpha(0); - container.appendChild(this.renderer.domElement); + this.renderer.setClearAlpha(0) + container.appendChild(this.renderer.domElement) }, /** * 初始化控制器,支持拖动和缩放 */ initControls() { - this.controls = new OrbitControls(this.camera, this.renderer.domElement); - this.controls.enableRotate = false; // 禁用旋转,保持2D视图 - this.controls.enableZoom = true; // 启用缩放 - this.controls.enablePan = true; // 启用平移(拖动) - this.controls.screenSpacePanning = true; // 2D平移模式 - this.controls.touchZoomSpeed = 0.5; - this.controls.panSpeed = 0.5; - + this.controls = new OrbitControls(this.camera, this.renderer.domElement) + this.controls.enableRotate = false // 禁用旋转,保持2D视图 + this.controls.enableZoom = true // 启用缩放 + this.controls.enablePan = true // 启用平移(拖动) + this.controls.screenSpacePanning = true // 2D平移模式 + this.controls.touchZoomSpeed = 1 + this.controls.panSpeed = 1 this.controls.touches = { ONE: THREE.TOUCH.PAN, // 单指拖动为平移 TWO: THREE.TOUCH.DOLLY_PAN // 双指拖动为缩放和平移 - }; + } // 添加错误处理逻辑 this.controls.addEventListener('error', (event) => { - console.error('OrbitControls error:', event); - }); + console.error('OrbitControls error:', event) + }) }, /** @@ -213,7 +166,7 @@ export default { */ initPointCloud() { // 使用BufferGeometry提高大量点的渲染性能 - this.pointCloudGeometry = new THREE.BufferGeometry(); + this.pointCloudGeometry = new THREE.BufferGeometry() // 点材质设置 this.pointCloudMaterial = new THREE.PointsMaterial({ @@ -222,16 +175,16 @@ export default { sizeAttenuation: true, // 关键! transparent: true, opacity: 1 - }); + }) // 创建点云网格 this.pointCloudMesh = new THREE.Points( this.pointCloudGeometry, this.pointCloudMaterial - ); - this.scene.add(this.pointCloudMesh); + ) + this.scene.add(this.pointCloudMesh) // 初始化历史数据为空 - this.allPoints = []; + this.allPoints = [] }, /** @@ -239,47 +192,47 @@ export default { */ initCar() { // 加载小车图片作为精灵 - const loader = new THREE.TextureLoader(); + const loader = new THREE.TextureLoader() this.carTexture = loader.load(this.vehicleImage, (texture) => { const material = new THREE.SpriteMaterial({ map: texture, transparent: true, // 保留图片透明区域 opacity: 1, depthWrite: false // 避免被点云遮挡(可选,根据层级需求调整) - }); + }) - this.carMesh = new THREE.Sprite(material); - this.scene.add(this.carMesh); + this.carMesh = new THREE.Sprite(material) + this.scene.add(this.carMesh) // 初始计算小车尺寸 - this.calculateCarSize(); + this.calculateCarSize() // 初始位置角度更新 if (this.carPosition) { - this.updateCarPosition(this.carPosition); + this.updateCarPosition(this.carPosition) } }, undefined, (error) => { - console.error('小车图片加载失败:', error); - }); + console.error('小车图片加载失败:', error) + }) }, /** - * 单独的小车尺寸计算方法,确保窗口缩放时可复用 - */ + * 单独的小车尺寸计算方法,确保窗口缩放时可复用 + */ calculateCarSize() { - if (!this.carMesh) return; + if (!this.carMesh) return - const container = this.$refs.canvasContainer; - const canvasWidth = container.clientWidth; - const canvasHeight = container.clientHeight; - const aspect = canvasWidth / canvasHeight; + const container = this.$refs.canvasContainer + const canvasWidth = container.clientWidth + const canvasHeight = container.clientHeight + const aspect = canvasWidth / canvasHeight // 核心计算:将50px转换为Three.js世界坐标 // 公式:(目标像素尺寸 / 画布尺寸) * 相机可视范围 * 校正系数 - const spriteSizeX = (50 / canvasWidth) * this.viewSize * aspect; - const spriteSizeY = (50 / canvasHeight) * this.viewSize; + const spriteSizeX = (50 / canvasWidth) * this.viewSize * aspect + const spriteSizeY = (50 / canvasHeight) * this.viewSize - this.carMesh.scale.set(spriteSizeX, spriteSizeY, 1); + this.carMesh.scale.set(spriteSizeX, spriteSizeY, 1) }, /** @@ -287,11 +240,11 @@ export default { */ updateCarPosition(position) { if (this.carMesh && position) { - this.carMesh.position.x = position.x * this.pointScale; - this.carMesh.position.y = position.y * this.pointScale; - this.carMesh.position.z = 1; + this.carMesh.position.x = position.x * this.pointScale + this.carMesh.position.y = position.y * this.pointScale + this.carMesh.position.z = 1 // 转换角度为弧度并调整方向(Three.js使用弧度) - this.carMesh.rotation.z = position.angle; + this.carMesh.rotation.z = position.angle } }, @@ -301,237 +254,325 @@ export default { initWebSocket() { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) const wsHost = this.serverUrl.replace(/^https?:\/\//, '') - this.socket = new WebSocket(`ws://${wsHost}/webSocket/PointCloudData/${this.userRole}`); + this.socket = new WebSocket(`ws://${wsHost}/webSocket/PointCloudData/${this.userRole}`) this.socket.onopen = () => { - console.log('WebSocket连接已建立'); - this.isLoading = false; - }; + console.log('WebSocket连接已建立') + this.isLoading = false + } this.socket.onmessage = (event) => { // 组件已销毁则直接返回,不处理消息 if (this.isDestroyed) return try { // 限制更新频率,优化性能 - const now = Date.now(); + const now = Date.now() if (now - this.lastUpdateTime < this.updateInterval) { - return; + return } - this.lastUpdateTime = now; + this.lastUpdateTime = now if (this.reconnectTimer) clearTimeout(this.reconnectTimer) - const pointData = JSON.parse(event.data); - this.updatePointCloud(pointData.data); + const pointData = JSON.parse(event.data) + this.updatePointCloud(pointData.data) } catch (error) { - console.error('解析点云数据失败:', error); + console.error('解析点云数据失败:', error) } - }; + } this.socket.onclose = (event) => { - console.log('WebSocket连接已关闭,代码:', event.code); - this.isLoading = true; + console.log('WebSocket连接已关闭,代码:', event.code) + this.isLoading = true // 自动重连 - this.reconnectTimer = setTimeout(() => this.initWebSocket(), 3000); - }; + this.reconnectTimer = setTimeout(() => this.initWebSocket(), 3000) + } this.socket.onerror = (error) => { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) - console.error('WebSocket错误:', error); - this.isLoading = true; - }; + console.error('WebSocket错误:', error) + this.isLoading = true + } }, - closeWebSocket () { + closeWebSocket() { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) if (this.socket) { - this.socket.onopen = null; - this.socket.onmessage = null; - this.socket.onclose = null; - this.socket.onerror = null; - this.socket.close(1000, '组件销毁'); - this.socket = null; + this.socket.onopen = null + this.socket.onmessage = null + this.socket.onclose = null + this.socket.onerror = null + this.socket.close(1000, '组件销毁') + this.socket = null } this.allPoints = [] }, - init () { - // 初始化WebSocket连接 - this.initWebSocket(); + init() { + this.initWebSocket() // const pointData = points.data // this.updatePointCloud(pointData); // setTimeout(() => { // const arr = points1.data // this.updatePointCloud(arr); - // }, 5000) + // }, 1000) // setTimeout(() => { // const arr = points2.data // this.updatePointCloud(arr); - // }, 10000) + // }, 2000) }, /** * 更新点云数据,优化大量点的渲染性能 */ updatePointCloud(points) { - if (!Array.isArray(points) || points.length === 0) return; - - // 用于跟踪已存在的点,使用"x,y"作为唯一标识 - const existingPoints = {}; - // 先将已有点添加到跟踪对象 - this.allPoints.forEach(point => { - const key = `${point.x},${point.y}`; - existingPoints[key] = true; - }); + if (!Array.isArray(points) || points.length === 0) return + // 使用Set数据结构提高查找性能 + const existingPoints = new Set(this.allPoints.map(p => `${p.x},${p.y}`)) + // 过滤新点中的重复点 const newUniquePoints = points.filter(point => { - const key = `${point.x},${point.y}`; - if (!existingPoints[key]) { - existingPoints[key] = true; - return true; - } - return false; - }); + const key = `${point.x},${point.y}` + return !existingPoints.has(key) + }) - this.allPoints = [...this.allPoints, ...newUniquePoints]; - this.pointCount = this.allPoints.length; // 更新总点数 + // 如果没有新点,直接返回 + if (newUniquePoints.length === 0) return + + this.allPoints = [...this.allPoints, ...newUniquePoints] + this.pointCount = this.allPoints.length // 更新总点数 - // 重新计算所有点云的边界 - this.pointBounds = { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }; - this.allPoints.forEach(point => { - const scaledX = (point.x || 0) * this.pointScale; // 缩放后的实际坐标 - const scaledY = (point.y || 0) * this.pointScale; + // 批量更新边界计算 + this.updatePointBounds(newUniquePoints) + + // 更新点云几何数据 + this.updatePointCloudGeometry(newUniquePoints) + }, + + /** + * 批量更新点云边界 + */ + updatePointBounds(newPoints) { + // 批量处理新点,减少循环次数 + for (const point of newPoints) { + const scaledX = (point.x || 0) * this.pointScale + const scaledY = (point.y || 0) * this.pointScale + // 更新最大/最小值 - this.pointBounds.minX = Math.min(this.pointBounds.minX, scaledX); - this.pointBounds.maxX = Math.max(this.pointBounds.maxX, scaledX); - this.pointBounds.minY = Math.min(this.pointBounds.minY, scaledY); - this.pointBounds.maxY = Math.max(this.pointBounds.maxY, scaledY); - }); - this.adjustCameraByBounds(); - - // 动态扩展缓冲区 - const positionAttribute = this.pointCloudGeometry.getAttribute('position'); - let positions; + if (scaledX < this.pointBounds.minX) this.pointBounds.minX = scaledX + if (scaledX > this.pointBounds.maxX) this.pointBounds.maxX = scaledX + if (scaledY < this.pointBounds.minY) this.pointBounds.minY = scaledY + if (scaledY > this.pointBounds.maxY) this.pointBounds.maxY = scaledY + } + + this.adjustCameraByBounds() + }, + + /** + * 更新点云几何数据 + */ + updatePointCloudGeometry(newUniquePoints) { + const positionAttribute = this.pointCloudGeometry.getAttribute('position') + let positions + const newPointsCount = newUniquePoints.length if (!positionAttribute) { // 首次初始化:创建缓冲区 - positions = new Float32Array(this.pointCount * 3); + positions = new Float32Array(this.pointCount * 3) this.pointCloudGeometry.setAttribute( 'position', new THREE.BufferAttribute(positions, 3) - ); + ) } else { // 后续更新:扩展现有缓冲区(避免重建) - positions = positionAttribute.array; - const newLength = this.pointCount * 3; + positions = positionAttribute.array + const newLength = this.pointCount * 3 + if (newLength > positions.length) { // 扩展时多预留20%空间,减少频繁扩展 - const newPositions = new Float32Array(Math.ceil(newLength * 1.2)); - newPositions.set(positions); // 复制原有数据 - positions = newPositions; + const newPositions = new Float32Array(Math.ceil(newLength * 1.2)) + newPositions.set(positions) // 复制原有数据 + positions = newPositions this.pointCloudGeometry.setAttribute( 'position', new THREE.BufferAttribute(positions, 3) - ); + ) } } // 填充新数据(仅更新新增部分,减少重复计算) - const startIndex = (this.pointCount - newUniquePoints.length) * 3; // 从新增点开始 - for (let i = 0; i < newUniquePoints.length; i++) { - const point = newUniquePoints[i]; - const index = startIndex + i * 3; - positions[index] = (point.x || 0) * this.pointScale; - positions[index + 1] = (point.y || 0) * this.pointScale; - positions[index + 2] = 0; + const startIndex = (this.pointCount - newPointsCount) * 3 + + // 使用for循环而不是forEach,性能更好 + for (let i = 0; i < newPointsCount; i++) { + const point = newUniquePoints[i] + const index = startIndex + i * 3 + positions[index] = (point.x || 0) * this.pointScale + positions[index + 1] = (point.y || 0) * this.pointScale + positions[index + 2] = 0 } // 标记更新 - this.pointCloudGeometry.getAttribute('position').needsUpdate = true; + this.pointCloudGeometry.getAttribute('position').needsUpdate = true }, /** * 根据边界动态调整相机可视范围 */ adjustCameraByBounds() { - const container = this.$refs.canvasContainer; - const aspect = container.clientWidth / container.clientHeight; // 画布宽高比 + const container = this.$refs.canvasContainer + const aspect = container.clientWidth / container.clientHeight // 画布宽高比 // 1. 计算点云的实际范围(宽和高) - const pointRangeX = this.pointBounds.maxX - this.pointBounds.minX; - const pointRangeY = this.pointBounds.maxY - this.pointBounds.minY; + const pointRangeX = this.pointBounds.maxX - this.pointBounds.minX + const pointRangeY = this.pointBounds.maxY - this.pointBounds.minY - // 2. 处理特殊情况:无点云或单点时,用默认viewSize - if (this.allPoints.length === 0 || (pointRangeX === 0 && pointRangeY === 0)) { - this.viewSize = this.defaultViewSize; - } else { - // 计算需要的可视范围:覆盖点云范围 + 10%边距 - const requiredViewSizeX = pointRangeX * (1 + this.boundMargin); - const requiredViewSizeY = pointRangeY * (1 + this.boundMargin); - // 取X/Y方向中较大的作为viewSize,确保宽高比适配后能覆盖所有点 - this.viewSize = Math.max(requiredViewSizeX / aspect, requiredViewSizeY); + // 2. 计算"基准可视范围" + let baseViewSize = DEFAULT_VIEW_SIZE // 默认值(无点云时) + if (this.allPoints.length > 0 && !(pointRangeX === 0 && pointRangeY === 0)) { + const requiredViewSizeX = pointRangeX * (1 + BOUND_MARGIN) // X方向需覆盖范围 + const requiredViewSizeY = pointRangeY * (1 + BOUND_MARGIN) // Y方向需覆盖范围 + // 基准可视范围:取X/Y方向中较大值(确保适配宽高比后覆盖所有点) + baseViewSize = Math.max(requiredViewSizeX / aspect, requiredViewSizeY) } + this.baseViewSize = baseViewSize // 缓存基准值,用于后续缩放限制 - // 3. 更新正交相机的可视范围(left/right/top/bottom) - this.camera.left = -this.viewSize * aspect / 2; - this.camera.right = this.viewSize * aspect / 2; - this.camera.top = this.viewSize / 2; - this.camera.bottom = -this.viewSize / 2; - this.camera.updateProjectionMatrix(); // 必须更新投影矩阵,否则不生效 + // 3. 更新相机可视范围(基于基准值) + this.viewSize = baseViewSize // 初始可视范围=基准范围 + this.camera.left = -this.viewSize * aspect / 2 + this.camera.right = this.viewSize * aspect / 2 + this.camera.top = this.viewSize / 2 + this.camera.bottom = -this.viewSize / 2 + this.camera.updateProjectionMatrix() // 必须更新投影矩阵,否则不生效 - // 4. 让相机焦点对准点云中心(可选,确保点云在屏幕中间) - const centerX = (this.pointBounds.minX + this.pointBounds.maxX) / 2; - const centerY = (this.pointBounds.minY + this.pointBounds.maxY) / 2; - this.controls.target.set(centerX, centerY, 0); // 控制器目标对准点云中心 - this.camera.position.set(centerX, centerY, 100); // 相机位置也对准中心(Z轴不变) + // 4. 让相机焦点对准点云中心 + const centerX = (this.pointBounds.minX + this.pointBounds.maxX) / 2 + const centerY = (this.pointBounds.minY + this.pointBounds.maxY) / 2 + this.controls.target.set(centerX, centerY, 0) // 控制器目标对准点云中心 + this.camera.position.set(centerX, centerY, 100) // 相机位置也对准中心(Z轴不变) - // 5. 同步更新小车尺寸(确保小车仍为50px,因viewSize变化了) - if (this.carMesh) { - const width = container.clientWidth; - const height = container.clientHeight; - const spriteSizeX = (50 / width) * this.viewSize * aspect; - const spriteSizeY = (50 / height) * this.viewSize; - this.carMesh.scale.set(spriteSizeX, spriteSizeY, 1); + // 5. 关键:设置OrbitControls缩放限制(正交相机专用) + if (this.controls) { + this.controls.minZoom = MIN_ZOOM_RATIO + this.controls.maxZoom = MAX_ZOOM_RATIO + this.controls.minDistance = 50 + this.controls.maxDistance = 200 + this.controls.zoomToCursor = false // 关闭"鼠标位置缩放",避免缩放偏移 } // 相机参数更新后,同步更新小车尺寸 - this.calculateCarSize(); + this.calculateCarSize() }, /** * 处理窗口大小变化 */ handleResize() { - const container = this.$refs.canvasContainer; - const width = container.clientWidth; - const height = container.clientHeight; + const container = this.$refs.canvasContainer + const width = container.clientWidth + const height = container.clientHeight // 原有渲染器尺寸更新 - this.renderer.setSize(width, height); + this.renderer.setSize(width, height) // 基于当前点云边界重新适配相机(关键) - this.adjustCameraByBounds(); + this.adjustCameraByBounds() // 窗口缩放时重新计算小车尺寸 - this.calculateCarSize(); + this.calculateCarSize() }, /** * 启动动画循环 */ - startAnimationLoop() { + startAnimationLoop() { const animate = () => { - this.animationId = requestAnimationFrame(animate); + this.animationId = requestAnimationFrame(animate) // 更新控制器(用于阻尼效果) if (this.controls) { - this.controls.update(); + this.controls.update() } // 渲染场景 - this.renderer.render(this.scene, this.camera); - }; + this.renderer.render(this.scene, this.camera) + } - animate(); + animate() + }, + + /** + * 清理资源 + */ + cleanup() { + // 先标记组件已销毁,阻止新消息处理 + this.isDestroyed = true + + // 1. 清理WebSocket + this.closeWebSocket() + + // 2. 停止动画循环 + if (this.animationId) { + cancelAnimationFrame(this.animationId) + this.animationId = null + } + + // 3. 清理OrbitControls + if (this.controls) { + this.controls.dispose() + this.controls = null + } + + // 4. 清理Three.js场景对象 + if (this.scene) { + // 移除点云网格 + if (this.pointCloudMesh) { + this.scene.remove(this.pointCloudMesh) + } + // 移除小车模型 + if (this.carMesh) { + this.scene.remove(this.carMesh) + } + // 清空场景 + this.scene.clear() + this.scene = null + } + + // 5. 释放Three.js渲染器及DOM + if (this.renderer) { + const container = this.$refs.canvasContainer + if (container && this.renderer.domElement) { + container.removeChild(this.renderer.domElement) + } + this.renderer.dispose() + this.renderer.forceContextLoss() + this.renderer = null + } + + // 6. 释放几何体和材质 + if (this.pointCloudGeometry) { + this.pointCloudGeometry.dispose() + this.pointCloudGeometry = null + } + + if (this.pointCloudMaterial) { + this.pointCloudMaterial.map = null + this.pointCloudMaterial.dispose() + this.pointCloudMaterial = null + } + + // 7. 释放小车纹理 + if (this.carTexture) { + this.carTexture.dispose() + this.carTexture = null + } + + // 8. 移除窗口大小变化监听 + window.removeEventListener('resize', this.handleResize) + + // 9. 清空其他引用 + this.camera = null + this.carMesh = null + this.pointCloudMesh = null } } } @@ -545,25 +586,25 @@ export default { background-color rgba(4, 33, 58, 70%) box-shadow inset 1px 1px 7px 2px #4d9bcd overflow hidden -.canvas-container { - width: 100%; - height: 100%; - position: relative; - z-index: 0; -} -.loading-indicator { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: rgba(255, 255, 255, 0.8); - padding: .01rem .02rem; - border-radius: 4px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); - font-size: .12rem; - color: #333; - z-index: 100; - display: flex; - align-items: center; -} + +.canvas-container + width: 100% + height: 100% + position: relative + z-index: 0 + +.loading-indicator + position: absolute + top: 50% + left: 50% + transform: translate(-50%, -50%) + background-color: rgba(255, 255, 255, 0.8) + padding: .01rem .02rem + border-radius: 4px + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1) + font-size: .12rem + color: #333 + z-index: 100 + display: flex + align-items: center \ No newline at end of file diff --git a/src/pages/modules/home.vue b/src/pages/modules/home.vue index 1b29fd4..025204f 100644 --- a/src/pages/modules/home.vue +++ b/src/pages/modules/home.vue @@ -22,10 +22,10 @@ export default { ...mapGetters(['userRole']), nav () { return [ - { title: '操作', zh_title: '操作', en_title: 'Operation', router: '/index/device', isVisible: true }, - { title: '建图', zh_title: '建图', en_title: 'Map building', router: '/index/building', isVisible: this.userRole === 1 }, + { title: '操作', zh_title: '操作', en_title: 'Operate', router: '/index/device', isVisible: true }, + { title: '建图', zh_title: '建图', en_title: 'Map - building', router: '/index/building', isVisible: this.userRole === 1 }, { title: '地图', zh_title: '地图', en_title: 'Map', router: '/index/map', isVisible: true }, - { title: '重定位', zh_title: '重定位', en_title: 'Relocation', router: '/index/relocation', isVisible: true } + { title: '重定位', zh_title: '重定位', en_title: 'Relocate', router: '/index/relocation', isVisible: true } ] }, visibleNav () { @@ -35,16 +35,16 @@ export default { methods: { toPage (e) { if (e.router === '/index/building') { - this.$confirm('是否开始新建地图,地图会进行覆盖, 是否继续?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', + this.$confirm(this.$t('Donewmap'), this.$t('Prompt'), { + confirmButtonText: this.$t('Confirm'), + cancelButtonText: this.$t('Cancel'), type: 'warning' }).then(() => { this.$router.push(e.router) }).catch(() => { this.$message({ type: 'info', - message: '已取消建图' + message: this.$t('Mapbuildingcancel') }) }) return diff --git a/src/pages/modules/login/login.vue b/src/pages/modules/login/login.vue index 74f4f5c..b087d55 100644 --- a/src/pages/modules/login/login.vue +++ b/src/pages/modules/login/login.vue @@ -1,4 +1,4 @@ - diff --git a/src/pages/shells/config-modal.vue b/src/pages/shells/config-modal.vue index e959923..528e807 100644 --- a/src/pages/shells/config-modal.vue +++ b/src/pages/shells/config-modal.vue @@ -1,12 +1,13 @@ @@ -66,7 +66,7 @@ export default { }, dataRule: { serverIp: [ - { required: true, message: '服务IP不能为空', trigger: 'blur' } + { required: true, message: this.$t('ServiceIPnotempty'), trigger: 'blur' } ], ip: [ { required: true, message: this.$t('Schedulingnotempty'), trigger: 'blur' } @@ -79,14 +79,7 @@ export default { {label: '中文', value: 'zh-cn'}, {label: 'English', value: 'en-us'} // {label: 'Español', value: 'es'} - ], - visible: false, - layout: 'normal', - input: null, - options: { - useKbEvents: false, - preventClickEvent: false - } + ] } }, computed: { @@ -112,23 +105,21 @@ export default { }, exitUser () { this.dialogVisible = false - this.visible = false }, dataFormSubmit () { this.dialogVisible = false - this.visible = false this.$i18n.locale = this.dataForm.selectedLanguage window.localStorage.setItem('locale', this.dataForm.selectedLanguage) this.setServerUrl(this.dataForm.serverIp) + this.$emit('refreshWebsocket') }, handleClose (done) { - this.visible = false done() }, toReboot () { - this.$confirm('是否确定关机重启?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', + this.$confirm('是否确定关机重启?', this.$t('Prompt'), { + confirmButtonText: this.$t('Confirm'), + cancelButtonText: this.$t('Cancel'), type: 'warning' }).then(() => { this._rebootVehicle() @@ -164,9 +155,9 @@ export default { a.click() }, synchronizedMapConfirm () { - this.$confirm('是否确定同步地图?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', + this.$confirm('是否确定同步地图?', this.$t('Prompt'), { + confirmButtonText: this.$t('Confirm'), + cancelButtonText: this.$t('Cancel'), type: 'warning' }).then(() => { this._synchronizedMap() @@ -193,40 +184,6 @@ export default { this.$message.error(e) this.loading.close() } - }, - show (e) { - // 关闭中文keyboard - let arr = document.querySelectorAll('.hg-theme-default') - arr.forEach((ele) => { - ele.style.visibility = 'hidden' - }) - this.input = e.target - this.layout = e.target.dataset.layout - if (!this.visible) { - this.visible = true - } - }, - hide () { - this.visible = false - }, - accept () { - this.hide() - }, - next () { - let inputs = document.querySelectorAll('input') - let found = false; - [].forEach.call(inputs, (item, i) => { - if (!found && item === this.input && i < inputs.length - 1 && this.input.dataset.next === '1') { - found = true - this.$nextTick(() => { - inputs[i + 1].focus() - }) - } - }) - if (!found) { - this.input.blur() - this.hide() - } } } } @@ -248,4 +205,7 @@ export default { width .72rem height .71rem background center / 100% auto url(../../images/new/reset.png) no-repeat +.enClass + .el-tabs__item + font-size .2rem diff --git a/src/pages/shells/index.vue b/src/pages/shells/index.vue index 06f2782..719e2dc 100644 --- a/src/pages/shells/index.vue +++ b/src/pages/shells/index.vue @@ -6,8 +6,8 @@ -
-
{{ topInfo.isManual ? '手动模式' : '自动模式' }}
+
+
{{ topInfo.isManual ? $t('ManualMode') : $t('AutomaticMode')}}
{{ topInfo.state }}
@@ -37,14 +37,14 @@
-
+
+
{{ e }}
-
- +
@@ -80,8 +80,33 @@ export default { theta: 0.9073792099952698, // 车辆航向角 x: 0.004027, // 车辆x坐标 y: -0.001812, // 车辆y坐标 - task_seq: '工作点1-工作点2-工作点3', - task_point: '工作点2' + task_seq: '点位1-点位2-顶顶位3-point layout location-顶地方的方法顶顶顶点位3', + task_point: '点位2', + error_category: 2, + errorData: [ + { + "error_id": "1", + "error_name": "导航定位失败", + "zh_error_name": '错误1', + "en_error_name": 'error1', + "error_code": "1", + "error_category": 1, + "error_description": "出现了定位切换失败", + "zh_error_description": '说明', + "en_error_description": 'en_error_description', + "error_type": 1}, + { + "error_id": "2", + "error_name": "重定位超时", + "zh_error_name": null, + "en_error_name": null, + "error_code": "2", + "error_category": 1, + "error_description": "出现了重定位超时", + "zh_error_description": null, + "en_error_description": null, + "error_type": 1} + ] }, taskSeq: [], currentStep: null, @@ -101,9 +126,14 @@ export default { }, created () { // this.$store.dispatch('setAgvObj', this.topInfo) - // this.taskSeq = this.topInfo.task_seq.split('-') - // const target = this.topInfo.task_point - // this.currentStep = this.taskSeq.findIndex(item => item === target) + // if (this.topInfo.task_seq) { + // this.taskSeq = this.topInfo.task_seq.split('-') + // const target = this.topInfo.task_point + // this.currentStep = this.taskSeq.findIndex(item => item === target) + // } else { + // this.taskSeq = [] + // this.currentStep = null + // } this.initWebSocket() }, mounted () { @@ -116,6 +146,11 @@ export default { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) }, methods: { + refreshWebsocket () { + this.closeWebSocket() + if (this.reconnectTimer) clearTimeout(this.reconnectTimer) + this.initWebSocket() + }, // 滚动区域 checkTextOverflow () { const container = this.$refs.scrollContainer @@ -150,7 +185,8 @@ export default { initWebSocket () { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) const wsHost = this.serverUrl.replace(/^https?:\/\//, '') - const wsUrl = `ws://${wsHost}/webSocket/VehicleInfo/${this.userRole}` + const lang = this.$i18n.locale.slice(0, 2) + const wsUrl = `ws://${wsHost}/webSocket/VehicleInfo/${this.userRole}?lang=${lang}` this.closeWebSocket() this.websocket = new WebSocket(wsUrl) this.websocket.onopen = () => {} @@ -163,7 +199,7 @@ export default { } } this.websocket.onerror = (error) => { - this.$message.error('WebSocket连接错误:', error) + this.$message.error(this.$t('WebSocketerror') + ':', error) } this.websocket.onclose = () => { this.topInfo = {} @@ -194,7 +230,7 @@ export default { reconnectWebSocket () { if (this.reconnectTimer) clearTimeout(this.reconnectTimer) this.reconnectTimer = setTimeout(() => { - this.$message.error('尝试重新连接WebSocket...') + this.$message.error(this.$t('AttreconnectWebSocket')) this.initWebSocket() }, 5000) // 5秒后重连 } @@ -280,17 +316,24 @@ export default { height .5rem padding 0 .09rem margin-bottom .1rem + border 1px dashed #1e9fe7 .task_content height 100% display flex justify-content: center - border 1px dashed #1e9fe7 .step_item display flex align-items center + width calc((100% - 2rem) / 5 + .5rem) + .step_name + width calc(100% - .5rem) + &:first-child + width calc((100% - 2rem) / 5) + .step_name + width 100% .step_arrow position relative - width .7rem + width .5rem height 12px background-color #0091de overflow hidden @@ -301,28 +344,32 @@ export default { top: 50%; transform: translate(-50%, -50%); .step_name + height .3rem _font(.18rem, .3rem, #fff, ,center) padding 0 .06rem border 1px solid #00d0fc background-image linear-gradient(to bottom,rgba(42, 83, 138, 50%), rgba(57, 101, 181, 50%)) box-shadow inset 0 0px 3px 1px rgba(98, 180, 243, 50%) border-radius 2px + white-space nowrap + overflow hidden + text-overflow ellipsis +.step_actived + .step_name + background-image linear-gradient(to bottom,rgba(210, 210, 227, 60%), rgba(210, 210, 227, 30%), rgba(210, 210, 227, 60%)) + box-shadow inset 0 0px 3px 1px rgba(210, 210, 227, 50%) + border-color #d2d2e3 + .step_arrow + background-color rgba(210, 210, 227, 50%) .step_active .step_name - background-image linear-gradient(to bottom,rgba(251, 143, 0, 30%), rgba(251, 143, 0, 60%)) + background-image linear-gradient(to bottom, #fb8f00, rgba(251,143,0,0.3), #fb8f00) box-shadow inset 0 0px 3px 1px rgba(251, 143, 0, 50%) border-color #fb8f00 .step_arrow img left 0 animation: moveRight 3s linear infinite; -.step_actived - .step_name - background-image linear-gradient(to bottom,rgba(210, 210, 227, 30%), rgba(210, 210, 227, 60%)) - box-shadow inset 0 0px 3px 1px rgba(210, 210, 227, 50%) - border-color #d2d2e3 - .step_arrow - background-color rgba(210, 210, 227, 50%) @keyframes moveRight { from { left: 0; @@ -347,4 +394,8 @@ export default { _wh(.36rem, .32rem) margin 0 .08rem 0 0 background center / 100% auto url(../../images/new/warn_green.png) no-repeat +.warn_image_1 + background-image url(../../images/new/warn_yellow.png) +.warn_image_2 + background-image url(../../images/new/warn_red.png) diff --git a/src/pages/shells/login-modal.vue b/src/pages/shells/login-modal.vue index 213cc64..889b5ab 100644 --- a/src/pages/shells/login-modal.vue +++ b/src/pages/shells/login-modal.vue @@ -4,12 +4,12 @@ :visible.sync="dialogVisible" width="55%" :before-close="handleClose"> -

登录成功

+

{{$t('Loginsuccessful')}}

{{$t('Notloggedinyet')}}

- +
@@ -17,7 +17,6 @@ - @@ -36,14 +35,7 @@ export default { { required: true, message: this.$t('Passwordnotempty'), trigger: 'blur' } ] }, - passwords: [], // 存储文件中的密码 - visible: false, - layout: 'normal', - input: null, - options: { - useKbEvents: false, - preventClickEvent: false - } + passwords: [] // 存储文件中的密码 } }, computed: { @@ -56,12 +48,10 @@ export default { }, exitUser () { this.dialogVisible = false - this.visible = false this.setUserRole(2) }, dataFormSubmit () { this.dialogVisible = false - this.visible = false if (this.userRole === 1) { return } @@ -77,42 +67,7 @@ export default { this.dataForm = {password: ''} }, handleClose (done) { - this.visible = false done() - }, - show (e) { - // 关闭中文keyboard - let arr = document.querySelectorAll('.hg-theme-default') - arr.forEach((ele) => { - ele.style.visibility = 'hidden' - }) - this.input = e.target - this.layout = e.target.dataset.layout - if (!this.visible) { - this.visible = true - } - }, - hide () { - this.visible = false - }, - accept () { - this.hide() - }, - next () { - let inputs = document.querySelectorAll('input') - let found = false; - [].forEach.call(inputs, (item, i) => { - if (!found && item === this.input && i < inputs.length - 1 && this.input.dataset.next === '1') { - found = true - this.$nextTick(() => { - inputs[i + 1].focus() - }) - } - }) - if (!found) { - this.input.blur() - this.hide() - } } } } diff --git a/src/style/common.styl b/src/style/common.styl index c5c20ff..fd62247 100644 --- a/src/style/common.styl +++ b/src/style/common.styl @@ -86,12 +86,25 @@ padding-bottom 0 height .32rem line-height .32rem +.el-button--text:focus, .el-button--text:hover + color #fff + border-color #ed865c + background-color #ed865c .el-dialog background center / 100% 100% url(../images/new/modal_bg.png) no-repeat .el-dialog__header padding .08rem .2rem .1rem -.warn_modal .el-dialog__header - padding .08rem .3rem .2rem .3rem +.warn_modal + .el-dialog + height 70vh + overflow hidden + .el-dialog__header + padding .08rem .3rem 0 .3rem + .el-dialog__body + height calc(100% - .32rem) + padding: 0.1rem 0.2rem 0.1rem 0.3rem; + .el-tabs + height calc(100% - 0.3rem) .el-dialog__headerbtn top .08rem right .2rem @@ -165,6 +178,8 @@ background: rgba(31,46,73,0.5); .el-table--enable-row-hover .el-table__body tr:hover>td background: rgba(63,106,202,0.3); +.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell + background-color: #3F6ACA; .el-tabs__header margin 0 0 .1rem .el-tabs__nav-wrap::after @@ -210,6 +225,9 @@ font-size .2rem .el-message-box__content font-size .2rem +.el-message-box__message p + font-size .2rem + line-height: .28rem .el-progress-bar__inner,.el-progress-bar__outer border-radius 0 .el-progress__text @@ -218,6 +236,9 @@ padding 0 0.2rem 0.2rem 0.2rem .el-form-item__error font-size .16rem +.el-table__empty-text + font-size .2rem + color #fff // driver .driver-popover.driverjs-theme { @@ -248,6 +269,9 @@ padding: 5px 8px; border-radius: 6px; } +.driver-popover.driverjs-theme button+button { + margin-left: 14px; +} .driver-popover.driverjs-theme button:hover { background-color: #000; @@ -289,6 +313,13 @@ .driver-popover.driverjs-theme .driver-popover-arrow-side-bottom.driver-popover-arrow { border-bottom-color: #fde047; } +.driver-popover-footer .driver-popover-next-btn { + animation: scale 1s infinite; +} +@keyframes scale { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } +} // button .button_control @@ -325,11 +356,18 @@ top 0 right 0 .zoom_data - width .6rem + width .7rem font-size .16rem - height .32rem - line-height .32rem + height .44rem + line-height .44rem color #00d9f3 text-align center - border-top 1px solid #009fde - border-bottom 1px solid #009fde + border-left 1px solid #009fde + border-right 1px solid #009fde + box-sizing border-box +.zoom_btn + display block + width .7rem + margin 0 !important + height .44rem + line-height .44rem diff --git a/src/vuex/modules/data.js b/src/vuex/modules/data.js index 7c46c8b..1cb6679 100644 --- a/src/vuex/modules/data.js +++ b/src/vuex/modules/data.js @@ -2,12 +2,14 @@ import * as types from '../types' const state = { carPosition: {x: '', y: '', angle: ''}, - isTop: false + isTop: false, + errorData: [] } const getters = { carPosition: state => state.carPosition, - isTop: state => state.isTop + isTop: state => state.isTop, + errorData: state => state.errorData } const actions = { @@ -27,6 +29,9 @@ const mutations = { state.isTop = data.exceptionInfo.exception.length > 0 ? true : false } } + if (Object.prototype.hasOwnProperty.call(data, 'errorData')) { + state.errorData = data.errorData + } } } diff --git a/yarn.lock b/yarn.lock index 6da4f38..cd34523 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2003,7 +2003,7 @@ babel-plugin-polyfill-regenerator@^0.6.5: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.5" -babel-runtime@6.x, babel-runtime@^6.26.0: +babel-runtime@6.x: version "6.26.0" resolved "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== @@ -6184,13 +6184,6 @@ vue-template-es2015-compiler@^1.9.0: resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== -vue-touch-keyboard@^0.3.2: - version "0.3.2" - resolved "https://registry.npmmirror.com/vue-touch-keyboard/-/vue-touch-keyboard-0.3.2.tgz#b005cddf1fc2f0f7f0137cc3201a998a4ba2a749" - integrity sha512-+GAZSMdiPfop2At9fhGLzs2Jodd6BQla4q4Mm+VkpQLUaxmFFU64NWsjxvkkYjncAgbv/oO5wi+rM1X4eVHCbg== - dependencies: - babel-runtime "^6.26.0" - vue@^2.6.14: version "2.7.16" resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"